7 from dnsdisttests
import DNSDistTest
, range
9 class DynBlocksTest(DNSDistTest
):
13 _webServerBasicAuthPassword
= 'secret'
14 _webServerAPIKey
= 'apisecret'
16 def doTestDynBlockViaAPI(self
, range, reason
, minSeconds
, maxSeconds
, minBlocks
, maxBlocks
):
17 headers
= {'x-api-key': self
._webServerAPIKey
}
18 url
= 'http://127.0.0.1:' + str(self
._webServerPort
) + '/jsonstat?command=dynblocklist'
19 r
= requests
.get(url
, headers
=headers
, timeout
=self
._webTimeout
)
21 self
.assertEquals(r
.status_code
, 200)
24 self
.assertIsNotNone(content
)
25 self
.assertIn(range, content
)
27 values
= content
[range]
28 for key
in ['reason', 'seconds', 'blocks', 'action']:
29 self
.assertIn(key
, values
)
31 self
.assertEqual(values
['reason'], reason
)
32 self
.assertGreaterEqual(values
['seconds'], minSeconds
)
33 self
.assertLessEqual(values
['seconds'], maxSeconds
)
34 self
.assertGreaterEqual(values
['blocks'], minBlocks
)
35 self
.assertLessEqual(values
['blocks'], maxBlocks
)
37 def doTestQRate(self
, name
, testViaAPI
=True):
38 query
= dns
.message
.make_query(name
, 'A', 'IN')
39 response
= dns
.message
.make_response(query
)
40 rrset
= dns
.rrset
.from_text(name
,
45 response
.answer
.append(rrset
)
49 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
50 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
53 receivedQuery
.id = query
.id
54 self
.assertEquals(query
, receivedQuery
)
55 self
.assertEquals(response
, receivedResponse
)
58 # the query has not reached the responder,
59 # let's clear the response queue
60 self
.clearToResponderQueue()
62 # we might be already blocked, but we should have been able to send
63 # at least self._dynBlockQPS queries
64 self
.assertGreaterEqual(allowed
, self
._dynBlockQPS
)
67 # wait for the maintenance function to run
70 # we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod
71 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
72 self
.assertEquals(receivedResponse
, None)
75 self
.doTestDynBlockViaAPI('127.0.0.1/32', 'Exceeded query rate', self
._dynBlockDuration
- 4, self
._dynBlockDuration
, (sent
-allowed
)+1, (sent
-allowed
)+1)
77 # wait until we are not blocked anymore
78 time
.sleep(self
._dynBlockDuration
+ self
._dynBlockPeriod
)
80 # this one should succeed
81 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
82 receivedQuery
.id = query
.id
83 self
.assertEquals(query
, receivedQuery
)
84 self
.assertEquals(response
, receivedResponse
)
86 # again, over TCP this time
89 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
90 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
93 receivedQuery
.id = query
.id
94 self
.assertEquals(query
, receivedQuery
)
95 self
.assertEquals(response
, receivedResponse
)
98 # the query has not reached the responder,
99 # let's clear the response queue
100 self
.clearToResponderQueue()
102 # we might be already blocked, but we should have been able to send
103 # at least self._dynBlockQPS queries
104 self
.assertGreaterEqual(allowed
, self
._dynBlockQPS
)
107 # wait for the maintenance function to run
110 # we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod
111 (_
, receivedResponse
) = self
.sendTCPQuery(query
, response
=None, useQueue
=False)
112 self
.assertEquals(receivedResponse
, None)
114 # wait until we are not blocked anymore
115 time
.sleep(self
._dynBlockDuration
+ self
._dynBlockPeriod
)
117 # this one should succeed
118 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
119 receivedQuery
.id = query
.id
120 self
.assertEquals(query
, receivedQuery
)
121 self
.assertEquals(response
, receivedResponse
)
123 def doTestQRateRCode(self
, name
, rcode
):
124 query
= dns
.message
.make_query(name
, 'A', 'IN')
125 response
= dns
.message
.make_response(query
)
126 rrset
= dns
.rrset
.from_text(name
,
131 response
.answer
.append(rrset
)
132 expectedResponse
= dns
.message
.make_response(query
)
133 expectedResponse
.set_rcode(rcode
)
137 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
138 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
141 receivedQuery
.id = query
.id
142 self
.assertEquals(query
, receivedQuery
)
143 self
.assertEquals(receivedResponse
, response
)
144 allowed
= allowed
+ 1
146 self
.assertEquals(receivedResponse
, expectedResponse
)
147 # the query has not reached the responder,
148 # let's clear the response queue
149 self
.clearToResponderQueue()
151 # we might be already blocked, but we should have been able to send
152 # at least self._dynBlockQPS queries
153 self
.assertGreaterEqual(allowed
, self
._dynBlockQPS
)
156 # wait for the maintenance function to run
159 # we should now be 'rcode' for up to self._dynBlockDuration + self._dynBlockPeriod
160 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
161 self
.assertEquals(receivedResponse
, expectedResponse
)
163 # wait until we are not blocked anymore
164 time
.sleep(self
._dynBlockDuration
+ self
._dynBlockPeriod
)
166 # this one should succeed
167 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
168 receivedQuery
.id = query
.id
169 self
.assertEquals(query
, receivedQuery
)
170 self
.assertEquals(response
, receivedResponse
)
174 # again, over TCP this time
175 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
176 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
179 receivedQuery
.id = query
.id
180 self
.assertEquals(query
, receivedQuery
)
181 self
.assertEquals(receivedResponse
, response
)
182 allowed
= allowed
+ 1
184 self
.assertEquals(receivedResponse
, expectedResponse
)
185 # the query has not reached the responder,
186 # let's clear the response queue
187 self
.clearToResponderQueue()
189 # we might be already blocked, but we should have been able to send
190 # at least self._dynBlockQPS queries
191 self
.assertGreaterEqual(allowed
, self
._dynBlockQPS
)
194 # wait for the maintenance function to run
197 # we should now be 'rcode' for up to self._dynBlockDuration + self._dynBlockPeriod
198 (_
, receivedResponse
) = self
.sendTCPQuery(query
, response
=None, useQueue
=False)
199 self
.assertEquals(receivedResponse
, expectedResponse
)
201 # wait until we are not blocked anymore
202 time
.sleep(self
._dynBlockDuration
+ self
._dynBlockPeriod
)
204 # this one should succeed
205 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
206 receivedQuery
.id = query
.id
207 self
.assertEquals(query
, receivedQuery
)
208 self
.assertEquals(response
, receivedResponse
)
210 def doTestResponseByteRate(self
, name
):
211 query
= dns
.message
.make_query(name
, 'A', 'IN')
212 response
= dns
.message
.make_response(query
)
213 response
.answer
.append(dns
.rrset
.from_text_list(name
,
217 ['192.0.2.1', '192.0.2.2', '192.0.2.3', '192.0.2.4']))
218 response
.answer
.append(dns
.rrset
.from_text(name
,
229 for _
in range(int(self
._dynBlockBytesPerSecond
* 5 / len(response
.to_wire()))):
230 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
231 sent
= sent
+ len(response
.to_wire())
233 receivedQuery
.id = query
.id
234 self
.assertEquals(query
, receivedQuery
)
235 self
.assertEquals(response
, receivedResponse
)
236 allowed
= allowed
+ len(response
.to_wire())
238 # the query has not reached the responder,
239 # let's clear the response queue
240 self
.clearToResponderQueue()
241 # and stop right there, otherwise we might
242 # wait for so long that the dynblock is gone
243 # by the time we finished
246 # we might be already blocked, but we should have been able to send
247 # at least self._dynBlockBytesPerSecond bytes
251 self
.assertGreaterEqual(allowed
, self
._dynBlockBytesPerSecond
)
253 print(self
.sendConsoleCommand("showDynBlocks()"))
254 print(self
.sendConsoleCommand("grepq(\"\")"))
258 # wait for the maintenance function to run
259 print("Waiting for the maintenance function to run")
262 print(self
.sendConsoleCommand("showDynBlocks()"))
263 print(self
.sendConsoleCommand("grepq(\"\")"))
266 # we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod
267 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
268 self
.assertEquals(receivedResponse
, None)
270 print(self
.sendConsoleCommand("showDynBlocks()"))
271 print(self
.sendConsoleCommand("grepq(\"\")"))
274 # wait until we are not blocked anymore
275 time
.sleep(self
._dynBlockDuration
+ self
._dynBlockPeriod
)
277 print(self
.sendConsoleCommand("showDynBlocks()"))
278 print(self
.sendConsoleCommand("grepq(\"\")"))
281 # this one should succeed
282 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
283 receivedQuery
.id = query
.id
284 self
.assertEquals(query
, receivedQuery
)
285 self
.assertEquals(response
, receivedResponse
)
287 # again, over TCP this time
290 for _
in range(int(self
._dynBlockBytesPerSecond
* 5 / len(response
.to_wire()))):
291 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
292 sent
= sent
+ len(response
.to_wire())
294 receivedQuery
.id = query
.id
295 self
.assertEquals(query
, receivedQuery
)
296 self
.assertEquals(response
, receivedResponse
)
297 allowed
= allowed
+ len(response
.to_wire())
299 # the query has not reached the responder,
300 # let's clear the response queue
301 self
.clearToResponderQueue()
302 # and stop right there, otherwise we might
303 # wait for so long that the dynblock is gone
304 # by the time we finished
307 # we might be already blocked, but we should have been able to send
308 # at least self._dynBlockBytesPerSecond bytes
309 self
.assertGreaterEqual(allowed
, self
._dynBlockBytesPerSecond
)
312 # wait for the maintenance function to run
315 # we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod
316 (_
, receivedResponse
) = self
.sendTCPQuery(query
, response
=None, useQueue
=False)
317 self
.assertEquals(receivedResponse
, None)
319 # wait until we are not blocked anymore
320 time
.sleep(self
._dynBlockDuration
+ self
._dynBlockPeriod
)
322 # this one should succeed
323 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
324 receivedQuery
.id = query
.id
325 self
.assertEquals(query
, receivedQuery
)
326 self
.assertEquals(response
, receivedResponse
)
328 def doTestRCodeRate(self
, name
, rcode
):
329 query
= dns
.message
.make_query(name
, 'A', 'IN')
330 response
= dns
.message
.make_response(query
)
331 rrset
= dns
.rrset
.from_text(name
,
336 response
.answer
.append(rrset
)
337 expectedResponse
= dns
.message
.make_response(query
)
338 expectedResponse
.set_rcode(rcode
)
340 # start with normal responses
341 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
342 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
343 receivedQuery
.id = query
.id
344 self
.assertEquals(query
, receivedQuery
)
345 self
.assertEquals(response
, receivedResponse
)
347 # wait for the maintenance function to run
350 # we should NOT be dropped!
351 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
352 self
.assertEquals(receivedResponse
, response
)
357 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
358 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, expectedResponse
)
361 receivedQuery
.id = query
.id
362 self
.assertEquals(query
, receivedQuery
)
363 self
.assertEquals(expectedResponse
, receivedResponse
)
364 allowed
= allowed
+ 1
366 # the query has not reached the responder,
367 # let's clear the response queue
368 self
.clearToResponderQueue()
370 # we might be already blocked, but we should have been able to send
371 # at least self._dynBlockQPS queries
372 self
.assertGreaterEqual(allowed
, self
._dynBlockQPS
)
375 # wait for the maintenance function to run
378 # we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod
379 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
380 self
.assertEquals(receivedResponse
, None)
382 # wait until we are not blocked anymore
383 time
.sleep(self
._dynBlockDuration
+ self
._dynBlockPeriod
)
385 # this one should succeed
386 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
387 receivedQuery
.id = query
.id
388 self
.assertEquals(query
, receivedQuery
)
389 self
.assertEquals(response
, receivedResponse
)
391 # again, over TCP this time
392 # start with normal responses
393 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
394 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
395 receivedQuery
.id = query
.id
396 self
.assertEquals(query
, receivedQuery
)
397 self
.assertEquals(response
, receivedResponse
)
399 # wait for the maintenance function to run
402 # we should NOT be dropped!
403 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
404 self
.assertEquals(receivedResponse
, response
)
409 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
410 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, expectedResponse
)
413 receivedQuery
.id = query
.id
414 self
.assertEquals(query
, receivedQuery
)
415 self
.assertEquals(expectedResponse
, receivedResponse
)
416 allowed
= allowed
+ 1
418 # the query has not reached the responder,
419 # let's clear the response queue
420 self
.clearToResponderQueue()
422 # we might be already blocked, but we should have been able to send
423 # at least self._dynBlockQPS queries
424 self
.assertGreaterEqual(allowed
, self
._dynBlockQPS
)
427 # wait for the maintenance function to run
430 # we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod
431 (_
, receivedResponse
) = self
.sendTCPQuery(query
, response
=None, useQueue
=False)
432 self
.assertEquals(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
.assertEquals(query
, receivedQuery
)
441 self
.assertEquals(response
, receivedResponse
)
443 class TestDynBlockQPS(DynBlocksTest
):
447 _dynBlockDuration
= 5
448 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort', '_webServerPort', '_webServerBasicAuthPassword', '_webServerAPIKey']
449 _config_template
= """
450 function maintenance()
451 addDynBlocks(exceedQRate(%d, %d), "Exceeded query rate", %d)
453 newServer{address="127.0.0.1:%s"}
454 webserver("127.0.0.1:%s", "%s", "%s")
457 def testDynBlocksQRate(self
):
461 name
= 'qrate.dynblocks.tests.powerdns.com.'
462 self
.doTestQRate(name
)
464 class TestDynBlockGroupQPS(DynBlocksTest
):
468 _dynBlockDuration
= 5
469 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort', '_webServerPort', '_webServerBasicAuthPassword', '_webServerAPIKey']
470 _config_template
= """
471 local dbr = dynBlockRulesGroup()
472 dbr:setQueryRate(%d, %d, "Exceeded query rate", %d)
474 function maintenance()
477 newServer{address="127.0.0.1:%s"}
478 webserver("127.0.0.1:%s", "%s", "%s")
481 def testDynBlocksQRate(self
):
483 Dyn Blocks (Group): QRate
485 name
= 'qrate.group.dynblocks.tests.powerdns.com.'
486 self
.doTestQRate(name
)
489 class TestDynBlockQPSRefused(DynBlocksTest
):
493 _dynBlockDuration
= 5
494 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
495 _config_template
= """
496 function maintenance()
497 addDynBlocks(exceedQRate(%d, %d), "Exceeded query rate", %d)
499 setDynBlocksAction(DNSAction.Refused)
500 newServer{address="127.0.0.1:%s"}
503 def testDynBlocksQRate(self
):
505 Dyn Blocks: QRate refused
507 name
= 'qraterefused.dynblocks.tests.powerdns.com.'
508 self
.doTestQRateRCode(name
, dns
.rcode
.REFUSED
)
510 class TestDynBlockGroupQPSRefused(DynBlocksTest
):
514 _dynBlockDuration
= 5
515 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
516 _config_template
= """
517 local dbr = dynBlockRulesGroup()
518 dbr:setQueryRate(%d, %d, "Exceeded query rate", %d)
520 function maintenance()
523 setDynBlocksAction(DNSAction.Refused)
524 newServer{address="127.0.0.1:%s"}
527 def testDynBlocksQRate(self
):
529 Dyn Blocks (Group): QRate refused
531 name
= 'qraterefused.group.dynblocks.tests.powerdns.com.'
532 self
.doTestQRateRCode(name
, dns
.rcode
.REFUSED
)
534 class TestDynBlockQPSActionRefused(DynBlocksTest
):
538 _dynBlockDuration
= 5
539 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
540 _config_template
= """
541 function maintenance()
542 addDynBlocks(exceedQRate(%d, %d), "Exceeded query rate", %d, DNSAction.Refused)
544 setDynBlocksAction(DNSAction.Drop)
545 newServer{address="127.0.0.1:%s"}
548 def testDynBlocksQRate(self
):
550 Dyn Blocks: QRate refused (action)
552 name
= 'qrateactionrefused.dynblocks.tests.powerdns.com.'
553 self
.doTestQRateRCode(name
, dns
.rcode
.REFUSED
)
555 class TestDynBlockQPSActionNXD(DynBlocksTest
):
559 _dynBlockDuration
= 5
560 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
561 _config_template
= """
562 function maintenance()
563 addDynBlocks(exceedQRate(%d, %d), "Exceeded query rate", %d, DNSAction.Nxdomain)
565 setDynBlocksAction(DNSAction.Drop)
566 newServer{address="127.0.0.1:%s"}
569 def testDynBlocksQRate(self
):
571 Dyn Blocks: QRate NXD (action)
573 name
= 'qrateactionnxd.dynblocks.tests.powerdns.com.'
574 self
.doTestQRateRCode(name
, dns
.rcode
.NXDOMAIN
)
576 class TestDynBlockGroupQPSActionRefused(DynBlocksTest
):
580 _dynBlockDuration
= 5
581 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
582 _config_template
= """
583 local dbr = dynBlockRulesGroup()
584 dbr:setQueryRate(%d, %d, "Exceeded query rate", %d, DNSAction.Refused)
586 function maintenance()
589 setDynBlocksAction(DNSAction.Drop)
590 newServer{address="127.0.0.1:%s"}
593 def testDynBlocksQRate(self
):
595 Dyn Blocks (group): QRate refused (action)
597 name
= 'qrateactionrefused.group.dynblocks.tests.powerdns.com.'
598 self
.doTestQRateRCode(name
, dns
.rcode
.REFUSED
)
600 class TestDynBlockQPSActionTruncated(DNSDistTest
):
604 _dynBlockDuration
= 5
605 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
606 _config_template
= """
607 function maintenance()
608 addDynBlocks(exceedQRate(%d, %d), "Exceeded query rate", %d, DNSAction.Truncate)
610 setDynBlocksAction(DNSAction.Drop)
611 newServer{address="127.0.0.1:%s"}
614 def testDynBlocksQRate(self
):
616 Dyn Blocks: QRate truncated (action)
618 name
= 'qrateactiontruncated.dynblocks.tests.powerdns.com.'
619 query
= dns
.message
.make_query(name
, 'A', 'IN')
620 response
= dns
.message
.make_response(query
)
621 rrset
= dns
.rrset
.from_text(name
,
626 response
.answer
.append(rrset
)
627 truncatedResponse
= dns
.message
.make_response(query
)
628 truncatedResponse
.flags |
= dns
.flags
.TC
632 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
633 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
636 receivedQuery
.id = query
.id
637 self
.assertEquals(query
, receivedQuery
)
638 self
.assertEquals(receivedResponse
, response
)
639 allowed
= allowed
+ 1
641 self
.assertEquals(receivedResponse
, truncatedResponse
)
642 # the query has not reached the responder,
643 # let's clear the response queue
644 self
.clearToResponderQueue()
646 # we might be already truncated, but we should have been able to send
647 # at least self._dynBlockQPS queries
648 self
.assertGreaterEqual(allowed
, self
._dynBlockQPS
)
651 # wait for the maintenance function to run
654 # we should now be 'truncated' for up to self._dynBlockDuration + self._dynBlockPeriod
655 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
656 self
.assertEquals(receivedResponse
, truncatedResponse
)
658 # check over TCP, which should not be truncated
659 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
661 self
.assertEquals(query
, receivedQuery
)
662 self
.assertEquals(receivedResponse
, response
)
664 # wait until we are not blocked anymore
665 time
.sleep(self
._dynBlockDuration
+ self
._dynBlockPeriod
)
667 # this one should succeed
668 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
669 receivedQuery
.id = query
.id
670 self
.assertEquals(query
, receivedQuery
)
671 self
.assertEquals(response
, receivedResponse
)
675 # again, over TCP this time, we should never get truncated!
676 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
677 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
679 self
.assertEquals(query
, receivedQuery
)
680 self
.assertEquals(receivedResponse
, response
)
681 receivedQuery
.id = query
.id
682 allowed
= allowed
+ 1
684 self
.assertEquals(allowed
, sent
)
686 class TestDynBlockServFails(DynBlocksTest
):
690 _dynBlockDuration
= 5
691 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
692 _config_template
= """
693 function maintenance()
694 addDynBlocks(exceedServFails(%d, %d), "Exceeded servfail rate", %d)
696 newServer{address="127.0.0.1:%s"}
699 def testDynBlocksServFailRate(self
):
701 Dyn Blocks: Server Failure Rate
703 name
= 'servfailrate.dynblocks.tests.powerdns.com.'
704 self
.doTestRCodeRate(name
, dns
.rcode
.SERVFAIL
)
706 class TestDynBlockWhitelist(DynBlocksTest
):
710 _dynBlockDuration
= 5
711 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
712 _config_template
= """
714 function maintenance()
715 toBlock = exceedQRate(%d, %d)
716 for addr, count in pairs(toBlock) do
717 if addr:toString() == "127.0.0.1" then
722 addDynBlocks(toBlock, "Exceeded query rate", %d)
725 function spoofrule(dq)
728 return DNSAction.Spoof, "192.0.2.42"
730 return DNSAction.None, ""
733 addAction("whitelisted-test.dynblocks.tests.powerdns.com.", LuaAction(spoofrule))
735 newServer{address="127.0.0.1:%s"}
738 def testWhitelisted(self
):
740 Dyn Blocks: Whitelisted from the dynamic blocks
742 name
= 'whitelisted.dynblocks.tests.powerdns.com.'
743 query
= dns
.message
.make_query(name
, 'A', 'IN')
744 response
= dns
.message
.make_response(query
)
745 rrset
= dns
.rrset
.from_text(name
,
750 response
.answer
.append(rrset
)
754 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
755 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
758 receivedQuery
.id = query
.id
759 self
.assertEquals(query
, receivedQuery
)
760 self
.assertEquals(response
, receivedResponse
)
761 allowed
= allowed
+ 1
763 # the query has not reached the responder,
764 # let's clear the response queue
765 self
.clearToResponderQueue()
767 # we should not have been blocked
768 self
.assertEqual(allowed
, sent
)
770 # wait for the maintenance function to run
773 # we should still not be blocked
774 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
775 receivedQuery
.id = query
.id
776 self
.assertEquals(query
, receivedQuery
)
777 self
.assertEquals(receivedResponse
, receivedResponse
)
779 # check that we would have been blocked without the whitelisting
780 name
= 'whitelisted-test.dynblocks.tests.powerdns.com.'
781 query
= dns
.message
.make_query(name
, 'A', 'IN')
782 # dnsdist set RA = RD for spoofed responses
783 query
.flags
&= ~dns
.flags
.RD
784 expectedResponse
= dns
.message
.make_response(query
)
785 rrset
= dns
.rrset
.from_text(name
,
790 expectedResponse
.answer
.append(rrset
)
791 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
792 self
.assertEquals(receivedResponse
, expectedResponse
)
794 class TestDynBlockGroupServFails(DynBlocksTest
):
798 _dynBlockDuration
= 5
799 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
800 _config_template
= """
801 local dbr = dynBlockRulesGroup()
802 dbr:setRCodeRate(dnsdist.SERVFAIL, %d, %d, "Exceeded query rate", %d)
804 function maintenance()
808 newServer{address="127.0.0.1:%s"}
811 def testDynBlocksServFailRate(self
):
813 Dyn Blocks (group): Server Failure Rate
815 name
= 'servfailrate.group.dynblocks.tests.powerdns.com.'
816 self
.doTestRCodeRate(name
, dns
.rcode
.SERVFAIL
)
818 class TestDynBlockResponseBytes(DynBlocksTest
):
820 _dynBlockBytesPerSecond
= 200
822 _dynBlockDuration
= 5
823 _consoleKey
= DNSDistTest
.generateConsoleKey()
824 _consoleKeyB64
= base64
.b64encode(_consoleKey
).decode('ascii')
825 _config_params
= ['_consoleKeyB64', '_consolePort', '_dynBlockBytesPerSecond', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
826 _config_template
= """
828 controlSocket("127.0.0.1:%s")
829 function maintenance()
830 addDynBlocks(exceedRespByterate(%d, %d), "Exceeded response byterate", %d)
832 newServer{address="127.0.0.1:%s"}
835 def testDynBlocksResponseByteRate(self
):
837 Dyn Blocks: Response Byte Rate
839 name
= 'responsebyterate.dynblocks.tests.powerdns.com.'
840 self
.doTestResponseByteRate(name
)
842 class TestDynBlockGroupResponseBytes(DynBlocksTest
):
844 _dynBlockBytesPerSecond
= 200
846 _dynBlockDuration
= 5
847 _consoleKey
= DNSDistTest
.generateConsoleKey()
848 _consoleKeyB64
= base64
.b64encode(_consoleKey
).decode('ascii')
849 _config_params
= ['_consoleKeyB64', '_consolePort', '_dynBlockBytesPerSecond', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
850 _config_template
= """
852 controlSocket("127.0.0.1:%s")
853 local dbr = dynBlockRulesGroup()
854 dbr:setResponseByteRate(%d, %d, "Exceeded query rate", %d)
856 function maintenance()
860 newServer{address="127.0.0.1:%s"}
863 def testDynBlocksResponseByteRate(self
):
865 Dyn Blocks (group) : Response Byte Rate
867 name
= 'responsebyterate.group.dynblocks.tests.powerdns.com.'
868 self
.doTestResponseByteRate(name
)
870 class TestDynBlockGroupExcluded(DynBlocksTest
):
874 _dynBlockDuration
= 5
875 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
876 _config_template
= """
877 local dbr = dynBlockRulesGroup()
878 dbr:setQueryRate(%d, %d, "Exceeded query rate", %d)
879 dbr:excludeRange("127.0.0.1/32")
881 function maintenance()
885 newServer{address="127.0.0.1:%s"}
888 def testExcluded(self
):
890 Dyn Blocks (group) : Excluded from the dynamic block rules
892 name
= 'excluded.group.dynblocks.tests.powerdns.com.'
893 query
= dns
.message
.make_query(name
, 'A', 'IN')
894 response
= dns
.message
.make_response(query
)
895 rrset
= dns
.rrset
.from_text(name
,
900 response
.answer
.append(rrset
)
904 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
905 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
908 receivedQuery
.id = query
.id
909 self
.assertEquals(query
, receivedQuery
)
910 self
.assertEquals(response
, receivedResponse
)
911 allowed
= allowed
+ 1
913 # the query has not reached the responder,
914 # let's clear the response queue
915 self
.clearToResponderQueue()
917 # we should not have been blocked
918 self
.assertEqual(allowed
, sent
)
920 # wait for the maintenance function to run
923 # we should still not be blocked
924 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
925 receivedQuery
.id = query
.id
926 self
.assertEquals(query
, receivedQuery
)
927 self
.assertEquals(receivedResponse
, receivedResponse
)
929 class TestDynBlockGroupNoOp(DynBlocksTest
):
933 _dynBlockDuration
= 5
934 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort', '_webServerPort', '_webServerBasicAuthPassword', '_webServerAPIKey']
935 _config_template
= """
936 local dbr = dynBlockRulesGroup()
937 dbr:setQueryRate(%d, %d, "Exceeded query rate", %d, DNSAction.NoOp)
939 function maintenance()
943 newServer{address="127.0.0.1:%s"}
944 webserver("127.0.0.1:%s", "%s", "%s")
949 Dyn Blocks (group) : NoOp
951 name
= 'noop.group.dynblocks.tests.powerdns.com.'
952 query
= dns
.message
.make_query(name
, 'A', 'IN')
953 response
= dns
.message
.make_response(query
)
954 rrset
= dns
.rrset
.from_text(name
,
959 response
.answer
.append(rrset
)
963 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
964 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
967 receivedQuery
.id = query
.id
968 self
.assertEquals(query
, receivedQuery
)
969 self
.assertEquals(response
, receivedResponse
)
970 allowed
= allowed
+ 1
972 # the query has not reached the responder,
973 # let's clear the response queue
974 self
.clearToResponderQueue()
976 # a dynamic rule should have been inserted, but the queries should still go on
977 self
.assertEqual(allowed
, sent
)
979 # wait for the maintenance function to run
982 # the rule should still be present, but the queries pass through anyway
983 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
984 receivedQuery
.id = query
.id
985 self
.assertEquals(query
, receivedQuery
)
986 self
.assertEquals(receivedResponse
, receivedResponse
)
988 # check that the rule has been inserted
989 self
.doTestDynBlockViaAPI('127.0.0.1/32', 'Exceeded query rate', self
._dynBlockDuration
- 4, self
._dynBlockDuration
, 0, sent
)
991 class TestDynBlockGroupWarning(DynBlocksTest
):
993 _dynBlockWarningQPS
= 5
996 _dynBlockDuration
= 5
997 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_dynBlockWarningQPS', '_testServerPort', '_webServerPort', '_webServerBasicAuthPassword', '_webServerAPIKey']
998 _config_template
= """
999 local dbr = dynBlockRulesGroup()
1000 dbr:setQueryRate(%d, %d, "Exceeded query rate", %d, DNSAction.Drop, %d)
1002 function maintenance()
1006 newServer{address="127.0.0.1:%s"}
1007 webserver("127.0.0.1:%s", "%s", "%s")
1010 def testWarning(self
):
1012 Dyn Blocks (group) : Warning
1014 name
= 'warning.group.dynblocks.tests.powerdns.com.'
1015 query
= dns
.message
.make_query(name
, 'A', 'IN')
1016 response
= dns
.message
.make_response(query
)
1017 rrset
= dns
.rrset
.from_text(name
,
1022 response
.answer
.append(rrset
)
1026 for _
in range((self
._dynBlockWarningQPS
* self
._dynBlockPeriod
) + 1):
1027 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
1030 receivedQuery
.id = query
.id
1031 self
.assertEquals(query
, receivedQuery
)
1032 self
.assertEquals(response
, receivedResponse
)
1033 allowed
= allowed
+ 1
1035 # the query has not reached the responder,
1036 # let's clear the response queue
1037 self
.clearToResponderQueue()
1039 # a dynamic rule should have been inserted, but the queries should
1040 # still go on because we are still at warning level
1041 self
.assertEqual(allowed
, sent
)
1043 # wait for the maintenance function to run
1046 # the rule should still be present, but the queries pass through anyway
1047 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
1048 receivedQuery
.id = query
.id
1049 self
.assertEquals(query
, receivedQuery
)
1050 self
.assertEquals(receivedResponse
, receivedResponse
)
1052 # check that the rule has been inserted
1053 self
.doTestDynBlockViaAPI('127.0.0.1/32', 'Exceeded query rate', self
._dynBlockDuration
- 4, self
._dynBlockDuration
, 0, sent
)
1055 self
.doTestQRate(name
)