From: Remi Gacogne Date: Mon, 15 Jul 2019 13:59:09 +0000 (+0200) Subject: dnsdist: Check that we only retry 'retries' times even with TFO X-Git-Tag: dnsdist-1.4.0-rc1~45^2~1 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=1573e7b893f41b0e861bd4778e6985b9fbeb6e3c;p=pdns dnsdist: Check that we only retry 'retries' times even with TFO --- diff --git a/regression-tests.dnsdist/test_TCPFastOpen.py b/regression-tests.dnsdist/test_TCPFastOpen.py new file mode 100644 index 000000000..9b7582734 --- /dev/null +++ b/regression-tests.dnsdist/test_TCPFastOpen.py @@ -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)