]>
Commit | Line | Data |
---|---|---|
1573e7b8 RG |
1 | #!/usr/bin/env python |
2 | import threading | |
3 | import clientsubnetoption | |
4 | import dns | |
5 | import requests | |
6 | import socket | |
7 | import struct | |
630eb526 | 8 | from dnsdisttests import DNSDistTest, pickAvailablePort |
1573e7b8 RG |
9 | |
10 | class TestBrokenTCPFastOpen(DNSDistTest): | |
11 | ||
12 | # this test suite uses a different responder port | |
13 | # because, contrary to the other ones, its | |
14 | # TCP responder will accept a connection, read the | |
15 | # query then just close the connection right away | |
630eb526 | 16 | _testServerPort = pickAvailablePort() |
1573e7b8 RG |
17 | _testServerRetries = 5 |
18 | _webTimeout = 2.0 | |
630eb526 | 19 | _webServerPort = pickAvailablePort() |
1573e7b8 | 20 | _webServerBasicAuthPassword = 'secret' |
2c0392a5 | 21 | _webServerBasicAuthPasswordHashed = '$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM=' |
1573e7b8 | 22 | _webServerAPIKey = 'apisecret' |
2c0392a5 | 23 | _webServerAPIKeyHashed = '$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso=' |
412f99ef | 24 | _config_params = ['_testServerPort', '_testServerRetries', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed'] |
1573e7b8 RG |
25 | _config_template = """ |
26 | newServer{address="127.0.0.1:%s", useClientSubnet=true, tcpFastOpen=true, retries=%d } | |
fa7e8b5d | 27 | webserver("127.0.0.1:%s") |
cfe95ada | 28 | setWebserverConfig({password="%s", apiKey="%s"}) |
1573e7b8 RG |
29 | """ |
30 | ||
31 | @classmethod | |
32 | def BrokenTCPResponder(cls, port): | |
7373e3a6 | 33 | cls._backgroundThreads[threading.get_native_id()] = True |
1573e7b8 RG |
34 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
35 | sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) | |
36 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) | |
37 | try: | |
38 | sock.bind(("127.0.0.1", port)) | |
39 | except socket.error as e: | |
40 | print("Error binding in the TCP responder: %s" % str(e)) | |
41 | sys.exit(1) | |
42 | ||
43 | sock.listen(100) | |
7373e3a6 | 44 | sock.settimeout(1.0) |
1573e7b8 | 45 | while True: |
7373e3a6 RG |
46 | try: |
47 | (conn, _) = sock.accept() | |
48 | except socket.timeout: | |
49 | if cls._backgroundThreads.get(threading.get_native_id(), False) == False: | |
50 | del cls._backgroundThreads[threading.get_native_id()] | |
51 | break | |
52 | else: | |
53 | continue | |
54 | ||
1573e7b8 RG |
55 | conn.settimeout(5.0) |
56 | data = conn.recv(2) | |
57 | if not data: | |
58 | conn.close() | |
59 | continue | |
60 | ||
61 | (datalen,) = struct.unpack("!H", data) | |
62 | data = conn.recv(datalen) | |
63 | conn.close() | |
64 | continue | |
65 | ||
66 | sock.close() | |
67 | ||
68 | @classmethod | |
69 | def startResponders(cls): | |
70 | print("Launching responders..") | |
71 | ||
72 | # Normal responder | |
73 | cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue]) | |
630eb526 | 74 | cls._UDPResponder.daemon = True |
1573e7b8 RG |
75 | cls._UDPResponder.start() |
76 | ||
77 | # Close the connection right after reading the query | |
78 | cls._TCPResponder = threading.Thread(name='Broken TCP Responder', target=cls.BrokenTCPResponder, args=[cls._testServerPort]) | |
630eb526 | 79 | cls._TCPResponder.daemon = True |
1573e7b8 RG |
80 | cls._TCPResponder.start() |
81 | ||
82 | def testTCOFastOpenOnCloseAfterRead(self): | |
83 | """ | |
84 | TCP Fast Open: Close after read | |
85 | """ | |
86 | name = 'close-after-read.tfo.tests.powerdns.com.' | |
87 | query = dns.message.make_query(name, 'A', 'IN') | |
88 | ||
89 | (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False) | |
90 | self.assertFalse(receivedQuery) | |
91 | self.assertFalse(receivedResponse) | |
92 | ||
93 | headers = {'x-api-key': self._webServerAPIKey} | |
94 | url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost' | |
95 | r = requests.get(url, headers=headers, timeout=self._webTimeout) | |
96 | self.assertTrue(r) | |
4bfebc93 | 97 | self.assertEqual(r.status_code, 200) |
1573e7b8 RG |
98 | self.assertTrue(r.json()) |
99 | content = r.json() | |
100 | self.assertTrue(len(content['servers']), 1) | |
101 | server = content['servers'][0] | |
102 | self.assertIn('tcpDiedReadingResponse', server) | |
4bfebc93 | 103 | self.assertEqual(server['tcpDiedReadingResponse'], self._testServerRetries) |