]> granicus.if.org Git - pdns/commitdiff
dnsdist: Check that we only retry 'retries' times even with TFO
authorRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 15 Jul 2019 13:59:09 +0000 (15:59 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 15 Jul 2019 13:59:50 +0000 (15:59 +0200)
regression-tests.dnsdist/test_TCPFastOpen.py [new file with mode: 0644]

diff --git a/regression-tests.dnsdist/test_TCPFastOpen.py b/regression-tests.dnsdist/test_TCPFastOpen.py
new file mode 100644 (file)
index 0000000..9b75827
--- /dev/null
@@ -0,0 +1,90 @@
+#!/usr/bin/env python
+import threading
+import clientsubnetoption
+import dns
+import requests
+import socket
+import struct
+from dnsdisttests import DNSDistTest
+
+class TestBrokenTCPFastOpen(DNSDistTest):
+
+    # this test suite uses a different responder port
+    # because, contrary to the other ones, its
+    # TCP responder will accept a connection, read the
+    # query then just close the connection right away
+    _testServerPort = 5410
+    _testServerRetries = 5
+    _webTimeout = 2.0
+    _webServerPort = 8083
+    _webServerBasicAuthPassword = 'secret'
+    _webServerAPIKey = 'apisecret'
+    _config_params = ['_testServerPort', '_testServerRetries', '_webServerPort', '_webServerBasicAuthPassword', '_webServerAPIKey']
+    _config_template = """
+    newServer{address="127.0.0.1:%s", useClientSubnet=true, tcpFastOpen=true, retries=%d }
+    webserver("127.0.0.1:%s", "%s", "%s")
+    """
+
+    @classmethod
+    def BrokenTCPResponder(cls, port):
+        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
+        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
+        try:
+            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)
+
+        sock.listen(100)
+        while True:
+            (conn, _) = sock.accept()
+            conn.settimeout(5.0)
+            data = conn.recv(2)
+            if not data:
+                conn.close()
+                continue
+
+            (datalen,) = struct.unpack("!H", data)
+            data = conn.recv(datalen)
+            conn.close()
+            continue
+
+        sock.close()
+
+    @classmethod
+    def startResponders(cls):
+        print("Launching responders..")
+
+        # Normal responder
+        cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+        cls._UDPResponder.setDaemon(True)
+        cls._UDPResponder.start()
+
+        # Close the connection right after reading the query
+        cls._TCPResponder = threading.Thread(name='Broken TCP Responder', target=cls.BrokenTCPResponder, args=[cls._testServerPort])
+        cls._TCPResponder.setDaemon(True)
+        cls._TCPResponder.start()
+
+    def testTCOFastOpenOnCloseAfterRead(self):
+        """
+        TCP Fast Open: Close after read
+        """
+        name = 'close-after-read.tfo.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+
+        (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
+        self.assertFalse(receivedQuery)
+        self.assertFalse(receivedResponse)
+
+        headers = {'x-api-key': self._webServerAPIKey}
+        url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost'
+        r = requests.get(url, headers=headers, timeout=self._webTimeout)
+        self.assertTrue(r)
+        self.assertEquals(r.status_code, 200)
+        self.assertTrue(r.json())
+        content = r.json()
+        self.assertTrue(len(content['servers']), 1)
+        server = content['servers'][0]
+        self.assertIn('tcpDiedReadingResponse', server)
+        self.assertEquals(server['tcpDiedReadingResponse'], self._testServerRetries)