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