]>
Commit | Line | Data |
---|---|---|
dcacff17 RG |
1 | #!/usr/bin/env python |
2 | import struct | |
3 | import time | |
4 | import dns | |
b4f23783 | 5 | from dnsdisttests import DNSDistTest, range |
dcacff17 RG |
6 | |
7 | class TestTCPKeepAlive(DNSDistTest): | |
8 | """ | |
9 | These tests make sure that dnsdist keeps the TCP connection alive | |
10 | in various cases, like cache hits, self-generated answer, and | |
11 | that it doesn't in error cases (Drop, invalid queries...) | |
12 | """ | |
13 | ||
14 | _tcpIdleTimeout = 20 | |
15 | _maxTCPQueriesPerConn = 99 | |
16 | _maxTCPConnsPerClient = 3 | |
17 | _maxTCPConnDuration = 99 | |
18 | _config_template = """ | |
19 | newServer{address="127.0.0.1:%s"} | |
20 | setTCPRecvTimeout(%s) | |
21 | setMaxTCPQueriesPerConnection(%s) | |
22 | setMaxTCPConnectionsPerClient(%s) | |
23 | setMaxTCPConnectionDuration(%s) | |
24 | pc = newPacketCache(100, 86400, 1) | |
25 | getPool(""):setCache(pc) | |
26 | addAction("refused.tcpka.tests.powerdns.com.", RCodeAction(dnsdist.REFUSED)) | |
27 | addAction("dropped.tcpka.tests.powerdns.com.", DropAction()) | |
28 | addResponseAction("dropped-response.tcpka.tests.powerdns.com.", DropResponseAction()) | |
29 | -- create the pool named "nosuchpool" | |
30 | getPool("nosuchpool") | |
31 | addAction("nodownstream-servfail.tcpka.tests.powerdns.com.", PoolAction("nosuchpool")) | |
32 | setServFailWhenNoServer(true) | |
33 | """ | |
34 | _config_params = ['_testServerPort', '_tcpIdleTimeout', '_maxTCPQueriesPerConn', '_maxTCPConnsPerClient', '_maxTCPConnDuration'] | |
35 | ||
36 | def testTCPKaSelfGenerated(self): | |
37 | """ | |
38 | TCP KeepAlive: Self-generated answer | |
39 | """ | |
40 | name = 'refused.tcpka.tests.powerdns.com.' | |
41 | query = dns.message.make_query(name, 'A', 'IN') | |
42 | expectedResponse = dns.message.make_response(query) | |
43 | expectedResponse.set_rcode(dns.rcode.REFUSED) | |
44 | ||
45 | conn = self.openTCPConnection() | |
46 | ||
47 | count = 0 | |
b4f23783 | 48 | for idx in range(5): |
dcacff17 RG |
49 | try: |
50 | self.sendTCPQueryOverConnection(conn, query) | |
51 | response = self.recvTCPResponseOverConnection(conn) | |
52 | if response is None: | |
53 | break | |
54 | self.assertEquals(expectedResponse, response) | |
55 | count = count + 1 | |
56 | except: | |
57 | pass | |
58 | ||
59 | conn.close() | |
60 | self.assertEqual(count, 5) | |
61 | ||
62 | def testTCPKaCacheHit(self): | |
63 | """ | |
64 | TCP KeepAlive: Cache Hit | |
65 | """ | |
66 | name = 'cachehit.tcpka.tests.powerdns.com.' | |
67 | query = dns.message.make_query(name, 'A', 'IN') | |
68 | expectedResponse = dns.message.make_response(query) | |
69 | rrset = dns.rrset.from_text(name, | |
70 | 3600, | |
71 | dns.rdataclass.IN, | |
72 | dns.rdatatype.A, | |
73 | '192.0.2.1') | |
74 | expectedResponse.answer.append(rrset) | |
75 | ||
76 | # first query to fill the cache | |
77 | (receivedQuery, receivedResponse) = self.sendTCPQuery(query, expectedResponse) | |
78 | self.assertTrue(receivedQuery) | |
79 | self.assertTrue(receivedResponse) | |
80 | receivedQuery.id = query.id | |
81 | self.assertEquals(query, receivedQuery) | |
82 | self.assertEquals(receivedResponse, expectedResponse) | |
83 | ||
84 | conn = self.openTCPConnection() | |
85 | ||
86 | count = 0 | |
b4f23783 | 87 | for idx in range(5): |
dcacff17 RG |
88 | try: |
89 | self.sendTCPQueryOverConnection(conn, query) | |
90 | response = self.recvTCPResponseOverConnection(conn) | |
91 | if response is None: | |
92 | break | |
93 | self.assertEquals(expectedResponse, response) | |
94 | count = count + 1 | |
95 | except: | |
96 | pass | |
97 | ||
98 | conn.close() | |
99 | self.assertEqual(count, 5) | |
100 | ||
101 | def testTCPKaNoDownstreamServFail(self): | |
102 | """ | |
103 | TCP KeepAlive: No downstream ServFail | |
104 | ||
105 | The query is routed to a pool that has no server, | |
106 | and dnsdist is configured to send a ServFail when | |
107 | that happens. We should keep the TCP connection open. | |
108 | """ | |
109 | name = 'nodownstream-servfail.tcpka.tests.powerdns.com.' | |
110 | query = dns.message.make_query(name, 'A', 'IN') | |
111 | expectedResponse = dns.message.make_response(query) | |
112 | expectedResponse.set_rcode(dns.rcode.SERVFAIL) | |
113 | ||
114 | conn = self.openTCPConnection() | |
115 | ||
116 | count = 0 | |
b4f23783 | 117 | for idx in range(5): |
dcacff17 RG |
118 | try: |
119 | self.sendTCPQueryOverConnection(conn, query) | |
120 | response = self.recvTCPResponseOverConnection(conn) | |
121 | if response is None: | |
122 | break | |
123 | self.assertEquals(expectedResponse, response) | |
124 | count = count + 1 | |
125 | except: | |
126 | pass | |
127 | ||
128 | conn.close() | |
129 | self.assertEqual(count, 5) | |
130 | ||
131 | def testTCPKaQRBitSet(self): | |
132 | """ | |
133 | TCP KeepAlive: QR bit set in question | |
134 | """ | |
135 | name = 'qrset.tcpka.tests.powerdns.com.' | |
136 | query = dns.message.make_query(name, 'A', 'IN') | |
137 | query.flags |= dns.flags.QR | |
138 | ||
139 | conn = self.openTCPConnection() | |
140 | ||
141 | count = 0 | |
b4f23783 | 142 | for idx in range(5): |
dcacff17 RG |
143 | try: |
144 | self.sendTCPQueryOverConnection(conn, query) | |
145 | response = self.recvTCPResponseOverConnection(conn) | |
146 | if response is None: | |
147 | break | |
148 | count = count + 1 | |
149 | except: | |
150 | pass | |
151 | ||
152 | conn.close() | |
153 | self.assertEqual(count, 0) | |
154 | ||
155 | def testTCPKaDrop(self): | |
156 | """ | |
157 | TCP KeepAlive: Drop | |
158 | """ | |
159 | name = 'dropped.tcpka.tests.powerdns.com.' | |
160 | query = dns.message.make_query(name, 'A', 'IN') | |
161 | query.flags |= dns.flags.QR | |
162 | ||
163 | conn = self.openTCPConnection() | |
164 | ||
165 | count = 0 | |
b4f23783 | 166 | for idx in range(5): |
dcacff17 RG |
167 | try: |
168 | self.sendTCPQueryOverConnection(conn, query) | |
169 | response = self.recvTCPResponseOverConnection(conn) | |
170 | if response is None: | |
171 | break | |
172 | count = count + 1 | |
173 | except: | |
174 | pass | |
175 | ||
176 | conn.close() | |
177 | self.assertEqual(count, 0) | |
178 | ||
179 | def testTCPKaDropResponse(self): | |
180 | """ | |
181 | TCP KeepAlive: Drop Response | |
182 | """ | |
183 | name = 'dropped-response.tcpka.tests.powerdns.com.' | |
184 | query = dns.message.make_query(name, 'A', 'IN') | |
185 | ||
186 | conn = self.openTCPConnection() | |
187 | ||
188 | count = 0 | |
b4f23783 | 189 | for idx in range(5): |
dcacff17 RG |
190 | try: |
191 | self.sendTCPQueryOverConnection(conn, query) | |
192 | response = self.recvTCPResponseOverConnection(conn) | |
193 | if response is None: | |
194 | break | |
195 | count = count + 1 | |
196 | except: | |
197 | pass | |
198 | ||
199 | conn.close() | |
200 | self.assertEqual(count, 0) | |
201 | ||
202 | class TestTCPKeepAliveNoDownstreamDrop(DNSDistTest): | |
203 | """ | |
204 | This test makes sure that dnsdist drops the TCP connection | |
205 | if no downstream server is available and setServFailWhenNoServer() | |
206 | is not set. | |
207 | """ | |
208 | ||
209 | _tcpIdleTimeout = 20 | |
210 | _maxTCPQueriesPerConn = 99 | |
211 | _maxTCPConnsPerClient = 3 | |
212 | _maxTCPConnDuration = 99 | |
213 | _config_template = """ | |
214 | newServer{address="127.0.0.1:%s"} | |
215 | setTCPRecvTimeout(%s) | |
216 | setMaxTCPQueriesPerConnection(%s) | |
217 | setMaxTCPConnectionsPerClient(%s) | |
218 | setMaxTCPConnectionDuration(%s) | |
219 | -- create the pool named "nosuchpool" | |
220 | getPool("nosuchpool") | |
221 | addAction("nodownstream-drop.tcpka.tests.powerdns.com.", PoolAction("nosuchpool")) | |
222 | """ | |
223 | _config_params = ['_testServerPort', '_tcpIdleTimeout', '_maxTCPQueriesPerConn', '_maxTCPConnsPerClient', '_maxTCPConnDuration'] | |
224 | ||
225 | def testTCPKaNoDownstreamDrop(self): | |
226 | """ | |
227 | TCP KeepAlive: No downstream Drop | |
228 | ||
229 | The query is routed to a pool that has no server, | |
230 | and dnsdist is configured to drop the query when | |
231 | that happens. We should close the TCP connection right away. | |
232 | """ | |
233 | name = 'nodownstream-drop.tcpka.tests.powerdns.com.' | |
234 | query = dns.message.make_query(name, 'A', 'IN') | |
235 | ||
236 | conn = self.openTCPConnection() | |
237 | ||
238 | count = 0 | |
b4f23783 | 239 | for idx in range(5): |
dcacff17 RG |
240 | try: |
241 | self.sendTCPQueryOverConnection(conn, query) | |
242 | response = self.recvTCPResponseOverConnection(conn) | |
243 | if response is None: | |
244 | break | |
245 | count = count + 1 | |
246 | except: | |
247 | pass | |
248 | ||
249 | conn.close() | |
250 | self.assertEqual(count, 0) |