]>
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 | |
8 | from dnsdisttests import DNSDistTest | |
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 | |
16 | _testServerPort = 5410 | |
17 | _testServerRetries = 5 | |
18 | _webTimeout = 2.0 | |
19 | _webServerPort = 8083 | |
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): | |
33 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
34 | sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) | |
35 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) | |
36 | try: | |
37 | sock.bind(("127.0.0.1", port)) | |
38 | except socket.error as e: | |
39 | print("Error binding in the TCP responder: %s" % str(e)) | |
40 | sys.exit(1) | |
41 | ||
42 | sock.listen(100) | |
43 | while True: | |
44 | (conn, _) = sock.accept() | |
45 | conn.settimeout(5.0) | |
46 | data = conn.recv(2) | |
47 | if not data: | |
48 | conn.close() | |
49 | continue | |
50 | ||
51 | (datalen,) = struct.unpack("!H", data) | |
52 | data = conn.recv(datalen) | |
53 | conn.close() | |
54 | continue | |
55 | ||
56 | sock.close() | |
57 | ||
58 | @classmethod | |
59 | def startResponders(cls): | |
60 | print("Launching responders..") | |
61 | ||
62 | # Normal responder | |
63 | cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue]) | |
64 | cls._UDPResponder.setDaemon(True) | |
65 | cls._UDPResponder.start() | |
66 | ||
67 | # Close the connection right after reading the query | |
68 | cls._TCPResponder = threading.Thread(name='Broken TCP Responder', target=cls.BrokenTCPResponder, args=[cls._testServerPort]) | |
69 | cls._TCPResponder.setDaemon(True) | |
70 | cls._TCPResponder.start() | |
71 | ||
72 | def testTCOFastOpenOnCloseAfterRead(self): | |
73 | """ | |
74 | TCP Fast Open: Close after read | |
75 | """ | |
76 | name = 'close-after-read.tfo.tests.powerdns.com.' | |
77 | query = dns.message.make_query(name, 'A', 'IN') | |
78 | ||
79 | (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False) | |
80 | self.assertFalse(receivedQuery) | |
81 | self.assertFalse(receivedResponse) | |
82 | ||
83 | headers = {'x-api-key': self._webServerAPIKey} | |
84 | url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost' | |
85 | r = requests.get(url, headers=headers, timeout=self._webTimeout) | |
86 | self.assertTrue(r) | |
4bfebc93 | 87 | self.assertEqual(r.status_code, 200) |
1573e7b8 RG |
88 | self.assertTrue(r.json()) |
89 | content = r.json() | |
90 | self.assertTrue(len(content['servers']), 1) | |
91 | server = content['servers'][0] | |
92 | self.assertIn('tcpDiedReadingResponse', server) | |
4bfebc93 | 93 | self.assertEqual(server['tcpDiedReadingResponse'], self._testServerRetries) |