7 from dnsdisttests
import DNSDistTest
13 class DynBlocksTest(DNSDistTest
):
17 _webServerBasicAuthPassword
= 'secret'
18 _webServerAPIKey
= 'apisecret'
20 def doTestDynBlockViaAPI(self
, range, reason
, minSeconds
, maxSeconds
, minBlocks
, maxBlocks
):
21 headers
= {'x-api-key': self
._webServerAPIKey
}
22 url
= 'http://127.0.0.1:' + str(self
._webServerPort
) + '/jsonstat?command=dynblocklist'
23 r
= requests
.get(url
, headers
=headers
, timeout
=self
._webTimeout
)
25 self
.assertEquals(r
.status_code
, 200)
28 self
.assertIsNotNone(content
)
29 self
.assertIn(range, content
)
31 values
= content
[range]
32 for key
in ['reason', 'seconds', 'blocks', 'action']:
33 self
.assertIn(key
, values
)
35 self
.assertEqual(values
['reason'], reason
)
36 self
.assertGreaterEqual(values
['seconds'], minSeconds
)
37 self
.assertLessEqual(values
['seconds'], maxSeconds
)
38 self
.assertGreaterEqual(values
['blocks'], minBlocks
)
39 self
.assertLessEqual(values
['blocks'], maxBlocks
)
41 def doTestQRate(self
, name
, testViaAPI
=True):
42 query
= dns
.message
.make_query(name
, 'A', 'IN')
43 response
= dns
.message
.make_response(query
)
44 rrset
= dns
.rrset
.from_text(name
,
49 response
.answer
.append(rrset
)
53 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
54 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
57 receivedQuery
.id = query
.id
58 self
.assertEquals(query
, receivedQuery
)
59 self
.assertEquals(response
, receivedResponse
)
62 # the query has not reached the responder,
63 # let's clear the response queue
64 self
.clearToResponderQueue()
66 # we might be already blocked, but we should have been able to send
67 # at least self._dynBlockQPS queries
68 self
.assertGreaterEqual(allowed
, self
._dynBlockQPS
)
71 # wait for the maintenance function to run
74 # we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod
75 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
76 self
.assertEquals(receivedResponse
, None)
79 self
.doTestDynBlockViaAPI('127.0.0.1/32', 'Exceeded query rate', self
._dynBlockDuration
- 4, self
._dynBlockDuration
, (sent
-allowed
)+1, (sent
-allowed
)+1)
81 # wait until we are not blocked anymore
82 time
.sleep(self
._dynBlockDuration
+ self
._dynBlockPeriod
)
84 # this one should succeed
85 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
86 receivedQuery
.id = query
.id
87 self
.assertEquals(query
, receivedQuery
)
88 self
.assertEquals(response
, receivedResponse
)
90 # again, over TCP this time
93 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
94 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
97 receivedQuery
.id = query
.id
98 self
.assertEquals(query
, receivedQuery
)
99 self
.assertEquals(response
, receivedResponse
)
100 allowed
= allowed
+ 1
102 # the query has not reached the responder,
103 # let's clear the response queue
104 self
.clearToResponderQueue()
106 # we might be already blocked, but we should have been able to send
107 # at least self._dynBlockQPS queries
108 self
.assertGreaterEqual(allowed
, self
._dynBlockQPS
)
111 # wait for the maintenance function to run
114 # we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod
115 (_
, receivedResponse
) = self
.sendTCPQuery(query
, response
=None, useQueue
=False)
116 self
.assertEquals(receivedResponse
, None)
118 # wait until we are not blocked anymore
119 time
.sleep(self
._dynBlockDuration
+ self
._dynBlockPeriod
)
121 # this one should succeed
122 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
123 receivedQuery
.id = query
.id
124 self
.assertEquals(query
, receivedQuery
)
125 self
.assertEquals(response
, receivedResponse
)
127 def doTestQRateRCode(self
, name
, rcode
):
128 query
= dns
.message
.make_query(name
, 'A', 'IN')
129 response
= dns
.message
.make_response(query
)
130 rrset
= dns
.rrset
.from_text(name
,
135 response
.answer
.append(rrset
)
136 expectedResponse
= dns
.message
.make_response(query
)
137 expectedResponse
.set_rcode(rcode
)
141 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
142 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
145 receivedQuery
.id = query
.id
146 self
.assertEquals(query
, receivedQuery
)
147 self
.assertEquals(receivedResponse
, response
)
148 allowed
= allowed
+ 1
150 self
.assertEquals(receivedResponse
, expectedResponse
)
151 # the query has not reached the responder,
152 # let's clear the response queue
153 self
.clearToResponderQueue()
155 # we might be already blocked, but we should have been able to send
156 # at least self._dynBlockQPS queries
157 self
.assertGreaterEqual(allowed
, self
._dynBlockQPS
)
160 # wait for the maintenance function to run
163 # we should now be 'rcode' for up to self._dynBlockDuration + self._dynBlockPeriod
164 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
165 self
.assertEquals(receivedResponse
, expectedResponse
)
167 # wait until we are not blocked anymore
168 time
.sleep(self
._dynBlockDuration
+ self
._dynBlockPeriod
)
170 # this one should succeed
171 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
172 receivedQuery
.id = query
.id
173 self
.assertEquals(query
, receivedQuery
)
174 self
.assertEquals(response
, receivedResponse
)
178 # again, over TCP this time
179 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
180 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
183 receivedQuery
.id = query
.id
184 self
.assertEquals(query
, receivedQuery
)
185 self
.assertEquals(receivedResponse
, response
)
186 allowed
= allowed
+ 1
188 self
.assertEquals(receivedResponse
, expectedResponse
)
189 # the query has not reached the responder,
190 # let's clear the response queue
191 self
.clearToResponderQueue()
193 # we might be already blocked, but we should have been able to send
194 # at least self._dynBlockQPS queries
195 self
.assertGreaterEqual(allowed
, self
._dynBlockQPS
)
198 # wait for the maintenance function to run
201 # we should now be 'rcode' for up to self._dynBlockDuration + self._dynBlockPeriod
202 (_
, receivedResponse
) = self
.sendTCPQuery(query
, response
=None, useQueue
=False)
203 self
.assertEquals(receivedResponse
, expectedResponse
)
205 # wait until we are not blocked anymore
206 time
.sleep(self
._dynBlockDuration
+ self
._dynBlockPeriod
)
208 # this one should succeed
209 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
210 receivedQuery
.id = query
.id
211 self
.assertEquals(query
, receivedQuery
)
212 self
.assertEquals(response
, receivedResponse
)
214 def doTestResponseByteRate(self
, name
):
215 query
= dns
.message
.make_query(name
, 'A', 'IN')
216 response
= dns
.message
.make_response(query
)
217 response
.answer
.append(dns
.rrset
.from_text_list(name
,
221 ['192.0.2.1', '192.0.2.2', '192.0.2.3', '192.0.2.4']))
222 response
.answer
.append(dns
.rrset
.from_text(name
,
233 for _
in range(int(self
._dynBlockBytesPerSecond
* 5 / len(response
.to_wire()))):
234 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
235 sent
= sent
+ len(response
.to_wire())
237 receivedQuery
.id = query
.id
238 self
.assertEquals(query
, receivedQuery
)
239 self
.assertEquals(response
, receivedResponse
)
240 allowed
= allowed
+ len(response
.to_wire())
242 # the query has not reached the responder,
243 # let's clear the response queue
244 self
.clearToResponderQueue()
245 # and stop right there, otherwise we might
246 # wait for so long that the dynblock is gone
247 # by the time we finished
250 # we might be already blocked, but we should have been able to send
251 # at least self._dynBlockBytesPerSecond bytes
255 self
.assertGreaterEqual(allowed
, self
._dynBlockBytesPerSecond
)
257 print(self
.sendConsoleCommand("showDynBlocks()"))
258 print(self
.sendConsoleCommand("grepq(\"\")"))
262 # wait for the maintenance function to run
263 print("Waiting for the maintenance function to run")
266 print(self
.sendConsoleCommand("showDynBlocks()"))
267 print(self
.sendConsoleCommand("grepq(\"\")"))
270 # we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod
271 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
272 self
.assertEquals(receivedResponse
, None)
274 print(self
.sendConsoleCommand("showDynBlocks()"))
275 print(self
.sendConsoleCommand("grepq(\"\")"))
278 # wait until we are not blocked anymore
279 time
.sleep(self
._dynBlockDuration
+ self
._dynBlockPeriod
)
281 print(self
.sendConsoleCommand("showDynBlocks()"))
282 print(self
.sendConsoleCommand("grepq(\"\")"))
285 # this one should succeed
286 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
287 receivedQuery
.id = query
.id
288 self
.assertEquals(query
, receivedQuery
)
289 self
.assertEquals(response
, receivedResponse
)
291 # again, over TCP this time
294 for _
in range(int(self
._dynBlockBytesPerSecond
* 5 / len(response
.to_wire()))):
295 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
296 sent
= sent
+ len(response
.to_wire())
298 receivedQuery
.id = query
.id
299 self
.assertEquals(query
, receivedQuery
)
300 self
.assertEquals(response
, receivedResponse
)
301 allowed
= allowed
+ len(response
.to_wire())
303 # the query has not reached the responder,
304 # let's clear the response queue
305 self
.clearToResponderQueue()
306 # and stop right there, otherwise we might
307 # wait for so long that the dynblock is gone
308 # by the time we finished
311 # we might be already blocked, but we should have been able to send
312 # at least self._dynBlockBytesPerSecond bytes
313 self
.assertGreaterEqual(allowed
, self
._dynBlockBytesPerSecond
)
316 # wait for the maintenance function to run
319 # we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod
320 (_
, receivedResponse
) = self
.sendTCPQuery(query
, response
=None, useQueue
=False)
321 self
.assertEquals(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
.assertEquals(query
, receivedQuery
)
330 self
.assertEquals(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
.assertEquals(query
, receivedQuery
)
349 self
.assertEquals(response
, receivedResponse
)
351 # wait for the maintenance function to run
354 # we should NOT be dropped!
355 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
356 self
.assertEquals(receivedResponse
, response
)
361 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
362 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, expectedResponse
)
365 receivedQuery
.id = query
.id
366 self
.assertEquals(query
, receivedQuery
)
367 self
.assertEquals(expectedResponse
, receivedResponse
)
368 allowed
= allowed
+ 1
370 # the query has not reached the responder,
371 # let's clear the response queue
372 self
.clearToResponderQueue()
374 # we might be already blocked, but we should have been able to send
375 # at least self._dynBlockQPS queries
376 self
.assertGreaterEqual(allowed
, self
._dynBlockQPS
)
379 # wait for the maintenance function to run
382 # we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod
383 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
384 self
.assertEquals(receivedResponse
, None)
386 # wait until we are not blocked anymore
387 time
.sleep(self
._dynBlockDuration
+ self
._dynBlockPeriod
)
389 # this one should succeed
390 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
391 receivedQuery
.id = query
.id
392 self
.assertEquals(query
, receivedQuery
)
393 self
.assertEquals(response
, receivedResponse
)
395 # again, over TCP this time
396 # start with normal responses
397 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
398 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
399 receivedQuery
.id = query
.id
400 self
.assertEquals(query
, receivedQuery
)
401 self
.assertEquals(response
, receivedResponse
)
403 # wait for the maintenance function to run
406 # we should NOT be dropped!
407 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
408 self
.assertEquals(receivedResponse
, response
)
413 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
414 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, expectedResponse
)
417 receivedQuery
.id = query
.id
418 self
.assertEquals(query
, receivedQuery
)
419 self
.assertEquals(expectedResponse
, receivedResponse
)
420 allowed
= allowed
+ 1
422 # the query has not reached the responder,
423 # let's clear the response queue
424 self
.clearToResponderQueue()
426 # we might be already blocked, but we should have been able to send
427 # at least self._dynBlockQPS queries
428 self
.assertGreaterEqual(allowed
, self
._dynBlockQPS
)
431 # wait for the maintenance function to run
434 # we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod
435 (_
, receivedResponse
) = self
.sendTCPQuery(query
, response
=None, useQueue
=False)
436 self
.assertEquals(receivedResponse
, None)
438 # wait until we are not blocked anymore
439 time
.sleep(self
._dynBlockDuration
+ self
._dynBlockPeriod
)
441 # this one should succeed
442 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
443 receivedQuery
.id = query
.id
444 self
.assertEquals(query
, receivedQuery
)
445 self
.assertEquals(response
, receivedResponse
)
447 class TestDynBlockQPS(DynBlocksTest
):
451 _dynBlockDuration
= 5
452 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort', '_webServerPort', '_webServerBasicAuthPassword', '_webServerAPIKey']
453 _config_template
= """
454 function maintenance()
455 addDynBlocks(exceedQRate(%d, %d), "Exceeded query rate", %d)
457 newServer{address="127.0.0.1:%s"}
458 webserver("127.0.0.1:%s", "%s", "%s")
461 def testDynBlocksQRate(self
):
465 name
= 'qrate.dynblocks.tests.powerdns.com.'
466 self
.doTestQRate(name
)
468 class TestDynBlockGroupQPS(DynBlocksTest
):
472 _dynBlockDuration
= 5
473 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort', '_webServerPort', '_webServerBasicAuthPassword', '_webServerAPIKey']
474 _config_template
= """
475 local dbr = dynBlockRulesGroup()
476 dbr:setQueryRate(%d, %d, "Exceeded query rate", %d)
478 function maintenance()
481 newServer{address="127.0.0.1:%s"}
482 webserver("127.0.0.1:%s", "%s", "%s")
485 def testDynBlocksQRate(self
):
487 Dyn Blocks (Group): QRate
489 name
= 'qrate.group.dynblocks.tests.powerdns.com.'
490 self
.doTestQRate(name
)
493 class TestDynBlockQPSRefused(DynBlocksTest
):
497 _dynBlockDuration
= 5
498 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
499 _config_template
= """
500 function maintenance()
501 addDynBlocks(exceedQRate(%d, %d), "Exceeded query rate", %d)
503 setDynBlocksAction(DNSAction.Refused)
504 newServer{address="127.0.0.1:%s"}
507 def testDynBlocksQRate(self
):
509 Dyn Blocks: QRate refused
511 name
= 'qraterefused.dynblocks.tests.powerdns.com.'
512 self
.doTestQRateRCode(name
, dns
.rcode
.REFUSED
)
514 class TestDynBlockGroupQPSRefused(DynBlocksTest
):
518 _dynBlockDuration
= 5
519 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
520 _config_template
= """
521 local dbr = dynBlockRulesGroup()
522 dbr:setQueryRate(%d, %d, "Exceeded query rate", %d)
524 function maintenance()
527 setDynBlocksAction(DNSAction.Refused)
528 newServer{address="127.0.0.1:%s"}
531 def testDynBlocksQRate(self
):
533 Dyn Blocks (Group): QRate refused
535 name
= 'qraterefused.group.dynblocks.tests.powerdns.com.'
536 self
.doTestQRateRCode(name
, dns
.rcode
.REFUSED
)
538 class TestDynBlockQPSActionRefused(DynBlocksTest
):
542 _dynBlockDuration
= 5
543 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
544 _config_template
= """
545 function maintenance()
546 addDynBlocks(exceedQRate(%d, %d), "Exceeded query rate", %d, DNSAction.Refused)
548 setDynBlocksAction(DNSAction.Drop)
549 newServer{address="127.0.0.1:%s"}
552 def testDynBlocksQRate(self
):
554 Dyn Blocks: QRate refused (action)
556 name
= 'qrateactionrefused.dynblocks.tests.powerdns.com.'
557 self
.doTestQRateRCode(name
, dns
.rcode
.REFUSED
)
559 class TestDynBlockQPSActionNXD(DynBlocksTest
):
563 _dynBlockDuration
= 5
564 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
565 _config_template
= """
566 function maintenance()
567 addDynBlocks(exceedQRate(%d, %d), "Exceeded query rate", %d, DNSAction.Nxdomain)
569 setDynBlocksAction(DNSAction.Drop)
570 newServer{address="127.0.0.1:%s"}
573 def testDynBlocksQRate(self
):
575 Dyn Blocks: QRate NXD (action)
577 name
= 'qrateactionnxd.dynblocks.tests.powerdns.com.'
578 self
.doTestQRateRCode(name
, dns
.rcode
.NXDOMAIN
)
580 class TestDynBlockGroupQPSActionRefused(DynBlocksTest
):
584 _dynBlockDuration
= 5
585 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
586 _config_template
= """
587 local dbr = dynBlockRulesGroup()
588 dbr:setQueryRate(%d, %d, "Exceeded query rate", %d, DNSAction.Refused)
590 function maintenance()
593 setDynBlocksAction(DNSAction.Drop)
594 newServer{address="127.0.0.1:%s"}
597 def testDynBlocksQRate(self
):
599 Dyn Blocks (group): QRate refused (action)
601 name
= 'qrateactionrefused.group.dynblocks.tests.powerdns.com.'
602 self
.doTestQRateRCode(name
, dns
.rcode
.REFUSED
)
604 class TestDynBlockQPSActionTruncated(DNSDistTest
):
608 _dynBlockDuration
= 5
609 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
610 _config_template
= """
611 function maintenance()
612 addDynBlocks(exceedQRate(%d, %d), "Exceeded query rate", %d, DNSAction.Truncate)
614 setDynBlocksAction(DNSAction.Drop)
615 newServer{address="127.0.0.1:%s"}
618 def testDynBlocksQRate(self
):
620 Dyn Blocks: QRate truncated (action)
622 name
= 'qrateactiontruncated.dynblocks.tests.powerdns.com.'
623 query
= dns
.message
.make_query(name
, 'A', 'IN')
624 response
= dns
.message
.make_response(query
)
625 rrset
= dns
.rrset
.from_text(name
,
630 response
.answer
.append(rrset
)
631 truncatedResponse
= dns
.message
.make_response(query
)
632 truncatedResponse
.flags |
= dns
.flags
.TC
636 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
637 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
640 receivedQuery
.id = query
.id
641 self
.assertEquals(query
, receivedQuery
)
642 self
.assertEquals(receivedResponse
, response
)
643 allowed
= allowed
+ 1
645 self
.assertEquals(receivedResponse
, truncatedResponse
)
646 # the query has not reached the responder,
647 # let's clear the response queue
648 self
.clearToResponderQueue()
650 # we might be already truncated, but we should have been able to send
651 # at least self._dynBlockQPS queries
652 self
.assertGreaterEqual(allowed
, self
._dynBlockQPS
)
655 # wait for the maintenance function to run
658 # we should now be 'truncated' for up to self._dynBlockDuration + self._dynBlockPeriod
659 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
660 self
.assertEquals(receivedResponse
, truncatedResponse
)
662 # check over TCP, which should not be truncated
663 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
665 self
.assertEquals(query
, receivedQuery
)
666 self
.assertEquals(receivedResponse
, response
)
668 # wait until we are not blocked anymore
669 time
.sleep(self
._dynBlockDuration
+ self
._dynBlockPeriod
)
671 # this one should succeed
672 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
673 receivedQuery
.id = query
.id
674 self
.assertEquals(query
, receivedQuery
)
675 self
.assertEquals(response
, receivedResponse
)
679 # again, over TCP this time, we should never get truncated!
680 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
681 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
683 self
.assertEquals(query
, receivedQuery
)
684 self
.assertEquals(receivedResponse
, response
)
685 receivedQuery
.id = query
.id
686 allowed
= allowed
+ 1
688 self
.assertEquals(allowed
, sent
)
690 class TestDynBlockServFails(DynBlocksTest
):
694 _dynBlockDuration
= 5
695 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
696 _config_template
= """
697 function maintenance()
698 addDynBlocks(exceedServFails(%d, %d), "Exceeded servfail rate", %d)
700 newServer{address="127.0.0.1:%s"}
703 def testDynBlocksServFailRate(self
):
705 Dyn Blocks: Server Failure Rate
707 name
= 'servfailrate.dynblocks.tests.powerdns.com.'
708 self
.doTestRCodeRate(name
, dns
.rcode
.SERVFAIL
)
710 class TestDynBlockWhitelist(DynBlocksTest
):
714 _dynBlockDuration
= 5
715 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
716 _config_template
= """
718 function maintenance()
719 toBlock = exceedQRate(%d, %d)
720 for addr, count in pairs(toBlock) do
721 if addr:toString() == "127.0.0.1" then
726 addDynBlocks(toBlock, "Exceeded query rate", %d)
729 function spoofrule(dq)
732 return DNSAction.Spoof, "192.0.2.42"
734 return DNSAction.None, ""
737 addAction("whitelisted-test.dynblocks.tests.powerdns.com.", LuaAction(spoofrule))
739 newServer{address="127.0.0.1:%s"}
742 def testWhitelisted(self
):
744 Dyn Blocks: Whitelisted from the dynamic blocks
746 name
= 'whitelisted.dynblocks.tests.powerdns.com.'
747 query
= dns
.message
.make_query(name
, 'A', 'IN')
748 response
= dns
.message
.make_response(query
)
749 rrset
= dns
.rrset
.from_text(name
,
754 response
.answer
.append(rrset
)
758 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
759 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
762 receivedQuery
.id = query
.id
763 self
.assertEquals(query
, receivedQuery
)
764 self
.assertEquals(response
, receivedResponse
)
765 allowed
= allowed
+ 1
767 # the query has not reached the responder,
768 # let's clear the response queue
769 self
.clearToResponderQueue()
771 # we should not have been blocked
772 self
.assertEqual(allowed
, sent
)
774 # wait for the maintenance function to run
777 # we should still not be blocked
778 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
779 receivedQuery
.id = query
.id
780 self
.assertEquals(query
, receivedQuery
)
781 self
.assertEquals(receivedResponse
, receivedResponse
)
783 # check that we would have been blocked without the whitelisting
784 name
= 'whitelisted-test.dynblocks.tests.powerdns.com.'
785 query
= dns
.message
.make_query(name
, 'A', 'IN')
786 # dnsdist set RA = RD for spoofed responses
787 query
.flags
&= ~dns
.flags
.RD
788 expectedResponse
= dns
.message
.make_response(query
)
789 rrset
= dns
.rrset
.from_text(name
,
794 expectedResponse
.answer
.append(rrset
)
795 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
796 self
.assertEquals(receivedResponse
, expectedResponse
)
798 class TestDynBlockGroupServFails(DynBlocksTest
):
802 _dynBlockDuration
= 5
803 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
804 _config_template
= """
805 local dbr = dynBlockRulesGroup()
806 dbr:setRCodeRate(DNSRCode.SERVFAIL, %d, %d, "Exceeded query rate", %d)
808 function maintenance()
812 newServer{address="127.0.0.1:%s"}
815 def testDynBlocksServFailRate(self
):
817 Dyn Blocks (group): Server Failure Rate
819 name
= 'servfailrate.group.dynblocks.tests.powerdns.com.'
820 self
.doTestRCodeRate(name
, dns
.rcode
.SERVFAIL
)
822 class TestDynBlockResponseBytes(DynBlocksTest
):
824 _dynBlockBytesPerSecond
= 200
826 _dynBlockDuration
= 5
827 _consoleKey
= DNSDistTest
.generateConsoleKey()
828 _consoleKeyB64
= base64
.b64encode(_consoleKey
).decode('ascii')
829 _config_params
= ['_consoleKeyB64', '_consolePort', '_dynBlockBytesPerSecond', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
830 _config_template
= """
832 controlSocket("127.0.0.1:%s")
833 function maintenance()
834 addDynBlocks(exceedRespByterate(%d, %d), "Exceeded response byterate", %d)
836 newServer{address="127.0.0.1:%s"}
839 def testDynBlocksResponseByteRate(self
):
841 Dyn Blocks: Response Byte Rate
843 name
= 'responsebyterate.dynblocks.tests.powerdns.com.'
844 self
.doTestResponseByteRate(name
)
846 class TestDynBlockGroupResponseBytes(DynBlocksTest
):
848 _dynBlockBytesPerSecond
= 200
850 _dynBlockDuration
= 5
851 _consoleKey
= DNSDistTest
.generateConsoleKey()
852 _consoleKeyB64
= base64
.b64encode(_consoleKey
).decode('ascii')
853 _config_params
= ['_consoleKeyB64', '_consolePort', '_dynBlockBytesPerSecond', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
854 _config_template
= """
856 controlSocket("127.0.0.1:%s")
857 local dbr = dynBlockRulesGroup()
858 dbr:setResponseByteRate(%d, %d, "Exceeded query rate", %d)
860 function maintenance()
864 newServer{address="127.0.0.1:%s"}
867 def testDynBlocksResponseByteRate(self
):
869 Dyn Blocks (group) : Response Byte Rate
871 name
= 'responsebyterate.group.dynblocks.tests.powerdns.com.'
872 self
.doTestResponseByteRate(name
)
874 class TestDynBlockGroupExcluded(DynBlocksTest
):
878 _dynBlockDuration
= 5
879 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
880 _config_template
= """
881 local dbr = dynBlockRulesGroup()
882 dbr:setQueryRate(%d, %d, "Exceeded query rate", %d)
883 dbr:excludeRange("127.0.0.1/32")
885 function maintenance()
889 newServer{address="127.0.0.1:%s"}
892 def testExcluded(self
):
894 Dyn Blocks (group) : Excluded from the dynamic block rules
896 name
= 'excluded.group.dynblocks.tests.powerdns.com.'
897 query
= dns
.message
.make_query(name
, 'A', 'IN')
898 response
= dns
.message
.make_response(query
)
899 rrset
= dns
.rrset
.from_text(name
,
904 response
.answer
.append(rrset
)
908 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
909 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
912 receivedQuery
.id = query
.id
913 self
.assertEquals(query
, receivedQuery
)
914 self
.assertEquals(response
, receivedResponse
)
915 allowed
= allowed
+ 1
917 # the query has not reached the responder,
918 # let's clear the response queue
919 self
.clearToResponderQueue()
921 # we should not have been blocked
922 self
.assertEqual(allowed
, sent
)
924 # wait for the maintenance function to run
927 # we should still not be blocked
928 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
929 receivedQuery
.id = query
.id
930 self
.assertEquals(query
, receivedQuery
)
931 self
.assertEquals(receivedResponse
, receivedResponse
)
933 class TestDynBlockGroupNoOp(DynBlocksTest
):
937 _dynBlockDuration
= 5
938 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort', '_webServerPort', '_webServerBasicAuthPassword', '_webServerAPIKey']
939 _config_template
= """
940 local dbr = dynBlockRulesGroup()
941 dbr:setQueryRate(%d, %d, "Exceeded query rate", %d, DNSAction.NoOp)
943 function maintenance()
947 newServer{address="127.0.0.1:%s"}
948 webserver("127.0.0.1:%s", "%s", "%s")
953 Dyn Blocks (group) : NoOp
955 name
= 'noop.group.dynblocks.tests.powerdns.com.'
956 query
= dns
.message
.make_query(name
, 'A', 'IN')
957 response
= dns
.message
.make_response(query
)
958 rrset
= dns
.rrset
.from_text(name
,
963 response
.answer
.append(rrset
)
967 for _
in range((self
._dynBlockQPS
* self
._dynBlockPeriod
) + 1):
968 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
971 receivedQuery
.id = query
.id
972 self
.assertEquals(query
, receivedQuery
)
973 self
.assertEquals(response
, receivedResponse
)
974 allowed
= allowed
+ 1
976 # the query has not reached the responder,
977 # let's clear the response queue
978 self
.clearToResponderQueue()
980 # a dynamic rule should have been inserted, but the queries should still go on
981 self
.assertEqual(allowed
, sent
)
983 # wait for the maintenance function to run
986 # the rule should still be present, but the queries pass through anyway
987 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
988 receivedQuery
.id = query
.id
989 self
.assertEquals(query
, receivedQuery
)
990 self
.assertEquals(receivedResponse
, receivedResponse
)
992 # check that the rule has been inserted
993 self
.doTestDynBlockViaAPI('127.0.0.1/32', 'Exceeded query rate', self
._dynBlockDuration
- 4, self
._dynBlockDuration
, 0, sent
)
995 class TestDynBlockGroupWarning(DynBlocksTest
):
997 _dynBlockWarningQPS
= 5
1000 _dynBlockDuration
= 5
1001 _config_params
= ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_dynBlockWarningQPS', '_testServerPort', '_webServerPort', '_webServerBasicAuthPassword', '_webServerAPIKey']
1002 _config_template
= """
1003 local dbr = dynBlockRulesGroup()
1004 dbr:setQueryRate(%d, %d, "Exceeded query rate", %d, DNSAction.Drop, %d)
1006 function maintenance()
1010 newServer{address="127.0.0.1:%s"}
1011 webserver("127.0.0.1:%s", "%s", "%s")
1014 def testWarning(self
):
1016 Dyn Blocks (group) : Warning
1018 name
= 'warning.group.dynblocks.tests.powerdns.com.'
1019 query
= dns
.message
.make_query(name
, 'A', 'IN')
1020 response
= dns
.message
.make_response(query
)
1021 rrset
= dns
.rrset
.from_text(name
,
1026 response
.answer
.append(rrset
)
1030 for _
in range((self
._dynBlockWarningQPS
* self
._dynBlockPeriod
) + 1):
1031 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
1034 receivedQuery
.id = query
.id
1035 self
.assertEquals(query
, receivedQuery
)
1036 self
.assertEquals(response
, receivedResponse
)
1037 allowed
= allowed
+ 1
1039 # the query has not reached the responder,
1040 # let's clear the response queue
1041 self
.clearToResponderQueue()
1043 # a dynamic rule should have been inserted, but the queries should
1044 # still go on because we are still at warning level
1045 self
.assertEqual(allowed
, sent
)
1047 # wait for the maintenance function to run
1050 # the rule should still be present, but the queries pass through anyway
1051 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
1052 receivedQuery
.id = query
.id
1053 self
.assertEquals(query
, receivedQuery
)
1054 self
.assertEquals(receivedResponse
, receivedResponse
)
1056 # check that the rule has been inserted
1057 self
.doTestDynBlockViaAPI('127.0.0.1/32', 'Exceeded query rate', self
._dynBlockDuration
- 4, self
._dynBlockDuration
, 0, sent
)
1059 self
.doTestQRate(name
)