]> git.ipfire.org Git - thirdparty/pdns.git/blob - regression-tests.dnsdist/test_Caching.py
Merge pull request #7351 from jsoref/tests-output
[thirdparty/pdns.git] / regression-tests.dnsdist / test_Caching.py
1 #!/usr/bin/env python
2 import base64
3 import time
4 import dns
5 import clientsubnetoption
6 from dnsdisttests import DNSDistTest
7
8 class TestCaching(DNSDistTest):
9
10 _config_template = """
11 pc = newPacketCache(100, 86400, 1)
12 getPool(""):setCache(pc)
13 addAction(makeRule("nocache.cache.tests.powerdns.com."), SkipCacheAction())
14 function skipViaLua(dq)
15 dq.skipCache = true
16 return DNSAction.None, ""
17 end
18 addAction("nocachevialua.cache.tests.powerdns.com.", LuaAction(skipViaLua))
19 newServer{address="127.0.0.1:%s"}
20 """
21
22 def testCached(self):
23 """
24 Cache: Served from cache
25
26 dnsdist is configured to cache entries, we are sending several
27 identical requests and checking that the backend only receive
28 the first one.
29 """
30 numberOfQueries = 10
31 name = 'cached.cache.tests.powerdns.com.'
32 query = dns.message.make_query(name, 'AAAA', 'IN')
33 response = dns.message.make_response(query)
34 rrset = dns.rrset.from_text(name,
35 3600,
36 dns.rdataclass.IN,
37 dns.rdatatype.AAAA,
38 '::1')
39 response.answer.append(rrset)
40
41 # first query to fill the cache
42 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
43 self.assertTrue(receivedQuery)
44 self.assertTrue(receivedResponse)
45 receivedQuery.id = query.id
46 self.assertEquals(query, receivedQuery)
47 self.assertEquals(receivedResponse, response)
48
49 for _ in range(numberOfQueries):
50 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
51 self.assertEquals(receivedResponse, response)
52
53 total = 0
54 for key in self._responsesCounter:
55 total += self._responsesCounter[key]
56 TestCaching._responsesCounter[key] = 0
57
58 self.assertEquals(total, 1)
59
60 # TCP should not be cached
61 # first query to fill the cache
62 (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
63 self.assertTrue(receivedQuery)
64 self.assertTrue(receivedResponse)
65 receivedQuery.id = query.id
66 self.assertEquals(query, receivedQuery)
67 self.assertEquals(receivedResponse, response)
68
69 for _ in range(numberOfQueries):
70 (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
71 self.assertEquals(receivedResponse, response)
72
73 total = 0
74 for key in self._responsesCounter:
75 total += self._responsesCounter[key]
76 TestCaching._responsesCounter[key] = 0
77
78 self.assertEquals(total, 1)
79
80 def testDOCached(self):
81 """
82 Cache: Served from cache, query has DO bit set
83
84 dnsdist is configured to cache entries, we are sending several
85 identical requests and checking that the backend only receive
86 the first one.
87 """
88 numberOfQueries = 10
89 name = 'cached-do.cache.tests.powerdns.com.'
90 query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, want_dnssec=True)
91 response = dns.message.make_response(query)
92 rrset = dns.rrset.from_text(name,
93 3600,
94 dns.rdataclass.IN,
95 dns.rdatatype.AAAA,
96 '::1')
97 response.answer.append(rrset)
98
99 # first query to fill the cache
100 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
101 self.assertTrue(receivedQuery)
102 self.assertTrue(receivedResponse)
103 receivedQuery.id = query.id
104 self.assertEquals(query, receivedQuery)
105 self.assertEquals(receivedResponse, response)
106
107 for _ in range(numberOfQueries):
108 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
109 self.assertEquals(receivedResponse, response)
110
111 total = 0
112 for key in self._responsesCounter:
113 total += self._responsesCounter[key]
114 TestCaching._responsesCounter[key] = 0
115
116 self.assertEquals(total, 1)
117
118 # TCP should not be cached
119 # first query to fill the cache
120 (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
121 self.assertTrue(receivedQuery)
122 self.assertTrue(receivedResponse)
123 receivedQuery.id = query.id
124 self.assertEquals(query, receivedQuery)
125 self.assertEquals(receivedResponse, response)
126
127 for _ in range(numberOfQueries):
128 (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
129 self.assertEquals(receivedResponse, response)
130
131 total = 0
132 for key in self._responsesCounter:
133 total += self._responsesCounter[key]
134 TestCaching._responsesCounter[key] = 0
135
136 self.assertEquals(total, 1)
137
138 def testSkipCache(self):
139 """
140 Cache: SkipCacheAction
141
142 dnsdist is configured to not cache entries for nocache.cache.tests.powerdns.com.
143 we are sending several requests and checking that the backend get them all.
144 """
145 name = 'nocache.cache.tests.powerdns.com.'
146 numberOfQueries = 10
147 query = dns.message.make_query(name, 'AAAA', 'IN')
148 response = dns.message.make_response(query)
149 rrset = dns.rrset.from_text(name,
150 3600,
151 dns.rdataclass.IN,
152 dns.rdatatype.AAAA,
153 '::1')
154 response.answer.append(rrset)
155
156 for _ in range(numberOfQueries):
157 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
158 self.assertTrue(receivedQuery)
159 self.assertTrue(receivedResponse)
160 receivedQuery.id = query.id
161 self.assertEquals(query, receivedQuery)
162 self.assertEquals(receivedResponse, response)
163
164 (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
165 self.assertTrue(receivedQuery)
166 self.assertTrue(receivedResponse)
167 receivedQuery.id = query.id
168 self.assertEquals(query, receivedQuery)
169 self.assertEquals(receivedResponse, response)
170
171 for key in self._responsesCounter:
172 value = self._responsesCounter[key]
173 self.assertEquals(value, numberOfQueries)
174
175 def testSkipCacheViaLua(self):
176 """
177 Cache: SkipCache via Lua
178
179 dnsdist is configured to not cache entries for nocachevialua.cache.tests.powerdns.com.
180 we are sending several requests and checking that the backend get them all.
181 """
182 name = 'nocachevialua.cache.tests.powerdns.com.'
183 numberOfQueries = 10
184 query = dns.message.make_query(name, 'AAAA', 'IN')
185 response = dns.message.make_response(query)
186 rrset = dns.rrset.from_text(name,
187 3600,
188 dns.rdataclass.IN,
189 dns.rdatatype.AAAA,
190 '::1')
191 response.answer.append(rrset)
192
193 for _ in range(numberOfQueries):
194 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
195 self.assertTrue(receivedQuery)
196 self.assertTrue(receivedResponse)
197 receivedQuery.id = query.id
198 self.assertEquals(query, receivedQuery)
199 self.assertEquals(receivedResponse, response)
200
201 (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
202 self.assertTrue(receivedQuery)
203 self.assertTrue(receivedResponse)
204 receivedQuery.id = query.id
205 self.assertEquals(query, receivedQuery)
206 self.assertEquals(receivedResponse, response)
207
208 for key in self._responsesCounter:
209 value = self._responsesCounter[key]
210 self.assertEquals(value, numberOfQueries)
211
212 def testCacheExpiration(self):
213 """
214 Cache: Cache expiration
215
216 dnsdist is configured to cache entries, we are sending one request
217 (cache miss) with a very short TTL, checking that the next requests
218 are cached. Then we wait for the TTL to expire, check that the
219 next request is a miss but the following one a hit.
220 """
221 ttl = 2
222 misses = 0
223 name = 'cacheexpiration.cache.tests.powerdns.com.'
224 query = dns.message.make_query(name, 'AAAA', 'IN')
225 response = dns.message.make_response(query)
226 rrset = dns.rrset.from_text(name,
227 ttl,
228 dns.rdataclass.IN,
229 dns.rdatatype.AAAA,
230 '::1')
231 response.answer.append(rrset)
232
233 # first query to fill the cache
234 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
235 self.assertTrue(receivedQuery)
236 self.assertTrue(receivedResponse)
237 receivedQuery.id = query.id
238 self.assertEquals(query, receivedQuery)
239 self.assertEquals(receivedResponse, response)
240 misses += 1
241
242 # next queries should hit the cache
243 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
244 self.assertEquals(receivedResponse, response)
245
246 # now we wait a bit for the cache entry to expire
247 time.sleep(ttl + 1)
248
249 # next query should be a miss, fill the cache again
250 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
251 self.assertTrue(receivedQuery)
252 self.assertTrue(receivedResponse)
253 receivedQuery.id = query.id
254 self.assertEquals(query, receivedQuery)
255 self.assertEquals(receivedResponse, response)
256 misses += 1
257
258 # following queries should hit the cache again
259 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
260 self.assertEquals(receivedResponse, response)
261
262 total = 0
263 for key in self._responsesCounter:
264 total += self._responsesCounter[key]
265
266 self.assertEquals(total, misses)
267
268 def testCacheExpirationDifferentSets(self):
269 """
270 Cache: Cache expiration with different sets
271
272 dnsdist is configured to cache entries, we are sending one request
273 (cache miss) whose response has a long and a very short TTL,
274 checking that the next requests are cached. Then we wait for the
275 short TTL to expire, check that the
276 next request is a miss but the following one a hit.
277 """
278 ttl = 2
279 misses = 0
280 name = 'cacheexpirationdifferentsets.cache.tests.powerdns.com.'
281 query = dns.message.make_query(name, 'AAAA', 'IN')
282 response = dns.message.make_response(query)
283 rrset = dns.rrset.from_text(name,
284 ttl,
285 dns.rdataclass.IN,
286 dns.rdatatype.CNAME,
287 'cname.cacheexpirationdifferentsets.cache.tests.powerdns.com.')
288 response.answer.append(rrset)
289 rrset = dns.rrset.from_text('cname.cacheexpirationdifferentsets.cache.tests.powerdns.com.',
290 ttl + 3600,
291 dns.rdataclass.IN,
292 dns.rdatatype.A,
293 '192.2.0.1')
294 response.additional.append(rrset)
295
296 # first query to fill the cache
297 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
298 self.assertTrue(receivedQuery)
299 self.assertTrue(receivedResponse)
300 receivedQuery.id = query.id
301 self.assertEquals(query, receivedQuery)
302 self.assertEquals(receivedResponse, response)
303 misses += 1
304
305 # next queries should hit the cache
306 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
307 self.assertEquals(receivedResponse, response)
308
309 # now we wait a bit for the cache entry to expire
310 time.sleep(ttl + 1)
311
312 # next query should be a miss, fill the cache again
313 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
314 self.assertTrue(receivedQuery)
315 self.assertTrue(receivedResponse)
316 receivedQuery.id = query.id
317 self.assertEquals(query, receivedQuery)
318 self.assertEquals(receivedResponse, response)
319 misses += 1
320
321 # following queries should hit the cache again
322 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
323 self.assertEquals(receivedResponse, response)
324
325 total = 0
326 for key in self._responsesCounter:
327 total += self._responsesCounter[key]
328
329 self.assertEquals(total, misses)
330
331 def testCacheDecreaseTTL(self):
332 """
333 Cache: Cache decreases TTL
334
335 dnsdist is configured to cache entries, we are sending one request
336 (cache miss) and verify that the cache hits have a decreasing TTL.
337 """
338 ttl = 600
339 misses = 0
340 name = 'cachedecreasettl.cache.tests.powerdns.com.'
341 query = dns.message.make_query(name, 'AAAA', 'IN')
342 response = dns.message.make_response(query)
343 rrset = dns.rrset.from_text(name,
344 ttl,
345 dns.rdataclass.IN,
346 dns.rdatatype.AAAA,
347 '::1')
348 response.answer.append(rrset)
349
350 # first query to fill the cache
351 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
352 self.assertTrue(receivedQuery)
353 self.assertTrue(receivedResponse)
354 receivedQuery.id = query.id
355 self.assertEquals(query, receivedQuery)
356 self.assertEquals(receivedResponse, response)
357 misses += 1
358
359 # next queries should hit the cache
360 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
361 self.assertEquals(receivedResponse, response)
362 for an in receivedResponse.answer:
363 self.assertTrue(an.ttl <= ttl)
364
365 # now we wait a bit for the TTL to decrease
366 time.sleep(1)
367
368 # next queries should hit the cache
369 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
370 self.assertEquals(receivedResponse, response)
371 for an in receivedResponse.answer:
372 self.assertTrue(an.ttl < ttl)
373
374 total = 0
375 for key in self._responsesCounter:
376 total += self._responsesCounter[key]
377
378 self.assertEquals(total, misses)
379
380 def testCacheDifferentCase(self):
381 """
382 Cache: Cache matches different case
383
384 dnsdist is configured to cache entries, we are sending one request
385 (cache miss) and verify that the same one with a different case
386 matches.
387 """
388 ttl = 600
389 name = 'cachedifferentcase.cache.tests.powerdns.com.'
390 differentCaseName = 'CacheDifferentCASE.cache.tests.powerdns.com.'
391 query = dns.message.make_query(name, 'AAAA', 'IN')
392 differentCaseQuery = dns.message.make_query(differentCaseName, 'AAAA', 'IN')
393 response = dns.message.make_response(query)
394 differentCaseResponse = dns.message.make_response(differentCaseQuery)
395 rrset = dns.rrset.from_text(name,
396 ttl,
397 dns.rdataclass.IN,
398 dns.rdatatype.AAAA,
399 '::1')
400 response.answer.append(rrset)
401 differentCaseResponse.answer.append(rrset)
402
403 # first query to fill the cache
404 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
405 self.assertTrue(receivedQuery)
406 self.assertTrue(receivedResponse)
407 receivedQuery.id = query.id
408 self.assertEquals(query, receivedQuery)
409 self.assertEquals(receivedResponse, response)
410
411 # different case query should still hit the cache
412 (_, receivedResponse) = self.sendUDPQuery(differentCaseQuery, response=None, useQueue=False)
413 self.assertEquals(receivedResponse, differentCaseResponse)
414
415
416 class TestTempFailureCacheTTLAction(DNSDistTest):
417
418 _config_template = """
419 pc = newPacketCache(100, 86400, 1)
420 getPool(""):setCache(pc)
421 addAction("servfail.cache.tests.powerdns.com.", TempFailureCacheTTLAction(1))
422 newServer{address="127.0.0.1:%s"}
423 """
424
425 def testTempFailureCacheTTLAction(self):
426 """
427 Cache: When a TempFailure TTL is set, it should be honored
428
429 dnsdist is configured to cache packets, plus a specific qname is
430 set up with a lower TempFailure Cache TTL. we are sending one request
431 (cache miss) and verify that the cache is hit for the following query,
432 but the TTL then expires before the larger "good" packetcache TTL.
433 """
434 name = 'servfail.cache.tests.powerdns.com.'
435 query = dns.message.make_query(name, 'AAAA', 'IN')
436 response = dns.message.make_response(query)
437 response.set_rcode(dns.rcode.SERVFAIL)
438
439 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
440 self.assertTrue(receivedQuery)
441 self.assertTrue(receivedResponse)
442 receivedQuery.id = query.id
443 self.assertEquals(query, receivedQuery)
444 self.assertEquals(receivedResponse, response)
445
446 # next query should hit the cache
447 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
448 self.assertFalse(receivedQuery)
449 self.assertTrue(receivedResponse)
450 self.assertEquals(receivedResponse, response)
451
452 # now we wait a bit for the Failure-Cache TTL to expire
453 time.sleep(2)
454
455 # next query should NOT hit the cache
456 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
457 self.assertTrue(receivedQuery)
458 self.assertTrue(receivedResponse)
459 self.assertEquals(receivedResponse, response)
460
461
462 class TestCachingWithExistingEDNS(DNSDistTest):
463
464 _config_template = """
465 pc = newPacketCache(5, 86400, 1)
466 getPool(""):setCache(pc)
467 newServer{address="127.0.0.1:%s"}
468 """
469 def testCacheWithEDNS(self):
470 """
471 Cache: Cache should not match different EDNS value
472
473 dnsdist is configured to cache entries, we are sending one request
474 (cache miss) and verify that the same one with a different EDNS UDP
475 Payload size is not served from the cache.
476 """
477 misses = 0
478 name = 'cachedifferentedns.cache.tests.powerdns.com.'
479 query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=512)
480 response = dns.message.make_response(query)
481 rrset = dns.rrset.from_text(name,
482 3600,
483 dns.rdataclass.IN,
484 dns.rdatatype.A,
485 '127.0.0.1')
486 response.answer.append(rrset)
487
488 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
489 self.assertTrue(receivedQuery)
490 self.assertTrue(receivedResponse)
491 receivedQuery.id = query.id
492 self.assertEquals(query, receivedQuery)
493 self.assertEquals(response, receivedResponse)
494 misses += 1
495
496 query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
497 response = dns.message.make_response(query)
498 rrset = dns.rrset.from_text(name,
499 3600,
500 dns.rdataclass.IN,
501 dns.rdatatype.A,
502 '127.0.0.1')
503 response.answer.append(rrset)
504
505 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
506 self.assertTrue(receivedQuery)
507 self.assertTrue(receivedResponse)
508 receivedQuery.id = query.id
509 self.assertEquals(query, receivedQuery)
510 self.assertEquals(response, receivedResponse)
511 misses += 1
512
513 total = 0
514 for key in self._responsesCounter:
515 total += self._responsesCounter[key]
516
517 self.assertEquals(total, misses)
518
519 class TestCachingCacheFull(DNSDistTest):
520
521 _config_template = """
522 pc = newPacketCache(1, 86400, 1)
523 getPool(""):setCache(pc)
524 newServer{address="127.0.0.1:%s"}
525 """
526 def testCacheFull(self):
527 """
528 Cache: No new entries are cached when the cache is full
529
530 """
531 misses = 0
532 name = 'cachenotfullyet.cache.tests.powerdns.com.'
533 query = dns.message.make_query(name, 'A', 'IN')
534 response = dns.message.make_response(query)
535 rrset = dns.rrset.from_text(name,
536 3600,
537 dns.rdataclass.IN,
538 dns.rdatatype.A,
539 '127.0.0.1')
540 response.answer.append(rrset)
541
542 # Miss
543 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
544 self.assertTrue(receivedQuery)
545 self.assertTrue(receivedResponse)
546 receivedQuery.id = query.id
547 self.assertEquals(query, receivedQuery)
548 self.assertEquals(response, receivedResponse)
549 misses += 1
550
551 # next queries should hit the cache
552 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
553 self.assertEquals(receivedResponse, response)
554
555 # ok, now the cache is full, send another query
556 name = 'cachefull.cache.tests.powerdns.com.'
557 query = dns.message.make_query(name, 'AAAA', 'IN')
558 response = dns.message.make_response(query)
559 rrset = dns.rrset.from_text(name,
560 3600,
561 dns.rdataclass.IN,
562 dns.rdatatype.AAAA,
563 '::1')
564 response.answer.append(rrset)
565
566 # Miss
567 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
568 self.assertTrue(receivedQuery)
569 self.assertTrue(receivedResponse)
570 receivedQuery.id = query.id
571 self.assertEquals(query, receivedQuery)
572 self.assertEquals(response, receivedResponse)
573 misses += 1
574
575 # next queries should NOT hit the cache
576 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
577 self.assertTrue(receivedQuery)
578 self.assertTrue(receivedResponse)
579 receivedQuery.id = query.id
580 self.assertEquals(query, receivedQuery)
581 self.assertEquals(response, receivedResponse)
582 misses += 1
583
584 total = 0
585 for key in self._responsesCounter:
586 total += self._responsesCounter[key]
587
588 self.assertEquals(total, misses)
589
590 class TestCachingNoStale(DNSDistTest):
591
592 _consoleKey = DNSDistTest.generateConsoleKey()
593 _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
594 _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
595 _config_template = """
596 pc = newPacketCache(100, 86400, 1)
597 getPool(""):setCache(pc)
598 setKey("%s")
599 controlSocket("127.0.0.1:%s")
600 newServer{address="127.0.0.1:%s"}
601 """
602 def testCacheNoStale(self):
603 """
604 Cache: Cache entry, set backend down, we should not get a stale entry
605
606 """
607 ttl = 1
608 name = 'nostale.cache.tests.powerdns.com.'
609 query = dns.message.make_query(name, 'A', 'IN')
610 response = dns.message.make_response(query)
611 rrset = dns.rrset.from_text(name,
612 1,
613 dns.rdataclass.IN,
614 dns.rdatatype.A,
615 '127.0.0.1')
616 response.answer.append(rrset)
617
618 # Miss
619 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
620 self.assertTrue(receivedQuery)
621 self.assertTrue(receivedResponse)
622 receivedQuery.id = query.id
623 self.assertEquals(query, receivedQuery)
624 self.assertEquals(response, receivedResponse)
625
626 # next queries should hit the cache
627 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
628 self.assertEquals(receivedResponse, response)
629
630 # ok, we mark the backend as down
631 self.sendConsoleCommand("getServer(0):setDown()")
632 # and we wait for the entry to expire
633 time.sleep(ttl + 1)
634
635 # we should NOT get a cached, stale, entry
636 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
637 self.assertEquals(receivedResponse, None)
638
639
640 class TestCachingStale(DNSDistTest):
641
642 _consoleKey = DNSDistTest.generateConsoleKey()
643 _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
644 _staleCacheTTL = 60
645 _config_params = ['_staleCacheTTL', '_consoleKeyB64', '_consolePort', '_testServerPort']
646 _config_template = """
647 pc = newPacketCache(100, 86400, 1, %s)
648 getPool(""):setCache(pc)
649 setStaleCacheEntriesTTL(600)
650 setKey("%s")
651 controlSocket("127.0.0.1:%s")
652 newServer{address="127.0.0.1:%s"}
653 """
654 def testCacheStale(self):
655 """
656 Cache: Cache entry, set backend down, get stale entry
657
658 """
659 misses = 0
660 ttl = 1
661 name = 'stale.cache.tests.powerdns.com.'
662 query = dns.message.make_query(name, 'A', 'IN')
663 response = dns.message.make_response(query)
664 rrset = dns.rrset.from_text(name,
665 ttl,
666 dns.rdataclass.IN,
667 dns.rdatatype.A,
668 '127.0.0.1')
669 response.answer.append(rrset)
670
671 # Miss
672 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
673 self.assertTrue(receivedQuery)
674 self.assertTrue(receivedResponse)
675 receivedQuery.id = query.id
676 self.assertEquals(query, receivedQuery)
677 self.assertEquals(response, receivedResponse)
678 misses += 1
679
680 # next queries should hit the cache
681 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
682 self.assertEquals(receivedResponse, response)
683
684 # ok, we mark the backend as down
685 self.sendConsoleCommand("getServer(0):setDown()")
686 # and we wait for the entry to expire
687 time.sleep(ttl + 1)
688
689 # we should get a cached, stale, entry
690 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
691 self.assertEquals(receivedResponse, response)
692 for an in receivedResponse.answer:
693 self.assertEquals(an.ttl, self._staleCacheTTL)
694
695 total = 0
696 for key in self._responsesCounter:
697 total += self._responsesCounter[key]
698
699 self.assertEquals(total, misses)
700
701 class TestCacheManagement(DNSDistTest):
702
703 _consoleKey = DNSDistTest.generateConsoleKey()
704 _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
705 _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
706 _config_template = """
707 pc = newPacketCache(100, 86400, 1)
708 getPool(""):setCache(pc)
709 setKey("%s")
710 controlSocket("127.0.0.1:%s")
711 newServer{address="127.0.0.1:%s"}
712 """
713 def testCacheExpunge(self):
714 """
715 Cache: Expunge
716
717 """
718 misses = 0
719 ttl = 600
720 name = 'expunge.cache.tests.powerdns.com.'
721 query = dns.message.make_query(name, 'A', 'IN')
722 response = dns.message.make_response(query)
723 rrset = dns.rrset.from_text(name,
724 ttl,
725 dns.rdataclass.IN,
726 dns.rdatatype.A,
727 '127.0.0.1')
728 response.answer.append(rrset)
729
730 # Miss
731 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
732 self.assertTrue(receivedQuery)
733 self.assertTrue(receivedResponse)
734 receivedQuery.id = query.id
735 self.assertEquals(query, receivedQuery)
736 self.assertEquals(response, receivedResponse)
737 misses += 1
738
739 # next queries should hit the cache
740 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
741 self.assertEquals(receivedResponse, response)
742
743 # remove cached entries
744 self.sendConsoleCommand("getPool(\"\"):getCache():expunge(0)")
745
746 # Miss
747 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
748 self.assertTrue(receivedQuery)
749 self.assertTrue(receivedResponse)
750 receivedQuery.id = query.id
751 self.assertEquals(query, receivedQuery)
752 self.assertEquals(response, receivedResponse)
753 misses += 1
754
755 # next queries should hit the cache again
756 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
757 self.assertEquals(receivedResponse, response)
758
759 total = 0
760 for key in self._responsesCounter:
761 total += self._responsesCounter[key]
762
763 self.assertEquals(total, misses)
764
765 def testCacheExpungeByName(self):
766 """
767 Cache: Expunge by name
768
769 """
770 misses = 0
771 ttl = 600
772 name = 'expungebyname.cache.tests.powerdns.com.'
773 query = dns.message.make_query(name, 'A', 'IN')
774 response = dns.message.make_response(query)
775 rrset = dns.rrset.from_text(name,
776 ttl,
777 dns.rdataclass.IN,
778 dns.rdatatype.A,
779 '127.0.0.1')
780 response.answer.append(rrset)
781
782 name2 = 'expungebynameother.cache.tests.powerdns.com.'
783 query2 = dns.message.make_query(name2, 'A', 'IN')
784 response2 = dns.message.make_response(query2)
785 rrset2 = dns.rrset.from_text(name2,
786 ttl,
787 dns.rdataclass.IN,
788 dns.rdatatype.A,
789 '127.0.0.1')
790 response2.answer.append(rrset2)
791
792 # Miss
793 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
794 self.assertTrue(receivedQuery)
795 self.assertTrue(receivedResponse)
796 receivedQuery.id = query.id
797 self.assertEquals(query, receivedQuery)
798 self.assertEquals(response, receivedResponse)
799 misses += 1
800
801 # next queries should hit the cache
802 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
803 self.assertEquals(receivedResponse, response)
804
805 # cache another entry
806 (receivedQuery, receivedResponse) = self.sendUDPQuery(query2, response2)
807 self.assertTrue(receivedQuery)
808 self.assertTrue(receivedResponse)
809 receivedQuery.id = query2.id
810 self.assertEquals(query2, receivedQuery)
811 self.assertEquals(response2, receivedResponse)
812 misses += 1
813
814 # queries for name and name 2 should hit the cache
815 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
816 self.assertEquals(receivedResponse, response)
817
818 (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
819 self.assertEquals(receivedResponse, response2)
820
821 # remove cached entries from name
822 self.sendConsoleCommand("getPool(\"\"):getCache():expungeByName(newDNSName(\"" + name + "\"))")
823
824 # Miss for name
825 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
826 self.assertTrue(receivedQuery)
827 self.assertTrue(receivedResponse)
828 receivedQuery.id = query.id
829 self.assertEquals(query, receivedQuery)
830 self.assertEquals(response, receivedResponse)
831 misses += 1
832
833 # next queries for name should hit the cache again
834 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
835 self.assertEquals(receivedResponse, response)
836
837 # queries for name2 should still hit the cache
838 (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
839 self.assertEquals(receivedResponse, response2)
840
841 total = 0
842 for key in self._responsesCounter:
843 total += self._responsesCounter[key]
844
845 self.assertEquals(total, misses)
846
847 def testCacheExpungeByNameAndType(self):
848 """
849 Cache: Expunge by name and type
850
851 """
852 misses = 0
853 ttl = 600
854 name = 'expungebynameandtype.cache.tests.powerdns.com.'
855 query = dns.message.make_query(name, 'A', 'IN')
856 response = dns.message.make_response(query)
857 rrset = dns.rrset.from_text(name,
858 ttl,
859 dns.rdataclass.IN,
860 dns.rdatatype.A,
861 '127.0.0.1')
862 response.answer.append(rrset)
863
864 query2 = dns.message.make_query(name, 'AAAA', 'IN')
865 response2 = dns.message.make_response(query2)
866 rrset2 = dns.rrset.from_text(name,
867 ttl,
868 dns.rdataclass.IN,
869 dns.rdatatype.AAAA,
870 '::1')
871 response2.answer.append(rrset2)
872
873 # Miss
874 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
875 self.assertTrue(receivedQuery)
876 self.assertTrue(receivedResponse)
877 receivedQuery.id = query.id
878 self.assertEquals(query, receivedQuery)
879 self.assertEquals(response, receivedResponse)
880 misses += 1
881
882 # next queries should hit the cache
883 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
884 self.assertEquals(receivedResponse, response)
885
886 # cache another entry
887 (receivedQuery, receivedResponse) = self.sendUDPQuery(query2, response2)
888 self.assertTrue(receivedQuery)
889 self.assertTrue(receivedResponse)
890 receivedQuery.id = query2.id
891 self.assertEquals(query2, receivedQuery)
892 self.assertEquals(response2, receivedResponse)
893 misses += 1
894
895 # queries for name A and AAAA should hit the cache
896 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
897 self.assertEquals(receivedResponse, response)
898
899 (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
900 self.assertEquals(receivedResponse, response2)
901
902 # remove cached entries from name A
903 self.sendConsoleCommand("getPool(\"\"):getCache():expungeByName(newDNSName(\"" + name + "\"), dnsdist.A)")
904
905 # Miss for name A
906 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
907 self.assertTrue(receivedQuery)
908 self.assertTrue(receivedResponse)
909 receivedQuery.id = query.id
910 self.assertEquals(query, receivedQuery)
911 self.assertEquals(response, receivedResponse)
912 misses += 1
913
914 # next queries for name A should hit the cache again
915 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
916 self.assertEquals(receivedResponse, response)
917
918 # queries for name AAAA should still hit the cache
919 (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
920 self.assertEquals(receivedResponse, response2)
921
922 total = 0
923 for key in self._responsesCounter:
924 total += self._responsesCounter[key]
925 self.assertEquals(total, misses)
926
927 def testCacheExpungeByNameAndSuffix(self):
928 """
929 Cache: Expunge by name
930
931 """
932 misses = 0
933 ttl = 600
934 name = 'expungebyname.suffix.cache.tests.powerdns.com.'
935 query = dns.message.make_query(name, 'A', 'IN')
936 response = dns.message.make_response(query)
937 rrset = dns.rrset.from_text(name,
938 ttl,
939 dns.rdataclass.IN,
940 dns.rdatatype.A,
941 '127.0.0.1')
942 response.answer.append(rrset)
943
944 name2 = 'expungebyname.suffixother.cache.tests.powerdns.com.'
945 query2 = dns.message.make_query(name2, 'A', 'IN')
946 response2 = dns.message.make_response(query2)
947 rrset2 = dns.rrset.from_text(name2,
948 ttl,
949 dns.rdataclass.IN,
950 dns.rdatatype.A,
951 '127.0.0.1')
952 response2.answer.append(rrset2)
953
954 # Miss
955 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
956 self.assertTrue(receivedQuery)
957 self.assertTrue(receivedResponse)
958 receivedQuery.id = query.id
959 self.assertEquals(query, receivedQuery)
960 self.assertEquals(response, receivedResponse)
961 misses += 1
962
963 # next queries should hit the cache
964 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
965 self.assertEquals(receivedResponse, response)
966
967 # cache another entry
968 (receivedQuery, receivedResponse) = self.sendUDPQuery(query2, response2)
969 self.assertTrue(receivedQuery)
970 self.assertTrue(receivedResponse)
971 receivedQuery.id = query2.id
972 self.assertEquals(query2, receivedQuery)
973 self.assertEquals(response2, receivedResponse)
974 misses += 1
975
976 # queries for name and name 2 should hit the cache
977 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
978 self.assertEquals(receivedResponse, response)
979
980 (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
981 self.assertEquals(receivedResponse, response2)
982
983 # remove cached entries from name
984 self.sendConsoleCommand("getPool(\"\"):getCache():expungeByName(newDNSName(\"suffix.cache.tests.powerdns.com.\"), dnsdist.ANY, true)")
985
986 # Miss for name
987 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
988 self.assertTrue(receivedQuery)
989 self.assertTrue(receivedResponse)
990 receivedQuery.id = query.id
991 self.assertEquals(query, receivedQuery)
992 self.assertEquals(response, receivedResponse)
993 misses += 1
994
995 # next queries for name should hit the cache again
996 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
997 self.assertEquals(receivedResponse, response)
998
999 # queries for name2 should still hit the cache
1000 (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
1001 self.assertEquals(receivedResponse, response2)
1002
1003 total = 0
1004 for key in self._responsesCounter:
1005 total += self._responsesCounter[key]
1006
1007 self.assertEquals(total, misses)
1008
1009 def testCacheExpungeByNameAndTypeAndSuffix(self):
1010 """
1011 Cache: Expunge by name and type
1012
1013 """
1014 misses = 0
1015 ttl = 600
1016 name = 'expungebynameandtype.suffixtype.cache.tests.powerdns.com.'
1017 query = dns.message.make_query(name, 'A', 'IN')
1018 response = dns.message.make_response(query)
1019 rrset = dns.rrset.from_text(name,
1020 ttl,
1021 dns.rdataclass.IN,
1022 dns.rdatatype.A,
1023 '127.0.0.1')
1024 response.answer.append(rrset)
1025
1026 query2 = dns.message.make_query(name, 'AAAA', 'IN')
1027 response2 = dns.message.make_response(query2)
1028 rrset2 = dns.rrset.from_text(name,
1029 ttl,
1030 dns.rdataclass.IN,
1031 dns.rdatatype.AAAA,
1032 '::1')
1033 response2.answer.append(rrset2)
1034
1035 # Miss
1036 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1037 self.assertTrue(receivedQuery)
1038 self.assertTrue(receivedResponse)
1039 receivedQuery.id = query.id
1040 self.assertEquals(query, receivedQuery)
1041 self.assertEquals(response, receivedResponse)
1042 misses += 1
1043
1044 # next queries should hit the cache
1045 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1046 self.assertEquals(receivedResponse, response)
1047
1048 # cache another entry
1049 (receivedQuery, receivedResponse) = self.sendUDPQuery(query2, response2)
1050 self.assertTrue(receivedQuery)
1051 self.assertTrue(receivedResponse)
1052 receivedQuery.id = query2.id
1053 self.assertEquals(query2, receivedQuery)
1054 self.assertEquals(response2, receivedResponse)
1055 misses += 1
1056
1057 # queries for name A and AAAA should hit the cache
1058 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1059 self.assertEquals(receivedResponse, response)
1060
1061 (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
1062 self.assertEquals(receivedResponse, response2)
1063
1064 # remove cached entries from name A
1065 self.sendConsoleCommand("getPool(\"\"):getCache():expungeByName(newDNSName(\"suffixtype.cache.tests.powerdns.com.\"), dnsdist.A, true)")
1066
1067 # Miss for name A
1068 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1069 self.assertTrue(receivedQuery)
1070 self.assertTrue(receivedResponse)
1071 receivedQuery.id = query.id
1072 self.assertEquals(query, receivedQuery)
1073 self.assertEquals(response, receivedResponse)
1074 misses += 1
1075
1076 # next queries for name A should hit the cache again
1077 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1078 self.assertEquals(receivedResponse, response)
1079
1080 # queries for name AAAA should still hit the cache
1081 (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
1082 self.assertEquals(receivedResponse, response2)
1083
1084 total = 0
1085 for key in self._responsesCounter:
1086 total += self._responsesCounter[key]
1087 self.assertEquals(total, misses)
1088
1089 class TestCachingTTL(DNSDistTest):
1090
1091 _maxCacheTTL = 86400
1092 _minCacheTTL = 600
1093 _config_params = ['_maxCacheTTL', '_minCacheTTL', '_testServerPort']
1094 _config_template = """
1095 pc = newPacketCache(1000, %s, %s)
1096 getPool(""):setCache(pc)
1097 newServer{address="127.0.0.1:%s"}
1098 """
1099 def testCacheShortTTL(self):
1100 """
1101 Cache: Entries with a TTL shorter than minTTL
1102
1103 """
1104 misses = 0
1105 ttl = 60
1106 name = 'ttltooshort.cache.tests.powerdns.com.'
1107 query = dns.message.make_query(name, 'A', 'IN')
1108 response = dns.message.make_response(query)
1109 rrset = dns.rrset.from_text(name,
1110 ttl,
1111 dns.rdataclass.IN,
1112 dns.rdatatype.A,
1113 '127.0.0.1')
1114 response.answer.append(rrset)
1115
1116 # Miss
1117 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1118 self.assertTrue(receivedQuery)
1119 self.assertTrue(receivedResponse)
1120 receivedQuery.id = query.id
1121 self.assertEquals(query, receivedQuery)
1122 self.assertEquals(response, receivedResponse)
1123 for an in receivedResponse.answer:
1124 self.assertEquals(an.ttl, ttl)
1125 misses += 1
1126
1127 # We should not have been cached
1128 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1129 self.assertTrue(receivedQuery)
1130 self.assertTrue(receivedResponse)
1131 receivedQuery.id = query.id
1132 self.assertEquals(query, receivedQuery)
1133 self.assertEquals(response, receivedResponse)
1134 for an in receivedResponse.answer:
1135 self.assertEquals(an.ttl, ttl)
1136 misses += 1
1137
1138 total = 0
1139 for key in self._responsesCounter:
1140 total += self._responsesCounter[key]
1141
1142 self.assertEquals(total, misses)
1143
1144 def testCacheNXWithNoRR(self):
1145 """
1146 Cache: NX with no RR
1147
1148 """
1149 misses = 0
1150 name = 'nxwithnorr.cache.tests.powerdns.com.'
1151 query = dns.message.make_query(name, 'A', 'IN')
1152 response = dns.message.make_response(query)
1153 response.set_rcode(dns.rcode.NXDOMAIN)
1154
1155 # Miss
1156 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1157 self.assertTrue(receivedQuery)
1158 self.assertTrue(receivedResponse)
1159 receivedQuery.id = query.id
1160 self.assertEquals(query, receivedQuery)
1161 self.assertEquals(response, receivedResponse)
1162 misses += 1
1163
1164 # We should not have been cached
1165 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1166 self.assertTrue(receivedQuery)
1167 self.assertTrue(receivedResponse)
1168 receivedQuery.id = query.id
1169 self.assertEquals(query, receivedQuery)
1170 self.assertEquals(response, receivedResponse)
1171 misses += 1
1172
1173 total = 0
1174 for key in self._responsesCounter:
1175 total += self._responsesCounter[key]
1176
1177 self.assertEquals(total, misses)
1178
1179 class TestCachingLongTTL(DNSDistTest):
1180
1181 _maxCacheTTL = 2
1182 _config_params = ['_maxCacheTTL', '_testServerPort']
1183 _config_template = """
1184 pc = newPacketCache(1000, %s)
1185 getPool(""):setCache(pc)
1186 newServer{address="127.0.0.1:%s"}
1187 """
1188 def testCacheLongTTL(self):
1189 """
1190 Cache: Entries with a longer TTL than the maximum
1191
1192 """
1193 misses = 0
1194 ttl = 172800
1195 name = 'longttl.cache.tests.powerdns.com.'
1196 query = dns.message.make_query(name, 'A', 'IN')
1197 response = dns.message.make_response(query)
1198 rrset = dns.rrset.from_text(name,
1199 ttl,
1200 dns.rdataclass.IN,
1201 dns.rdatatype.A,
1202 '127.0.0.1')
1203 response.answer.append(rrset)
1204
1205 # Miss
1206 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1207 self.assertTrue(receivedQuery)
1208 self.assertTrue(receivedResponse)
1209 receivedQuery.id = query.id
1210 self.assertEquals(query, receivedQuery)
1211 self.assertEquals(response, receivedResponse)
1212 for an in receivedResponse.answer:
1213 self.assertEquals(an.ttl, ttl)
1214 misses += 1
1215
1216 # next queries should hit the cache
1217 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1218 self.assertEquals(receivedResponse, response)
1219 for an in receivedResponse.answer:
1220 self.assertTrue(an.ttl <= ttl)
1221
1222 time.sleep(self._maxCacheTTL + 1)
1223
1224 # we should not have cached for longer than max cache
1225 # so it should be a miss
1226 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1227 self.assertTrue(receivedQuery)
1228 self.assertTrue(receivedResponse)
1229 receivedQuery.id = query.id
1230 self.assertEquals(query, receivedQuery)
1231 self.assertEquals(response, receivedResponse)
1232 for an in receivedResponse.answer:
1233 self.assertEquals(an.ttl, ttl)
1234 misses += 1
1235
1236 total = 0
1237 for key in self._responsesCounter:
1238 total += self._responsesCounter[key]
1239
1240 self.assertEquals(total, misses)
1241
1242 class TestCachingFailureTTL(DNSDistTest):
1243
1244 _failureCacheTTL = 2
1245 _config_params = ['_failureCacheTTL', '_testServerPort']
1246 _config_template = """
1247 pc = newPacketCache(1000, 86400, 0, %d, 60)
1248 getPool(""):setCache(pc)
1249 newServer{address="127.0.0.1:%s"}
1250 """
1251 def testCacheServFailTTL(self):
1252 """
1253 Cache: ServFail TTL
1254
1255 """
1256 misses = 0
1257 name = 'servfail.failure.cache.tests.powerdns.com.'
1258 query = dns.message.make_query(name, 'A', 'IN')
1259 response = dns.message.make_response(query)
1260 response.set_rcode(dns.rcode.SERVFAIL)
1261
1262 # Miss
1263 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1264 self.assertTrue(receivedQuery)
1265 self.assertTrue(receivedResponse)
1266 receivedQuery.id = query.id
1267 self.assertEquals(query, receivedQuery)
1268 self.assertEquals(response, receivedResponse)
1269 misses += 1
1270
1271 # next queries should hit the cache
1272 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1273 self.assertEquals(receivedResponse, response)
1274
1275 time.sleep(self._failureCacheTTL + 1)
1276
1277 # we should not have cached for longer than failure cache
1278 # so it should be a miss
1279 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1280 self.assertTrue(receivedQuery)
1281 self.assertTrue(receivedResponse)
1282 receivedQuery.id = query.id
1283 self.assertEquals(query, receivedQuery)
1284 self.assertEquals(response, receivedResponse)
1285 misses += 1
1286
1287 total = 0
1288 for key in self._responsesCounter:
1289 total += self._responsesCounter[key]
1290
1291 self.assertEquals(total, misses)
1292
1293 def testCacheRefusedTTL(self):
1294 """
1295 Cache: Refused TTL
1296
1297 """
1298 misses = 0
1299 name = 'refused.failure.cache.tests.powerdns.com.'
1300 query = dns.message.make_query(name, 'A', 'IN')
1301 response = dns.message.make_response(query)
1302 response.set_rcode(dns.rcode.REFUSED)
1303
1304 # Miss
1305 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1306 self.assertTrue(receivedQuery)
1307 self.assertTrue(receivedResponse)
1308 receivedQuery.id = query.id
1309 self.assertEquals(query, receivedQuery)
1310 self.assertEquals(response, receivedResponse)
1311 misses += 1
1312
1313 # next queries should hit the cache
1314 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1315 self.assertEquals(receivedResponse, response)
1316
1317 time.sleep(self._failureCacheTTL + 1)
1318
1319 # we should not have cached for longer than failure cache
1320 # so it should be a miss
1321 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1322 self.assertTrue(receivedQuery)
1323 self.assertTrue(receivedResponse)
1324 receivedQuery.id = query.id
1325 self.assertEquals(query, receivedQuery)
1326 self.assertEquals(response, receivedResponse)
1327 misses += 1
1328
1329 total = 0
1330 for key in self._responsesCounter:
1331 total += self._responsesCounter[key]
1332
1333 self.assertEquals(total, misses)
1334
1335 def testCacheHeaderOnlyRefusedTTL(self):
1336 """
1337 Cache: Header-Only Refused TTL
1338
1339 """
1340 misses = 0
1341 name = 'header-only-refused.failure.cache.tests.powerdns.com.'
1342 query = dns.message.make_query(name, 'A', 'IN')
1343 response = dns.message.make_response(query)
1344 response.set_rcode(dns.rcode.REFUSED)
1345 response.question = []
1346
1347 # Miss
1348 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1349 self.assertTrue(receivedQuery)
1350 self.assertTrue(receivedResponse)
1351 receivedQuery.id = query.id
1352 self.assertEquals(query, receivedQuery)
1353 self.assertEquals(response, receivedResponse)
1354 misses += 1
1355
1356 # next queries should hit the cache
1357 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1358 self.assertEquals(receivedResponse, response)
1359
1360 time.sleep(self._failureCacheTTL + 1)
1361
1362 # we should not have cached for longer than failure cache
1363 # so it should be a miss
1364 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1365 self.assertTrue(receivedQuery)
1366 self.assertTrue(receivedResponse)
1367 receivedQuery.id = query.id
1368 self.assertEquals(query, receivedQuery)
1369 self.assertEquals(response, receivedResponse)
1370 misses += 1
1371
1372 total = 0
1373 for key in self._responsesCounter:
1374 total += self._responsesCounter[key]
1375
1376 self.assertEquals(total, misses)
1377
1378 class TestCachingNegativeTTL(DNSDistTest):
1379
1380 _negCacheTTL = 1
1381 _config_params = ['_negCacheTTL', '_testServerPort']
1382 _config_template = """
1383 pc = newPacketCache(1000, 86400, 0, 60, 60, false, 1, true, %d)
1384 getPool(""):setCache(pc)
1385 newServer{address="127.0.0.1:%s"}
1386 """
1387
1388 def testCacheNegativeTTLNXDomain(self):
1389 """
1390 Cache: Negative TTL on NXDOMAIN
1391
1392 """
1393 misses = 0
1394 name = 'nxdomain.negativettl.cache.tests.powerdns.com.'
1395 query = dns.message.make_query(name, 'A', 'IN')
1396 response = dns.message.make_response(query)
1397 response.set_rcode(dns.rcode.NXDOMAIN)
1398 soa = dns.rrset.from_text(name,
1399 60,
1400 dns.rdataclass.IN,
1401 dns.rdatatype.SOA,
1402 'ns.' + name + ' hostmaster.' + name + ' 1 3600 3600 3600 60')
1403 response.authority.append(soa)
1404
1405 # Miss
1406 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1407 self.assertTrue(receivedQuery)
1408 self.assertTrue(receivedResponse)
1409 receivedQuery.id = query.id
1410 self.assertEquals(query, receivedQuery)
1411 self.assertEquals(response, receivedResponse)
1412 misses += 1
1413
1414 # next queries should hit the cache
1415 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1416 self.assertEquals(receivedResponse, response)
1417
1418 time.sleep(self._negCacheTTL + 1)
1419
1420 # we should not have cached for longer than the negativel TTL
1421 # so it should be a miss
1422 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1423 self.assertTrue(receivedQuery)
1424 self.assertTrue(receivedResponse)
1425 receivedQuery.id = query.id
1426 self.assertEquals(query, receivedQuery)
1427 self.assertEquals(response, receivedResponse)
1428 misses += 1
1429
1430 total = 0
1431 for key in self._responsesCounter:
1432 total += self._responsesCounter[key]
1433
1434 self.assertEquals(total, misses)
1435
1436 def testCacheNegativeTTLNoData(self):
1437 """
1438 Cache: Negative TTL on NoData
1439
1440 """
1441 misses = 0
1442 name = 'nodata.negativettl.cache.tests.powerdns.com.'
1443 query = dns.message.make_query(name, 'A', 'IN')
1444 response = dns.message.make_response(query)
1445 response.set_rcode(dns.rcode.NOERROR)
1446 soa = dns.rrset.from_text(name,
1447 60,
1448 dns.rdataclass.IN,
1449 dns.rdatatype.SOA,
1450 'ns.' + name + ' hostmaster.' + name + ' 1 3600 3600 3600 60')
1451 response.authority.append(soa)
1452
1453 # Miss
1454 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1455 self.assertTrue(receivedQuery)
1456 self.assertTrue(receivedResponse)
1457 receivedQuery.id = query.id
1458 self.assertEquals(query, receivedQuery)
1459 self.assertEquals(response, receivedResponse)
1460 misses += 1
1461
1462 # next queries should hit the cache
1463 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1464 self.assertEquals(receivedResponse, response)
1465
1466 time.sleep(self._negCacheTTL + 1)
1467
1468 # we should not have cached for longer than the negativel TTL
1469 # so it should be a miss
1470 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1471 self.assertTrue(receivedQuery)
1472 self.assertTrue(receivedResponse)
1473 receivedQuery.id = query.id
1474 self.assertEquals(query, receivedQuery)
1475 self.assertEquals(response, receivedResponse)
1476 misses += 1
1477
1478 total = 0
1479 for key in self._responsesCounter:
1480 total += self._responsesCounter[key]
1481
1482 self.assertEquals(total, misses)
1483
1484 class TestCachingDontAge(DNSDistTest):
1485
1486 _config_template = """
1487 pc = newPacketCache(100, 86400, 0, 60, 60, true)
1488 getPool(""):setCache(pc)
1489 newServer{address="127.0.0.1:%s"}
1490 """
1491 def testCacheDoesntDecreaseTTL(self):
1492 """
1493 Cache: Cache doesn't decrease TTL with 'don't age' set
1494
1495 dnsdist is configured to cache entries but without aging the TTL,
1496 we are sending one request (cache miss) and verify that the cache
1497 hits don't have a decreasing TTL.
1498 """
1499 ttl = 600
1500 misses = 0
1501 name = 'cachedoesntdecreasettl.cache-dont-age.tests.powerdns.com.'
1502 query = dns.message.make_query(name, 'AAAA', 'IN')
1503 response = dns.message.make_response(query)
1504 rrset = dns.rrset.from_text(name,
1505 ttl,
1506 dns.rdataclass.IN,
1507 dns.rdatatype.AAAA,
1508 '::1')
1509 response.answer.append(rrset)
1510
1511 # first query to fill the cache
1512 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1513 self.assertTrue(receivedQuery)
1514 self.assertTrue(receivedResponse)
1515 receivedQuery.id = query.id
1516 self.assertEquals(query, receivedQuery)
1517 self.assertEquals(receivedResponse, response)
1518 misses += 1
1519
1520 # next queries should hit the cache
1521 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1522 self.assertEquals(receivedResponse, response)
1523 for an in receivedResponse.answer:
1524 self.assertTrue(an.ttl == ttl)
1525
1526 # now we wait a bit for the TTL to decrease
1527 time.sleep(1)
1528
1529 # next queries should hit the cache
1530 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1531 self.assertEquals(receivedResponse, response)
1532 for an in receivedResponse.answer:
1533 self.assertTrue(an.ttl == ttl)
1534
1535 total = 0
1536 for key in self._responsesCounter:
1537 total += self._responsesCounter[key]
1538
1539 self.assertEquals(total, misses)
1540
1541 class TestCachingECSWithoutPoolECS(DNSDistTest):
1542
1543 _consoleKey = DNSDistTest.generateConsoleKey()
1544 _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
1545 _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
1546 _config_template = """
1547 pc = newPacketCache(100, 86400, 1)
1548 getPool(""):setCache(pc)
1549 setKey("%s")
1550 controlSocket("127.0.0.1:%d")
1551 newServer{address="127.0.0.1:%d", useClientSubnet=true}
1552 """
1553
1554 def testCached(self):
1555 """
1556 Cache: Cached entry with ECS is a miss when no backend are available
1557 """
1558 ttl = 600
1559 name = 'cached.cache-ecs-without-pool-ecs.tests.powerdns.com.'
1560 query = dns.message.make_query(name, 'AAAA', 'IN')
1561 response = dns.message.make_response(query)
1562 rrset = dns.rrset.from_text(name,
1563 ttl,
1564 dns.rdataclass.IN,
1565 dns.rdatatype.AAAA,
1566 '::1')
1567 response.answer.append(rrset)
1568
1569 # first query to fill the cache
1570 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1571 self.assertTrue(receivedQuery)
1572 self.assertTrue(receivedResponse)
1573 receivedQuery.id = query.id
1574 self.assertEquals(query, receivedQuery)
1575 self.assertEquals(receivedResponse, response)
1576 (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
1577 self.assertTrue(receivedQuery)
1578 self.assertTrue(receivedResponse)
1579 receivedQuery.id = query.id
1580 self.assertEquals(query, receivedQuery)
1581 self.assertEquals(receivedResponse, response)
1582
1583 # next queries should hit the cache
1584 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1585 self.assertEquals(receivedResponse, response)
1586
1587 # over TCP too
1588 (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
1589 self.assertEquals(receivedResponse, response)
1590
1591 # we mark the backend as down
1592 self.sendConsoleCommand("getServer(0):setDown()")
1593
1594 # we should NOT get a cached entry since it has ECS and we haven't asked the pool
1595 # to add ECS when no backend is up
1596 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1597 self.assertEquals(receivedResponse, None)
1598
1599 # same over TCP
1600 (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
1601 self.assertEquals(receivedResponse, None)
1602
1603 class TestCachingECSWithPoolECS(DNSDistTest):
1604
1605 _consoleKey = DNSDistTest.generateConsoleKey()
1606 _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
1607 _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
1608 _config_template = """
1609 pc = newPacketCache(100, 86400, 1)
1610 getPool(""):setCache(pc)
1611 getPool(""):setECS(true)
1612 setKey("%s")
1613 controlSocket("127.0.0.1:%d")
1614 newServer{address="127.0.0.1:%d", useClientSubnet=true}
1615 """
1616
1617 def testCached(self):
1618 """
1619 Cache: Cached entry with ECS is a hit when no backend are available
1620 """
1621 ttl = 600
1622 name = 'cached.cache-ecs-with-pool-ecs.tests.powerdns.com.'
1623 query = dns.message.make_query(name, 'AAAA', 'IN')
1624 response = dns.message.make_response(query)
1625 rrset = dns.rrset.from_text(name,
1626 ttl,
1627 dns.rdataclass.IN,
1628 dns.rdatatype.AAAA,
1629 '::1')
1630 response.answer.append(rrset)
1631
1632 # first query to fill the cache
1633 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1634 self.assertTrue(receivedQuery)
1635 self.assertTrue(receivedResponse)
1636 receivedQuery.id = query.id
1637 self.assertEquals(query, receivedQuery)
1638 self.assertEquals(receivedResponse, response)
1639 (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
1640 self.assertTrue(receivedQuery)
1641 self.assertTrue(receivedResponse)
1642 receivedQuery.id = query.id
1643 self.assertEquals(query, receivedQuery)
1644 self.assertEquals(receivedResponse, response)
1645
1646 # next queries should hit the cache
1647 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1648 self.assertEquals(receivedResponse, response)
1649
1650 # over TCP too
1651 (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
1652 self.assertEquals(receivedResponse, response)
1653
1654 # we mark the backend as down
1655 self.sendConsoleCommand("getServer(0):setDown()")
1656
1657 # we should STILL get a cached entry since it has ECS and we have asked the pool
1658 # to add ECS when no backend is up
1659 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1660 self.assertEquals(receivedResponse, response)
1661
1662 # same over TCP
1663 (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
1664 self.assertEquals(receivedResponse, response)
1665
1666 class TestCachingCollisionNoECSParsing(DNSDistTest):
1667
1668 _config_template = """
1669 pc = newPacketCache(100, 86400, 1)
1670 getPool(""):setCache(pc)
1671 newServer{address="127.0.0.1:%s"}
1672 """
1673
1674 def testCacheCollisionNoECSParsing(self):
1675 """
1676 Cache: Collision with no ECS parsing
1677 """
1678 name = 'collision-no-ecs-parsing.cache.tests.powerdns.com.'
1679 ecso = clientsubnetoption.ClientSubnetOption('10.0.188.3', 32)
1680 query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
1681 query.flags = dns.flags.RD
1682 response = dns.message.make_response(query)
1683 rrset = dns.rrset.from_text(name,
1684 3600,
1685 dns.rdataclass.IN,
1686 dns.rdatatype.AAAA,
1687 '::1')
1688 response.answer.append(rrset)
1689
1690 # first query should to fill the cache
1691 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1692 self.assertTrue(receivedQuery)
1693 self.assertTrue(receivedResponse)
1694 receivedQuery.id = query.id
1695 self.assertEquals(query, receivedQuery)
1696 self.assertEquals(receivedResponse, response)
1697
1698 # second query will hash to the same key, triggering a collision which
1699 # will not be detected because the qname, qtype, qclass and flags will
1700 # match and EDNS Client Subnet parsing has not been enabled
1701 ecso2 = clientsubnetoption.ClientSubnetOption('10.0.192.138', 32)
1702 query2 = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso2], payload=512)
1703 query2.flags = dns.flags.RD
1704 (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
1705 receivedResponse.id = response.id
1706 self.assertEquals(receivedResponse, response)
1707
1708 class TestCachingCollisionWithECSParsing(DNSDistTest):
1709
1710 _config_template = """
1711 pc = newPacketCache(100, 86400, 1, 60, 60, false, 1, true, 3600, true)
1712 getPool(""):setCache(pc)
1713 newServer{address="127.0.0.1:%s"}
1714 """
1715
1716 def testCacheCollisionWithECSParsing(self):
1717 """
1718 Cache: Collision with ECS parsing
1719 """
1720 name = 'collision-with-ecs-parsing.cache.tests.powerdns.com.'
1721 ecso = clientsubnetoption.ClientSubnetOption('10.0.115.61', 32)
1722 query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
1723 query.flags = dns.flags.RD
1724 response = dns.message.make_response(query)
1725 rrset = dns.rrset.from_text(name,
1726 3600,
1727 dns.rdataclass.IN,
1728 dns.rdatatype.AAAA,
1729 '::1')
1730 response.answer.append(rrset)
1731
1732 # first query should to fill the cache
1733 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1734 self.assertTrue(receivedQuery)
1735 self.assertTrue(receivedResponse)
1736 receivedQuery.id = query.id
1737 self.assertEquals(query, receivedQuery)
1738 self.assertEquals(receivedResponse, response)
1739
1740 # second query will hash to the same key, triggering a collision which
1741 # _will_ be detected this time because the qname, qtype, qclass and flags will
1742 # match but EDNS Client Subnet parsing is now enabled and will detect the issue
1743 ecso2 = clientsubnetoption.ClientSubnetOption('10.0.143.21', 32)
1744 query2 = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso2], payload=512)
1745 query2.flags = dns.flags.RD
1746 response2 = dns.message.make_response(query2)
1747 rrset = dns.rrset.from_text(name,
1748 3600,
1749 dns.rdataclass.IN,
1750 dns.rdatatype.AAAA,
1751 '2001:DB8::1')
1752 response2.answer.append(rrset)
1753 (receivedQuery, receivedResponse) = self.sendUDPQuery(query2, response2)
1754 self.assertEquals(receivedResponse, response2)
1755
1756 class TestCachingScopeZero(DNSDistTest):
1757
1758 _config_template = """
1759 -- Be careful to enable ECS parsing in the packet cache, otherwise scope zero is disabled
1760 pc = newPacketCache(100, 86400, 1, 60, 60, false, 1, true, 3600, true)
1761 getPool(""):setCache(pc)
1762 newServer{address="127.0.0.1:%d", useClientSubnet=true}
1763 -- to simulate a second client coming from a different IP address,
1764 -- we will force the ECS value added to the query if RD is set (note that we need
1765 -- to unset it using rules before the first cache lookup)
1766 addAction(RDRule(), SetECSAction("192.0.2.1/32"))
1767 addAction(RDRule(), NoRecurseAction())
1768 """
1769
1770 def testScopeZero(self):
1771 """
1772 Cache: Test the scope-zero feature, backend returns a scope of zero
1773 """
1774 ttl = 600
1775 name = 'scope-zero.cache.tests.powerdns.com.'
1776 query = dns.message.make_query(name, 'AAAA', 'IN')
1777 query.flags &= ~dns.flags.RD
1778 ecso = clientsubnetoption.ClientSubnetOption('127.0.0.0', 24)
1779 expectedQuery = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
1780 expectedQuery.flags &= ~dns.flags.RD
1781 ecsoResponse = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24, 0)
1782 expectedResponse = dns.message.make_response(query)
1783 scopedResponse = dns.message.make_response(query)
1784 scopedResponse.use_edns(edns=True, payload=4096, options=[ecsoResponse])
1785 rrset = dns.rrset.from_text(name,
1786 ttl,
1787 dns.rdataclass.IN,
1788 dns.rdatatype.AAAA,
1789 '::1')
1790 scopedResponse.answer.append(rrset)
1791 expectedResponse.answer.append(rrset)
1792
1793 for method in ("sendUDPQuery", "sendTCPQuery"):
1794 sender = getattr(self, method)
1795 (receivedQuery, receivedResponse) = sender(query, scopedResponse)
1796 receivedQuery.id = expectedQuery.id
1797 self.checkMessageEDNSWithECS(expectedQuery, receivedQuery)
1798 self.checkMessageNoEDNS(receivedResponse, expectedResponse)
1799
1800 # next query should hit the cache, nothing special about that
1801 for method in ("sendUDPQuery", "sendTCPQuery"):
1802 sender = getattr(self, method)
1803 (_, receivedResponse) = sender(query, response=None, useQueue=False)
1804 self.checkMessageNoEDNS(receivedResponse, expectedResponse)
1805
1806 query = dns.message.make_query(name, 'AAAA', 'IN')
1807 query.flags &= dns.flags.RD
1808 # next query FROM A DIFFERENT CLIENT since RD is now set should STILL hit the cache
1809 for method in ("sendUDPQuery", "sendTCPQuery"):
1810 sender = getattr(self, method)
1811 (_, receivedResponse) = sender(query, response=None, useQueue=False)
1812 receivedResponse.id = expectedResponse.id
1813 self.checkMessageNoEDNS(receivedResponse, expectedResponse)
1814
1815 name = 'scope-zero-with-ecs.cache.tests.powerdns.com.'
1816 ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
1817 query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
1818 query.flags &= ~dns.flags.RD
1819 expectedQuery = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
1820 expectedQuery.flags &= ~dns.flags.RD
1821 expectedResponse = dns.message.make_response(query)
1822 expectedResponse.use_edns(edns=True, payload=4096, options=[ecsoResponse])
1823 expectedResponse.answer.append(rrset)
1824 scopedResponse = dns.message.make_response(query)
1825 scopedResponse.use_edns(edns=True, payload=4096, options=[ecsoResponse])
1826 scopedResponse.answer.append(rrset)
1827 # this query has ECS, it should NOT be able to use the scope-zero cached entry since the hash will be
1828 # different
1829 for method in ("sendUDPQuery", "sendTCPQuery"):
1830 sender = getattr(self, method)
1831 (receivedQuery, receivedResponse) = sender(query, scopedResponse)
1832 receivedQuery.id = expectedQuery.id
1833 self.checkMessageEDNSWithECS(expectedQuery, receivedQuery)
1834 self.checkMessageEDNSWithECS(receivedResponse, expectedResponse)
1835
1836 # it should still have been cached, though, so the next query should be a hit
1837 for method in ("sendUDPQuery", "sendTCPQuery"):
1838 sender = getattr(self, method)
1839 (_, receivedResponse) = sender(query, response=None, useQueue=False)
1840 self.checkMessageEDNSWithECS(receivedResponse, expectedResponse)
1841
1842 def testScopeNotZero(self):
1843 """
1844 Cache: Test the scope-zero feature, backend returns a scope of non-zero
1845 """
1846 ttl = 600
1847 name = 'scope-not-zero.cache.tests.powerdns.com.'
1848 query = dns.message.make_query(name, 'AAAA', 'IN')
1849 query.flags &= ~dns.flags.RD
1850 ecso = clientsubnetoption.ClientSubnetOption('127.0.0.0', 24)
1851 expectedQuery = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
1852 expectedQuery.flags &= ~dns.flags.RD
1853 ecso2 = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
1854 expectedQuery2 = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso2], payload=512)
1855 expectedQuery2.flags &= ~dns.flags.RD
1856 ecsoResponse = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24, 24)
1857 ecsoResponse2 = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32, 24)
1858 rrset = dns.rrset.from_text(name,
1859 ttl,
1860 dns.rdataclass.IN,
1861 dns.rdatatype.AAAA,
1862 '::1')
1863 expectedResponse = dns.message.make_response(query)
1864 expectedResponse.answer.append(rrset)
1865 scopedResponse = dns.message.make_response(query)
1866 scopedResponse.use_edns(edns=True, payload=4096, options=[ecsoResponse])
1867 scopedResponse.answer.append(rrset)
1868 scopedResponse2 = dns.message.make_response(query)
1869 scopedResponse2.use_edns(edns=True, payload=4096, options=[ecsoResponse2])
1870 scopedResponse2.answer.append(rrset)
1871
1872 for method in ("sendUDPQuery", "sendTCPQuery"):
1873 sender = getattr(self, method)
1874 (receivedQuery, receivedResponse) = sender(query, scopedResponse)
1875 receivedQuery.id = expectedQuery.id
1876 self.checkMessageEDNSWithECS(expectedQuery, receivedQuery)
1877 self.checkMessageNoEDNS(receivedResponse, expectedResponse)
1878
1879 # next query should hit the cache, nothing special about that
1880 for method in ("sendUDPQuery", "sendTCPQuery"):
1881 sender = getattr(self, method)
1882 (_, receivedResponse) = sender(query, response=None, useQueue=False)
1883 self.checkMessageNoEDNS(receivedResponse, expectedResponse)
1884
1885 query = dns.message.make_query(name, 'AAAA', 'IN')
1886 query.flags &= dns.flags.RD
1887 expectedResponse = dns.message.make_response(query)
1888 expectedResponse.answer.append(rrset)
1889 # next query FROM A DIFFERENT CLIENT since RD is now set should NOT hit the cache
1890 for method in ("sendUDPQuery", "sendTCPQuery"):
1891 sender = getattr(self, method)
1892 (receivedQuery, receivedResponse) = sender(query, scopedResponse2)
1893 receivedQuery.id = expectedQuery2.id
1894 self.checkMessageEDNSWithECS(expectedQuery2, receivedQuery)
1895 self.checkMessageNoEDNS(receivedResponse, expectedResponse)
1896
1897 def testNoECS(self):
1898 """
1899 Cache: Test the scope-zero feature, backend returns no ECS at all
1900 """
1901 ttl = 600
1902 name = 'scope-zero-no-ecs.cache.tests.powerdns.com.'
1903 query = dns.message.make_query(name, 'AAAA', 'IN')
1904 query.flags &= ~dns.flags.RD
1905 ecso = clientsubnetoption.ClientSubnetOption('127.0.0.0', 24)
1906 expectedQuery = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
1907 expectedQuery.flags &= ~dns.flags.RD
1908 ecso2 = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
1909 expectedQuery2 = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso2], payload=512)
1910 expectedQuery2.flags &= ~dns.flags.RD
1911 rrset = dns.rrset.from_text(name,
1912 ttl,
1913 dns.rdataclass.IN,
1914 dns.rdatatype.AAAA,
1915 '::1')
1916 response = dns.message.make_response(query)
1917 response.answer.append(rrset)
1918
1919 for method in ("sendUDPQuery", "sendTCPQuery"):
1920 sender = getattr(self, method)
1921 (receivedQuery, receivedResponse) = sender(query, response)
1922 receivedQuery.id = expectedQuery.id
1923 self.checkMessageEDNSWithECS(expectedQuery, receivedQuery)
1924 self.checkMessageNoEDNS(receivedResponse, response)
1925
1926 # next query should hit the cache, nothing special about that
1927 for method in ("sendUDPQuery", "sendTCPQuery"):
1928 sender = getattr(self, method)
1929 (_, receivedResponse) = sender(query, response=None, useQueue=False)
1930 self.checkMessageNoEDNS(receivedResponse, response)
1931
1932 query = dns.message.make_query(name, 'AAAA', 'IN')
1933 query.flags &= dns.flags.RD
1934 response = dns.message.make_response(query)
1935 response.answer.append(rrset)
1936 # next query FROM A DIFFERENT CLIENT since RD is now set should NOT hit the cache
1937 for method in ("sendUDPQuery", "sendTCPQuery"):
1938 sender = getattr(self, method)
1939 (receivedQuery, receivedResponse) = sender(query, response)
1940 receivedQuery.id = expectedQuery2.id
1941 self.checkMessageEDNSWithECS(expectedQuery2, receivedQuery)
1942 self.checkMessageNoEDNS(receivedResponse, response)
1943
1944 class TestCachingScopeZeroButNoSubnetcheck(DNSDistTest):
1945
1946 _config_template = """
1947 -- We disable ECS parsing in the packet cache, meaning scope zero is disabled
1948 pc = newPacketCache(100, 86400, 1, 60, 60, false, 1, true, 3600, false)
1949 getPool(""):setCache(pc)
1950 newServer{address="127.0.0.1:%d", useClientSubnet=true}
1951 -- to simulate a second client coming from a different IP address,
1952 -- we will force the ECS value added to the query if RD is set (note that we need
1953 -- to unset it using rules before the first cache lookup)
1954 addAction(RDRule(), SetECSAction("192.0.2.1/32"))
1955 addAction(RDRule(), NoRecurseAction())
1956 """
1957
1958 def testScopeZero(self):
1959 """
1960 Cache: Test that the scope-zero feature is disabled when ECS parsing is not enabled in the cache
1961 """
1962 ttl = 600
1963 name = 'scope-zero-no-subnet.cache.tests.powerdns.com.'
1964 query = dns.message.make_query(name, 'AAAA', 'IN')
1965 query.flags &= ~dns.flags.RD
1966 ecso = clientsubnetoption.ClientSubnetOption('127.0.0.0', 24)
1967 expectedQuery = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
1968 expectedQuery.flags &= ~dns.flags.RD
1969 ecso2 = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
1970 expectedQuery2 = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso2], payload=512)
1971 expectedQuery2.flags &= ~dns.flags.RD
1972 ecsoResponse = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24, 0)
1973 expectedResponse = dns.message.make_response(query)
1974 scopedResponse = dns.message.make_response(query)
1975 scopedResponse.use_edns(edns=True, payload=4096, options=[ecsoResponse])
1976 rrset = dns.rrset.from_text(name,
1977 ttl,
1978 dns.rdataclass.IN,
1979 dns.rdatatype.AAAA,
1980 '::1')
1981 scopedResponse.answer.append(rrset)
1982 expectedResponse.answer.append(rrset)
1983
1984 for method in ("sendUDPQuery", "sendTCPQuery"):
1985 sender = getattr(self, method)
1986 (receivedQuery, receivedResponse) = sender(query, scopedResponse)
1987 receivedQuery.id = expectedQuery.id
1988 self.checkMessageEDNSWithECS(expectedQuery, receivedQuery)
1989 self.checkMessageNoEDNS(receivedResponse, expectedResponse)
1990
1991 # next query should hit the cache, nothing special about that
1992 for method in ("sendUDPQuery", "sendTCPQuery"):
1993 sender = getattr(self, method)
1994 (_, receivedResponse) = sender(query, response=None, useQueue=False)
1995 self.checkMessageNoEDNS(receivedResponse, expectedResponse)
1996
1997 query = dns.message.make_query(name, 'AAAA', 'IN')
1998 query.flags &= dns.flags.RD
1999 response = dns.message.make_response(query)
2000 response.answer.append(rrset)
2001 # next query FROM A DIFFERENT CLIENT since RD is now set should NOT hit the cache
2002 for method in ("sendUDPQuery", "sendTCPQuery"):
2003 sender = getattr(self, method)
2004 (receivedQuery, receivedResponse) = sender(query, response)
2005 receivedQuery.id = expectedQuery2.id
2006 self.checkMessageEDNSWithECS(expectedQuery2, receivedQuery)
2007 self.checkMessageNoEDNS(receivedResponse, response)