]>
Commit | Line | Data |
---|---|---|
d354e773 | 1 | #!/usr/bin/env python |
87b0577d | 2 | import base64 |
d3473b8c RG |
3 | import json |
4 | import requests | |
d354e773 RG |
5 | import time |
6 | import dns | |
b4f23783 | 7 | from dnsdisttests import DNSDistTest, range |
d354e773 | 8 | |
dc2fd93a | 9 | class DynBlocksTest(DNSDistTest): |
d354e773 | 10 | |
d3473b8c RG |
11 | _webTimeout = 2.0 |
12 | _webServerPort = 8083 | |
13 | _webServerBasicAuthPassword = 'secret' | |
14 | _webServerAPIKey = 'apisecret' | |
15 | ||
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) | |
20 | self.assertTrue(r) | |
21 | self.assertEquals(r.status_code, 200) | |
22 | ||
23 | content = r.json() | |
24 | self.assertIsNotNone(content) | |
25 | self.assertIn(range, content) | |
26 | ||
27 | values = content[range] | |
477c86a0 | 28 | for key in ['reason', 'seconds', 'blocks', 'action']: |
d3473b8c RG |
29 | self.assertIn(key, values) |
30 | ||
31 | self.assertEqual(values['reason'], reason) | |
b8753918 | 32 | self.assertGreaterEqual(values['seconds'], minSeconds) |
d3473b8c RG |
33 | self.assertLessEqual(values['seconds'], maxSeconds) |
34 | self.assertGreaterEqual(values['blocks'], minBlocks) | |
35 | self.assertLessEqual(values['blocks'], maxBlocks) | |
36 | ||
e44df0f1 | 37 | def doTestQRate(self, name, testViaAPI=True): |
d354e773 RG |
38 | query = dns.message.make_query(name, 'A', 'IN') |
39 | response = dns.message.make_response(query) | |
40 | rrset = dns.rrset.from_text(name, | |
41 | 60, | |
42 | dns.rdataclass.IN, | |
43 | dns.rdatatype.A, | |
44 | '192.0.2.1') | |
45 | response.answer.append(rrset) | |
46 | ||
3bef39c3 RG |
47 | allowed = 0 |
48 | sent = 0 | |
b4f23783 | 49 | for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1): |
d354e773 | 50 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) |
3bef39c3 RG |
51 | sent = sent + 1 |
52 | if receivedQuery: | |
53 | receivedQuery.id = query.id | |
54 | self.assertEquals(query, receivedQuery) | |
55 | self.assertEquals(response, receivedResponse) | |
56 | allowed = allowed + 1 | |
57 | else: | |
58 | # the query has not reached the responder, | |
59 | # let's clear the response queue | |
98883b8f | 60 | self.clearToResponderQueue() |
3bef39c3 RG |
61 | |
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) | |
65 | ||
66 | if allowed == sent: | |
67 | # wait for the maintenance function to run | |
68 | time.sleep(2) | |
d354e773 RG |
69 | |
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) | |
73 | ||
d3473b8c RG |
74 | if testViaAPI: |
75 | self.doTestDynBlockViaAPI('127.0.0.1/32', 'Exceeded query rate', self._dynBlockDuration - 4, self._dynBlockDuration, (sent-allowed)+1, (sent-allowed)+1) | |
76 | ||
3bef39c3 | 77 | # wait until we are not blocked anymore |
d354e773 RG |
78 | time.sleep(self._dynBlockDuration + self._dynBlockPeriod) |
79 | ||
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) | |
85 | ||
86 | # again, over TCP this time | |
3bef39c3 RG |
87 | allowed = 0 |
88 | sent = 0 | |
b4f23783 | 89 | for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1): |
d354e773 | 90 | (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response) |
3bef39c3 RG |
91 | sent = sent + 1 |
92 | if receivedQuery: | |
93 | receivedQuery.id = query.id | |
94 | self.assertEquals(query, receivedQuery) | |
95 | self.assertEquals(response, receivedResponse) | |
96 | allowed = allowed + 1 | |
97 | else: | |
98 | # the query has not reached the responder, | |
99 | # let's clear the response queue | |
98883b8f | 100 | self.clearToResponderQueue() |
3bef39c3 RG |
101 | |
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) | |
105 | ||
106 | if allowed == sent: | |
107 | # wait for the maintenance function to run | |
108 | time.sleep(2) | |
d354e773 RG |
109 | |
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) | |
113 | ||
3bef39c3 | 114 | # wait until we are not blocked anymore |
d354e773 RG |
115 | time.sleep(self._dynBlockDuration + self._dynBlockPeriod) |
116 | ||
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) | |
122 | ||
dc2fd93a | 123 | def doTestQRateRCode(self, name, rcode): |
d354e773 RG |
124 | query = dns.message.make_query(name, 'A', 'IN') |
125 | response = dns.message.make_response(query) | |
126 | rrset = dns.rrset.from_text(name, | |
127 | 60, | |
128 | dns.rdataclass.IN, | |
129 | dns.rdatatype.A, | |
130 | '192.0.2.1') | |
131 | response.answer.append(rrset) | |
dc2fd93a RG |
132 | expectedResponse = dns.message.make_response(query) |
133 | expectedResponse.set_rcode(rcode) | |
d354e773 | 134 | |
3bef39c3 RG |
135 | allowed = 0 |
136 | sent = 0 | |
b4f23783 | 137 | for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1): |
d354e773 | 138 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) |
7b925432 RG |
139 | sent = sent + 1 |
140 | if receivedQuery: | |
141 | receivedQuery.id = query.id | |
142 | self.assertEquals(query, receivedQuery) | |
143 | self.assertEquals(receivedResponse, response) | |
144 | allowed = allowed + 1 | |
145 | else: | |
dc2fd93a | 146 | self.assertEquals(receivedResponse, expectedResponse) |
7b925432 RG |
147 | # the query has not reached the responder, |
148 | # let's clear the response queue | |
149 | self.clearToResponderQueue() | |
150 | ||
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) | |
154 | ||
155 | if allowed == sent: | |
156 | # wait for the maintenance function to run | |
157 | time.sleep(2) | |
158 | ||
dc2fd93a | 159 | # we should now be 'rcode' for up to self._dynBlockDuration + self._dynBlockPeriod |
7b925432 | 160 | (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False) |
dc2fd93a | 161 | self.assertEquals(receivedResponse, expectedResponse) |
7b925432 RG |
162 | |
163 | # wait until we are not blocked anymore | |
164 | time.sleep(self._dynBlockDuration + self._dynBlockPeriod) | |
165 | ||
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) | |
171 | ||
172 | allowed = 0 | |
173 | sent = 0 | |
174 | # again, over TCP this time | |
b4f23783 | 175 | for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1): |
7b925432 RG |
176 | (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response) |
177 | sent = sent + 1 | |
178 | if receivedQuery: | |
179 | receivedQuery.id = query.id | |
180 | self.assertEquals(query, receivedQuery) | |
181 | self.assertEquals(receivedResponse, response) | |
182 | allowed = allowed + 1 | |
183 | else: | |
dc2fd93a | 184 | self.assertEquals(receivedResponse, expectedResponse) |
7b925432 RG |
185 | # the query has not reached the responder, |
186 | # let's clear the response queue | |
187 | self.clearToResponderQueue() | |
188 | ||
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) | |
192 | ||
193 | if allowed == sent: | |
194 | # wait for the maintenance function to run | |
195 | time.sleep(2) | |
196 | ||
dc2fd93a | 197 | # we should now be 'rcode' for up to self._dynBlockDuration + self._dynBlockPeriod |
7b925432 | 198 | (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False) |
dc2fd93a | 199 | self.assertEquals(receivedResponse, expectedResponse) |
7b925432 RG |
200 | |
201 | # wait until we are not blocked anymore | |
202 | time.sleep(self._dynBlockDuration + self._dynBlockPeriod) | |
203 | ||
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) | |
209 | ||
dc2fd93a | 210 | def doTestResponseByteRate(self, name): |
7b925432 RG |
211 | query = dns.message.make_query(name, 'A', 'IN') |
212 | response = dns.message.make_response(query) | |
dc2fd93a RG |
213 | response.answer.append(dns.rrset.from_text_list(name, |
214 | 60, | |
215 | dns.rdataclass.IN, | |
216 | dns.rdatatype.A, | |
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, | |
219 | 60, | |
220 | dns.rdataclass.IN, | |
221 | dns.rdatatype.AAAA, | |
222 | '2001:DB8::1')) | |
7b925432 RG |
223 | |
224 | allowed = 0 | |
225 | sent = 0 | |
dc2fd93a RG |
226 | |
227 | print(time.time()) | |
228 | ||
229 | for _ in range(int(self._dynBlockBytesPerSecond * 5 / len(response.to_wire()))): | |
7b925432 | 230 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) |
dc2fd93a | 231 | sent = sent + len(response.to_wire()) |
3bef39c3 RG |
232 | if receivedQuery: |
233 | receivedQuery.id = query.id | |
234 | self.assertEquals(query, receivedQuery) | |
dc2fd93a RG |
235 | self.assertEquals(response, receivedResponse) |
236 | allowed = allowed + len(response.to_wire()) | |
3bef39c3 | 237 | else: |
3bef39c3 RG |
238 | # the query has not reached the responder, |
239 | # let's clear the response queue | |
98883b8f | 240 | self.clearToResponderQueue() |
dc2fd93a RG |
241 | # and stop right there, otherwise we might |
242 | # wait for so long that the dynblock is gone | |
243 | # by the time we finished | |
244 | break | |
3bef39c3 RG |
245 | |
246 | # we might be already blocked, but we should have been able to send | |
dc2fd93a RG |
247 | # at least self._dynBlockBytesPerSecond bytes |
248 | print(allowed) | |
249 | print(sent) | |
250 | print(time.time()) | |
251 | self.assertGreaterEqual(allowed, self._dynBlockBytesPerSecond) | |
252 | ||
253 | print(self.sendConsoleCommand("showDynBlocks()")) | |
254 | print(self.sendConsoleCommand("grepq(\"\")")) | |
255 | print(time.time()) | |
3bef39c3 RG |
256 | |
257 | if allowed == sent: | |
258 | # wait for the maintenance function to run | |
dc2fd93a | 259 | print("Waiting for the maintenance function to run") |
3bef39c3 | 260 | time.sleep(2) |
d354e773 | 261 | |
dc2fd93a RG |
262 | print(self.sendConsoleCommand("showDynBlocks()")) |
263 | print(self.sendConsoleCommand("grepq(\"\")")) | |
264 | print(time.time()) | |
265 | ||
266 | # we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod | |
d354e773 | 267 | (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False) |
dc2fd93a RG |
268 | self.assertEquals(receivedResponse, None) |
269 | ||
270 | print(self.sendConsoleCommand("showDynBlocks()")) | |
271 | print(self.sendConsoleCommand("grepq(\"\")")) | |
272 | print(time.time()) | |
d354e773 | 273 | |
3bef39c3 | 274 | # wait until we are not blocked anymore |
d354e773 RG |
275 | time.sleep(self._dynBlockDuration + self._dynBlockPeriod) |
276 | ||
dc2fd93a RG |
277 | print(self.sendConsoleCommand("showDynBlocks()")) |
278 | print(self.sendConsoleCommand("grepq(\"\")")) | |
279 | print(time.time()) | |
280 | ||
d354e773 RG |
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) | |
286 | ||
dc2fd93a | 287 | # again, over TCP this time |
3bef39c3 RG |
288 | allowed = 0 |
289 | sent = 0 | |
dc2fd93a | 290 | for _ in range(int(self._dynBlockBytesPerSecond * 5 / len(response.to_wire()))): |
d354e773 | 291 | (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response) |
dc2fd93a | 292 | sent = sent + len(response.to_wire()) |
3bef39c3 RG |
293 | if receivedQuery: |
294 | receivedQuery.id = query.id | |
295 | self.assertEquals(query, receivedQuery) | |
dc2fd93a RG |
296 | self.assertEquals(response, receivedResponse) |
297 | allowed = allowed + len(response.to_wire()) | |
3bef39c3 | 298 | else: |
3bef39c3 RG |
299 | # the query has not reached the responder, |
300 | # let's clear the response queue | |
98883b8f | 301 | self.clearToResponderQueue() |
dc2fd93a RG |
302 | # and stop right there, otherwise we might |
303 | # wait for so long that the dynblock is gone | |
304 | # by the time we finished | |
305 | break | |
3bef39c3 RG |
306 | |
307 | # we might be already blocked, but we should have been able to send | |
dc2fd93a RG |
308 | # at least self._dynBlockBytesPerSecond bytes |
309 | self.assertGreaterEqual(allowed, self._dynBlockBytesPerSecond) | |
3bef39c3 RG |
310 | |
311 | if allowed == sent: | |
312 | # wait for the maintenance function to run | |
313 | time.sleep(2) | |
d354e773 | 314 | |
dc2fd93a | 315 | # we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod |
d354e773 | 316 | (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False) |
dc2fd93a | 317 | self.assertEquals(receivedResponse, None) |
d354e773 | 318 | |
3bef39c3 | 319 | # wait until we are not blocked anymore |
d354e773 RG |
320 | time.sleep(self._dynBlockDuration + self._dynBlockPeriod) |
321 | ||
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) | |
327 | ||
dc2fd93a | 328 | def doTestRCodeRate(self, name, rcode): |
d354e773 RG |
329 | query = dns.message.make_query(name, 'A', 'IN') |
330 | response = dns.message.make_response(query) | |
331 | rrset = dns.rrset.from_text(name, | |
332 | 60, | |
333 | dns.rdataclass.IN, | |
334 | dns.rdatatype.A, | |
335 | '192.0.2.1') | |
336 | response.answer.append(rrset) | |
dc2fd93a RG |
337 | expectedResponse = dns.message.make_response(query) |
338 | expectedResponse.set_rcode(rcode) | |
d354e773 RG |
339 | |
340 | # start with normal responses | |
b4f23783 | 341 | for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1): |
d354e773 RG |
342 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) |
343 | receivedQuery.id = query.id | |
344 | self.assertEquals(query, receivedQuery) | |
345 | self.assertEquals(response, receivedResponse) | |
346 | ||
3bef39c3 RG |
347 | # wait for the maintenance function to run |
348 | time.sleep(2) | |
d354e773 RG |
349 | |
350 | # we should NOT be dropped! | |
351 | (_, receivedResponse) = self.sendUDPQuery(query, response) | |
352 | self.assertEquals(receivedResponse, response) | |
353 | ||
dc2fd93a | 354 | # now with rcode! |
3bef39c3 RG |
355 | sent = 0 |
356 | allowed = 0 | |
b4f23783 | 357 | for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1): |
dc2fd93a | 358 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, expectedResponse) |
3bef39c3 RG |
359 | sent = sent + 1 |
360 | if receivedQuery: | |
361 | receivedQuery.id = query.id | |
362 | self.assertEquals(query, receivedQuery) | |
dc2fd93a | 363 | self.assertEquals(expectedResponse, receivedResponse) |
3bef39c3 RG |
364 | allowed = allowed + 1 |
365 | else: | |
366 | # the query has not reached the responder, | |
367 | # let's clear the response queue | |
98883b8f | 368 | self.clearToResponderQueue() |
3bef39c3 RG |
369 | |
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) | |
373 | ||
374 | if allowed == sent: | |
375 | # wait for the maintenance function to run | |
376 | time.sleep(2) | |
d354e773 RG |
377 | |
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) | |
381 | ||
3bef39c3 | 382 | # wait until we are not blocked anymore |
d354e773 RG |
383 | time.sleep(self._dynBlockDuration + self._dynBlockPeriod) |
384 | ||
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) | |
390 | ||
391 | # again, over TCP this time | |
392 | # start with normal responses | |
b4f23783 | 393 | for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1): |
d354e773 RG |
394 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) |
395 | receivedQuery.id = query.id | |
396 | self.assertEquals(query, receivedQuery) | |
397 | self.assertEquals(response, receivedResponse) | |
398 | ||
3bef39c3 RG |
399 | # wait for the maintenance function to run |
400 | time.sleep(2) | |
d354e773 RG |
401 | |
402 | # we should NOT be dropped! | |
403 | (_, receivedResponse) = self.sendUDPQuery(query, response) | |
404 | self.assertEquals(receivedResponse, response) | |
405 | ||
dc2fd93a | 406 | # now with rcode! |
3bef39c3 RG |
407 | sent = 0 |
408 | allowed = 0 | |
b4f23783 | 409 | for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1): |
dc2fd93a | 410 | (receivedQuery, receivedResponse) = self.sendTCPQuery(query, expectedResponse) |
3bef39c3 RG |
411 | sent = sent + 1 |
412 | if receivedQuery: | |
413 | receivedQuery.id = query.id | |
414 | self.assertEquals(query, receivedQuery) | |
dc2fd93a | 415 | self.assertEquals(expectedResponse, receivedResponse) |
3bef39c3 RG |
416 | allowed = allowed + 1 |
417 | else: | |
418 | # the query has not reached the responder, | |
419 | # let's clear the response queue | |
98883b8f | 420 | self.clearToResponderQueue() |
3bef39c3 RG |
421 | |
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) | |
425 | ||
426 | if allowed == sent: | |
427 | # wait for the maintenance function to run | |
428 | time.sleep(2) | |
d354e773 RG |
429 | |
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) | |
433 | ||
3bef39c3 | 434 | # wait until we are not blocked anymore |
d354e773 RG |
435 | time.sleep(self._dynBlockDuration + self._dynBlockPeriod) |
436 | ||
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) | |
442 | ||
dc2fd93a | 443 | class TestDynBlockQPS(DynBlocksTest): |
d354e773 | 444 | |
dc2fd93a | 445 | _dynBlockQPS = 10 |
d354e773 RG |
446 | _dynBlockPeriod = 2 |
447 | _dynBlockDuration = 5 | |
d3473b8c | 448 | _config_params = ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort', '_webServerPort', '_webServerBasicAuthPassword', '_webServerAPIKey'] |
d354e773 RG |
449 | _config_template = """ |
450 | function maintenance() | |
dc2fd93a | 451 | addDynBlocks(exceedQRate(%d, %d), "Exceeded query rate", %d) |
d354e773 RG |
452 | end |
453 | newServer{address="127.0.0.1:%s"} | |
d3473b8c | 454 | webserver("127.0.0.1:%s", "%s", "%s") |
d354e773 RG |
455 | """ |
456 | ||
dc2fd93a | 457 | def testDynBlocksQRate(self): |
d354e773 | 458 | """ |
dc2fd93a | 459 | Dyn Blocks: QRate |
d354e773 | 460 | """ |
dc2fd93a | 461 | name = 'qrate.dynblocks.tests.powerdns.com.' |
e44df0f1 | 462 | self.doTestQRate(name) |
d354e773 | 463 | |
dc2fd93a | 464 | class TestDynBlockGroupQPS(DynBlocksTest): |
87b0577d | 465 | |
dc2fd93a RG |
466 | _dynBlockQPS = 10 |
467 | _dynBlockPeriod = 2 | |
468 | _dynBlockDuration = 5 | |
d3473b8c | 469 | _config_params = ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort', '_webServerPort', '_webServerBasicAuthPassword', '_webServerAPIKey'] |
dc2fd93a RG |
470 | _config_template = """ |
471 | local dbr = dynBlockRulesGroup() | |
472 | dbr:setQueryRate(%d, %d, "Exceeded query rate", %d) | |
87b0577d | 473 | |
dc2fd93a RG |
474 | function maintenance() |
475 | dbr:apply() | |
476 | end | |
477 | newServer{address="127.0.0.1:%s"} | |
d3473b8c | 478 | webserver("127.0.0.1:%s", "%s", "%s") |
dc2fd93a RG |
479 | """ |
480 | ||
481 | def testDynBlocksQRate(self): | |
482 | """ | |
483 | Dyn Blocks (Group): QRate | |
484 | """ | |
485 | name = 'qrate.group.dynblocks.tests.powerdns.com.' | |
e44df0f1 | 486 | self.doTestQRate(name) |
dc2fd93a RG |
487 | |
488 | ||
489 | class TestDynBlockQPSRefused(DynBlocksTest): | |
490 | ||
491 | _dynBlockQPS = 10 | |
492 | _dynBlockPeriod = 2 | |
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) | |
498 | end | |
499 | setDynBlocksAction(DNSAction.Refused) | |
500 | newServer{address="127.0.0.1:%s"} | |
501 | """ | |
502 | ||
503 | def testDynBlocksQRate(self): | |
504 | """ | |
505 | Dyn Blocks: QRate refused | |
506 | """ | |
507 | name = 'qraterefused.dynblocks.tests.powerdns.com.' | |
508 | self.doTestQRateRCode(name, dns.rcode.REFUSED) | |
509 | ||
510 | class TestDynBlockGroupQPSRefused(DynBlocksTest): | |
511 | ||
512 | _dynBlockQPS = 10 | |
513 | _dynBlockPeriod = 2 | |
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) | |
519 | ||
520 | function maintenance() | |
521 | dbr:apply() | |
522 | end | |
523 | setDynBlocksAction(DNSAction.Refused) | |
524 | newServer{address="127.0.0.1:%s"} | |
525 | """ | |
526 | ||
527 | def testDynBlocksQRate(self): | |
528 | """ | |
529 | Dyn Blocks (Group): QRate refused | |
530 | """ | |
531 | name = 'qraterefused.group.dynblocks.tests.powerdns.com.' | |
532 | self.doTestQRateRCode(name, dns.rcode.REFUSED) | |
533 | ||
534 | class TestDynBlockQPSActionRefused(DynBlocksTest): | |
535 | ||
536 | _dynBlockQPS = 10 | |
537 | _dynBlockPeriod = 2 | |
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) | |
543 | end | |
544 | setDynBlocksAction(DNSAction.Drop) | |
545 | newServer{address="127.0.0.1:%s"} | |
546 | """ | |
547 | ||
548 | def testDynBlocksQRate(self): | |
549 | """ | |
550 | Dyn Blocks: QRate refused (action) | |
551 | """ | |
552 | name = 'qrateactionrefused.dynblocks.tests.powerdns.com.' | |
553 | self.doTestQRateRCode(name, dns.rcode.REFUSED) | |
554 | ||
79ee8ff9 RG |
555 | class TestDynBlockQPSActionNXD(DynBlocksTest): |
556 | ||
557 | _dynBlockQPS = 10 | |
558 | _dynBlockPeriod = 2 | |
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) | |
564 | end | |
565 | setDynBlocksAction(DNSAction.Drop) | |
566 | newServer{address="127.0.0.1:%s"} | |
567 | """ | |
568 | ||
569 | def testDynBlocksQRate(self): | |
570 | """ | |
571 | Dyn Blocks: QRate NXD (action) | |
572 | """ | |
573 | name = 'qrateactionnxd.dynblocks.tests.powerdns.com.' | |
574 | self.doTestQRateRCode(name, dns.rcode.NXDOMAIN) | |
575 | ||
d3473b8c | 576 | class TestDynBlockGroupQPSActionRefused(DynBlocksTest): |
dc2fd93a RG |
577 | |
578 | _dynBlockQPS = 10 | |
579 | _dynBlockPeriod = 2 | |
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) | |
585 | ||
586 | function maintenance() | |
587 | dbr:apply() | |
588 | end | |
589 | setDynBlocksAction(DNSAction.Drop) | |
590 | newServer{address="127.0.0.1:%s"} | |
591 | """ | |
592 | ||
593 | def testDynBlocksQRate(self): | |
594 | """ | |
595 | Dyn Blocks (group): QRate refused (action) | |
596 | """ | |
597 | name = 'qrateactionrefused.group.dynblocks.tests.powerdns.com.' | |
598 | self.doTestQRateRCode(name, dns.rcode.REFUSED) | |
599 | ||
600 | class TestDynBlockQPSActionTruncated(DNSDistTest): | |
601 | ||
602 | _dynBlockQPS = 10 | |
603 | _dynBlockPeriod = 2 | |
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) | |
609 | end | |
610 | setDynBlocksAction(DNSAction.Drop) | |
611 | newServer{address="127.0.0.1:%s"} | |
612 | """ | |
613 | ||
614 | def testDynBlocksQRate(self): | |
615 | """ | |
616 | Dyn Blocks: QRate truncated (action) | |
617 | """ | |
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, | |
622 | 60, | |
623 | dns.rdataclass.IN, | |
624 | dns.rdatatype.A, | |
625 | '192.0.2.1') | |
626 | response.answer.append(rrset) | |
627 | truncatedResponse = dns.message.make_response(query) | |
628 | truncatedResponse.flags |= dns.flags.TC | |
629 | ||
630 | allowed = 0 | |
631 | sent = 0 | |
632 | for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1): | |
d354e773 | 633 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) |
dc2fd93a | 634 | sent = sent + 1 |
3bef39c3 RG |
635 | if receivedQuery: |
636 | receivedQuery.id = query.id | |
637 | self.assertEquals(query, receivedQuery) | |
dc2fd93a RG |
638 | self.assertEquals(receivedResponse, response) |
639 | allowed = allowed + 1 | |
3bef39c3 | 640 | else: |
dc2fd93a | 641 | self.assertEquals(receivedResponse, truncatedResponse) |
3bef39c3 RG |
642 | # the query has not reached the responder, |
643 | # let's clear the response queue | |
98883b8f | 644 | self.clearToResponderQueue() |
3bef39c3 | 645 | |
dc2fd93a RG |
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) | |
87b0577d | 649 | |
3bef39c3 RG |
650 | if allowed == sent: |
651 | # wait for the maintenance function to run | |
652 | time.sleep(2) | |
d354e773 | 653 | |
dc2fd93a | 654 | # we should now be 'truncated' for up to self._dynBlockDuration + self._dynBlockPeriod |
d354e773 | 655 | (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False) |
dc2fd93a | 656 | self.assertEquals(receivedResponse, truncatedResponse) |
d354e773 | 657 | |
dc2fd93a RG |
658 | # check over TCP, which should not be truncated |
659 | (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response) | |
660 | ||
661 | self.assertEquals(query, receivedQuery) | |
662 | self.assertEquals(receivedResponse, response) | |
87b0577d | 663 | |
3bef39c3 | 664 | # wait until we are not blocked anymore |
d354e773 RG |
665 | time.sleep(self._dynBlockDuration + self._dynBlockPeriod) |
666 | ||
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) | |
672 | ||
3bef39c3 RG |
673 | allowed = 0 |
674 | sent = 0 | |
dc2fd93a RG |
675 | # again, over TCP this time, we should never get truncated! |
676 | for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1): | |
d354e773 | 677 | (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response) |
dc2fd93a RG |
678 | sent = sent + 1 |
679 | self.assertEquals(query, receivedQuery) | |
680 | self.assertEquals(receivedResponse, response) | |
681 | receivedQuery.id = query.id | |
682 | allowed = allowed + 1 | |
3bef39c3 | 683 | |
dc2fd93a | 684 | self.assertEquals(allowed, sent) |
3bef39c3 | 685 | |
dc2fd93a | 686 | class TestDynBlockServFails(DynBlocksTest): |
d354e773 | 687 | |
dc2fd93a RG |
688 | _dynBlockQPS = 10 |
689 | _dynBlockPeriod = 2 | |
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) | |
695 | end | |
696 | newServer{address="127.0.0.1:%s"} | |
697 | """ | |
d354e773 | 698 | |
dc2fd93a RG |
699 | def testDynBlocksServFailRate(self): |
700 | """ | |
701 | Dyn Blocks: Server Failure Rate | |
702 | """ | |
703 | name = 'servfailrate.dynblocks.tests.powerdns.com.' | |
704 | self.doTestRCodeRate(name, dns.rcode.SERVFAIL) | |
d354e773 | 705 | |
ad3f984f RG |
706 | class TestDynBlockWhitelist(DynBlocksTest): |
707 | ||
708 | _dynBlockQPS = 10 | |
709 | _dynBlockPeriod = 2 | |
710 | _dynBlockDuration = 5 | |
711 | _config_params = ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort'] | |
712 | _config_template = """ | |
713 | whitelisted = false | |
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 | |
718 | whitelisted = true | |
719 | toBlock[addr] = nil | |
720 | end | |
721 | end | |
722 | addDynBlocks(toBlock, "Exceeded query rate", %d) | |
723 | end | |
724 | ||
725 | function spoofrule(dq) | |
726 | if (whitelisted) | |
727 | then | |
728 | return DNSAction.Spoof, "192.0.2.42" | |
729 | else | |
730 | return DNSAction.None, "" | |
731 | end | |
732 | end | |
733 | addAction("whitelisted-test.dynblocks.tests.powerdns.com.", LuaAction(spoofrule)) | |
734 | ||
735 | newServer{address="127.0.0.1:%s"} | |
736 | """ | |
737 | ||
738 | def testWhitelisted(self): | |
739 | """ | |
740 | Dyn Blocks: Whitelisted from the dynamic blocks | |
741 | """ | |
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, | |
746 | 60, | |
747 | dns.rdataclass.IN, | |
748 | dns.rdatatype.A, | |
749 | '192.0.2.1') | |
750 | response.answer.append(rrset) | |
751 | ||
752 | allowed = 0 | |
753 | sent = 0 | |
754 | for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1): | |
755 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) | |
756 | sent = sent + 1 | |
757 | if receivedQuery: | |
758 | receivedQuery.id = query.id | |
759 | self.assertEquals(query, receivedQuery) | |
760 | self.assertEquals(response, receivedResponse) | |
761 | allowed = allowed + 1 | |
762 | else: | |
763 | # the query has not reached the responder, | |
764 | # let's clear the response queue | |
765 | self.clearToResponderQueue() | |
766 | ||
767 | # we should not have been blocked | |
768 | self.assertEqual(allowed, sent) | |
769 | ||
770 | # wait for the maintenance function to run | |
771 | time.sleep(2) | |
772 | ||
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) | |
778 | ||
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, | |
786 | 60, | |
787 | dns.rdataclass.IN, | |
788 | dns.rdatatype.A, | |
789 | '192.0.2.42') | |
790 | expectedResponse.answer.append(rrset) | |
791 | (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False) | |
792 | self.assertEquals(receivedResponse, expectedResponse) | |
793 | ||
dc2fd93a RG |
794 | class TestDynBlockGroupServFails(DynBlocksTest): |
795 | ||
796 | _dynBlockQPS = 10 | |
797 | _dynBlockPeriod = 2 | |
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) | |
803 | ||
804 | function maintenance() | |
805 | dbr:apply() | |
806 | end | |
807 | ||
808 | newServer{address="127.0.0.1:%s"} | |
809 | """ | |
810 | ||
811 | def testDynBlocksServFailRate(self): | |
812 | """ | |
813 | Dyn Blocks (group): Server Failure Rate | |
814 | """ | |
815 | name = 'servfailrate.group.dynblocks.tests.powerdns.com.' | |
816 | self.doTestRCodeRate(name, dns.rcode.SERVFAIL) | |
817 | ||
818 | class TestDynBlockResponseBytes(DynBlocksTest): | |
819 | ||
820 | _dynBlockBytesPerSecond = 200 | |
821 | _dynBlockPeriod = 2 | |
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 = """ | |
827 | setKey("%s") | |
828 | controlSocket("127.0.0.1:%s") | |
829 | function maintenance() | |
830 | addDynBlocks(exceedRespByterate(%d, %d), "Exceeded response byterate", %d) | |
831 | end | |
832 | newServer{address="127.0.0.1:%s"} | |
833 | """ | |
834 | ||
835 | def testDynBlocksResponseByteRate(self): | |
836 | """ | |
837 | Dyn Blocks: Response Byte Rate | |
838 | """ | |
839 | name = 'responsebyterate.dynblocks.tests.powerdns.com.' | |
840 | self.doTestResponseByteRate(name) | |
841 | ||
842 | class TestDynBlockGroupResponseBytes(DynBlocksTest): | |
843 | ||
844 | _dynBlockBytesPerSecond = 200 | |
845 | _dynBlockPeriod = 2 | |
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 = """ | |
851 | setKey("%s") | |
852 | controlSocket("127.0.0.1:%s") | |
853 | local dbr = dynBlockRulesGroup() | |
854 | dbr:setResponseByteRate(%d, %d, "Exceeded query rate", %d) | |
855 | ||
856 | function maintenance() | |
857 | dbr:apply() | |
858 | end | |
859 | ||
860 | newServer{address="127.0.0.1:%s"} | |
861 | """ | |
862 | ||
863 | def testDynBlocksResponseByteRate(self): | |
864 | """ | |
865 | Dyn Blocks (group) : Response Byte Rate | |
866 | """ | |
867 | name = 'responsebyterate.group.dynblocks.tests.powerdns.com.' | |
868 | self.doTestResponseByteRate(name) | |
b718792f | 869 | |
477c86a0 | 870 | class TestDynBlockGroupExcluded(DynBlocksTest): |
b718792f RG |
871 | |
872 | _dynBlockQPS = 10 | |
873 | _dynBlockPeriod = 2 | |
874 | _dynBlockDuration = 5 | |
477c86a0 | 875 | _config_params = ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort'] |
b718792f | 876 | _config_template = """ |
b718792f RG |
877 | local dbr = dynBlockRulesGroup() |
878 | dbr:setQueryRate(%d, %d, "Exceeded query rate", %d) | |
879 | dbr:excludeRange("127.0.0.1/32") | |
880 | ||
881 | function maintenance() | |
882 | dbr:apply() | |
883 | end | |
884 | ||
885 | newServer{address="127.0.0.1:%s"} | |
886 | """ | |
887 | ||
888 | def testExcluded(self): | |
889 | """ | |
890 | Dyn Blocks (group) : Excluded from the dynamic block rules | |
891 | """ | |
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, | |
896 | 60, | |
897 | dns.rdataclass.IN, | |
898 | dns.rdatatype.A, | |
899 | '192.0.2.1') | |
900 | response.answer.append(rrset) | |
901 | ||
902 | allowed = 0 | |
903 | sent = 0 | |
904 | for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1): | |
905 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) | |
906 | sent = sent + 1 | |
907 | if receivedQuery: | |
908 | receivedQuery.id = query.id | |
909 | self.assertEquals(query, receivedQuery) | |
910 | self.assertEquals(response, receivedResponse) | |
911 | allowed = allowed + 1 | |
912 | else: | |
913 | # the query has not reached the responder, | |
914 | # let's clear the response queue | |
915 | self.clearToResponderQueue() | |
916 | ||
477c86a0 | 917 | # we should not have been blocked |
b718792f RG |
918 | self.assertEqual(allowed, sent) |
919 | ||
920 | # wait for the maintenance function to run | |
921 | time.sleep(2) | |
922 | ||
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) | |
477c86a0 RG |
928 | |
929 | class TestDynBlockGroupNoOp(DynBlocksTest): | |
930 | ||
931 | _dynBlockQPS = 10 | |
932 | _dynBlockPeriod = 2 | |
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) | |
938 | ||
939 | function maintenance() | |
940 | dbr:apply() | |
941 | end | |
942 | ||
943 | newServer{address="127.0.0.1:%s"} | |
944 | webserver("127.0.0.1:%s", "%s", "%s") | |
945 | """ | |
946 | ||
947 | def testNoOp(self): | |
948 | """ | |
949 | Dyn Blocks (group) : NoOp | |
950 | """ | |
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, | |
955 | 60, | |
956 | dns.rdataclass.IN, | |
957 | dns.rdatatype.A, | |
958 | '192.0.2.1') | |
959 | response.answer.append(rrset) | |
960 | ||
961 | allowed = 0 | |
962 | sent = 0 | |
963 | for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1): | |
964 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) | |
965 | sent = sent + 1 | |
966 | if receivedQuery: | |
967 | receivedQuery.id = query.id | |
968 | self.assertEquals(query, receivedQuery) | |
969 | self.assertEquals(response, receivedResponse) | |
970 | allowed = allowed + 1 | |
971 | else: | |
972 | # the query has not reached the responder, | |
973 | # let's clear the response queue | |
974 | self.clearToResponderQueue() | |
975 | ||
976 | # a dynamic rule should have been inserted, but the queries should still go on | |
977 | self.assertEqual(allowed, sent) | |
978 | ||
979 | # wait for the maintenance function to run | |
980 | time.sleep(2) | |
981 | ||
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) | |
987 | ||
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) | |
1d3ba133 RG |
990 | |
991 | class TestDynBlockGroupWarning(DynBlocksTest): | |
992 | ||
993 | _dynBlockWarningQPS = 5 | |
994 | _dynBlockQPS = 20 | |
995 | _dynBlockPeriod = 2 | |
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) | |
1001 | ||
1002 | function maintenance() | |
1003 | dbr:apply() | |
1004 | end | |
1005 | ||
1006 | newServer{address="127.0.0.1:%s"} | |
1007 | webserver("127.0.0.1:%s", "%s", "%s") | |
1008 | """ | |
1009 | ||
1010 | def testWarning(self): | |
1011 | """ | |
1012 | Dyn Blocks (group) : Warning | |
1013 | """ | |
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, | |
1018 | 60, | |
1019 | dns.rdataclass.IN, | |
1020 | dns.rdatatype.A, | |
1021 | '192.0.2.1') | |
1022 | response.answer.append(rrset) | |
1023 | ||
1024 | allowed = 0 | |
1025 | sent = 0 | |
1026 | for _ in range((self._dynBlockWarningQPS * self._dynBlockPeriod) + 1): | |
1027 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) | |
1028 | sent = sent + 1 | |
1029 | if receivedQuery: | |
1030 | receivedQuery.id = query.id | |
1031 | self.assertEquals(query, receivedQuery) | |
1032 | self.assertEquals(response, receivedResponse) | |
1033 | allowed = allowed + 1 | |
1034 | else: | |
1035 | # the query has not reached the responder, | |
1036 | # let's clear the response queue | |
1037 | self.clearToResponderQueue() | |
1038 | ||
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) | |
1042 | ||
1043 | # wait for the maintenance function to run | |
1044 | time.sleep(2) | |
1045 | ||
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) | |
1051 | ||
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) | |
1054 | ||
1055 | self.doTestQRate(name) |