]>
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 | |
06b0e003 RG |
7 | from dnsdisttests import DNSDistTest |
8 | try: | |
9 | range = xrange | |
10 | except NameError: | |
11 | pass | |
d354e773 | 12 | |
dc2fd93a | 13 | class DynBlocksTest(DNSDistTest): |
d354e773 | 14 | |
d3473b8c RG |
15 | _webTimeout = 2.0 |
16 | _webServerPort = 8083 | |
17 | _webServerBasicAuthPassword = 'secret' | |
18 | _webServerAPIKey = 'apisecret' | |
19 | ||
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) | |
24 | self.assertTrue(r) | |
25 | self.assertEquals(r.status_code, 200) | |
26 | ||
27 | content = r.json() | |
28 | self.assertIsNotNone(content) | |
29 | self.assertIn(range, content) | |
30 | ||
31 | values = content[range] | |
477c86a0 | 32 | for key in ['reason', 'seconds', 'blocks', 'action']: |
d3473b8c RG |
33 | self.assertIn(key, values) |
34 | ||
35 | self.assertEqual(values['reason'], reason) | |
b8753918 | 36 | self.assertGreaterEqual(values['seconds'], minSeconds) |
d3473b8c RG |
37 | self.assertLessEqual(values['seconds'], maxSeconds) |
38 | self.assertGreaterEqual(values['blocks'], minBlocks) | |
39 | self.assertLessEqual(values['blocks'], maxBlocks) | |
40 | ||
e44df0f1 | 41 | def doTestQRate(self, name, testViaAPI=True): |
d354e773 RG |
42 | query = dns.message.make_query(name, 'A', 'IN') |
43 | response = dns.message.make_response(query) | |
44 | rrset = dns.rrset.from_text(name, | |
45 | 60, | |
46 | dns.rdataclass.IN, | |
47 | dns.rdatatype.A, | |
48 | '192.0.2.1') | |
49 | response.answer.append(rrset) | |
50 | ||
3bef39c3 RG |
51 | allowed = 0 |
52 | sent = 0 | |
b4f23783 | 53 | for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1): |
d354e773 | 54 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) |
3bef39c3 RG |
55 | sent = sent + 1 |
56 | if receivedQuery: | |
57 | receivedQuery.id = query.id | |
58 | self.assertEquals(query, receivedQuery) | |
59 | self.assertEquals(response, receivedResponse) | |
60 | allowed = allowed + 1 | |
61 | else: | |
62 | # the query has not reached the responder, | |
63 | # let's clear the response queue | |
98883b8f | 64 | self.clearToResponderQueue() |
3bef39c3 RG |
65 | |
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) | |
69 | ||
70 | if allowed == sent: | |
71 | # wait for the maintenance function to run | |
72 | time.sleep(2) | |
d354e773 RG |
73 | |
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) | |
77 | ||
d3473b8c RG |
78 | if testViaAPI: |
79 | self.doTestDynBlockViaAPI('127.0.0.1/32', 'Exceeded query rate', self._dynBlockDuration - 4, self._dynBlockDuration, (sent-allowed)+1, (sent-allowed)+1) | |
80 | ||
3bef39c3 | 81 | # wait until we are not blocked anymore |
d354e773 RG |
82 | time.sleep(self._dynBlockDuration + self._dynBlockPeriod) |
83 | ||
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) | |
89 | ||
90 | # again, over TCP this time | |
3bef39c3 RG |
91 | allowed = 0 |
92 | sent = 0 | |
b4f23783 | 93 | for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1): |
d354e773 | 94 | (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response) |
3bef39c3 RG |
95 | sent = sent + 1 |
96 | if receivedQuery: | |
97 | receivedQuery.id = query.id | |
98 | self.assertEquals(query, receivedQuery) | |
99 | self.assertEquals(response, receivedResponse) | |
100 | allowed = allowed + 1 | |
101 | else: | |
102 | # the query has not reached the responder, | |
103 | # let's clear the response queue | |
98883b8f | 104 | self.clearToResponderQueue() |
3bef39c3 RG |
105 | |
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) | |
109 | ||
110 | if allowed == sent: | |
111 | # wait for the maintenance function to run | |
112 | time.sleep(2) | |
d354e773 RG |
113 | |
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) | |
117 | ||
3bef39c3 | 118 | # wait until we are not blocked anymore |
d354e773 RG |
119 | time.sleep(self._dynBlockDuration + self._dynBlockPeriod) |
120 | ||
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) | |
126 | ||
dc2fd93a | 127 | def doTestQRateRCode(self, name, rcode): |
d354e773 RG |
128 | query = dns.message.make_query(name, 'A', 'IN') |
129 | response = dns.message.make_response(query) | |
130 | rrset = dns.rrset.from_text(name, | |
131 | 60, | |
132 | dns.rdataclass.IN, | |
133 | dns.rdatatype.A, | |
134 | '192.0.2.1') | |
135 | response.answer.append(rrset) | |
dc2fd93a RG |
136 | expectedResponse = dns.message.make_response(query) |
137 | expectedResponse.set_rcode(rcode) | |
d354e773 | 138 | |
3bef39c3 RG |
139 | allowed = 0 |
140 | sent = 0 | |
b4f23783 | 141 | for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1): |
d354e773 | 142 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) |
7b925432 RG |
143 | sent = sent + 1 |
144 | if receivedQuery: | |
145 | receivedQuery.id = query.id | |
146 | self.assertEquals(query, receivedQuery) | |
147 | self.assertEquals(receivedResponse, response) | |
148 | allowed = allowed + 1 | |
149 | else: | |
dc2fd93a | 150 | self.assertEquals(receivedResponse, expectedResponse) |
7b925432 RG |
151 | # the query has not reached the responder, |
152 | # let's clear the response queue | |
153 | self.clearToResponderQueue() | |
154 | ||
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) | |
158 | ||
159 | if allowed == sent: | |
160 | # wait for the maintenance function to run | |
161 | time.sleep(2) | |
162 | ||
dc2fd93a | 163 | # we should now be 'rcode' for up to self._dynBlockDuration + self._dynBlockPeriod |
7b925432 | 164 | (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False) |
dc2fd93a | 165 | self.assertEquals(receivedResponse, expectedResponse) |
7b925432 RG |
166 | |
167 | # wait until we are not blocked anymore | |
168 | time.sleep(self._dynBlockDuration + self._dynBlockPeriod) | |
169 | ||
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) | |
175 | ||
176 | allowed = 0 | |
177 | sent = 0 | |
178 | # again, over TCP this time | |
b4f23783 | 179 | for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1): |
7b925432 RG |
180 | (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response) |
181 | sent = sent + 1 | |
182 | if receivedQuery: | |
183 | receivedQuery.id = query.id | |
184 | self.assertEquals(query, receivedQuery) | |
185 | self.assertEquals(receivedResponse, response) | |
186 | allowed = allowed + 1 | |
187 | else: | |
dc2fd93a | 188 | self.assertEquals(receivedResponse, expectedResponse) |
7b925432 RG |
189 | # the query has not reached the responder, |
190 | # let's clear the response queue | |
191 | self.clearToResponderQueue() | |
192 | ||
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) | |
196 | ||
197 | if allowed == sent: | |
198 | # wait for the maintenance function to run | |
199 | time.sleep(2) | |
200 | ||
dc2fd93a | 201 | # we should now be 'rcode' for up to self._dynBlockDuration + self._dynBlockPeriod |
7b925432 | 202 | (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False) |
dc2fd93a | 203 | self.assertEquals(receivedResponse, expectedResponse) |
7b925432 RG |
204 | |
205 | # wait until we are not blocked anymore | |
206 | time.sleep(self._dynBlockDuration + self._dynBlockPeriod) | |
207 | ||
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) | |
213 | ||
dc2fd93a | 214 | def doTestResponseByteRate(self, name): |
7b925432 RG |
215 | query = dns.message.make_query(name, 'A', 'IN') |
216 | response = dns.message.make_response(query) | |
dc2fd93a RG |
217 | response.answer.append(dns.rrset.from_text_list(name, |
218 | 60, | |
219 | dns.rdataclass.IN, | |
220 | dns.rdatatype.A, | |
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, | |
223 | 60, | |
224 | dns.rdataclass.IN, | |
225 | dns.rdatatype.AAAA, | |
226 | '2001:DB8::1')) | |
7b925432 RG |
227 | |
228 | allowed = 0 | |
229 | sent = 0 | |
dc2fd93a RG |
230 | |
231 | print(time.time()) | |
232 | ||
233 | for _ in range(int(self._dynBlockBytesPerSecond * 5 / len(response.to_wire()))): | |
7b925432 | 234 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) |
dc2fd93a | 235 | sent = sent + len(response.to_wire()) |
3bef39c3 RG |
236 | if receivedQuery: |
237 | receivedQuery.id = query.id | |
238 | self.assertEquals(query, receivedQuery) | |
dc2fd93a RG |
239 | self.assertEquals(response, receivedResponse) |
240 | allowed = allowed + len(response.to_wire()) | |
3bef39c3 | 241 | else: |
3bef39c3 RG |
242 | # the query has not reached the responder, |
243 | # let's clear the response queue | |
98883b8f | 244 | self.clearToResponderQueue() |
dc2fd93a RG |
245 | # and stop right there, otherwise we might |
246 | # wait for so long that the dynblock is gone | |
247 | # by the time we finished | |
248 | break | |
3bef39c3 RG |
249 | |
250 | # we might be already blocked, but we should have been able to send | |
dc2fd93a RG |
251 | # at least self._dynBlockBytesPerSecond bytes |
252 | print(allowed) | |
253 | print(sent) | |
254 | print(time.time()) | |
255 | self.assertGreaterEqual(allowed, self._dynBlockBytesPerSecond) | |
256 | ||
257 | print(self.sendConsoleCommand("showDynBlocks()")) | |
258 | print(self.sendConsoleCommand("grepq(\"\")")) | |
259 | print(time.time()) | |
3bef39c3 RG |
260 | |
261 | if allowed == sent: | |
262 | # wait for the maintenance function to run | |
dc2fd93a | 263 | print("Waiting for the maintenance function to run") |
3bef39c3 | 264 | time.sleep(2) |
d354e773 | 265 | |
dc2fd93a RG |
266 | print(self.sendConsoleCommand("showDynBlocks()")) |
267 | print(self.sendConsoleCommand("grepq(\"\")")) | |
268 | print(time.time()) | |
269 | ||
270 | # we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod | |
d354e773 | 271 | (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False) |
dc2fd93a RG |
272 | self.assertEquals(receivedResponse, None) |
273 | ||
274 | print(self.sendConsoleCommand("showDynBlocks()")) | |
275 | print(self.sendConsoleCommand("grepq(\"\")")) | |
276 | print(time.time()) | |
d354e773 | 277 | |
3bef39c3 | 278 | # wait until we are not blocked anymore |
d354e773 RG |
279 | time.sleep(self._dynBlockDuration + self._dynBlockPeriod) |
280 | ||
dc2fd93a RG |
281 | print(self.sendConsoleCommand("showDynBlocks()")) |
282 | print(self.sendConsoleCommand("grepq(\"\")")) | |
283 | print(time.time()) | |
284 | ||
d354e773 RG |
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) | |
290 | ||
dc2fd93a | 291 | # again, over TCP this time |
3bef39c3 RG |
292 | allowed = 0 |
293 | sent = 0 | |
dc2fd93a | 294 | for _ in range(int(self._dynBlockBytesPerSecond * 5 / len(response.to_wire()))): |
d354e773 | 295 | (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response) |
dc2fd93a | 296 | sent = sent + len(response.to_wire()) |
3bef39c3 RG |
297 | if receivedQuery: |
298 | receivedQuery.id = query.id | |
299 | self.assertEquals(query, receivedQuery) | |
dc2fd93a RG |
300 | self.assertEquals(response, receivedResponse) |
301 | allowed = allowed + len(response.to_wire()) | |
3bef39c3 | 302 | else: |
3bef39c3 RG |
303 | # the query has not reached the responder, |
304 | # let's clear the response queue | |
98883b8f | 305 | self.clearToResponderQueue() |
dc2fd93a RG |
306 | # and stop right there, otherwise we might |
307 | # wait for so long that the dynblock is gone | |
308 | # by the time we finished | |
309 | break | |
3bef39c3 RG |
310 | |
311 | # we might be already blocked, but we should have been able to send | |
dc2fd93a RG |
312 | # at least self._dynBlockBytesPerSecond bytes |
313 | self.assertGreaterEqual(allowed, self._dynBlockBytesPerSecond) | |
3bef39c3 RG |
314 | |
315 | if allowed == sent: | |
316 | # wait for the maintenance function to run | |
317 | time.sleep(2) | |
d354e773 | 318 | |
dc2fd93a | 319 | # we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod |
d354e773 | 320 | (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False) |
dc2fd93a | 321 | self.assertEquals(receivedResponse, None) |
d354e773 | 322 | |
3bef39c3 | 323 | # wait until we are not blocked anymore |
d354e773 RG |
324 | time.sleep(self._dynBlockDuration + self._dynBlockPeriod) |
325 | ||
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) | |
331 | ||
dc2fd93a | 332 | def doTestRCodeRate(self, name, rcode): |
d354e773 RG |
333 | query = dns.message.make_query(name, 'A', 'IN') |
334 | response = dns.message.make_response(query) | |
335 | rrset = dns.rrset.from_text(name, | |
336 | 60, | |
337 | dns.rdataclass.IN, | |
338 | dns.rdatatype.A, | |
339 | '192.0.2.1') | |
340 | response.answer.append(rrset) | |
dc2fd93a RG |
341 | expectedResponse = dns.message.make_response(query) |
342 | expectedResponse.set_rcode(rcode) | |
d354e773 RG |
343 | |
344 | # start with normal responses | |
b4f23783 | 345 | for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1): |
d354e773 RG |
346 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) |
347 | receivedQuery.id = query.id | |
348 | self.assertEquals(query, receivedQuery) | |
349 | self.assertEquals(response, receivedResponse) | |
350 | ||
3bef39c3 RG |
351 | # wait for the maintenance function to run |
352 | time.sleep(2) | |
d354e773 RG |
353 | |
354 | # we should NOT be dropped! | |
355 | (_, receivedResponse) = self.sendUDPQuery(query, response) | |
356 | self.assertEquals(receivedResponse, response) | |
357 | ||
dc2fd93a | 358 | # now with rcode! |
3bef39c3 RG |
359 | sent = 0 |
360 | allowed = 0 | |
b4f23783 | 361 | for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1): |
dc2fd93a | 362 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, expectedResponse) |
3bef39c3 RG |
363 | sent = sent + 1 |
364 | if receivedQuery: | |
365 | receivedQuery.id = query.id | |
366 | self.assertEquals(query, receivedQuery) | |
dc2fd93a | 367 | self.assertEquals(expectedResponse, receivedResponse) |
3bef39c3 RG |
368 | allowed = allowed + 1 |
369 | else: | |
370 | # the query has not reached the responder, | |
371 | # let's clear the response queue | |
98883b8f | 372 | self.clearToResponderQueue() |
3bef39c3 RG |
373 | |
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) | |
377 | ||
378 | if allowed == sent: | |
379 | # wait for the maintenance function to run | |
380 | time.sleep(2) | |
d354e773 RG |
381 | |
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) | |
385 | ||
3bef39c3 | 386 | # wait until we are not blocked anymore |
d354e773 RG |
387 | time.sleep(self._dynBlockDuration + self._dynBlockPeriod) |
388 | ||
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) | |
394 | ||
395 | # again, over TCP this time | |
396 | # start with normal responses | |
b4f23783 | 397 | for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1): |
d354e773 RG |
398 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) |
399 | receivedQuery.id = query.id | |
400 | self.assertEquals(query, receivedQuery) | |
401 | self.assertEquals(response, receivedResponse) | |
402 | ||
3bef39c3 RG |
403 | # wait for the maintenance function to run |
404 | time.sleep(2) | |
d354e773 RG |
405 | |
406 | # we should NOT be dropped! | |
407 | (_, receivedResponse) = self.sendUDPQuery(query, response) | |
408 | self.assertEquals(receivedResponse, response) | |
409 | ||
dc2fd93a | 410 | # now with rcode! |
3bef39c3 RG |
411 | sent = 0 |
412 | allowed = 0 | |
b4f23783 | 413 | for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1): |
dc2fd93a | 414 | (receivedQuery, receivedResponse) = self.sendTCPQuery(query, expectedResponse) |
3bef39c3 RG |
415 | sent = sent + 1 |
416 | if receivedQuery: | |
417 | receivedQuery.id = query.id | |
418 | self.assertEquals(query, receivedQuery) | |
dc2fd93a | 419 | self.assertEquals(expectedResponse, receivedResponse) |
3bef39c3 RG |
420 | allowed = allowed + 1 |
421 | else: | |
422 | # the query has not reached the responder, | |
423 | # let's clear the response queue | |
98883b8f | 424 | self.clearToResponderQueue() |
3bef39c3 RG |
425 | |
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) | |
429 | ||
430 | if allowed == sent: | |
431 | # wait for the maintenance function to run | |
432 | time.sleep(2) | |
d354e773 RG |
433 | |
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) | |
437 | ||
3bef39c3 | 438 | # wait until we are not blocked anymore |
d354e773 RG |
439 | time.sleep(self._dynBlockDuration + self._dynBlockPeriod) |
440 | ||
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) | |
446 | ||
dc2fd93a | 447 | class TestDynBlockQPS(DynBlocksTest): |
d354e773 | 448 | |
dc2fd93a | 449 | _dynBlockQPS = 10 |
d354e773 RG |
450 | _dynBlockPeriod = 2 |
451 | _dynBlockDuration = 5 | |
d3473b8c | 452 | _config_params = ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort', '_webServerPort', '_webServerBasicAuthPassword', '_webServerAPIKey'] |
d354e773 RG |
453 | _config_template = """ |
454 | function maintenance() | |
dc2fd93a | 455 | addDynBlocks(exceedQRate(%d, %d), "Exceeded query rate", %d) |
d354e773 RG |
456 | end |
457 | newServer{address="127.0.0.1:%s"} | |
d3473b8c | 458 | webserver("127.0.0.1:%s", "%s", "%s") |
d354e773 RG |
459 | """ |
460 | ||
dc2fd93a | 461 | def testDynBlocksQRate(self): |
d354e773 | 462 | """ |
dc2fd93a | 463 | Dyn Blocks: QRate |
d354e773 | 464 | """ |
dc2fd93a | 465 | name = 'qrate.dynblocks.tests.powerdns.com.' |
e44df0f1 | 466 | self.doTestQRate(name) |
d354e773 | 467 | |
dc2fd93a | 468 | class TestDynBlockGroupQPS(DynBlocksTest): |
87b0577d | 469 | |
dc2fd93a RG |
470 | _dynBlockQPS = 10 |
471 | _dynBlockPeriod = 2 | |
472 | _dynBlockDuration = 5 | |
d3473b8c | 473 | _config_params = ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort', '_webServerPort', '_webServerBasicAuthPassword', '_webServerAPIKey'] |
dc2fd93a RG |
474 | _config_template = """ |
475 | local dbr = dynBlockRulesGroup() | |
476 | dbr:setQueryRate(%d, %d, "Exceeded query rate", %d) | |
87b0577d | 477 | |
dc2fd93a RG |
478 | function maintenance() |
479 | dbr:apply() | |
480 | end | |
481 | newServer{address="127.0.0.1:%s"} | |
d3473b8c | 482 | webserver("127.0.0.1:%s", "%s", "%s") |
dc2fd93a RG |
483 | """ |
484 | ||
485 | def testDynBlocksQRate(self): | |
486 | """ | |
487 | Dyn Blocks (Group): QRate | |
488 | """ | |
489 | name = 'qrate.group.dynblocks.tests.powerdns.com.' | |
e44df0f1 | 490 | self.doTestQRate(name) |
dc2fd93a RG |
491 | |
492 | ||
493 | class TestDynBlockQPSRefused(DynBlocksTest): | |
494 | ||
495 | _dynBlockQPS = 10 | |
496 | _dynBlockPeriod = 2 | |
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) | |
502 | end | |
503 | setDynBlocksAction(DNSAction.Refused) | |
504 | newServer{address="127.0.0.1:%s"} | |
505 | """ | |
506 | ||
507 | def testDynBlocksQRate(self): | |
508 | """ | |
509 | Dyn Blocks: QRate refused | |
510 | """ | |
511 | name = 'qraterefused.dynblocks.tests.powerdns.com.' | |
512 | self.doTestQRateRCode(name, dns.rcode.REFUSED) | |
513 | ||
514 | class TestDynBlockGroupQPSRefused(DynBlocksTest): | |
515 | ||
516 | _dynBlockQPS = 10 | |
517 | _dynBlockPeriod = 2 | |
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) | |
523 | ||
524 | function maintenance() | |
525 | dbr:apply() | |
526 | end | |
527 | setDynBlocksAction(DNSAction.Refused) | |
528 | newServer{address="127.0.0.1:%s"} | |
529 | """ | |
530 | ||
531 | def testDynBlocksQRate(self): | |
532 | """ | |
533 | Dyn Blocks (Group): QRate refused | |
534 | """ | |
535 | name = 'qraterefused.group.dynblocks.tests.powerdns.com.' | |
536 | self.doTestQRateRCode(name, dns.rcode.REFUSED) | |
537 | ||
538 | class TestDynBlockQPSActionRefused(DynBlocksTest): | |
539 | ||
540 | _dynBlockQPS = 10 | |
541 | _dynBlockPeriod = 2 | |
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) | |
547 | end | |
548 | setDynBlocksAction(DNSAction.Drop) | |
549 | newServer{address="127.0.0.1:%s"} | |
550 | """ | |
551 | ||
552 | def testDynBlocksQRate(self): | |
553 | """ | |
554 | Dyn Blocks: QRate refused (action) | |
555 | """ | |
556 | name = 'qrateactionrefused.dynblocks.tests.powerdns.com.' | |
557 | self.doTestQRateRCode(name, dns.rcode.REFUSED) | |
558 | ||
79ee8ff9 RG |
559 | class TestDynBlockQPSActionNXD(DynBlocksTest): |
560 | ||
561 | _dynBlockQPS = 10 | |
562 | _dynBlockPeriod = 2 | |
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) | |
568 | end | |
569 | setDynBlocksAction(DNSAction.Drop) | |
570 | newServer{address="127.0.0.1:%s"} | |
571 | """ | |
572 | ||
573 | def testDynBlocksQRate(self): | |
574 | """ | |
575 | Dyn Blocks: QRate NXD (action) | |
576 | """ | |
577 | name = 'qrateactionnxd.dynblocks.tests.powerdns.com.' | |
578 | self.doTestQRateRCode(name, dns.rcode.NXDOMAIN) | |
579 | ||
d3473b8c | 580 | class TestDynBlockGroupQPSActionRefused(DynBlocksTest): |
dc2fd93a RG |
581 | |
582 | _dynBlockQPS = 10 | |
583 | _dynBlockPeriod = 2 | |
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) | |
589 | ||
590 | function maintenance() | |
591 | dbr:apply() | |
592 | end | |
593 | setDynBlocksAction(DNSAction.Drop) | |
594 | newServer{address="127.0.0.1:%s"} | |
595 | """ | |
596 | ||
597 | def testDynBlocksQRate(self): | |
598 | """ | |
599 | Dyn Blocks (group): QRate refused (action) | |
600 | """ | |
601 | name = 'qrateactionrefused.group.dynblocks.tests.powerdns.com.' | |
602 | self.doTestQRateRCode(name, dns.rcode.REFUSED) | |
603 | ||
604 | class TestDynBlockQPSActionTruncated(DNSDistTest): | |
605 | ||
606 | _dynBlockQPS = 10 | |
607 | _dynBlockPeriod = 2 | |
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) | |
613 | end | |
614 | setDynBlocksAction(DNSAction.Drop) | |
615 | newServer{address="127.0.0.1:%s"} | |
616 | """ | |
617 | ||
618 | def testDynBlocksQRate(self): | |
619 | """ | |
620 | Dyn Blocks: QRate truncated (action) | |
621 | """ | |
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, | |
626 | 60, | |
627 | dns.rdataclass.IN, | |
628 | dns.rdatatype.A, | |
629 | '192.0.2.1') | |
630 | response.answer.append(rrset) | |
631 | truncatedResponse = dns.message.make_response(query) | |
632 | truncatedResponse.flags |= dns.flags.TC | |
633 | ||
634 | allowed = 0 | |
635 | sent = 0 | |
636 | for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1): | |
d354e773 | 637 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) |
dc2fd93a | 638 | sent = sent + 1 |
3bef39c3 RG |
639 | if receivedQuery: |
640 | receivedQuery.id = query.id | |
641 | self.assertEquals(query, receivedQuery) | |
dc2fd93a RG |
642 | self.assertEquals(receivedResponse, response) |
643 | allowed = allowed + 1 | |
3bef39c3 | 644 | else: |
dc2fd93a | 645 | self.assertEquals(receivedResponse, truncatedResponse) |
3bef39c3 RG |
646 | # the query has not reached the responder, |
647 | # let's clear the response queue | |
98883b8f | 648 | self.clearToResponderQueue() |
3bef39c3 | 649 | |
dc2fd93a RG |
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) | |
87b0577d | 653 | |
3bef39c3 RG |
654 | if allowed == sent: |
655 | # wait for the maintenance function to run | |
656 | time.sleep(2) | |
d354e773 | 657 | |
dc2fd93a | 658 | # we should now be 'truncated' for up to self._dynBlockDuration + self._dynBlockPeriod |
d354e773 | 659 | (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False) |
dc2fd93a | 660 | self.assertEquals(receivedResponse, truncatedResponse) |
d354e773 | 661 | |
dc2fd93a RG |
662 | # check over TCP, which should not be truncated |
663 | (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response) | |
664 | ||
665 | self.assertEquals(query, receivedQuery) | |
666 | self.assertEquals(receivedResponse, response) | |
87b0577d | 667 | |
3bef39c3 | 668 | # wait until we are not blocked anymore |
d354e773 RG |
669 | time.sleep(self._dynBlockDuration + self._dynBlockPeriod) |
670 | ||
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) | |
676 | ||
3bef39c3 RG |
677 | allowed = 0 |
678 | sent = 0 | |
dc2fd93a RG |
679 | # again, over TCP this time, we should never get truncated! |
680 | for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1): | |
d354e773 | 681 | (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response) |
dc2fd93a RG |
682 | sent = sent + 1 |
683 | self.assertEquals(query, receivedQuery) | |
684 | self.assertEquals(receivedResponse, response) | |
685 | receivedQuery.id = query.id | |
686 | allowed = allowed + 1 | |
3bef39c3 | 687 | |
dc2fd93a | 688 | self.assertEquals(allowed, sent) |
3bef39c3 | 689 | |
dc2fd93a | 690 | class TestDynBlockServFails(DynBlocksTest): |
d354e773 | 691 | |
dc2fd93a RG |
692 | _dynBlockQPS = 10 |
693 | _dynBlockPeriod = 2 | |
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) | |
699 | end | |
700 | newServer{address="127.0.0.1:%s"} | |
701 | """ | |
d354e773 | 702 | |
dc2fd93a RG |
703 | def testDynBlocksServFailRate(self): |
704 | """ | |
705 | Dyn Blocks: Server Failure Rate | |
706 | """ | |
707 | name = 'servfailrate.dynblocks.tests.powerdns.com.' | |
708 | self.doTestRCodeRate(name, dns.rcode.SERVFAIL) | |
d354e773 | 709 | |
ad3f984f RG |
710 | class TestDynBlockWhitelist(DynBlocksTest): |
711 | ||
712 | _dynBlockQPS = 10 | |
713 | _dynBlockPeriod = 2 | |
714 | _dynBlockDuration = 5 | |
715 | _config_params = ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort'] | |
716 | _config_template = """ | |
717 | whitelisted = false | |
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 | |
722 | whitelisted = true | |
723 | toBlock[addr] = nil | |
724 | end | |
725 | end | |
726 | addDynBlocks(toBlock, "Exceeded query rate", %d) | |
727 | end | |
728 | ||
729 | function spoofrule(dq) | |
730 | if (whitelisted) | |
731 | then | |
732 | return DNSAction.Spoof, "192.0.2.42" | |
733 | else | |
734 | return DNSAction.None, "" | |
735 | end | |
736 | end | |
737 | addAction("whitelisted-test.dynblocks.tests.powerdns.com.", LuaAction(spoofrule)) | |
738 | ||
739 | newServer{address="127.0.0.1:%s"} | |
740 | """ | |
741 | ||
742 | def testWhitelisted(self): | |
743 | """ | |
744 | Dyn Blocks: Whitelisted from the dynamic blocks | |
745 | """ | |
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, | |
750 | 60, | |
751 | dns.rdataclass.IN, | |
752 | dns.rdatatype.A, | |
753 | '192.0.2.1') | |
754 | response.answer.append(rrset) | |
755 | ||
756 | allowed = 0 | |
757 | sent = 0 | |
758 | for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1): | |
759 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) | |
760 | sent = sent + 1 | |
761 | if receivedQuery: | |
762 | receivedQuery.id = query.id | |
763 | self.assertEquals(query, receivedQuery) | |
764 | self.assertEquals(response, receivedResponse) | |
765 | allowed = allowed + 1 | |
766 | else: | |
767 | # the query has not reached the responder, | |
768 | # let's clear the response queue | |
769 | self.clearToResponderQueue() | |
770 | ||
771 | # we should not have been blocked | |
772 | self.assertEqual(allowed, sent) | |
773 | ||
774 | # wait for the maintenance function to run | |
775 | time.sleep(2) | |
776 | ||
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) | |
782 | ||
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, | |
790 | 60, | |
791 | dns.rdataclass.IN, | |
792 | dns.rdatatype.A, | |
793 | '192.0.2.42') | |
794 | expectedResponse.answer.append(rrset) | |
795 | (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False) | |
796 | self.assertEquals(receivedResponse, expectedResponse) | |
797 | ||
dc2fd93a RG |
798 | class TestDynBlockGroupServFails(DynBlocksTest): |
799 | ||
800 | _dynBlockQPS = 10 | |
801 | _dynBlockPeriod = 2 | |
802 | _dynBlockDuration = 5 | |
803 | _config_params = ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort'] | |
804 | _config_template = """ | |
805 | local dbr = dynBlockRulesGroup() | |
d3ec24f9 | 806 | dbr:setRCodeRate(DNSRCode.SERVFAIL, %d, %d, "Exceeded query rate", %d) |
dc2fd93a RG |
807 | |
808 | function maintenance() | |
809 | dbr:apply() | |
810 | end | |
811 | ||
812 | newServer{address="127.0.0.1:%s"} | |
813 | """ | |
814 | ||
815 | def testDynBlocksServFailRate(self): | |
816 | """ | |
817 | Dyn Blocks (group): Server Failure Rate | |
818 | """ | |
819 | name = 'servfailrate.group.dynblocks.tests.powerdns.com.' | |
820 | self.doTestRCodeRate(name, dns.rcode.SERVFAIL) | |
821 | ||
822 | class TestDynBlockResponseBytes(DynBlocksTest): | |
823 | ||
824 | _dynBlockBytesPerSecond = 200 | |
825 | _dynBlockPeriod = 2 | |
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 = """ | |
831 | setKey("%s") | |
832 | controlSocket("127.0.0.1:%s") | |
833 | function maintenance() | |
834 | addDynBlocks(exceedRespByterate(%d, %d), "Exceeded response byterate", %d) | |
835 | end | |
836 | newServer{address="127.0.0.1:%s"} | |
837 | """ | |
838 | ||
839 | def testDynBlocksResponseByteRate(self): | |
840 | """ | |
841 | Dyn Blocks: Response Byte Rate | |
842 | """ | |
843 | name = 'responsebyterate.dynblocks.tests.powerdns.com.' | |
844 | self.doTestResponseByteRate(name) | |
845 | ||
846 | class TestDynBlockGroupResponseBytes(DynBlocksTest): | |
847 | ||
848 | _dynBlockBytesPerSecond = 200 | |
849 | _dynBlockPeriod = 2 | |
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 = """ | |
855 | setKey("%s") | |
856 | controlSocket("127.0.0.1:%s") | |
857 | local dbr = dynBlockRulesGroup() | |
858 | dbr:setResponseByteRate(%d, %d, "Exceeded query rate", %d) | |
859 | ||
860 | function maintenance() | |
861 | dbr:apply() | |
862 | end | |
863 | ||
864 | newServer{address="127.0.0.1:%s"} | |
865 | """ | |
866 | ||
867 | def testDynBlocksResponseByteRate(self): | |
868 | """ | |
869 | Dyn Blocks (group) : Response Byte Rate | |
870 | """ | |
871 | name = 'responsebyterate.group.dynblocks.tests.powerdns.com.' | |
872 | self.doTestResponseByteRate(name) | |
b718792f | 873 | |
477c86a0 | 874 | class TestDynBlockGroupExcluded(DynBlocksTest): |
b718792f RG |
875 | |
876 | _dynBlockQPS = 10 | |
877 | _dynBlockPeriod = 2 | |
878 | _dynBlockDuration = 5 | |
477c86a0 | 879 | _config_params = ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort'] |
b718792f | 880 | _config_template = """ |
b718792f RG |
881 | local dbr = dynBlockRulesGroup() |
882 | dbr:setQueryRate(%d, %d, "Exceeded query rate", %d) | |
883 | dbr:excludeRange("127.0.0.1/32") | |
884 | ||
885 | function maintenance() | |
886 | dbr:apply() | |
887 | end | |
888 | ||
889 | newServer{address="127.0.0.1:%s"} | |
890 | """ | |
891 | ||
892 | def testExcluded(self): | |
893 | """ | |
894 | Dyn Blocks (group) : Excluded from the dynamic block rules | |
895 | """ | |
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, | |
900 | 60, | |
901 | dns.rdataclass.IN, | |
902 | dns.rdatatype.A, | |
903 | '192.0.2.1') | |
904 | response.answer.append(rrset) | |
905 | ||
906 | allowed = 0 | |
907 | sent = 0 | |
908 | for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1): | |
909 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) | |
910 | sent = sent + 1 | |
911 | if receivedQuery: | |
912 | receivedQuery.id = query.id | |
913 | self.assertEquals(query, receivedQuery) | |
914 | self.assertEquals(response, receivedResponse) | |
915 | allowed = allowed + 1 | |
916 | else: | |
917 | # the query has not reached the responder, | |
918 | # let's clear the response queue | |
919 | self.clearToResponderQueue() | |
920 | ||
477c86a0 | 921 | # we should not have been blocked |
b718792f RG |
922 | self.assertEqual(allowed, sent) |
923 | ||
924 | # wait for the maintenance function to run | |
925 | time.sleep(2) | |
926 | ||
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) | |
477c86a0 RG |
932 | |
933 | class TestDynBlockGroupNoOp(DynBlocksTest): | |
934 | ||
935 | _dynBlockQPS = 10 | |
936 | _dynBlockPeriod = 2 | |
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) | |
942 | ||
943 | function maintenance() | |
944 | dbr:apply() | |
945 | end | |
946 | ||
947 | newServer{address="127.0.0.1:%s"} | |
948 | webserver("127.0.0.1:%s", "%s", "%s") | |
949 | """ | |
950 | ||
951 | def testNoOp(self): | |
952 | """ | |
953 | Dyn Blocks (group) : NoOp | |
954 | """ | |
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, | |
959 | 60, | |
960 | dns.rdataclass.IN, | |
961 | dns.rdatatype.A, | |
962 | '192.0.2.1') | |
963 | response.answer.append(rrset) | |
964 | ||
965 | allowed = 0 | |
966 | sent = 0 | |
967 | for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1): | |
968 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) | |
969 | sent = sent + 1 | |
970 | if receivedQuery: | |
971 | receivedQuery.id = query.id | |
972 | self.assertEquals(query, receivedQuery) | |
973 | self.assertEquals(response, receivedResponse) | |
974 | allowed = allowed + 1 | |
975 | else: | |
976 | # the query has not reached the responder, | |
977 | # let's clear the response queue | |
978 | self.clearToResponderQueue() | |
979 | ||
980 | # a dynamic rule should have been inserted, but the queries should still go on | |
981 | self.assertEqual(allowed, sent) | |
982 | ||
983 | # wait for the maintenance function to run | |
984 | time.sleep(2) | |
985 | ||
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) | |
991 | ||
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) | |
1d3ba133 RG |
994 | |
995 | class TestDynBlockGroupWarning(DynBlocksTest): | |
996 | ||
997 | _dynBlockWarningQPS = 5 | |
998 | _dynBlockQPS = 20 | |
999 | _dynBlockPeriod = 2 | |
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) | |
1005 | ||
1006 | function maintenance() | |
1007 | dbr:apply() | |
1008 | end | |
1009 | ||
1010 | newServer{address="127.0.0.1:%s"} | |
1011 | webserver("127.0.0.1:%s", "%s", "%s") | |
1012 | """ | |
1013 | ||
1014 | def testWarning(self): | |
1015 | """ | |
1016 | Dyn Blocks (group) : Warning | |
1017 | """ | |
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, | |
1022 | 60, | |
1023 | dns.rdataclass.IN, | |
1024 | dns.rdatatype.A, | |
1025 | '192.0.2.1') | |
1026 | response.answer.append(rrset) | |
1027 | ||
1028 | allowed = 0 | |
1029 | sent = 0 | |
1030 | for _ in range((self._dynBlockWarningQPS * self._dynBlockPeriod) + 1): | |
1031 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) | |
1032 | sent = sent + 1 | |
1033 | if receivedQuery: | |
1034 | receivedQuery.id = query.id | |
1035 | self.assertEquals(query, receivedQuery) | |
1036 | self.assertEquals(response, receivedResponse) | |
1037 | allowed = allowed + 1 | |
1038 | else: | |
1039 | # the query has not reached the responder, | |
1040 | # let's clear the response queue | |
1041 | self.clearToResponderQueue() | |
1042 | ||
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) | |
1046 | ||
1047 | # wait for the maintenance function to run | |
1048 | time.sleep(2) | |
1049 | ||
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) | |
1055 | ||
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) | |
1058 | ||
1059 | self.doTestQRate(name) |