]> git.ipfire.org Git - thirdparty/pdns.git/blobdiff - regression-tests.recursor-dnssec/test_Lua.py
Add test for RD=0 is disallowed by default and basic RD=1 processing
[thirdparty/pdns.git] / regression-tests.recursor-dnssec / test_Lua.py
index 6053fbb9938b36cb38b2ad4e9dc6205bfbe978d7..dad74051f5d687abc1ccdbefa5e2be0fc2bab903 100644 (file)
@@ -99,6 +99,13 @@ class GettagRecursorTest(RecursorTest):
         return true
       end
 
+      local tm = os.time()
+      if dq.queryTime.tv_sec < tm - 1 or dq.queryTime.tv_sec > tm + 1 then
+        pdnslog("queryTime is wrong")
+        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
@@ -233,6 +240,10 @@ class UDPHooksResponder(DatagramProtocol):
             answer = dns.rrset.from_text('postresolve.luahooks.example.', 3600, dns.rdataclass.IN, 'A', '192.0.2.1')
             response.answer.append(answer)
 
+        elif request.question[0].name == dns.name.from_text('preresolve.luahooks.example.'):
+            answer = dns.rrset.from_text('preresolve.luahooks.example.', 3600, dns.rdataclass.IN, 'A', '192.0.2.2')
+            response.answer.append(answer)
+
         self.transport.write(response.to_wire(), address)
 
 class LuaHooksRecursorTest(RecursorTest):
@@ -256,6 +267,29 @@ quiet=no
       return true
     end
 
+    function preresolve(dq)
+      -- test both preresolve hooking and udpQueryResponse
+      if dq.qtype == pdns.A and dq.qname == newDN("preresolve.luahooks.example.") then
+        dq.followupFunction = "udpQueryResponse"
+        dq.udpCallback = "gotUdpResponse"
+        dq.udpQueryDest = newCA("%s.23:53")
+        -- synthesize query, responder should answer with regular answer
+        dq.udpQuery = "\\254\\255\\001\\000\\000\\001\\000\\000\\000\\000\\000\\000\\npreresolve\\008luahooks\\007example\\000\\000\\001\\000\\001"
+        return true
+      end
+      return false
+    end
+
+    function gotUdpResponse(dq)
+      dq.followupFunction = ""
+      if string.len(dq.udpAnswer) == 61 then
+        dq:addAnswer(pdns.A, "192.0.2.2")
+        return true;
+      end
+      dq:addAnswer(pdns.A, "0.0.0.0")
+      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")
@@ -300,7 +334,7 @@ quiet=no
       return false
     end
 
-    """ % (os.environ['PREFIX'], os.environ['PREFIX'])
+    """ % (os.environ['PREFIX'], os.environ['PREFIX'], os.environ['PREFIX'])
 
     @classmethod
     def startResponders(cls):
@@ -377,6 +411,15 @@ quiet=no
             self.assertRRsetInAnswer(res, expected)
             self.assertEqual(res.answer[0].ttl, 1)
 
+    def testPreResolve(self):
+        expected = dns.rrset.from_text('preresolve.luahooks.example.', 1, dns.rdataclass.IN, 'A', '192.0.2.2')
+        query = dns.message.make_query('preresolve.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 testIPFilterHeader(self):
         query = dns.message.make_query('ipfilter.luahooks.example.', 'A', 'IN')
         query.flags |= dns.flags.AD
@@ -536,6 +579,7 @@ class PDNSRandomTest(RecursorTest):
     function preresolve (dq)
       dq.rcode = pdns.NOERROR
       dq:addAnswer(pdns.TXT, pdnsrandom())
+      dq.variable = true
       return true
     end
     """
@@ -642,23 +686,13 @@ class PDNSValidationStatesTest(RecursorTest):
     """Tests that we have access to the validation states from Lua"""
 
     _confdir = 'validation-states-from-lua'
-    _config_template_default = """
+    _config_template = """
 dnssec=validate
-daemon=no
-trace=yes
-packetcache-ttl=0
-packetcache-servfail-ttl=0
-max-cache-ttl=15
-threads=1
-loglevel=9
-disable-syslog=yes
-log-common-errors=yes
 """
     _roothints = None
     _lua_config_file = """
     """
-    _config_template = """
-    """
+
     _lua_dns_script_file = """
     function postresolve (dq)
       if pdns.validationstates.Indeterminate == nil or
@@ -714,7 +748,7 @@ log-common-errors=yes
         self.assertEqual(len(res.authority), 0)
 
 class PolicyEventFilterOnFollowUpTest(RecursorTest):
-    """Tests the interaction between RPZ and followup queries (dns64, folliwCNAME)
+    """Tests the interaction between RPZ and followup queries (dns64, followCNAME)
     """
 
     _confdir = 'policyeventfilter-followup'
@@ -763,3 +797,267 @@ secure.example.zone.rpz. 60 IN A 192.0.2.42
             self.assertEqual(len(res.answer), 2)
             self.assertEqual(len(res.authority), 0)
             self.assertResponseMatches(query, expected, res)
+
+class PolicyEventFilterOnFollowUpWithNativeDNS64Test(RecursorTest):
+    """Tests the interaction between followup queries and native dns64
+    """
+
+    _confdir = 'policyeventfilter-followup-dns64'
+    _config_template = """
+    dns64-prefix=1234::/96
+    """
+    _lua_config_file = """
+    """
+    _lua_dns_script_file = """
+    function preresolve(dq)
+      dq:addAnswer(pdns.CNAME, "cname.secure.example.")
+      dq.followupFunction="followCNAMERecords"
+      dq.rcode = pdns.NOERROR
+      return true
+    end
+
+    """
+
+    def testAAAA(self):
+        expected = [
+            dns.rrset.from_text('mx1.secure.example.', 15, dns.rdataclass.IN, 'CNAME', 'cname.secure.example.'),
+            dns.rrset.from_text('cname.secure.example.', 15, dns.rdataclass.IN, 'CNAME', ' host1.secure.example.'),
+            dns.rrset.from_text('host1.secure.example.', 15, dns.rdataclass.IN, 'AAAA', '1234::c000:202')
+        ]
+        query = dns.message.make_query('mx1.secure.example', 'AAAA')
+
+        for method in ("sendUDPQuery", "sendTCPQuery"):
+            sender = getattr(self, method)
+            res = sender(query)
+            print(res)
+            self.assertRcodeEqual(res, dns.rcode.NOERROR)
+            self.assertEqual(len(res.answer), 3)
+            self.assertEqual(len(res.authority), 0)
+            self.assertResponseMatches(query, expected, res)
+
+class LuaPostResolveFFITest(RecursorTest):
+    """Tests postresolve_ffi interface"""
+
+    _confdir = 'LuaPostResolveFFITest'
+    _config_template = """
+    """
+    _lua_dns_script_file = """
+local ffi = require("ffi")
+
+ffi.cdef[[
+  typedef struct pdns_postresolve_ffi_handle pdns_postresolve_ffi_handle_t;
+
+  typedef enum
+  {
+    pdns_record_place_answer = 1,
+    pdns_record_place_authority = 2,
+    pdns_record_place_additional = 3
+  } pdns_record_place_t;
+
+ typedef enum
+  {
+    pdns_policy_kind_noaction = 0,
+    pdns_policy_kind_drop = 1,
+    pdns_policy_kind_nxdomain = 2,
+    pdns_policy_kind_nodata = 3,
+    pdns_policy_kind_truncate = 4,
+    pdns_policy_kind_custom = 5
+  } pdns_policy_kind_t;
+
+  typedef struct pdns_ffi_record {
+    const char* name;
+    size_t name_len;
+    const char* content;
+    size_t content_len;
+    uint32_t ttl;
+    pdns_record_place_t place;
+    uint16_t type;
+  } pdns_ffi_record_t;
+
+  const char* pdns_postresolve_ffi_handle_get_qname(pdns_postresolve_ffi_handle_t* ref);
+  const char* pdns_postresolve_ffi_handle_get_qname_raw(pdns_postresolve_ffi_handle_t* ref, const char** name, size_t* len);
+  uint16_t pdns_postresolve_ffi_handle_get_qtype(const pdns_postresolve_ffi_handle_t* ref);
+  uint16_t pdns_postresolve_ffi_handle_get_rcode(const pdns_postresolve_ffi_handle_t* ref);
+  void pdns_postresolve_ffi_handle_set_rcode(const pdns_postresolve_ffi_handle_t* ref, uint16_t rcode);
+  void pdns_postresolve_ffi_handle_set_appliedpolicy_kind(pdns_postresolve_ffi_handle_t* ref, pdns_policy_kind_t kind);
+  bool pdns_postresolve_ffi_handle_get_record(pdns_postresolve_ffi_handle_t* ref, unsigned int i, pdns_ffi_record_t *record, bool raw);
+  bool pdns_postresolve_ffi_handle_set_record(pdns_postresolve_ffi_handle_t* ref, unsigned int i, const char* content, size_t contentLen, bool raw);
+  void pdns_postresolve_ffi_handle_clear_records(pdns_postresolve_ffi_handle_t* ref);
+  bool pdns_postresolve_ffi_handle_add_record(pdns_postresolve_ffi_handle_t* ref, const char* name, uint16_t type, uint32_t ttl, const char* content, size_t contentLen, pdns_record_place_t place, bool raw);
+  const char* pdns_postresolve_ffi_handle_get_authip(pdns_postresolve_ffi_handle_t* ref);
+  void pdns_postresolve_ffi_handle_get_authip_raw(pdns_postresolve_ffi_handle_t* ref, const void** addr, size_t* addrSize);
+]]
+
+
+function tohex(str)
+  return (str:gsub('.', function (c) return string.format('%02X', string.byte(c)) end))
+end
+function toA(str)
+  return (str:gsub('.', function (c) return string.format('%d.', string.byte(c)) end))
+end
+
+function postresolve_ffi(ref)
+  local qname = ffi.string(ffi.C.pdns_postresolve_ffi_handle_get_qname(ref))
+  local qtype = ffi.C.pdns_postresolve_ffi_handle_get_qtype(ref)
+
+  if qname  == "example" and qtype == pdns.SOA
+  then
+     local addr = ffi.string(ffi.C.pdns_postresolve_ffi_handle_get_authip(ref))
+     if string.sub(addr, -3) ~= ".10" and string.sub(addr, -3) ~= ".18"
+     then
+       -- signal error by clearing all
+       ffi.C.pdns_postresolve_ffi_handle_clear_records(ref)
+     end
+     local qaddr = ffi.new("const char *[1]")
+     local qlen = ffi.new("size_t [1]")
+     ffi.C.pdns_postresolve_ffi_handle_get_qname_raw(ref, qaddr, qlen)
+     local q = ffi.string(qaddr[0], qlen[0])
+     if tohex(q) ~= "076578616D706C6500"
+     then
+       -- pdnslog("Error "..tohex(q))
+       -- signal error by clearing all
+       ffi.C.pdns_postresolve_ffi_handle_clear_records(ref)
+     end
+     -- as a bonus check from which auth the data came
+     local addr = ffi.new("const void *[1]")
+     local len = ffi.new("size_t [1]")
+     ffi.C.pdns_postresolve_ffi_handle_get_authip_raw(ref, addr, len)
+     local a = ffi.string(addr[0], len[0])
+     if string.byte(a, 4) ~= 10 and string.byte(a,4) ~= 18
+     then
+       -- signal error by clearing all
+       ffi.C.pdns_postresolve_ffi_handle_clear_records(ref)
+     end
+     ffi.C.pdns_postresolve_ffi_handle_set_appliedpolicy_kind(ref, "pdns_policy_kind_noaction")
+     return true
+  end
+  if qname == "example" and qtype == pdns.TXT
+  then
+     ffi.C.pdns_postresolve_ffi_handle_set_appliedpolicy_kind(ref, "pdns_policy_kind_drop")
+     return true
+  end
+  if qname == "ns1.example" and qtype == pdns.A
+  then
+     ffi.C.pdns_postresolve_ffi_handle_set_appliedpolicy_kind(ref, "pdns_policy_kind_nxdomain")
+     return true
+  end
+  if qname == "ns1.example" and qtype == pdns.AAAA
+  then
+     ffi.C.pdns_postresolve_ffi_handle_set_appliedpolicy_kind(ref, "pdns_policy_kind_nodata")
+     return true
+  end
+  if qname == "ns2.example" and qtype == pdns.A
+  then
+     ffi.C.pdns_postresolve_ffi_handle_set_appliedpolicy_kind(ref, "pdns_policy_kind_truncate")
+     return true
+  end
+
+  if qname ~= "postresolve_ffi.example" or (qtype ~= pdns.A and qtype ~= pdns.AAAA)
+  then
+    return false
+  end
+
+  local record = ffi.new("pdns_ffi_record_t")
+  local i = 0
+
+  while ffi.C.pdns_postresolve_ffi_handle_get_record(ref, i, record, false)
+  do
+     local content = ffi.string(record.content, record.content_len)
+     local name = ffi.string(record.name)
+     if name ~= "postresolve_ffi.example"
+     then
+       return false
+     end
+     if record.type == pdns.A and content == "1.2.3.4"
+     then
+       ffi.C.pdns_postresolve_ffi_handle_set_record(ref, i, "0.1.2.3", 7, false);
+       ffi.C.pdns_postresolve_ffi_handle_add_record(ref, "add."..name, pdns.A, record.ttl, '4.5.6.7', 7, "pdns_record_place_additional", false);
+     elseif record.type == pdns.AAAA and content == "::1"
+     then
+       ffi.C.pdns_postresolve_ffi_handle_set_record(ref, i, "\\0\\1\\2\\3\\4\\5\\6\\7\\8\\9\\10\\11\\12\\13\\14\\15", 16, true);
+     end
+     i = i + 1
+  end
+  -- loop again using raw
+  i = 0
+  while ffi.C.pdns_postresolve_ffi_handle_get_record(ref, i, record, true)
+  do
+     local content = ffi.string(record.content, record.content_len)
+     local name = ffi.string(record.name, record.name_len)
+     --pdnslog("R  "..tohex(name))
+     if tohex(name) ~= "0F706F73747265736F6C76655F666669076578616D706C6500"
+     then
+       ffi.C.pdns_postresolve_ffi_handle_clear_records(ref)
+     end
+     i = i + 1
+  end
+  return true
+end
+    """
+
+    def testNOACTION(self):
+        """ postresolve_ffi: test that we can do a NOACTION for a name and type combo"""
+        query = dns.message.make_query('example', 'SOA')
+        res = self.sendUDPQuery(query)
+        self.assertRcodeEqual(res, dns.rcode.NOERROR)
+        self.assertEqual(len(res.answer), 1)
+
+    def testDROP(self):
+        """ postresolve_ffi: test that we can do a DROP for a name and type combo"""
+        query = dns.message.make_query('example', 'TXT')
+        res = self.sendUDPQuery(query)
+        self.assertEqual(res, None)
+
+    def testNXDOMAIN(self):
+        """ postresolve_ffi: test that we can return a NXDOMAIN for a name and type combo"""
+        query = dns.message.make_query('ns1.example', 'A')
+        res = self.sendUDPQuery(query)
+        self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
+        self.assertEqual(len(res.answer), 0)
+
+    def testNODATA(self):
+        """ postresolve_ffi: test that we can return a NODATA for a name and type combo"""
+        query = dns.message.make_query('ns1.example', 'AAAA')
+        res = self.sendUDPQuery(query)
+        self.assertRcodeEqual(res, dns.rcode.NOERROR)
+        self.assertEqual(len(res.answer), 0)
+
+    def testTRUNCATE(self):
+        """ postresolve_ffi: test that we can return a truncated for a name and type combo"""
+        query = dns.message.make_query('ns2.example', 'A')
+        res = self.sendUDPQuery(query)
+        self.assertRcodeEqual(res, dns.rcode.NOERROR)
+        self.assertEqual(len(res.answer), 0)
+        self.assertMessageHasFlags(res, ['QR', 'TC', 'RD', 'RA'])
+
+
+    def testModifyA(self):
+        """postresolve_ffi: test that we can modify A answers"""
+        expectedAnswerRecords = [
+            dns.rrset.from_text('postresolve_ffi.example.', 60, dns.rdataclass.IN, 'A', '0.1.2.3', '1.2.3.5'),
+        ]
+        expectedAdditionalRecords = [
+            dns.rrset.from_text('add.postresolve_ffi.example.', 60, dns.rdataclass.IN, 'A', '4.5.6.7'),
+        ]
+
+        query = dns.message.make_query('postresolve_ffi.example', 'A')
+        res = self.sendUDPQuery(query)
+        self.assertRcodeEqual(res, dns.rcode.NOERROR)
+        self.assertEqual(len(res.answer), 1)
+        self.assertEqual(len(res.authority), 0)
+        self.assertEqual(len(res.additional), 1)
+        self.assertEqual(res.answer, expectedAnswerRecords)
+        self.assertEqual(res.additional, expectedAdditionalRecords)
+
+    def testModifyAAAA(self):
+        """postresolve_ffi: test that we can modify AAAA answers"""
+        expectedAnswerRecords = [
+            dns.rrset.from_text('postresolve_ffi.example.', 60, dns.rdataclass.IN, 'AAAA', '1:203:405:607:809:a0b:c0d:e0f', '::2'),
+        ]
+        query = dns.message.make_query('postresolve_ffi.example', 'AAAA')
+        res = self.sendUDPQuery(query)
+        self.assertRcodeEqual(res, dns.rcode.NOERROR)
+        self.assertEqual(len(res.answer), 1)
+        self.assertEqual(len(res.authority), 0)
+        self.assertEqual(len(res.additional), 0)
+        self.assertEqual(res.answer, expectedAnswerRecords)