+import errno
import os
import socket
import struct
import threading
import dns
import dnstap_pb2
-from nose import SkipTest
+from unittest import SkipTest
from recursortests import RecursorTest
FSTRM_CONTROL_ACCEPT = 0x01
pass
-def checkDnstapBase(testinstance, dnstap, protocol, initiator):
+def checkDnstapBase(testinstance, dnstap, protocol, initiator, responder, response_port=53):
testinstance.assertTrue(dnstap)
testinstance.assertTrue(dnstap.HasField('identity'))
#testinstance.assertEqual(dnstap.identity, b'a.server')
testinstance.assertTrue(dnstap.message.HasField('socket_protocol'))
testinstance.assertEqual(dnstap.message.socket_protocol, protocol)
testinstance.assertTrue(dnstap.message.HasField('socket_family'))
- testinstance.assertEquals(dnstap.message.socket_family, dnstap_pb2.INET)
+ testinstance.assertEqual(dnstap.message.socket_family, dnstap_pb2.INET)
#
- # We cannot check the query address and port since we only log outgoing queries via dnstap
+ # The query address and port are from the the recursor, we don't know the port
#
- #testinstance.assertTrue(dnstap.message.HasField('query_address'))
- #testinstance.assertEquals(socket.inet_ntop(socket.AF_INET, dnstap.message.query_address), initiator)
+ testinstance.assertTrue(dnstap.message.HasField('query_address'))
+ testinstance.assertEqual(socket.inet_ntop(socket.AF_INET, dnstap.message.query_address), initiator)
+ testinstance.assertTrue(dnstap.message.HasField('query_port'))
testinstance.assertTrue(dnstap.message.HasField('response_address'))
- testinstance.assertEquals(socket.inet_ntop(socket.AF_INET, dnstap.message.response_address), initiator)
+ testinstance.assertEqual(socket.inet_ntop(socket.AF_INET, dnstap.message.response_address), responder)
testinstance.assertTrue(dnstap.message.HasField('response_port'))
- testinstance.assertEquals(dnstap.message.response_port, 53)
+ testinstance.assertEqual(dnstap.message.response_port, response_port)
-def checkDnstapQuery(testinstance, dnstap, protocol, initiator='127.0.0.1'):
- testinstance.assertEquals(dnstap.message.type, dnstap_pb2.Message.RESOLVER_QUERY)
- checkDnstapBase(testinstance, dnstap, protocol, initiator)
+def checkDnstapQuery(testinstance, dnstap, protocol, initiator, responder):
+ testinstance.assertEqual(dnstap.message.type, dnstap_pb2.Message.RESOLVER_QUERY)
+ checkDnstapBase(testinstance, dnstap, protocol, initiator, responder)
testinstance.assertTrue(dnstap.message.HasField('query_time_sec'))
testinstance.assertTrue(dnstap.message.HasField('query_time_nsec'))
#wire_message = dns.message.from_wire(dnstap.message.query_message)
#testinstance.assertEqual(wire_message, query)
+def checkDnstapNOD(testinstance, dnstap, protocol, initiator, responder, response_port, query_zone):
+ testinstance.assertEqual(dnstap.message.type, dnstap_pb2.Message.CLIENT_QUERY)
+ checkDnstapBase(testinstance, dnstap, protocol, initiator, responder, response_port)
+
+ testinstance.assertTrue(dnstap.message.HasField('query_time_sec'))
+ testinstance.assertTrue(dnstap.message.HasField('query_time_nsec'))
+
+ testinstance.assertTrue(dnstap.message.HasField('query_zone'))
+ testinstance.assertEqual(dns.name.from_wire(dnstap.message.query_zone, 0)[0].to_text(), query_zone)
+
+def checkDnstapUDR(testinstance, dnstap, protocol, initiator, responder, response_port, query_zone):
+ testinstance.assertEqual(dnstap.message.type, dnstap_pb2.Message.RESOLVER_RESPONSE)
+ checkDnstapBase(testinstance, dnstap, protocol, initiator, responder, response_port)
+
+ testinstance.assertTrue(dnstap.message.HasField('query_time_sec'))
+ testinstance.assertTrue(dnstap.message.HasField('query_time_nsec'))
+
+ testinstance.assertTrue(dnstap.message.HasField('query_zone'))
+ testinstance.assertEqual(dns.name.from_wire(dnstap.message.query_zone, 0)[0].to_text(), query_zone)
+
+ testinstance.assertTrue(dnstap.message.HasField('response_message'))
+ wire_message = dns.message.from_wire(dnstap.message.response_message)
def checkDnstapExtra(testinstance, dnstap, expected):
testinstance.assertTrue(dnstap.HasField('extra'))
testinstance.assertFalse(dnstap.HasField('extra'))
-def checkDnstapResponse(testinstance, dnstap, protocol, response, initiator='127.0.0.1'):
- testinstance.assertEquals(dnstap.message.type, dnstap_pb2.Message.RESOLVER_RESPONSE)
- checkDnstapBase(testinstance, dnstap, protocol, initiator)
+def checkDnstapResponse(testinstance, dnstap, protocol, response, initiator, responder):
+ testinstance.assertEqual(dnstap.message.type, dnstap_pb2.Message.RESOLVER_RESPONSE)
+ checkDnstapBase(testinstance, dnstap, protocol, initiator, responder)
testinstance.assertTrue(dnstap.message.HasField('query_time_sec'))
testinstance.assertTrue(dnstap.message.HasField('query_time_nsec'))
fstrm_handle_bidir_connection(conn, lambda data: \
param.queue.put(data, True, timeout=2.0))
except socket.error as e:
- if e.errno == 9:
+ if e.errno in (errno.EBADF, errno.EPIPE):
break
sys.stderr.write("Unexpected socket error %s\n" % str(e))
sys.exit(1)
except exception as e:
sys.stderr.write("Unexpected socket error %s\n" % str(e))
- sys.exit(1)
+ sys.exit(1)
conn.close()
@classmethod
listener.setDaemon(True)
listener.start()
except socket.error as e:
- if e.errno != 9:
+ if e.errno != errno.EBADF:
sys.stderr.write("Socket error on accept: %s\n" % str(e))
else:
break
query = dns.message.make_query(name, 'A', want_dnssec=True)
query.flags |= dns.flags.RD
res = self.sendUDPQuery(query)
- self.assertNotEquals(res, None)
-
+ self.assertNotEqual(res, None)
+
# check the dnstap message corresponding to the UDP query
dnstap = self.getFirstDnstap()
- checkDnstapQuery(self, dnstap, dnstap_pb2.UDP, '127.0.0.8')
+ checkDnstapQuery(self, dnstap, dnstap_pb2.UDP, '127.0.0.1', '127.0.0.8')
# We don't expect a response
checkDnstapNoExtra(self, dnstap)
class DNSTapLogNoQueriesTest(TestRecursorDNSTap):
- """
- This test makes sure that we correctly export outgoing queries over DNSTap.
- 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 DNSTap server.
- """
_confdir = 'DNSTapLogNoQueries'
_config_template = """
query = dns.message.make_query(name, 'A', want_dnssec=True)
query.flags |= dns.flags.RD
res = self.sendUDPQuery(query)
- self.assertNotEquals(res, None)
+ self.assertNotEqual(res, None)
# We don't expect anything
self.assertTrue(DNSTapServerParameters.queue.empty())
+
+class DNSTapLogNODTest(TestRecursorDNSTap):
+ """
+ This test makes sure that we correctly export outgoing queries over DNSTap.
+ 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 DNSTap server.
+ """
+
+ _confdir = 'DNSTapLogNODQueries'
+ _config_template = """
+new-domain-tracking=yes
+new-domain-history-dir=configs/%s/nod
+unique-response-tracking=yes
+unique-response-history-dir=configs/%s/udr
+auth-zones=example=configs/%s/example.zone""" % (_confdir, _confdir, _confdir)
+ _lua_config_file = """
+dnstapNODFrameStreamServer({"%s"})
+ """ % (DNSTapServerParameters.path)
+
+ @classmethod
+ def generateRecursorConfig(cls, confdir):
+ for directory in ["nod", "udr"]:
+ path = os.path.join('configs', cls._confdir, directory)
+ cls.createConfigDir(path)
+ super(DNSTapLogNODTest, cls).generateRecursorConfig(confdir)
+
+ def getFirstDnstap(self):
+ try:
+ data = DNSTapServerParameters.queue.get(True, timeout=2.0)
+ except:
+ data = False
+ self.assertTrue(data)
+ dnstap = dnstap_pb2.Dnstap()
+ dnstap.ParseFromString(data)
+ return dnstap
+
+ def testA(self):
+ name = 'www.example.org.'
+ query = dns.message.make_query(name, 'A', want_dnssec=True)
+ query.flags |= dns.flags.RD
+ res = self.sendUDPQuery(query)
+ self.assertNotEqual(res, None)
+
+ # check the dnstap message corresponding to the UDP query
+ dnstap = self.getFirstDnstap()
+
+ checkDnstapNOD(self, dnstap, dnstap_pb2.UDP, '127.0.0.1', '127.0.0.1', 5300, name)
+ # We don't expect a response
+ checkDnstapNoExtra(self, dnstap)
+
+class DNSTapLogUDRTest(TestRecursorDNSTap):
+
+ _confdir = 'DNSTapLogUDRResponses'
+ _config_template = """
+new-domain-tracking=yes
+new-domain-history-dir=configs/%s/nod
+unique-response-tracking=yes
+unique-response-history-dir=configs/%s/udr
+auth-zones=example=configs/%s/example.zone""" % (_confdir, _confdir, _confdir)
+ _lua_config_file = """
+dnstapNODFrameStreamServer({"%s"}, {logNODs=false, logUDRs=true})
+ """ % (DNSTapServerParameters.path)
+
+ @classmethod
+ def generateRecursorConfig(cls, confdir):
+ for directory in ["nod", "udr"]:
+ path = os.path.join('configs', cls._confdir, directory)
+ cls.createConfigDir(path)
+ super(DNSTapLogUDRTest, cls).generateRecursorConfig(confdir)
+
+ def getFirstDnstap(self):
+ try:
+ data = DNSTapServerParameters.queue.get(True, timeout=2.0)
+ except:
+ data = False
+ self.assertTrue(data)
+ dnstap = dnstap_pb2.Dnstap()
+ dnstap.ParseFromString(data)
+ return dnstap
+
+ def testA(self):
+ name = 'types.example.'
+ query = dns.message.make_query(name, 'A', want_dnssec=True)
+ query.flags |= dns.flags.RD
+ res = self.sendUDPQuery(query)
+ self.assertNotEqual(res, None)
+
+ # check the dnstap message corresponding to the UDP query
+ dnstap = self.getFirstDnstap()
+
+ checkDnstapUDR(self, dnstap, dnstap_pb2.UDP, '127.0.0.1', '127.0.0.1', 5300, name)
+ # We don't expect a rpasesponse
+ checkDnstapNoExtra(self, dnstap)
+
+class DNSTapLogNODUDRTest(TestRecursorDNSTap):
+
+ _confdir = 'DNSTapLogNODUDRs'
+ _config_template = """
+new-domain-tracking=yes
+new-domain-history-dir=configs/%s/nod
+unique-response-tracking=yes
+unique-response-history-dir=configs/%s/udr
+auth-zones=example=configs/%s/example.zone""" % (_confdir, _confdir, _confdir)
+ _lua_config_file = """
+dnstapNODFrameStreamServer({"%s"}, {logNODs=true, logUDRs=true})
+ """ % (DNSTapServerParameters.path)
+
+ @classmethod
+ def generateRecursorConfig(cls, confdir):
+ for directory in ["nod", "udr"]:
+ path = os.path.join('configs', cls._confdir, directory)
+ cls.createConfigDir(path)
+ super(DNSTapLogNODUDRTest, cls).generateRecursorConfig(confdir)
+
+ def getFirstDnstap(self):
+ try:
+ data = DNSTapServerParameters.queue.get(True, timeout=2.0)
+ except:
+ data = False
+ self.assertTrue(data)
+ dnstap = dnstap_pb2.Dnstap()
+ dnstap.ParseFromString(data)
+ return dnstap
+
+ def testA(self):
+ name = 'types.example.'
+ query = dns.message.make_query(name, 'A', want_dnssec=True)
+ query.flags |= dns.flags.RD
+ res = self.sendUDPQuery(query)
+ self.assertNotEqual(res, None)
+
+ dnstap = self.getFirstDnstap()
+ checkDnstapUDR(self, dnstap, dnstap_pb2.UDP, '127.0.0.1', '127.0.0.1', 5300, name)
+
+ dnstap = self.getFirstDnstap()
+ checkDnstapNOD(self, dnstap, dnstap_pb2.UDP, '127.0.0.1', '127.0.0.1', 5300, name)
+
+ checkDnstapNoExtra(self, dnstap)