]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Add more regression tests 3126/head
authorRemi Gacogne <rgacogne-github@coredump.fr>
Mon, 28 Dec 2015 17:20:07 +0000 (18:20 +0100)
committerRemi Gacogne <rgacogne-github@coredump.fr>
Mon, 28 Dec 2015 17:20:07 +0000 (18:20 +0100)
Tests for:

Actions:
* DelayAction
* RCodeAction

Rules:
* AllRule
* AndRule
* addAnyTCRule
* addDisableValidationRule
* addNoRecurseRule
* addPoolRule
* QTypeRule
* RegexRule
* SuffixMatchNodeRule

Misc:
* ACL
* truncateTC
* fixupCase
* addDomainSpoof
* Round Robin balancing

regression-tests.dnsdist/.gitignore
regression-tests.dnsdist/dnsdist.conf
regression-tests.dnsdist/dnsdisttests.py
regression-tests.dnsdist/test_Advanced.py [new file with mode: 0644]
regression-tests.dnsdist/test_Basics.py
regression-tests.dnsdist/test_EdnsClientSubnet.py

index 1cc50b47caea8e8038d96e01c3d0fb02a1ba7050..5132bb74a865b58ccd7221089be6447543e4a0f2 100644 (file)
@@ -2,6 +2,6 @@
 /*.xml
 /*.pid
 /*.pyc
-dnsdist_ecs*.conf
+dnsdist_*.conf
 .dnsdist_history
 .history
index aa4975239dd7e9d0995573f7983697b0ea338689..13f311214bf59da7a32ef73721f660cd47a5244f 100644 (file)
@@ -1,4 +1,9 @@
     truncateTC(true)
+    addAnyTCRule()
+    addAction(RegexRule("evil[0-9]{4,}\\.regex\\.tests\\.powerdns\\.com$"), RCodeAction(5))
+    mySMN = newSuffixMatchNode()
+    mySMN:add(newDNSName("nameAndQtype.tests.powerdns.com."))
+    addAction(AndRule{SuffixMatchNodeRule(mySMN), QTypeRule("TXT")}, RCodeAction(4))
     block=newDNSName("powerdns.org.")
     function blockFilter(remote, qname, qtype, dh)
         if(qname:isPartOf(block))
index 8b62a08b08d37af80e25b1d064100a858a6da2cf..b4a289948e3db856073051e4ba96ab0b6cdc7143 100644 (file)
@@ -27,14 +27,19 @@ class DNSDistTest(unittest.TestCase):
     _toResponderQueue = Queue.Queue()
     _fromResponderQueue = Queue.Queue()
     _dnsdist = None
+    _responsesCounter = {}
 
     @classmethod
     def startResponders(cls):
         print("Launching responders..")
-        cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[])
+        # clear counters
+        for key in cls._responsesCounter:
+            cls._responsesCounter[key] = 0
+
+        cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort])
         cls._UDPResponder.setDaemon(True)
         cls._UDPResponder.start()
-        cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[])
+        cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort])
         cls._TCPResponder.setDaemon(True)
         cls._TCPResponder.start()
 
@@ -77,10 +82,17 @@ class DNSDistTest(unittest.TestCase):
             cls._dnsdist.wait()
 
     @classmethod
-    def UDPResponder(cls):
+    def ResponderIncrementCounter(cls):
+        if threading.currentThread().name in cls._responsesCounter:
+            cls._responsesCounter[threading.currentThread().name] += 1
+        else:
+            cls._responsesCounter[threading.currentThread().name] = 1
+
+    @classmethod
+    def UDPResponder(cls, port):
         sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
         sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
-        sock.bind(("127.0.0.1", cls._testServerPort))
+        sock.bind(("127.0.0.1", port))
         while True:
             data, addr = sock.recvfrom(4096)
             request = dns.message.from_wire(data)
@@ -91,6 +103,7 @@ class DNSDistTest(unittest.TestCase):
                 response = cls._toResponderQueue.get()
                 response.id = request.id
                 cls._fromResponderQueue.put(request)
+                cls.ResponderIncrementCounter()
             else:
                 # unexpected query, or health check
                 response = dns.message.make_response(request)
@@ -100,16 +113,15 @@ class DNSDistTest(unittest.TestCase):
                                             request.question[0].rdtype,
                                             '127.0.0.1')
                 response.answer.append(rrset)
-
             sock.sendto(response.to_wire(), addr)
         sock.close()
 
     @classmethod
-    def TCPResponder(cls):
+    def TCPResponder(cls, port):
         sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
         try:
-            sock.bind(("127.0.0.1", cls._testServerPort))
+            sock.bind(("127.0.0.1", port))
         except socket.error as e:
             print("Error binding in the TCP responder: %s" % str(e))
             sys.exit(1)
@@ -128,6 +140,7 @@ class DNSDistTest(unittest.TestCase):
                 response = cls._toResponderQueue.get()
                 response.id = request.id
                 cls._fromResponderQueue.put(request)
+                cls.ResponderIncrementCounter()
             else:
                 # unexpected query, or health check
                 response = dns.message.make_response(request)
diff --git a/regression-tests.dnsdist/test_Advanced.py b/regression-tests.dnsdist/test_Advanced.py
new file mode 100644 (file)
index 0000000..b60fc75
--- /dev/null
@@ -0,0 +1,701 @@
+#!/usr/bin/env python
+from datetime import datetime, timedelta
+import dns
+import os
+import subprocess
+import threading
+import time
+import unittest
+from dnsdisttests import DNSDistTest
+
+class TestAdvancedFixupCase(DNSDistTest):
+
+    _dnsDistPort = 5340
+    _config_template = """
+    truncateTC(true)
+    fixupCase(true)
+    newServer{address="127.0.0.1:%s"}
+    """
+
+    _dnsdistcmd = (os.environ['DNSDISTBIN'] + " -C dnsdist_fixupcase.conf --acl 127.0.0.1/32 -l 127.0.0.1:" + str(_dnsDistPort)).split()
+
+    @classmethod
+    def startDNSDist(cls, shutUp=True):
+        print("Launching dnsdist..")
+        with open('dnsdist_fixupcase.conf', 'w') as conf:
+            conf.write(cls._config_template % str(cls._testServerPort))
+
+        print(' '.join(cls._dnsdistcmd))
+        if shutUp:
+            with open(os.devnull, 'w') as fdDevNull:
+                cls._dnsdist = subprocess.Popen(cls._dnsdistcmd, close_fds=True, stdout=fdDevNull, stderr=fdDevNull)
+        else:
+            cls._dnsdist = subprocess.Popen(cls._dnsdistcmd, close_fds=True)
+
+        time.sleep(1)
+
+        if cls._dnsdist.poll() is not None:
+            cls._dnsdist.terminate()
+            cls._dnsdist.wait()
+            sys.exit(cls._dnsdist.returncode)
+
+    def testAdvancedFixupCase(self):
+        """
+        Send a query with lower and upper chars,
+        make the backend return a lowercase version,
+        check that dnsdist fixes the response.
+        """
+        name = 'fiXuPCasE.advanced.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+        lowercasequery = dns.message.make_query(name.lower(), 'A', 'IN')
+        response = dns.message.make_response(lowercasequery)
+        expectedResponse = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    3600,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.A,
+                                    '127.0.0.1')
+        response.answer.append(rrset)
+        expectedResponse.answer.append(rrset)
+
+        (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = query.id
+        receivedResponse.id = expectedResponse.id
+        self.assertEquals(query, receivedQuery)
+        self.assertEquals(expectedResponse, receivedResponse)
+
+        (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = query.id
+        receivedResponse.id = expectedResponse.id
+        self.assertEquals(query, receivedQuery)
+        self.assertEquals(expectedResponse, receivedResponse)
+
+
+class TestAdvancedRemoveRD(DNSDistTest):
+
+    _dnsDistPort = 5340
+    _config_template = """
+    addNoRecurseRule("norecurse.advanced.tests.powerdns.com.")
+    newServer{address="127.0.0.1:%s"}
+    """
+
+    _dnsdistcmd = (os.environ['DNSDISTBIN'] + " -C dnsdist_noRD.conf --acl 127.0.0.1/32 -l 127.0.0.1:" + str(_dnsDistPort)).split()
+
+    @classmethod
+    def startDNSDist(cls, shutUp=True):
+        print("Launching dnsdist..")
+        with open('dnsdist_noRD.conf', 'w') as conf:
+            conf.write(cls._config_template % str(cls._testServerPort))
+
+        print(' '.join(cls._dnsdistcmd))
+        if shutUp:
+            with open(os.devnull, 'w') as fdDevNull:
+                cls._dnsdist = subprocess.Popen(cls._dnsdistcmd, close_fds=True, stdout=fdDevNull, stderr=fdDevNull)
+        else:
+            cls._dnsdist = subprocess.Popen(cls._dnsdistcmd, close_fds=True)
+
+        time.sleep(1)
+
+        if cls._dnsdist.poll() is not None:
+            cls._dnsdist.terminate()
+            cls._dnsdist.wait()
+            sys.exit(cls._dnsdist.returncode)
+
+    def testAdvancedNoRD(self):
+        """
+        Send a query with RD,
+        check that dnsdist clears the RD flag.
+        """
+        name = 'norecurse.advanced.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+        expectedQuery = dns.message.make_query(name, 'A', 'IN')
+        expectedQuery.flags &= ~dns.flags.RD
+
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    3600,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.A,
+                                    '127.0.0.1')
+        response.answer.append(rrset)
+
+        (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = expectedQuery.id
+        receivedResponse.id = response.id
+        self.assertEquals(expectedQuery, receivedQuery)
+        self.assertEquals(response, receivedResponse)
+
+        (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = expectedQuery.id
+        receivedResponse.id = response.id
+        self.assertEquals(expectedQuery, receivedQuery)
+        self.assertEquals(response, receivedResponse)
+
+    def testAdvancedKeepRD(self):
+        """
+        Send a query with RD for a canary domain,
+        check that dnsdist does not clear the RD flag.
+        """
+        name = 'keeprecurse.advanced.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    3600,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.A,
+                                    '127.0.0.1')
+        response.answer.append(rrset)
+
+        (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = query.id
+        receivedResponse.id = response.id
+        self.assertEquals(query, receivedQuery)
+        self.assertEquals(response, receivedResponse)
+
+        (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = query.id
+        receivedResponse.id = response.id
+        self.assertEquals(query, receivedQuery)
+        self.assertEquals(response, receivedResponse)
+
+
+class TestAdvancedAddCD(DNSDistTest):
+
+    _dnsDistPort = 5340
+    _config_template = """
+    addDisableValidationRule("setcd.advanced.tests.powerdns.com.")
+    newServer{address="127.0.0.1:%s"}
+    """
+
+    _dnsdistcmd = (os.environ['DNSDISTBIN'] + " -C dnsdist_setCD.conf --acl 127.0.0.1/32 -l 127.0.0.1:" + str(_dnsDistPort)).split()
+
+    @classmethod
+    def startDNSDist(cls, shutUp=True):
+        print("Launching dnsdist..")
+        with open('dnsdist_setCD.conf', 'w') as conf:
+            conf.write(cls._config_template % str(cls._testServerPort))
+
+        print(' '.join(cls._dnsdistcmd))
+        if shutUp:
+            with open(os.devnull, 'w') as fdDevNull:
+                cls._dnsdist = subprocess.Popen(cls._dnsdistcmd, close_fds=True, stdout=fdDevNull, stderr=fdDevNull)
+        else:
+            cls._dnsdist = subprocess.Popen(cls._dnsdistcmd, close_fds=True)
+
+        time.sleep(1)
+
+        if cls._dnsdist.poll() is not None:
+            cls._dnsdist.terminate()
+            cls._dnsdist.wait()
+            sys.exit(cls._dnsdist.returncode)
+
+    def testAdvancedSetCD(self):
+        """
+        Send a query with CD cleared,
+        check that dnsdist set the CD flag.
+        """
+        name = 'setcd.advanced.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+        expectedQuery = dns.message.make_query(name.lower(), 'A', 'IN')
+        expectedQuery.flags |= dns.flags.CD
+
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    3600,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.A,
+                                    '127.0.0.1')
+        response.answer.append(rrset)
+
+        (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = expectedQuery.id
+        receivedResponse.id = response.id
+        self.assertEquals(expectedQuery, receivedQuery)
+        self.assertEquals(response, receivedResponse)
+
+        (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = expectedQuery.id
+        receivedResponse.id = response.id
+        self.assertEquals(expectedQuery, receivedQuery)
+        self.assertEquals(response, receivedResponse)
+
+    def testAdvancedKeepNoCD(self):
+        """
+        Send a query without CD for a canary domain,
+        check that dnsdist does not set the CD flag.
+        """
+        name = 'keepnocd.advanced.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    3600,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.A,
+                                    '127.0.0.1')
+        response.answer.append(rrset)
+
+        (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = query.id
+        receivedResponse.id = response.id
+        self.assertEquals(query, receivedQuery)
+        self.assertEquals(response, receivedResponse)
+
+        (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = query.id
+        receivedResponse.id = response.id
+        self.assertEquals(query, receivedQuery)
+        self.assertEquals(response, receivedResponse)
+
+class TestAdvancedSpoof(DNSDistTest):
+
+    _dnsDistPort = 5340
+    _config_template = """
+    addDomainSpoof("spoof.tests.powerdns.com.", "192.0.2.1", "2001:DB8::1")
+    newServer{address="127.0.0.1:%s"}
+    """
+
+    _dnsdistcmd = (os.environ['DNSDISTBIN'] + " -C dnsdist_spoof.conf --acl 127.0.0.1/32 -l 127.0.0.1:" + str(_dnsDistPort)).split()
+
+    @classmethod
+    def startDNSDist(cls, shutUp=True):
+        print("Launching dnsdist..")
+        with open('dnsdist_spoof.conf', 'w') as conf:
+            conf.write(cls._config_template % str(cls._testServerPort))
+
+        print(' '.join(cls._dnsdistcmd))
+        if shutUp:
+            with open(os.devnull, 'w') as fdDevNull:
+                cls._dnsdist = subprocess.Popen(cls._dnsdistcmd, close_fds=True, stdout=fdDevNull, stderr=fdDevNull)
+        else:
+            cls._dnsdist = subprocess.Popen(cls._dnsdistcmd, close_fds=True)
+
+        time.sleep(1)
+
+        if cls._dnsdist.poll() is not None:
+            cls._dnsdist.terminate()
+            cls._dnsdist.wait()
+            sys.exit(cls._dnsdist.returncode)
+
+    def testSpoofA(self):
+        """
+        Send an A query to "spoof.tests.powerdns.com.",
+        check that dnsdist sends a spoofed result.
+        """
+        name = 'spoof.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+        # dnsdist set RA = RD for spoofed responses
+        query.flags &= ~dns.flags.RD
+        expectedResponse = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    60,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.A,
+                                    '192.0.2.1')
+        expectedResponse.answer.append(rrset)
+
+        (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False, timeout=2.0)
+        self.assertTrue(receivedResponse)
+        receivedResponse.id = expectedResponse.id
+        self.assertEquals(expectedResponse, receivedResponse)
+
+        (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False, timeout=2.0)
+        self.assertTrue(receivedResponse)
+        receivedResponse.id = expectedResponse.id
+        self.assertEquals(expectedResponse, receivedResponse)
+
+    def testSpoofAAAA(self):
+        """
+        Send an AAAA query to "spoof.tests.powerdns.com.",
+        check that dnsdist sends a spoofed result.
+        """
+        name = 'spoof.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'AAAA', 'IN')
+        # dnsdist set RA = RD for spoofed responses
+        query.flags &= ~dns.flags.RD
+        expectedResponse = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    60,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.AAAA,
+                                    '2001:DB8::1')
+        expectedResponse.answer.append(rrset)
+
+        (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False, timeout=2.0)
+        self.assertTrue(receivedResponse)
+        receivedResponse.id = expectedResponse.id
+        self.assertEquals(expectedResponse, receivedResponse)
+
+        (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False, timeout=2.0)
+        self.assertTrue(receivedResponse)
+        receivedResponse.id = expectedResponse.id
+        self.assertEquals(expectedResponse, receivedResponse)
+
+
+class TestAdvancedPoolRouting(DNSDistTest):
+
+    _dnsDistPort = 5340
+    _config_template = """
+    newServer{address="127.0.0.1:%s", pool="real"}
+    addPoolRule("pool.tests.powerdns.com", "real")
+    """
+
+    _dnsdistcmd = (os.environ['DNSDISTBIN'] + " -C dnsdist_pool.conf --acl 127.0.0.1/32 -l 127.0.0.1:" + str(_dnsDistPort)).split()
+
+    @classmethod
+    def startDNSDist(cls, shutUp=True):
+        print("Launching dnsdist..")
+        with open('dnsdist_pool.conf', 'w') as conf:
+            conf.write(cls._config_template % str(cls._testServerPort))
+
+        print(' '.join(cls._dnsdistcmd))
+        if shutUp:
+            with open(os.devnull, 'w') as fdDevNull:
+                cls._dnsdist = subprocess.Popen(cls._dnsdistcmd, close_fds=True, stdout=fdDevNull, stderr=fdDevNull)
+        else:
+            cls._dnsdist = subprocess.Popen(cls._dnsdistcmd, close_fds=True)
+
+        time.sleep(1)
+
+        if cls._dnsdist.poll() is not None:
+            cls._dnsdist.terminate()
+            cls._dnsdist.wait()
+            sys.exit(cls._dnsdist.returncode)
+
+    def testPolicyPool(self):
+        """
+        Send an A query to "pool.tests.powerdns.com.",
+        check that dnsdist routes the query to the "real" pool.
+        """
+        name = 'pool.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    60,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.A,
+                                    '192.0.2.1')
+        response.answer.append(rrset)
+
+        (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+        receivedQuery.id = query.id
+        receivedResponse.id = response.id
+        self.assertEquals(query, receivedQuery)
+        self.assertEquals(response, receivedResponse)
+
+        (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+        receivedQuery.id = query.id
+        receivedResponse.id = response.id
+        self.assertEquals(query, receivedQuery)
+        self.assertEquals(response, receivedResponse)
+
+    def testDefaultPool(self):
+        """
+        Send an A query to "notpool.tests.powerdns.com.",
+        check that dnsdist sends no response (no servers
+        in the default pool).
+        """
+        name = 'notpool.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+
+        (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False, timeout=2.0)
+        self.assertEquals(receivedResponse, None)
+
+        (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False, timeout=2.0)
+        self.assertEquals(receivedResponse, None)
+
+class TestAdvancedRoundRobinLB(DNSDistTest):
+
+    _dnsDistPort = 5340
+    _testServer2Port = 5351
+    _config_template = """
+    setServerPolicy(roundrobin)
+    s1 = newServer{address="127.0.0.1:%s"}
+    s1:setUp()
+    s2 = newServer{address="127.0.0.1:%s"}
+    s2:setUp()
+    """
+
+    _dnsdistcmd = (os.environ['DNSDISTBIN'] + " -C dnsdist_rr_lb.conf --acl 127.0.0.1/32 -l 127.0.0.1:" + str(_dnsDistPort)).split()
+
+    @classmethod
+    def startDNSDist(cls, shutUp=True):
+        print("Launching dnsdist..")
+        with open('dnsdist_rr_lb.conf', 'w') as conf:
+            conf.write(cls._config_template % (str(cls._testServerPort), str(cls._testServer2Port)))
+
+        print(' '.join(cls._dnsdistcmd))
+        if shutUp:
+            with open(os.devnull, 'w') as fdDevNull:
+                cls._dnsdist = subprocess.Popen(cls._dnsdistcmd, close_fds=True, stdout=fdDevNull, stderr=fdDevNull)
+        else:
+            cls._dnsdist = subprocess.Popen(cls._dnsdistcmd, close_fds=True)
+
+        time.sleep(1)
+
+        if cls._dnsdist.poll() is not None:
+            cls._dnsdist.terminate()
+            cls._dnsdist.wait()
+            sys.exit(cls._dnsdist.returncode)
+
+    @classmethod
+    def startResponders(cls):
+        print("Launching responders..")
+        cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort])
+        cls._UDPResponder.setDaemon(True)
+        cls._UDPResponder.start()
+        cls._UDPResponder2 = threading.Thread(name='UDP Responder 2', target=cls.UDPResponder, args=[cls._testServer2Port])
+        cls._UDPResponder2.setDaemon(True)
+        cls._UDPResponder2.start()
+
+        cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort])
+        cls._TCPResponder.setDaemon(True)
+        cls._TCPResponder.start()
+
+        cls._TCPResponder2 = threading.Thread(name='TCP Responder 2', target=cls.TCPResponder, args=[cls._testServer2Port])
+        cls._TCPResponder2.setDaemon(True)
+        cls._TCPResponder2.start()
+
+    def testRR(self):
+        """
+        Send 100 A queries to "rr.tests.powerdns.com.",
+        check that dnsdist routes half of it to each backend.
+        """
+        numberOfQueries = 10
+        name = 'rr.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    60,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.A,
+                                    '192.0.2.1')
+        response.answer.append(rrset)
+
+        # clear counters
+        for key in TestAdvancedRoundRobinLB._responsesCounter:
+            TestAdvancedRoundRobinLB._responsesCounter[key] = 0
+
+        # the round robin counter is shared for UDP and TCP,
+        # so we need to do UDP then TCP to have a clean count
+        for idx in range(numberOfQueries):
+            (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+            receivedQuery.id = query.id
+            receivedResponse.id = response.id
+            self.assertEquals(query, receivedQuery)
+            self.assertEquals(response, receivedResponse)
+
+        for idx in range(numberOfQueries):
+            (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+            receivedQuery.id = query.id
+            receivedResponse.id = response.id
+            self.assertEquals(query, receivedQuery)
+            self.assertEquals(response, receivedResponse)
+
+        for key in TestAdvancedRoundRobinLB._responsesCounter:
+            value = TestAdvancedRoundRobinLB._responsesCounter[key]
+            self.assertEquals(value, numberOfQueries / 2)
+
+class TestAdvancedRoundRobinLBOneDown(DNSDistTest):
+
+    _dnsDistPort = 5340
+    _testServer2Port = 5351
+    _config_template = """
+    setServerPolicy(roundrobin)
+    s1 = newServer{address="127.0.0.1:%s"}
+    s1:setUp()
+    s2 = newServer{address="127.0.0.1:%s"}
+    s2:setDown()
+    """
+
+    _dnsdistcmd = (os.environ['DNSDISTBIN'] + " -C dnsdist_rr_lb_down.conf --acl 127.0.0.1/32 -l 127.0.0.1:" + str(_dnsDistPort)).split()
+
+    @classmethod
+    def startDNSDist(cls, shutUp=True):
+        print("Launching dnsdist..")
+        with open('dnsdist_rr_lb_down.conf', 'w') as conf:
+            conf.write(cls._config_template % (str(cls._testServerPort), str(cls._testServer2Port)))
+
+        print(' '.join(cls._dnsdistcmd))
+        if shutUp:
+            with open(os.devnull, 'w') as fdDevNull:
+                cls._dnsdist = subprocess.Popen(cls._dnsdistcmd, close_fds=True, stdout=fdDevNull, stderr=fdDevNull)
+        else:
+            cls._dnsdist = subprocess.Popen(cls._dnsdistcmd, close_fds=True)
+
+        time.sleep(1)
+
+        if cls._dnsdist.poll() is not None:
+            cls._dnsdist.terminate()
+            cls._dnsdist.wait()
+            sys.exit(cls._dnsdist.returncode)
+
+    def testRRWithOneDown(self):
+        """
+        Send 100 A queries to "rr.tests.powerdns.com.",
+        check that dnsdist routes all of it to the only backend up.
+        """
+        numberOfQueries = 10
+        name = 'rr.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    60,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.A,
+                                    '192.0.2.1')
+        response.answer.append(rrset)
+
+        # the round robin counter is shared for UDP and TCP,
+        # so we need to do UDP then TCP to have a clean count
+        for idx in range(numberOfQueries):
+            (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+            receivedQuery.id = query.id
+            receivedResponse.id = response.id
+            self.assertEquals(query, receivedQuery)
+            self.assertEquals(response, receivedResponse)
+
+        for idx in range(numberOfQueries):
+            (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+            receivedQuery.id = query.id
+            receivedResponse.id = response.id
+            self.assertEquals(query, receivedQuery)
+            self.assertEquals(response, receivedResponse)
+
+        print(TestAdvancedRoundRobinLB._responsesCounter)
+        total = 0
+        for key in TestAdvancedRoundRobinLB._responsesCounter:
+            value = TestAdvancedRoundRobinLB._responsesCounter[key]
+            self.assertTrue(value == numberOfQueries or value == 0)
+            total += value
+
+        self.assertEquals(total, numberOfQueries * 2)
+
+class TestAdvancedACL(DNSDistTest):
+
+    _dnsDistPort = 5340
+    _config_template = """
+    newServer{address="127.0.0.1:%s"}
+    """
+
+    _dnsdistcmd = (os.environ['DNSDISTBIN'] + " -C dnsdist_acl.conf --acl 192.0.2.1/32 -l 127.0.0.1:" + str(_dnsDistPort)).split()
+
+    @classmethod
+    def startDNSDist(cls, shutUp=True):
+        print("Launching dnsdist..")
+        with open('dnsdist_acl.conf', 'w') as conf:
+            conf.write(cls._config_template % str(cls._testServerPort))
+
+        print(' '.join(cls._dnsdistcmd))
+        if shutUp:
+            with open(os.devnull, 'w') as fdDevNull:
+                cls._dnsdist = subprocess.Popen(cls._dnsdistcmd, close_fds=True, stdout=fdDevNull, stderr=fdDevNull)
+        else:
+            cls._dnsdist = subprocess.Popen(cls._dnsdistcmd, close_fds=True)
+
+        time.sleep(1)
+
+        if cls._dnsdist.poll() is not None:
+            cls._dnsdist.terminate()
+            cls._dnsdist.wait()
+            sys.exit(cls._dnsdist.returncode)
+
+    def testACLBlocked(self):
+        """
+        Send an A query to "tests.powerdns.com.",
+        we expect no response since 127.0.0.1 is not on the
+        ACL.
+        """
+        name = 'tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+
+        (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False, timeout=2.0)
+        self.assertEquals(receivedResponse, None)
+
+        (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False, timeout=2.0)
+        self.assertEquals(receivedResponse, None)
+
+class TestAdvancedDelay(DNSDistTest):
+
+    _dnsDistPort = 5340
+    _config_template = """
+    addAction(AllRule(), DelayAction(1000))
+    newServer{address="127.0.0.1:%s"}
+    """
+
+    _dnsdistcmd = (os.environ['DNSDISTBIN'] + " -C dnsdist_delay.conf --acl 127.0.0.1/32 -l 127.0.0.1:" + str(_dnsDistPort)).split()
+
+    @classmethod
+    def startDNSDist(cls, shutUp=True):
+        print("Launching dnsdist..")
+        with open('dnsdist_delay.conf', 'w') as conf:
+            conf.write(cls._config_template % str(cls._testServerPort))
+
+        print(' '.join(cls._dnsdistcmd))
+        if shutUp:
+            with open(os.devnull, 'w') as fdDevNull:
+                cls._dnsdist = subprocess.Popen(cls._dnsdistcmd, close_fds=True, stdout=fdDevNull, stderr=fdDevNull)
+        else:
+            cls._dnsdist = subprocess.Popen(cls._dnsdistcmd, close_fds=True)
+
+        time.sleep(1)
+
+        if cls._dnsdist.poll() is not None:
+            cls._dnsdist.terminate()
+            cls._dnsdist.wait()
+            sys.exit(cls._dnsdist.returncode)
+
+    def testDelayed(self):
+        """
+        Send an A query to "tests.powerdns.com.",
+        check that the response delay is longer than 1000 ms
+        over UDP, less than that over TCP.
+        """
+        name = 'tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    60,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.A,
+                                    '192.0.2.1')
+        response.answer.append(rrset)
+
+        begin = datetime.now()
+        (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+        end = datetime.now()
+        receivedQuery.id = query.id
+        receivedResponse.id = response.id
+        self.assertEquals(query, receivedQuery)
+        self.assertEquals(response, receivedResponse)
+        self.assertTrue((end - begin) > timedelta(0, 1));
+
+        begin = datetime.now()
+        (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+        end = datetime.now()
+        receivedQuery.id = query.id
+        receivedResponse.id = response.id
+        self.assertEquals(query, receivedQuery)
+        self.assertEquals(response, receivedResponse)
+        self.assertTrue((end - begin) < timedelta(0, 1));
index ae0de1adad0f6c879c965effb02e612d982f35ca..c2b96915f9b6e298ee6cf6af1af998715d0d382e 100644 (file)
@@ -78,6 +78,160 @@ class TestBasics(DNSDistTest):
         self.assertEquals(query, receivedQuery)
         self.assertEquals(response, receivedResponse)
 
+    def testAnyIsTruncated(self):
+        """
+        dnsdist is configured to reply with TC to ANY queries,
+        send an ANY query and check the result.
+        """
+        name = 'any.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'ANY', 'IN')
+        expectedResponse = dns.message.make_response(query)
+        expectedResponse.flags |= dns.flags.TC
+
+        (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False, timeout=2.0)
+        receivedResponse.id = expectedResponse.id
+        self.assertEquals(receivedResponse, expectedResponse)
+
+        (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False, timeout=2.0)
+        receivedResponse.id = expectedResponse.id
+        self.assertEquals(receivedResponse, expectedResponse)
+
+    def testTruncateTC(self):
+        """
+        dnsdist is configured to truncate TC (default),
+        we make the backend send responses
+        with TC set and additional content,
+        and check that the received response has been fixed.
+        """
+        name = 'atruncatetc.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    3600,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.A,
+                                    '127.0.0.1')
+
+        response.answer.append(rrset)
+        response.flags |= dns.flags.TC
+
+        (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+        receivedQuery.id = query.id
+        receivedResponse.id = response.id
+        self.assertEquals(query, receivedQuery)
+        self.assertEquals(response.flags, receivedResponse.flags)
+        self.assertEquals(response.question, receivedResponse.question)
+        self.assertFalse(response.answer == receivedResponse.answer)
+        self.assertEquals(len(receivedResponse.answer), 0)
+        self.assertEquals(len(receivedResponse.authority), 0)
+        self.assertEquals(len(receivedResponse.additional), 0)
+
+    def testRegexReturnsRefused(self):
+        """
+        dnsdist is configured to reply 'refused' for query
+        matching "evil[0-9]{4,}\\.regex\\.tests\\.powerdns\\.com$".
+        We send a query for evil4242.powerdns.com
+        and check that the response is "refused".
+        """
+        name = 'evil4242.regex.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+        expectedResponse = dns.message.make_response(query)
+        expectedResponse.set_rcode(dns.rcode.REFUSED)
+
+        (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False, timeout=2.0)
+        receivedResponse.id = expectedResponse.id
+        self.assertEquals(receivedResponse, expectedResponse)
+
+        (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False, timeout=2.0)
+        receivedResponse.id = expectedResponse.id
+        self.assertEquals(receivedResponse, expectedResponse)
+
+    def testDomainAndQTypeReturnsNotImplemented(self):
+        """
+        dnsdist is configured to reply 'not implemented' for query
+        matching "nameAndQtype.tests.powerdns.com." AND qtype TXT/
+        We send a TXT query for "nameAndQtype.powerdns.com."
+        and check that the response is 'not implemented'.
+        """
+        name = 'nameAndQtype.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'TXT', 'IN')
+        expectedResponse = dns.message.make_response(query)
+        expectedResponse.set_rcode(dns.rcode.NOTIMP)
+
+        (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False, timeout=2.0)
+        receivedResponse.id = expectedResponse.id
+        self.assertEquals(receivedResponse, expectedResponse)
+
+        (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False, timeout=2.0)
+        receivedResponse.id = expectedResponse.id
+        self.assertEquals(receivedResponse, expectedResponse)
+
+    def testDomainWithoutQTypeIsNotAffected(self):
+        """
+        dnsdist is configured to reply 'not implemented' for query
+        matching "nameAndQtype.tests.powerdns.com." AND qtype TXT/
+        We send a A query for "nameAndQtype.tests.powerdns.com."
+        and check that the response is OK.
+        """
+        name = 'nameAndQtype.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    3600,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.A,
+                                    '127.0.0.1')
+        response.answer.append(rrset)
+
+        (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = query.id
+        receivedResponse.id = response.id
+        self.assertEquals(query, receivedQuery)
+        self.assertEquals(response, receivedResponse)
+
+        (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = query.id
+        receivedResponse.id = response.id
+        self.assertEquals(query, receivedQuery)
+        self.assertEquals(response, receivedResponse)
+
+    def testOtherDomainANDQTypeIsNotAffected(self):
+        """
+        dnsdist is configured to reply 'not implemented' for query
+        matching "nameAndQtype.tests.powerdns.com." AND qtype TXT/
+        We send a TXT query for "OtherNameAndQtype.tests.powerdns.com."
+        and check that the response is OK.
+        """
+        name = 'OtherNameAndQtype.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'TXT', 'IN')
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    3600,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.TXT,
+                                    'nothing to see here')
+        response.answer.append(rrset)
+
+        (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = query.id
+        receivedResponse.id = response.id
+        self.assertEquals(query, receivedQuery)
+        self.assertEquals(response, receivedResponse)
+
+        (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = query.id
+        receivedResponse.id = response.id
+        self.assertEquals(query, receivedQuery)
+        self.assertEquals(response, receivedResponse)
+
 
 if __name__ == '__main__':
     unittest.main()
index aa4d87389421cc15cc405f96563b01d9d2cb9329..aa8ca34c5e1af9350910b629fd9b6c893e6ae8df 100644 (file)
@@ -9,7 +9,7 @@ from dnsdisttests import DNSDistTest
 
 class TestEdnsClientSubnetNoOverride(DNSDistTest):
     """
-    DNSdist is configured to add the EDNS0 Client Subnet
+    dnsdist is configured to add the EDNS0 Client Subnet
     option, but only if it's not already present in the
     original query.
     """
@@ -162,7 +162,7 @@ class TestEdnsClientSubnetNoOverride(DNSDistTest):
 
 class TestEdnsClientSubnetOverride(DNSDistTest):
     """
-    DNSdist is configured to add the EDNS0 Client Subnet
+    dnsdist is configured to add the EDNS0 Client Subnet
     option, overwriting any existing value.
     """