]> git.ipfire.org Git - thirdparty/pdns.git/blobdiff - regression-tests.recursor-dnssec/test_Protobuf.py
Make sure we can install unsigned packages.
[thirdparty/pdns.git] / regression-tests.recursor-dnssec / test_Protobuf.py
index 50a6449d4b45487175a9d1813e3d973eefd0ed67..689f5478db1966aa9c7af68acfc55c2620d0c234 100644 (file)
@@ -143,7 +143,7 @@ class TestRecursorProtobuf(RecursorTest):
             self.assertEquals(len(msg.originalRequestorSubnet), 4)
             self.assertEquals(socket.inet_ntop(socket.AF_INET, msg.originalRequestorSubnet), '127.0.0.1')
 
-    def checkOutgoingProtobufBase(self, msg, protocol, query, initiator):
+    def checkOutgoingProtobufBase(self, msg, protocol, query, initiator, length=None):
         self.assertTrue(msg)
         self.assertTrue(msg.HasField('timeSec'))
         self.assertTrue(msg.HasField('socketFamily'))
@@ -155,8 +155,11 @@ class TestRecursorProtobuf(RecursorTest):
         self.assertTrue(msg.HasField('id'))
         self.assertNotEquals(msg.id, query.id)
         self.assertTrue(msg.HasField('inBytes'))
-        # compare inBytes with length of query/response
-        self.assertEquals(msg.inBytes, len(query.to_wire()))
+        if length is not None:
+          self.assertEquals(msg.inBytes, length)
+        else:
+          # compare inBytes with length of query/response
+          self.assertEquals(msg.inBytes, len(query.to_wire()))
 
     def checkProtobufQuery(self, msg, protocol, query, qclass, qtype, qname, initiator='127.0.0.1'):
         self.assertEquals(msg.type, dnsmessage_pb2.PBDNSMessage.DNSQueryType)
@@ -199,13 +202,16 @@ class TestRecursorProtobuf(RecursorTest):
         self.assertEquals(msg.response.appliedPolicyType, policyType)
 
     def checkProtobufTags(self, msg, tags):
+        print(tags)
+        print('---')
+        print(msg.response.tags)
         self.assertEquals(len(msg.response.tags), len(tags))
         for tag in msg.response.tags:
             self.assertTrue(tag in tags)
 
-    def checkProtobufOutgoingQuery(self, msg, protocol, query, qclass, qtype, qname, initiator='127.0.0.1'):
+    def checkProtobufOutgoingQuery(self, msg, protocol, query, qclass, qtype, qname, initiator='127.0.0.1', length=None):
         self.assertEquals(msg.type, dnsmessage_pb2.PBDNSMessage.DNSOutgoingQueryType)
-        self.checkOutgoingProtobufBase(msg, protocol, query, initiator)
+        self.checkOutgoingProtobufBase(msg, protocol, query, initiator, length=length)
         self.assertTrue(msg.HasField('to'))
         self.assertTrue(msg.HasField('question'))
         self.assertTrue(msg.question.HasField('qClass'))
@@ -215,12 +221,25 @@ class TestRecursorProtobuf(RecursorTest):
         self.assertTrue(msg.question.HasField('qName'))
         self.assertEquals(msg.question.qName, qname)
 
-    def checkProtobufIncomingResponse(self, msg, protocol, response, initiator='127.0.0.1'):
+    def checkProtobufIncomingResponse(self, msg, protocol, response, initiator='127.0.0.1', length=None):
         self.assertEquals(msg.type, dnsmessage_pb2.PBDNSMessage.DNSIncomingResponseType)
-        self.checkOutgoingProtobufBase(msg, protocol, response, initiator)
+        self.checkOutgoingProtobufBase(msg, protocol, response, initiator, length=length)
         self.assertTrue(msg.HasField('response'))
+        self.assertTrue(msg.response.HasField('rcode'))
         self.assertTrue(msg.response.HasField('queryTimeSec'))
 
+    def checkProtobufIncomingNetworkErrorResponse(self, msg, protocol, response, initiator='127.0.0.1'):
+        self.checkProtobufIncomingResponse(msg, protocol, response, initiator, length=0)
+        self.assertEquals(msg.response.rcode, 65536)
+
+    def checkProtobufIdentity(self, msg, requestorId, deviceId, deviceName):
+        self.assertTrue(msg.HasField('requestorId'))
+        self.assertTrue(msg.HasField('deviceId'))
+        self.assertTrue(msg.HasField('deviceName'))
+        self.assertEquals(msg.requestorId, requestorId)
+        self.assertEquals(msg.deviceId, deviceId)
+        self.assertEquals(msg.deviceName, deviceName)
+
     @classmethod
     def setUpClass(cls):
 
@@ -335,7 +354,10 @@ class OutgoingProtobufDefaultTest(TestRecursorProtobuf):
 
     _confdir = 'OutgoingProtobufDefault'
     _config_template = """
-auth-zones=example=configs/%s/example.zone""" % _confdir
+    # Switch off QName Minimization, it generates much more protobuf messages
+    # (or make the test much more smart!)
+    qname-minimization=no
+    auth-zones=example=configs/%s/example.zone""" % _confdir
     _lua_config_file = """
     outgoingProtobufServer({"127.0.0.1:%d", "127.0.0.1:%d"})
     """ % (protobufServersParameters[0].port, protobufServersParameters[1].port)
@@ -350,9 +372,38 @@ auth-zones=example=configs/%s/example.zone""" % _confdir
         # check the protobuf messages corresponding to the UDP query and answer
         msg = self.getFirstProtobufMessage()
         self.checkProtobufOutgoingQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
-#        # then the response
-#        msg = self.getFirstProtobufMessage()
-#        self.checkProtobufIncomingResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res)
+        # then the response
+        msg = self.getFirstProtobufMessage()
+        self.checkProtobufIncomingNetworkErrorResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res)
+        self.checkNoRemainingMessage()
+
+class OutgoingProtobufNoQueriesTest(TestRecursorProtobuf):
+    """
+    This test makes sure that we correctly export incoming responses but not outgoing queries over protobuf.
+    It must be improved and setup env so we can check for incoming responses, but makes sure for now
+    that the recursor at least connects to the protobuf server.
+    """
+
+    _confdir = 'OutgoingProtobufNoQueries'
+    _config_template = """
+    # Switch off QName Minimization, it generates much more protobuf messages
+    # (or make the test much more smart!)
+    qname-minimization=no
+    auth-zones=example=configs/%s/example.zone""" % _confdir
+    _lua_config_file = """
+    outgoingProtobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=false, logResponses=true })
+    """ % (protobufServersParameters[0].port, protobufServersParameters[1].port)
+
+    def testA(self):
+        name = 'www.example.org.'
+        expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
+        query = dns.message.make_query(name, 'A', want_dnssec=True)
+        query.flags |= dns.flags.RD
+        res = self.sendUDPQuery(query)
+
+        # check the response
+        msg = self.getFirstProtobufMessage()
+        self.checkProtobufIncomingNetworkErrorResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res)
         self.checkNoRemainingMessage()
 
 class ProtobufMasksTest(TestRecursorProtobuf):
@@ -502,7 +553,7 @@ auth-zones=example=configs/%s/example.zone""" % _confdir
         # check the protobuf messages corresponding to the UDP query and answer
         msg = self.getFirstProtobufMessage()
         self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
-        self.checkProtobufTags(msg, [self._tag_from_gettag])
+        self.checkProtobufTags(msg, [ self._tag_from_gettag ])
         # then the response
         msg = self.getFirstProtobufMessage()
         self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res)
@@ -511,7 +562,7 @@ auth-zones=example=configs/%s/example.zone""" % _confdir
         # we have max-cache-ttl set to 15
         self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
         self.assertEquals(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.84')
-        tags = [self._tag_from_gettag] + self._tags
+        tags = [ self._tag_from_gettag ] + self._tags
         self.checkProtobufTags(msg, tags)
         self.checkNoRemainingMessage()
 
@@ -652,3 +703,234 @@ auth-zones=example=configs/%s/example.zone""" % _confdir
                 self.assertEquals(rr.rdata, 'a.example.')
 
         self.checkNoRemainingMessage()
+
+class ProtobufTaggedExtraFieldsTest(TestRecursorProtobuf):
+    """
+    This test makes sure that we correctly export extra fields that may have been set while being tagged.
+    """
+
+    _confdir = 'ProtobufTaggedExtraFields'
+    _config_template = """
+auth-zones=example=configs/%s/example.zone""" % _confdir
+    _lua_config_file = """
+    protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=true } )
+    """ % (protobufServersParameters[0].port, protobufServersParameters[1].port)
+    _requestorId = 'S-000001727'
+    _deviceId = 'd1:0a:91:dc:cc:82'
+    _deviceName = 'Joe'
+    _lua_dns_script_file = """
+    function gettag(remote, ednssubnet, localip, qname, qtype, ednsoptions, tcp)
+      if qname:equal('tagged.example.') then
+        -- tag number, policy tags, data, requestorId, deviceId, deviceName
+        return 0, {}, {}, '%s', '%s', '%s'
+      end
+      return 0
+    end
+    """ % (_requestorId, _deviceId, _deviceName)
+
+    def testA(self):
+        name = 'a.example.'
+        expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
+        query = dns.message.make_query(name, 'A', want_dnssec=True)
+        query.flags |= dns.flags.CD
+        res = self.sendUDPQuery(query)
+        self.assertRRsetInAnswer(res, expected)
+
+        # check the protobuf message corresponding to the UDP response
+        # the first query and answer are not tagged, so there is nothing in the queue
+        # check the protobuf messages corresponding to the UDP query and answer
+        msg = self.getFirstProtobufMessage()
+        self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
+        self.checkProtobufIdentity(msg, '', '', '')
+
+        # then the response
+        msg = self.getFirstProtobufMessage()
+        self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, '127.0.0.1')
+        self.assertEquals(len(msg.response.rrs), 1)
+        rr = msg.response.rrs[0]
+        # we have max-cache-ttl set to 15
+        self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
+        self.assertEquals(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
+        self.checkProtobufIdentity(msg, '', '', '')
+        self.checkNoRemainingMessage()
+
+    def testTagged(self):
+        name = 'tagged.example.'
+        expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.84')
+        query = dns.message.make_query(name, 'A', want_dnssec=True)
+        query.flags |= dns.flags.CD
+        res = self.sendUDPQuery(query)
+        self.assertRRsetInAnswer(res, expected)
+
+        # check the protobuf messages corresponding to the UDP query and answer
+        msg = self.getFirstProtobufMessage()
+        self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
+        self.checkProtobufIdentity(msg, self._requestorId, self._deviceId, self._deviceName)
+
+        # then the response
+        msg = self.getFirstProtobufMessage()
+        self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res)
+        self.assertEquals(len(msg.response.rrs), 1)
+        rr = msg.response.rrs[0]
+        # we have max-cache-ttl set to 15
+        self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
+        self.assertEquals(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.84')
+        self.checkProtobufIdentity(msg, self._requestorId, self._deviceId, self._deviceName)
+        self.checkNoRemainingMessage()
+
+class ProtobufTaggedExtraFieldsFFITest(ProtobufTaggedExtraFieldsTest):
+    """
+    This test makes sure that we correctly export extra fields that may have been set while being tagged (FFI version).
+    """
+    _confdir = 'ProtobufTaggedExtraFieldsFFI'
+    _config_template = """
+auth-zones=example=configs/%s/example.zone""" % _confdir
+    _lua_config_file = """
+    protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=true } )
+    """ % (protobufServersParameters[0].port, protobufServersParameters[1].port)
+    _lua_dns_script_file = """
+    local ffi = require("ffi")
+
+    ffi.cdef[[
+      typedef struct pdns_ffi_param pdns_ffi_param_t;
+
+      const char* pdns_ffi_param_get_qname(pdns_ffi_param_t* ref);
+      void pdns_ffi_param_set_tag(pdns_ffi_param_t* ref, unsigned int tag);
+      void pdns_ffi_param_set_requestorid(pdns_ffi_param_t* ref, const char* name);
+      void pdns_ffi_param_set_devicename(pdns_ffi_param_t* ref, const char* name);
+      void pdns_ffi_param_set_deviceid(pdns_ffi_param_t* ref, size_t len, const void* name);
+    ]]
+
+    function gettag_ffi(obj)
+      qname = ffi.string(ffi.C.pdns_ffi_param_get_qname(obj))
+      if qname == 'tagged.example' then
+        ffi.C.pdns_ffi_param_set_requestorid(obj, "%s")
+        deviceid = "%s"
+        ffi.C.pdns_ffi_param_set_deviceid(obj, string.len(deviceid), deviceid)
+        ffi.C.pdns_ffi_param_set_devicename(obj, "%s")
+      end
+      return 0
+    end
+    """ % (ProtobufTaggedExtraFieldsTest._requestorId, ProtobufTaggedExtraFieldsTest._deviceId, ProtobufTaggedExtraFieldsTest._deviceName)
+
+class ProtobufRPZTest(TestRecursorProtobuf):
+    """
+    This test makes sure that we correctly export the RPZ applied policy in our protobuf messages
+    """
+
+    _confdir = 'ProtobufRPZ'
+    _config_template = """
+auth-zones=example=configs/%s/example.rpz.zone""" % _confdir
+    _lua_config_file = """
+    protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=true } )
+    rpzFile('configs/%s/zone.rpz', { policyName="zone.rpz."})
+    """ % (protobufServersParameters[0].port, protobufServersParameters[1].port, _confdir)
+
+    @classmethod
+    def generateRecursorConfig(cls, confdir):
+        authzonepath = os.path.join(confdir, 'example.rpz.zone')
+        with open(authzonepath, 'w') as authzone:
+            authzone.write("""$ORIGIN example.
+@ 3600 IN SOA {soa}
+sub.test 3600 IN A 192.0.2.42
+""".format(soa=cls._SOA))
+
+        rpzFilePath = os.path.join(confdir, 'zone.rpz')
+        with open(rpzFilePath, 'w') as rpzZone:
+            rpzZone.write("""$ORIGIN zone.rpz.
+@ 3600 IN SOA {soa}
+*.test.example.zone.rpz. 60 IN CNAME rpz-passthru.
+""".format(soa=cls._SOA))
+
+        super(ProtobufRPZTest, cls).generateRecursorConfig(confdir)
+
+    def testA(self):
+        name = 'sub.test.example.'
+        expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
+        query = dns.message.make_query(name, 'A', want_dnssec=True)
+        query.flags |= dns.flags.CD
+        res = self.sendUDPQuery(query)
+        self.assertRRsetInAnswer(res, expected)
+
+        # check the protobuf messages corresponding to the UDP query and answer
+        msg = self.getFirstProtobufMessage()
+        self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
+
+        # then the response
+        msg = self.getFirstProtobufMessage()
+        self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res)
+        self.checkProtobufPolicy(msg, dnsmessage_pb2.PBDNSMessage.PolicyType.QNAME, 'zone.rpz.')
+        self.assertEquals(len(msg.response.rrs), 1)
+        rr = msg.response.rrs[0]
+        # we have max-cache-ttl set to 15
+        self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
+        self.assertEquals(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
+        self.checkNoRemainingMessage()
+
+class ProtobufRPZTagsTest(TestRecursorProtobuf):
+    """
+    This test makes sure that we correctly export the RPZ tags in our protobuf messages
+    """
+
+    _confdir = 'ProtobufRPZTags'
+    _config_template = """
+auth-zones=example=configs/%s/example.rpz.zone""" % _confdir
+    _tags = ['tag1', 'tag2']
+    _tags_from_gettag = ['tag1-from-gettag', 'tag2-from-gettag']
+    _tags_from_rpz = ['tag1-from-rpz', 'tag2-from-rpz' ]
+    _lua_config_file = """
+    protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=true, tags={'tag1', 'tag2'} } )
+    rpzFile('configs/%s/zone.rpz', { policyName="zone.rpz.", tags={ '%s', '%s'} })
+    """ % (protobufServersParameters[0].port, protobufServersParameters[1].port, _confdir, _tags_from_rpz[0], _tags_from_rpz[1])
+    _lua_dns_script_file = """
+    function gettag(remote, ednssubnet, localip, qname, qtype, ednsoptions, tcp)
+      return 0, { '%s', '%s' }
+    end
+    function preresolve(dq)
+      dq:addPolicyTag('%s')
+      dq:addPolicyTag('%s')
+      return false
+    end
+    """ % (_tags_from_gettag[0], _tags_from_gettag[1], _tags[0], _tags[1])
+
+    @classmethod
+    def generateRecursorConfig(cls, confdir):
+        authzonepath = os.path.join(confdir, 'example.rpz.zone')
+        with open(authzonepath, 'w') as authzone:
+            authzone.write("""$ORIGIN example.
+@ 3600 IN SOA {soa}
+sub.test 3600 IN A 192.0.2.42
+""".format(soa=cls._SOA))
+
+        rpzFilePath = os.path.join(confdir, 'zone.rpz')
+        with open(rpzFilePath, 'w') as rpzZone:
+            rpzZone.write("""$ORIGIN zone.rpz.
+@ 3600 IN SOA {soa}
+*.test.example.zone.rpz. 60 IN CNAME rpz-passthru.
+""".format(soa=cls._SOA))
+
+        super(ProtobufRPZTagsTest, cls).generateRecursorConfig(confdir)
+
+    def testA(self):
+        name = 'sub.test.example.'
+        expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
+        query = dns.message.make_query(name, 'A', want_dnssec=True)
+        query.flags |= dns.flags.CD
+        res = self.sendUDPQuery(query)
+        self.assertRRsetInAnswer(res, expected)
+
+        # check the protobuf messages corresponding to the UDP query and answer
+        msg = self.getFirstProtobufMessage()
+        self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
+
+        # then the response
+        msg = self.getFirstProtobufMessage()
+        self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res)
+        self.checkProtobufPolicy(msg, dnsmessage_pb2.PBDNSMessage.PolicyType.QNAME, 'zone.rpz.')
+        self.checkProtobufTags(msg, self._tags + self._tags_from_gettag + self._tags_from_rpz)
+        self.assertEquals(len(msg.response.rrs), 1)
+        rr = msg.response.rrs[0]
+        # we have max-cache-ttl set to 15
+        self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
+        self.assertEquals(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
+        self.checkNoRemainingMessage()