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