5 from dnsdisttests
import DNSDistTest
, pickAvailablePort
7 _maintenanceWaitTime
= 2
9 def waitForMaintenanceToRun():
10 time
.sleep(_maintenanceWaitTime
)
12 class DynBlocksTest(DNSDistTest
):
15 _webServerPort
= pickAvailablePort()
16 _webServerBasicAuthPassword
= 'secret'
17 _webServerBasicAuthPasswordHashed
= '$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM='
18 _webServerAPIKey
= 'apisecret'
19 _webServerAPIKeyHashed
= '$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso='
22 # this needs to be greater than maintenanceWaitTime
23 _dynBlockDuration
= _maintenanceWaitTime
+ 2
24 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
26 def doTestDynBlockViaAPI(self
, ipRange
, reason
, minSeconds
, maxSeconds
, minBlocks
, maxBlocks
):
27 headers
= {'x-api-key': self
._webServerAPIKey
}
28 url
= 'http://127.0.0.1:' + str(self
._webServerPort
) + '/jsonstat?command=dynblocklist'
29 r
= requests
.get(url
, headers
=headers
, timeout
=self
._webTimeout
)
31 self
.assertEqual(r
.status_code
, 200)
34 self
.assertIsNotNone(content
)
35 self
.assertIn(ipRange
, content
)
37 values
= content
[ipRange
]
38 for key
in ['reason', 'seconds', 'blocks', 'action']:
39 self
.assertIn(key
, values
)
41 self
.assertEqual(values
['reason'], reason
)
42 self
.assertGreaterEqual(values
['seconds'], minSeconds
)
43 self
.assertLessEqual(values
['seconds'], maxSeconds
)
44 self
.assertGreaterEqual(values
['blocks'], minBlocks
)
45 self
.assertLessEqual(values
['blocks'], maxBlocks
)
47 def doTestQRate(self
, name
, testViaAPI
=True):
48 query
= dns
.message
.make_query(name
, 'A', 'IN')
49 response
= dns
.message
.make_response(query
)
50 rrset
= dns
.rrset
.from_text(name
,
55 response
.answer
.append(rrset
)
59 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
60 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
63 receivedQuery
.id = query
.id
64 self
.assertEqual(query
, receivedQuery
)
65 self
.assertEqual(response
, receivedResponse
)
68 # the query has not reached the responder,
69 # let's clear the response queue
70 self
.clearToResponderQueue()
72 # we might be already blocked, but we should have been able to send
73 # at least self._dynBlockQPS queries
74 self
.assertGreaterEqual(allowed
, self
._dynBlockQPS
)
77 waitForMaintenanceToRun()
79 # we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod
80 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False, timeout
=1)
81 self
.assertEqual(receivedResponse
, None)
84 self
.doTestDynBlockViaAPI('127.0.0.1/32', 'Exceeded query rate', 1, self
._dynBlockDuration
, (sent
-allowed
)+1, (sent
-allowed
)+1)
86 # wait until we are not blocked anymore
87 time
.sleep(self
._dynBlockDuration
+ self
._dynBlockPeriod
)
89 # this one should succeed
90 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
91 receivedQuery
.id = query
.id
92 self
.assertEqual(query
, receivedQuery
)
93 self
.assertEqual(response
, receivedResponse
)
95 # again, over TCP this time
98 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
99 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
102 receivedQuery
.id = query
.id
103 self
.assertEqual(query
, receivedQuery
)
104 self
.assertEqual(response
, receivedResponse
)
105 allowed
= allowed
+ 1
107 # the query has not reached the responder,
108 # let's clear the response queue
109 self
.clearToResponderQueue()
111 # we might be already blocked, but we should have been able to send
112 # at least self._dynBlockQPS queries
113 self
.assertGreaterEqual(allowed
, self
._dynBlockQPS
)
116 waitForMaintenanceToRun()
118 # we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod
119 (_
, receivedResponse
) = self
.sendTCPQuery(query
, response
=None, useQueue
=False)
120 self
.assertEqual(receivedResponse
, None)
122 # wait until we are not blocked anymore
123 time
.sleep(self
._dynBlockDuration
+ self
._dynBlockPeriod
)
125 # this one should succeed
126 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
127 receivedQuery
.id = query
.id
128 self
.assertEqual(query
, receivedQuery
)
129 self
.assertEqual(response
, receivedResponse
)
131 def doTestQRateRCode(self
, name
, rcode
):
132 query
= dns
.message
.make_query(name
, 'A', 'IN')
133 response
= dns
.message
.make_response(query
)
134 rrset
= dns
.rrset
.from_text(name
,
139 response
.answer
.append(rrset
)
140 expectedResponse
= dns
.message
.make_response(query
)
141 expectedResponse
.set_rcode(rcode
)
145 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
146 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
149 receivedQuery
.id = query
.id
150 self
.assertEqual(query
, receivedQuery
)
151 self
.assertEqual(receivedResponse
, response
)
152 allowed
= allowed
+ 1
154 self
.assertEqual(receivedResponse
, expectedResponse
)
155 # the query has not reached the responder,
156 # let's clear the response queue
157 self
.clearToResponderQueue()
159 # we might be already blocked, but we should have been able to send
160 # at least self._dynBlockQPS queries
161 self
.assertGreaterEqual(allowed
, self
._dynBlockQPS
)
164 waitForMaintenanceToRun()
166 # we should now be 'rcode' for up to self._dynBlockDuration + self._dynBlockPeriod
167 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
168 self
.assertEqual(receivedResponse
, expectedResponse
)
170 # wait until we are not blocked anymore
171 time
.sleep(self
._dynBlockDuration
+ self
._dynBlockPeriod
)
173 # this one should succeed
174 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
175 receivedQuery
.id = query
.id
176 self
.assertEqual(query
, receivedQuery
)
177 self
.assertEqual(response
, receivedResponse
)
181 # again, over TCP this time
182 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
183 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
186 receivedQuery
.id = query
.id
187 self
.assertEqual(query
, receivedQuery
)
188 self
.assertEqual(receivedResponse
, response
)
189 allowed
= allowed
+ 1
191 self
.assertEqual(receivedResponse
, expectedResponse
)
192 # the query has not reached the responder,
193 # let's clear the response queue
194 self
.clearToResponderQueue()
196 # we might be already blocked, but we should have been able to send
197 # at least self._dynBlockQPS queries
198 self
.assertGreaterEqual(allowed
, self
._dynBlockQPS
)
201 waitForMaintenanceToRun()
203 # we should now be 'rcode' for up to self._dynBlockDuration + self._dynBlockPeriod
204 (_
, receivedResponse
) = self
.sendTCPQuery(query
, response
=None, useQueue
=False)
205 self
.assertEqual(receivedResponse
, expectedResponse
)
207 # wait until we are not blocked anymore
208 time
.sleep(self
._dynBlockDuration
+ self
._dynBlockPeriod
)
210 # this one should succeed
211 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
212 receivedQuery
.id = query
.id
213 self
.assertEqual(query
, receivedQuery
)
214 self
.assertEqual(response
, receivedResponse
)
216 def doTestResponseByteRate(self
, name
, dynBlockBytesPerSecond
):
217 query
= dns
.message
.make_query(name
, 'A', 'IN')
218 response
= dns
.message
.make_response(query
)
219 response
.answer
.append(dns
.rrset
.from_text_list(name
,
223 ['192.0.2.1', '192.0.2.2', '192.0.2.3', '192.0.2.4']))
224 response
.answer
.append(dns
.rrset
.from_text(name
,
235 for _
in range(int(dynBlockBytesPerSecond
* 5 / len(response
.to_wire()))):
236 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
237 sent
= sent
+ len(response
.to_wire())
239 receivedQuery
.id = query
.id
240 self
.assertEqual(query
, receivedQuery
)
241 self
.assertEqual(response
, receivedResponse
)
242 allowed
= allowed
+ len(response
.to_wire())
244 # the query has not reached the responder,
245 # let's clear the response queue
246 self
.clearToResponderQueue()
247 # and stop right there, otherwise we might
248 # wait for so long that the dynblock is gone
249 # by the time we finished
252 # we might be already blocked, but we should have been able to send
253 # at least dynBlockBytesPerSecond bytes
257 self
.assertGreaterEqual(allowed
, dynBlockBytesPerSecond
)
259 print(self
.sendConsoleCommand("showDynBlocks()"))
260 print(self
.sendConsoleCommand("grepq(\"\")"))
264 print("Waiting for the maintenance function to run")
265 waitForMaintenanceToRun()
267 print(self
.sendConsoleCommand("showDynBlocks()"))
268 print(self
.sendConsoleCommand("grepq(\"\")"))
271 # we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod
272 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False, timeout
=1)
273 self
.assertEqual(receivedResponse
, None)
275 print(self
.sendConsoleCommand("showDynBlocks()"))
276 print(self
.sendConsoleCommand("grepq(\"\")"))
279 # wait until we are not blocked anymore
280 time
.sleep(self
._dynBlockDuration
+ self
._dynBlockPeriod
)
282 print(self
.sendConsoleCommand("showDynBlocks()"))
283 print(self
.sendConsoleCommand("grepq(\"\")"))
286 # this one should succeed
287 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
288 receivedQuery
.id = query
.id
289 self
.assertEqual(query
, receivedQuery
)
290 self
.assertEqual(response
, receivedResponse
)
292 # again, over TCP this time
295 for _
in range(int(dynBlockBytesPerSecond
* 5 / len(response
.to_wire()))):
296 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
297 sent
= sent
+ len(response
.to_wire())
299 receivedQuery
.id = query
.id
300 self
.assertEqual(query
, receivedQuery
)
301 self
.assertEqual(response
, receivedResponse
)
302 allowed
= allowed
+ len(response
.to_wire())
304 # the query has not reached the responder,
305 # let's clear the response queue
306 self
.clearToResponderQueue()
307 # and stop right there, otherwise we might
308 # wait for so long that the dynblock is gone
309 # by the time we finished
312 # we might be already blocked, but we should have been able to send
313 # at least dynBlockBytesPerSecond bytes
314 self
.assertGreaterEqual(allowed
, dynBlockBytesPerSecond
)
317 waitForMaintenanceToRun()
319 # we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod
320 (_
, receivedResponse
) = self
.sendTCPQuery(query
, response
=None, useQueue
=False)
321 self
.assertEqual(receivedResponse
, None)
323 # wait until we are not blocked anymore
324 time
.sleep(self
._dynBlockDuration
+ self
._dynBlockPeriod
)
326 # this one should succeed
327 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
328 receivedQuery
.id = query
.id
329 self
.assertEqual(query
, receivedQuery
)
330 self
.assertEqual(response
, receivedResponse
)
332 def doTestRCodeRate(self
, name
, rcode
):
333 query
= dns
.message
.make_query(name
, 'A', 'IN')
334 response
= dns
.message
.make_response(query
)
335 rrset
= dns
.rrset
.from_text(name
,
340 response
.answer
.append(rrset
)
341 expectedResponse
= dns
.message
.make_response(query
)
342 expectedResponse
.set_rcode(rcode
)
344 # start with normal responses
345 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
346 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
347 receivedQuery
.id = query
.id
348 self
.assertEqual(query
, receivedQuery
)
349 self
.assertEqual(response
, receivedResponse
)
351 waitForMaintenanceToRun()
353 # we should NOT be dropped!
354 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
355 self
.assertEqual(receivedResponse
, response
)
360 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
361 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, expectedResponse
)
364 receivedQuery
.id = query
.id
365 self
.assertEqual(query
, receivedQuery
)
366 self
.assertEqual(expectedResponse
, receivedResponse
)
367 allowed
= allowed
+ 1
369 # the query has not reached the responder,
370 # let's clear the response queue
371 self
.clearToResponderQueue()
373 # we might be already blocked, but we should have been able to send
374 # at least self._dynBlockQPS queries
375 self
.assertGreaterEqual(allowed
, self
._dynBlockQPS
)
378 waitForMaintenanceToRun()
380 # we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod
381 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False, timeout
=1)
382 self
.assertEqual(receivedResponse
, None)
384 # wait until we are not blocked anymore
385 time
.sleep(self
._dynBlockDuration
+ self
._dynBlockPeriod
)
387 # this one should succeed
388 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
389 receivedQuery
.id = query
.id
390 self
.assertEqual(query
, receivedQuery
)
391 self
.assertEqual(response
, receivedResponse
)
393 # again, over TCP this time
394 # start with normal responses
395 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
396 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
397 receivedQuery
.id = query
.id
398 self
.assertEqual(query
, receivedQuery
)
399 self
.assertEqual(response
, receivedResponse
)
401 waitForMaintenanceToRun()
403 # we should NOT be dropped!
404 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
405 self
.assertEqual(receivedResponse
, response
)
410 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
411 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, expectedResponse
)
414 receivedQuery
.id = query
.id
415 self
.assertEqual(query
, receivedQuery
)
416 self
.assertEqual(expectedResponse
, receivedResponse
)
417 allowed
= allowed
+ 1
419 # the query has not reached the responder,
420 # let's clear the response queue
421 self
.clearToResponderQueue()
423 # we might be already blocked, but we should have been able to send
424 # at least self._dynBlockQPS queries
425 self
.assertGreaterEqual(allowed
, self
._dynBlockQPS
)
428 waitForMaintenanceToRun()
430 # we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod
431 (_
, receivedResponse
) = self
.sendTCPQuery(query
, response
=None, useQueue
=False)
432 self
.assertEqual(receivedResponse
, None)
434 # wait until we are not blocked anymore
435 time
.sleep(self
._dynBlockDuration
+ self
._dynBlockPeriod
)
437 # this one should succeed
438 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
439 receivedQuery
.id = query
.id
440 self
.assertEqual(query
, receivedQuery
)
441 self
.assertEqual(response
, receivedResponse
)
443 def doTestRCodeRatio(self
, name
, rcode
, noerrorcount
, rcodecount
):
444 query
= dns
.message
.make_query(name
, 'A', 'IN')
445 response
= dns
.message
.make_response(query
)
446 rrset
= dns
.rrset
.from_text(name
,
451 response
.answer
.append(rrset
)
452 expectedResponse
= dns
.message
.make_response(query
)
453 expectedResponse
.set_rcode(rcode
)
455 # start with normal responses
456 for _
in range(noerrorcount
-1):
457 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
458 receivedQuery
.id = query
.id
459 self
.assertEqual(query
, receivedQuery
)
460 self
.assertEqual(response
, receivedResponse
)
462 waitForMaintenanceToRun()
464 # we should NOT be dropped!
465 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
466 self
.assertEqual(receivedResponse
, response
)
471 for _
in range(rcodecount
):
472 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, expectedResponse
)
475 receivedQuery
.id = query
.id
476 self
.assertEqual(query
, receivedQuery
)
477 self
.assertEqual(expectedResponse
, receivedResponse
)
478 allowed
= allowed
+ 1
480 # the query has not reached the responder,
481 # let's clear the response queue
482 self
.clearToResponderQueue()
484 # we should have been able to send all our queries since the minimum number of queries is set to noerrorcount + rcodecount
485 self
.assertGreaterEqual(allowed
, rcodecount
)
487 waitForMaintenanceToRun()
489 # we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod
490 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False, timeout
=1)
491 self
.assertEqual(receivedResponse
, None)
493 # wait until we are not blocked anymore
494 time
.sleep(self
._dynBlockDuration
+ self
._dynBlockPeriod
)
496 # this one should succeed
497 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
498 receivedQuery
.id = query
.id
499 self
.assertEqual(query
, receivedQuery
)
500 self
.assertEqual(response
, receivedResponse
)
502 # again, over TCP this time
503 # start with normal responses
504 for _
in range(noerrorcount
-1):
505 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
506 receivedQuery
.id = query
.id
507 self
.assertEqual(query
, receivedQuery
)
508 self
.assertEqual(response
, receivedResponse
)
510 waitForMaintenanceToRun()
512 # we should NOT be dropped!
513 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
514 self
.assertEqual(receivedResponse
, response
)
519 for _
in range(rcodecount
):
520 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, expectedResponse
)
523 receivedQuery
.id = query
.id
524 self
.assertEqual(query
, receivedQuery
)
525 self
.assertEqual(expectedResponse
, receivedResponse
)
526 allowed
= allowed
+ 1
528 # the query has not reached the responder,
529 # let's clear the response queue
530 self
.clearToResponderQueue()
532 # we should have been able to send all our queries since the minimum number of queries is set to noerrorcount + rcodecount
533 self
.assertGreaterEqual(allowed
, rcodecount
)
535 waitForMaintenanceToRun()
537 # we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod
538 (_
, receivedResponse
) = self
.sendTCPQuery(query
, response
=None, useQueue
=False)
539 self
.assertEqual(receivedResponse
, None)
541 # wait until we are not blocked anymore
542 time
.sleep(self
._dynBlockDuration
+ self
._dynBlockPeriod
)
544 # this one should succeed
545 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
546 receivedQuery
.id = query
.id
547 self
.assertEqual(query
, receivedQuery
)
548 self
.assertEqual(response
, receivedResponse
)