]> git.ipfire.org Git - thirdparty/pdns.git/blame_incremental - regression-tests.dnsdist/test_Caching.py
Merge pull request #13914 from rgacogne/ddist-fix-xsk-error
[thirdparty/pdns.git] / regression-tests.dnsdist / test_Caching.py
... / ...
CommitLineData
1#!/usr/bin/env python
2import base64
3import time
4import dns
5import clientsubnetoption
6import cookiesoption
7import requests
8from dnsdisttests import DNSDistTest, pickAvailablePort
9
10class TestCaching(DNSDistTest):
11
12 _config_template = """
13 pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
14 getPool(""):setCache(pc)
15 addAction(SuffixMatchNodeRule("nocache.cache.tests.powerdns.com."), SetSkipCacheAction())
16 addResponseAction(SuffixMatchNodeRule("nocache-response.cache.tests.powerdns.com."), SetSkipCacheResponseAction())
17 function skipViaLua(dq)
18 dq.skipCache = true
19 return DNSAction.None, ""
20 end
21 addAction("nocachevialua.cache.tests.powerdns.com.", LuaAction(skipViaLua))
22 newServer{address="127.0.0.1:%d"}
23 """
24
25 def testCached(self):
26 """
27 Cache: Served from cache
28
29 dnsdist is configured to cache entries, we are sending several
30 identical requests and checking that the backend only receive
31 the first one.
32 """
33 numberOfQueries = 10
34 name = 'cached.cache.tests.powerdns.com.'
35 query = dns.message.make_query(name, 'AAAA', 'IN')
36 response = dns.message.make_response(query)
37 rrset = dns.rrset.from_text(name,
38 3600,
39 dns.rdataclass.IN,
40 dns.rdatatype.AAAA,
41 '::1')
42 response.answer.append(rrset)
43
44 # first query to fill the cache
45 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
46 self.assertTrue(receivedQuery)
47 self.assertTrue(receivedResponse)
48 receivedQuery.id = query.id
49 self.assertEqual(query, receivedQuery)
50 self.assertEqual(receivedResponse, response)
51
52 for _ in range(numberOfQueries):
53 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
54 self.assertEqual(receivedResponse, response)
55
56 total = 0
57 for key in self._responsesCounter:
58 total += self._responsesCounter[key]
59 TestCaching._responsesCounter[key] = 0
60
61 self.assertEqual(total, 1)
62
63 # TCP should not be cached
64 # first query to fill the cache
65 (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
66 self.assertTrue(receivedQuery)
67 self.assertTrue(receivedResponse)
68 receivedQuery.id = query.id
69 self.assertEqual(query, receivedQuery)
70 self.assertEqual(receivedResponse, response)
71
72 for _ in range(numberOfQueries):
73 (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
74 self.assertEqual(receivedResponse, response)
75
76 total = 0
77 for key in self._responsesCounter:
78 total += self._responsesCounter[key]
79 TestCaching._responsesCounter[key] = 0
80
81 self.assertEqual(total, 1)
82
83 def testDOCached(self):
84 """
85 Cache: Served from cache, query has DO bit set
86
87 dnsdist is configured to cache entries, we are sending several
88 identical requests and checking that the backend only receive
89 the first one.
90 """
91 numberOfQueries = 10
92 name = 'cached-do.cache.tests.powerdns.com.'
93 query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, want_dnssec=True)
94 response = dns.message.make_response(query)
95 rrset = dns.rrset.from_text(name,
96 3600,
97 dns.rdataclass.IN,
98 dns.rdatatype.AAAA,
99 '::1')
100 response.answer.append(rrset)
101
102 # first query to fill the cache
103 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
104 self.assertTrue(receivedQuery)
105 self.assertTrue(receivedResponse)
106 receivedQuery.id = query.id
107 self.assertEqual(query, receivedQuery)
108 self.assertEqual(receivedResponse, response)
109
110 for _ in range(numberOfQueries):
111 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
112 self.assertEqual(receivedResponse, response)
113
114 total = 0
115 for key in self._responsesCounter:
116 total += self._responsesCounter[key]
117 TestCaching._responsesCounter[key] = 0
118
119 self.assertEqual(total, 1)
120
121 # TCP should not be cached
122 # first query to fill the cache
123 (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
124 self.assertTrue(receivedQuery)
125 self.assertTrue(receivedResponse)
126 receivedQuery.id = query.id
127 self.assertEqual(query, receivedQuery)
128 self.assertEqual(receivedResponse, response)
129
130 for _ in range(numberOfQueries):
131 (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
132 self.assertEqual(receivedResponse, response)
133
134 total = 0
135 for key in self._responsesCounter:
136 total += self._responsesCounter[key]
137 TestCaching._responsesCounter[key] = 0
138
139 self.assertEqual(total, 1)
140
141 def testSkipCache(self):
142 """
143 Cache: SetSkipCacheAction
144
145 dnsdist is configured to not cache entries for nocache.cache.tests.powerdns.com.
146 we are sending several requests and checking that the backend get them all.
147 """
148 name = 'nocache.cache.tests.powerdns.com.'
149 numberOfQueries = 10
150 query = dns.message.make_query(name, 'AAAA', 'IN')
151 response = dns.message.make_response(query)
152 rrset = dns.rrset.from_text(name,
153 3600,
154 dns.rdataclass.IN,
155 dns.rdatatype.AAAA,
156 '::1')
157 response.answer.append(rrset)
158
159 for _ in range(numberOfQueries):
160 for method in ("sendUDPQuery", "sendTCPQuery"):
161 sender = getattr(self, method)
162 (receivedQuery, receivedResponse) = sender(query, response)
163 self.assertTrue(receivedQuery)
164 self.assertTrue(receivedResponse)
165 receivedQuery.id = query.id
166 self.assertEqual(query, receivedQuery)
167 self.assertEqual(receivedResponse, response)
168
169 for key in self._responsesCounter:
170 value = self._responsesCounter[key]
171 self.assertEqual(value, numberOfQueries)
172
173 def testSkipCacheViaLua(self):
174 """
175 Cache: SkipCache via Lua
176
177 dnsdist is configured to not cache entries for nocachevialua.cache.tests.powerdns.com.
178 we are sending several requests and checking that the backend get them all.
179 """
180 name = 'nocachevialua.cache.tests.powerdns.com.'
181 numberOfQueries = 10
182 query = dns.message.make_query(name, 'AAAA', 'IN')
183 response = dns.message.make_response(query)
184 rrset = dns.rrset.from_text(name,
185 3600,
186 dns.rdataclass.IN,
187 dns.rdatatype.AAAA,
188 '::1')
189 response.answer.append(rrset)
190
191 for _ in range(numberOfQueries):
192 for method in ("sendUDPQuery", "sendTCPQuery"):
193 sender = getattr(self, method)
194 (receivedQuery, receivedResponse) = sender(query, response)
195 self.assertTrue(receivedQuery)
196 self.assertTrue(receivedResponse)
197 receivedQuery.id = query.id
198 self.assertEqual(query, receivedQuery)
199 self.assertEqual(receivedResponse, response)
200
201 for key in self._responsesCounter:
202 value = self._responsesCounter[key]
203 self.assertEqual(value, numberOfQueries)
204
205 def testSkipCacheResponse(self):
206 """
207 Cache: SetSkipCacheResponseAction
208
209 dnsdist is configured to not cache entries for answer matching nocache-response.cache.tests.powerdns.com.
210 we are sending several requests and checking that the backend get them all.
211 """
212 name = 'nocache-response.cache.tests.powerdns.com.'
213 numberOfQueries = 10
214 query = dns.message.make_query(name, 'AAAA', 'IN')
215 response = dns.message.make_response(query)
216 rrset = dns.rrset.from_text(name,
217 3600,
218 dns.rdataclass.IN,
219 dns.rdatatype.AAAA,
220 '::1')
221 response.answer.append(rrset)
222
223 for _ in range(numberOfQueries):
224 for method in ("sendUDPQuery", "sendTCPQuery"):
225 sender = getattr(self, method)
226 (receivedQuery, receivedResponse) = sender(query, response)
227 self.assertTrue(receivedQuery)
228 self.assertTrue(receivedResponse)
229 receivedQuery.id = query.id
230 self.assertEqual(query, receivedQuery)
231 self.assertEqual(receivedResponse, response)
232
233 for key in self._responsesCounter:
234 value = self._responsesCounter[key]
235 self.assertEqual(value, numberOfQueries)
236
237 def testAXFRResponse(self):
238 """
239 Cache: AXFR should not be cached
240
241 dnsdist should not cache responses to AXFR queries.
242 """
243 name = 'axfr.cache.tests.powerdns.com.'
244 query = dns.message.make_query(name, 'AXFR', 'IN')
245 response = dns.message.make_response(query)
246 soa = dns.rrset.from_text(name,
247 60,
248 dns.rdataclass.IN,
249 dns.rdatatype.SOA,
250 'ns.' + name + ' hostmaster.' + name + ' 1 3600 3600 3600 60')
251 response.answer.append(soa)
252 response.answer.append(dns.rrset.from_text(name,
253 60,
254 dns.rdataclass.IN,
255 dns.rdatatype.A,
256 '192.0.2.1'))
257 response.answer.append(soa)
258 numberOfQueries = 5
259
260 for _ in range(numberOfQueries):
261 for method in ("sendUDPQuery", "sendTCPQuery"):
262 sender = getattr(self, method)
263 (receivedQuery, receivedResponse) = sender(query, response)
264 self.assertTrue(receivedQuery)
265 self.assertTrue(receivedResponse)
266 receivedQuery.id = query.id
267 self.assertEqual(query, receivedQuery)
268 self.assertEqual(receivedResponse, response)
269
270 for key in self._responsesCounter:
271 value = self._responsesCounter[key]
272 self.assertEqual(value, numberOfQueries)
273
274 def testIXFRResponse(self):
275 """
276 Cache: IXFR should not be cached
277
278 dnsdist should not cache responses to IXFR queries.
279 """
280 name = 'ixfr.cache.tests.powerdns.com.'
281 query = dns.message.make_query(name, 'IXFR', 'IN')
282 response = dns.message.make_response(query)
283 soa = dns.rrset.from_text(name,
284 60,
285 dns.rdataclass.IN,
286 dns.rdatatype.SOA,
287 'ns.' + name + ' hostmaster.' + name + ' 1 3600 3600 3600 60')
288 response.answer.append(soa)
289 response.answer.append(dns.rrset.from_text(name,
290 60,
291 dns.rdataclass.IN,
292 dns.rdatatype.A,
293 '192.0.2.1'))
294 response.answer.append(soa)
295 numberOfQueries = 5
296
297 for _ in range(numberOfQueries):
298 for method in ("sendUDPQuery", "sendTCPQuery"):
299 sender = getattr(self, method)
300 (receivedQuery, receivedResponse) = sender(query, response)
301 self.assertTrue(receivedQuery)
302 self.assertTrue(receivedResponse)
303 receivedQuery.id = query.id
304 self.assertEqual(query, receivedQuery)
305 self.assertEqual(receivedResponse, response)
306
307 for key in self._responsesCounter:
308 value = self._responsesCounter[key]
309 self.assertEqual(value, numberOfQueries)
310
311 def testCacheExpiration(self):
312 """
313 Cache: Cache expiration
314
315 dnsdist is configured to cache entries, we are sending one request
316 (cache miss) with a very short TTL, checking that the next requests
317 are cached. Then we wait for the TTL to expire, check that the
318 next request is a miss but the following one a hit.
319 """
320 ttl = 2
321 misses = 0
322 name = 'cacheexpiration.cache.tests.powerdns.com.'
323 query = dns.message.make_query(name, 'AAAA', 'IN')
324 response = dns.message.make_response(query)
325 rrset = dns.rrset.from_text(name,
326 ttl,
327 dns.rdataclass.IN,
328 dns.rdatatype.AAAA,
329 '::1')
330 response.answer.append(rrset)
331
332 # first query to fill the cache
333 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
334 self.assertTrue(receivedQuery)
335 self.assertTrue(receivedResponse)
336 receivedQuery.id = query.id
337 self.assertEqual(query, receivedQuery)
338 self.assertEqual(receivedResponse, response)
339 misses += 1
340
341 # next queries should hit the cache
342 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
343 self.assertEqual(receivedResponse, response)
344
345 # now we wait a bit for the cache entry to expire
346 time.sleep(ttl + 1)
347
348 # next query should be a miss, fill the cache again
349 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
350 self.assertTrue(receivedQuery)
351 self.assertTrue(receivedResponse)
352 receivedQuery.id = query.id
353 self.assertEqual(query, receivedQuery)
354 self.assertEqual(receivedResponse, response)
355 misses += 1
356
357 # following queries should hit the cache again
358 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
359 self.assertEqual(receivedResponse, response)
360
361 total = 0
362 for key in self._responsesCounter:
363 total += self._responsesCounter[key]
364
365 self.assertEqual(total, misses)
366
367 def testCacheExpirationDifferentSets(self):
368 """
369 Cache: Cache expiration with different sets
370
371 dnsdist is configured to cache entries, we are sending one request
372 (cache miss) whose response has a long and a very short TTL,
373 checking that the next requests are cached. Then we wait for the
374 short TTL to expire, check that the
375 next request is a miss but the following one a hit.
376 """
377 ttl = 2
378 misses = 0
379 name = 'cacheexpirationdifferentsets.cache.tests.powerdns.com.'
380 query = dns.message.make_query(name, 'AAAA', 'IN')
381 response = dns.message.make_response(query)
382 rrset = dns.rrset.from_text(name,
383 ttl,
384 dns.rdataclass.IN,
385 dns.rdatatype.CNAME,
386 'cname.cacheexpirationdifferentsets.cache.tests.powerdns.com.')
387 response.answer.append(rrset)
388 rrset = dns.rrset.from_text('cname.cacheexpirationdifferentsets.cache.tests.powerdns.com.',
389 ttl + 3600,
390 dns.rdataclass.IN,
391 dns.rdatatype.A,
392 '192.2.0.1')
393 response.additional.append(rrset)
394
395 # first query to fill the cache
396 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
397 self.assertTrue(receivedQuery)
398 self.assertTrue(receivedResponse)
399 receivedQuery.id = query.id
400 self.assertEqual(query, receivedQuery)
401 self.assertEqual(receivedResponse, response)
402 misses += 1
403
404 # next queries should hit the cache
405 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
406 self.assertEqual(receivedResponse, response)
407
408 # now we wait a bit for the cache entry to expire
409 time.sleep(ttl + 1)
410
411 # next query should be a miss, fill the cache again
412 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
413 self.assertTrue(receivedQuery)
414 self.assertTrue(receivedResponse)
415 receivedQuery.id = query.id
416 self.assertEqual(query, receivedQuery)
417 self.assertEqual(receivedResponse, response)
418 misses += 1
419
420 # following queries should hit the cache again
421 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
422 self.assertEqual(receivedResponse, response)
423
424 total = 0
425 for key in self._responsesCounter:
426 total += self._responsesCounter[key]
427
428 self.assertEqual(total, misses)
429
430 def testCacheDecreaseTTL(self):
431 """
432 Cache: Cache decreases TTL
433
434 dnsdist is configured to cache entries, we are sending one request
435 (cache miss) and verify that the cache hits have a decreasing TTL.
436 """
437 ttl = 600
438 misses = 0
439 name = 'cachedecreasettl.cache.tests.powerdns.com.'
440 query = dns.message.make_query(name, 'AAAA', 'IN')
441 response = dns.message.make_response(query)
442 rrset = dns.rrset.from_text(name,
443 ttl,
444 dns.rdataclass.IN,
445 dns.rdatatype.AAAA,
446 '::1')
447 response.answer.append(rrset)
448
449 # first query to fill the cache
450 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
451 self.assertTrue(receivedQuery)
452 self.assertTrue(receivedResponse)
453 receivedQuery.id = query.id
454 self.assertEqual(query, receivedQuery)
455 self.assertEqual(receivedResponse, response)
456 misses += 1
457
458 # next queries should hit the cache
459 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
460 self.assertEqual(receivedResponse, response)
461 for an in receivedResponse.answer:
462 self.assertTrue(an.ttl <= ttl)
463
464 # now we wait a bit for the TTL to decrease
465 time.sleep(1)
466
467 # next queries should hit the cache
468 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
469 self.assertEqual(receivedResponse, response)
470 for an in receivedResponse.answer:
471 self.assertTrue(an.ttl < ttl)
472
473 total = 0
474 for key in self._responsesCounter:
475 total += self._responsesCounter[key]
476
477 self.assertEqual(total, misses)
478
479 def testCacheDifferentCase(self):
480 """
481 Cache: Cache matches different case
482
483 dnsdist is configured to cache entries, we are sending one request
484 (cache miss) and verify that the same one with a different case
485 matches.
486 """
487 ttl = 600
488 name = 'cachedifferentcase.cache.tests.powerdns.com.'
489 differentCaseName = 'CacheDifferentCASE.cache.tests.powerdns.com.'
490 query = dns.message.make_query(name, 'AAAA', 'IN')
491 differentCaseQuery = dns.message.make_query(differentCaseName, 'AAAA', 'IN')
492 response = dns.message.make_response(query)
493 differentCaseResponse = dns.message.make_response(differentCaseQuery)
494 rrset = dns.rrset.from_text(name,
495 ttl,
496 dns.rdataclass.IN,
497 dns.rdatatype.AAAA,
498 '::1')
499 response.answer.append(rrset)
500 differentCaseResponse.answer.append(rrset)
501
502 # first query to fill the cache
503 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
504 self.assertTrue(receivedQuery)
505 self.assertTrue(receivedResponse)
506 receivedQuery.id = query.id
507 self.assertEqual(query, receivedQuery)
508 self.assertEqual(receivedResponse, response)
509
510 # different case query should still hit the cache
511 (_, receivedResponse) = self.sendUDPQuery(differentCaseQuery, response=None, useQueue=False)
512 self.assertEqual(receivedResponse, differentCaseResponse)
513
514 def testLargeAnswer(self):
515 """
516 Cache: Check that we can cache (and retrieve) large answers
517
518 We should be able to get answers as large as 4096 bytes
519 """
520 numberOfQueries = 10
521 name = 'large-answer.cache.tests.powerdns.com.'
522 query = dns.message.make_query(name, 'TXT', 'IN')
523 response = dns.message.make_response(query)
524 # we prepare a large answer
525 content = ""
526 for i in range(44):
527 if len(content) > 0:
528 content = content + ', '
529 content = content + (str(i)*50)
530 # pad up to 4096
531 content = content + 'A'*42
532
533 rrset = dns.rrset.from_text(name,
534 3600,
535 dns.rdataclass.IN,
536 dns.rdatatype.TXT,
537 content)
538 response.answer.append(rrset)
539 self.assertEqual(len(response.to_wire()), 4096)
540
541 # first query to fill the cache
542 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
543 self.assertTrue(receivedQuery)
544 self.assertTrue(receivedResponse)
545 receivedQuery.id = query.id
546 self.assertEqual(query, receivedQuery)
547 self.assertEqual(receivedResponse, response)
548
549 for _ in range(numberOfQueries):
550 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
551 self.assertEqual(receivedResponse, response)
552
553 total = 0
554 for key in self._responsesCounter:
555 total += self._responsesCounter[key]
556 TestCaching._responsesCounter[key] = 0
557
558 self.assertEqual(total, 1)
559
560 # TCP should not be cached
561 # first query to fill the cache
562 (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
563 self.assertTrue(receivedQuery)
564 self.assertTrue(receivedResponse)
565 receivedQuery.id = query.id
566 self.assertEqual(query, receivedQuery)
567 self.assertEqual(receivedResponse, response)
568
569 for _ in range(numberOfQueries):
570 (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
571 self.assertEqual(receivedResponse, response)
572
573 total = 0
574 for key in self._responsesCounter:
575 total += self._responsesCounter[key]
576 TestCaching._responsesCounter[key] = 0
577
578 self.assertEqual(total, 1)
579
580 def testCacheDifferentCookies(self):
581 """
582 Cache: The content of cookies should be ignored by the cache
583 """
584 ttl = 600
585 name = 'cache-different-cookies.cache.tests.powerdns.com.'
586 eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
587 query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, options=[eco])
588 response = dns.message.make_response(query)
589 rrset = dns.rrset.from_text(name,
590 ttl,
591 dns.rdataclass.IN,
592 dns.rdatatype.AAAA,
593 '::1')
594 response.answer.append(rrset)
595
596 # first query to fill the cache
597 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
598 self.assertTrue(receivedQuery)
599 self.assertTrue(receivedResponse)
600 receivedQuery.id = query.id
601 self.assertEqual(query, receivedQuery)
602 self.assertEqual(receivedResponse, response)
603
604 eco = cookiesoption.CookiesOption(b'badc0fee', b'badc0fee')
605 query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, options=[eco])
606 # second query should be served from the cache
607 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
608 receivedResponse.id = response.id
609 self.assertEqual(receivedResponse, response)
610
611 def testCacheCookies(self):
612 """
613 Cache: A query with a cookie should not match one without any cookie
614 """
615 ttl = 600
616 name = 'cache-cookie.cache.tests.powerdns.com.'
617 eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
618 query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[eco])
619 response = dns.message.make_response(query)
620 rrset = dns.rrset.from_text(name,
621 ttl,
622 dns.rdataclass.IN,
623 dns.rdatatype.A,
624 '192.0.2.1')
625 response.answer.append(rrset)
626
627 # first query to fill the cache
628 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
629 self.assertTrue(receivedQuery)
630 self.assertTrue(receivedResponse)
631 receivedQuery.id = query.id
632 self.assertEqual(query, receivedQuery)
633 self.assertEqual(receivedResponse, response)
634
635 query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[])
636 response = dns.message.make_response(query)
637 rrset = dns.rrset.from_text(name,
638 ttl,
639 dns.rdataclass.IN,
640 dns.rdatatype.A,
641 '127.0.0.1')
642 response.answer.append(rrset)
643 # second query should NOT be served from the cache
644 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
645 self.assertTrue(receivedQuery)
646 self.assertTrue(receivedResponse)
647 receivedQuery.id = query.id
648 self.assertEqual(query, receivedQuery)
649 self.assertEqual(receivedResponse, response)
650
651 def testCacheSameCookieDifferentECS(self):
652 """
653 Cache: The content of cookies should be ignored by the cache but not the ECS one
654 """
655 ttl = 600
656 name = 'cache-different-cookies-different-ecs.cache.tests.powerdns.com.'
657 eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
658 ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
659 query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, options=[eco,ecso])
660 response = dns.message.make_response(query)
661 rrset = dns.rrset.from_text(name,
662 ttl,
663 dns.rdataclass.IN,
664 dns.rdatatype.AAAA,
665 '::1')
666 response.answer.append(rrset)
667
668 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
669 self.assertTrue(receivedQuery)
670 self.assertTrue(receivedResponse)
671 receivedQuery.id = query.id
672 self.assertEqual(query, receivedQuery)
673 self.assertEqual(receivedResponse, response)
674
675 eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
676 ecso = clientsubnetoption.ClientSubnetOption('192.0.2.2', 32)
677 query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, options=[eco,ecso])
678 response = dns.message.make_response(query)
679 rrset = dns.rrset.from_text(name,
680 ttl,
681 dns.rdataclass.IN,
682 dns.rdatatype.AAAA,
683 '::1')
684 response.answer.append(rrset)
685
686 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
687 self.assertTrue(receivedQuery)
688 self.assertTrue(receivedResponse)
689 receivedQuery.id = query.id
690 self.assertEqual(query, receivedQuery)
691 self.assertEqual(receivedResponse, response)
692
693class TestCachingHashingOptions(DNSDistTest):
694
695 _config_template = """
696 pc = newPacketCache(100, {maxTTL=86400, minTTL=1, cookieHashing=true, skipOptions={8}})
697 getPool(""):setCache(pc)
698 newServer{address="127.0.0.1:%d"}
699 """
700
701 def testCacheDifferentECSSameCookie(self):
702 """
703 Cache: ECS should be ignored by the cache even if cookie is present
704 """
705 ttl = 600
706 name = 'cache-different-ecs.cache.tests.powerdns.com.'
707 eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
708 ecso = clientsubnetoption.ClientSubnetOption('192.0.2.2', 32)
709 query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, options=[eco,ecso])
710 response = dns.message.make_response(query)
711 rrset = dns.rrset.from_text(name,
712 ttl,
713 dns.rdataclass.IN,
714 dns.rdatatype.AAAA,
715 '::1')
716 response.answer.append(rrset)
717
718 # first query to fill the cache
719 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
720 self.assertTrue(receivedQuery)
721 self.assertTrue(receivedResponse)
722 receivedQuery.id = query.id
723 self.assertEqual(query, receivedQuery)
724 self.assertEqual(receivedResponse, response)
725
726 eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
727 ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
728 query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, options=[eco,ecso])
729 # second query should be served from the cache
730 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
731 receivedResponse.id = response.id
732 self.assertEqual(receivedResponse, response)
733
734class TestCachingHashingCookies(DNSDistTest):
735
736 _config_template = """
737 pc = newPacketCache(100, {maxTTL=86400, minTTL=1, cookieHashing=true})
738 getPool(""):setCache(pc)
739 newServer{address="127.0.0.1:%d"}
740 """
741
742 def testCached(self):
743 """
744 Cache: Served from cache
745
746 dnsdist is configured to cache entries, we are sending several
747 identical requests and checking that the backend only receive
748 the first one.
749 """
750 numberOfQueries = 10
751 name = 'cached.cache.tests.powerdns.com.'
752 query = dns.message.make_query(name, 'AAAA', 'IN')
753 response = dns.message.make_response(query)
754 rrset = dns.rrset.from_text(name,
755 3600,
756 dns.rdataclass.IN,
757 dns.rdatatype.AAAA,
758 '::1')
759 response.answer.append(rrset)
760
761 # first query to fill the cache
762 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
763 self.assertTrue(receivedQuery)
764 self.assertTrue(receivedResponse)
765 receivedQuery.id = query.id
766 self.assertEqual(query, receivedQuery)
767 self.assertEqual(receivedResponse, response)
768
769 for _ in range(numberOfQueries):
770 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
771 self.assertEqual(receivedResponse, response)
772
773 total = 0
774 for key in self._responsesCounter:
775 total += self._responsesCounter[key]
776 TestCaching._responsesCounter[key] = 0
777
778 self.assertEqual(total, 1)
779
780 # TCP should not be cached
781 # first query to fill the cache
782 (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
783 self.assertTrue(receivedQuery)
784 self.assertTrue(receivedResponse)
785 receivedQuery.id = query.id
786 self.assertEqual(query, receivedQuery)
787 self.assertEqual(receivedResponse, response)
788
789 for _ in range(numberOfQueries):
790 (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
791 self.assertEqual(receivedResponse, response)
792
793 total = 0
794 for key in self._responsesCounter:
795 total += self._responsesCounter[key]
796 TestCaching._responsesCounter[key] = 0
797
798 self.assertEqual(total, 1)
799
800
801 def testCacheDifferentCookies(self):
802 """
803 Cache: The content of cookies should NOT be ignored by the cache (cookieHashing is set)
804 """
805 ttl = 600
806 name = 'cache-different-cookies.cache-cookie-hashing.tests.powerdns.com.'
807 eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
808 query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, options=[eco])
809 response = dns.message.make_response(query)
810 rrset = dns.rrset.from_text(name,
811 ttl,
812 dns.rdataclass.IN,
813 dns.rdatatype.AAAA,
814 '::1')
815 response.answer.append(rrset)
816
817 # first query to fill the cache
818 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
819 self.assertTrue(receivedQuery)
820 self.assertTrue(receivedResponse)
821 receivedQuery.id = query.id
822 self.assertEqual(query, receivedQuery)
823 self.assertEqual(receivedResponse, response)
824
825 eco = cookiesoption.CookiesOption(b'badc0fee', b'badc0fee')
826 query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, options=[eco])
827 differentResponse = dns.message.make_response(query)
828 rrset = dns.rrset.from_text(name,
829 ttl,
830 dns.rdataclass.IN,
831 dns.rdatatype.AAAA,
832 '2001:DB8::1')
833 differentResponse.answer.append(rrset)
834 # second query should NOT be served from the cache
835 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, differentResponse)
836 self.assertTrue(receivedQuery)
837 self.assertTrue(receivedResponse)
838 receivedQuery.id = query.id
839 self.assertEqual(query, receivedQuery)
840 self.assertEqual(receivedResponse, differentResponse)
841 self.assertNotEqual(receivedResponse, response)
842
843 def testCacheCookies(self):
844 """
845 Cache: A query with a cookie should not match one without any cookie (cookieHashing=true)
846 """
847 ttl = 600
848 name = 'cache-cookie.cache-cookie-hashing.tests.powerdns.com.'
849 eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
850 query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[eco])
851 response = dns.message.make_response(query)
852 rrset = dns.rrset.from_text(name,
853 ttl,
854 dns.rdataclass.IN,
855 dns.rdatatype.A,
856 '192.0.2.1')
857 response.answer.append(rrset)
858
859 # first query to fill the cache
860 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
861 self.assertTrue(receivedQuery)
862 self.assertTrue(receivedResponse)
863 receivedQuery.id = query.id
864 self.assertEqual(query, receivedQuery)
865 self.assertEqual(receivedResponse, response)
866
867 query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[])
868 response = dns.message.make_response(query)
869 rrset = dns.rrset.from_text(name,
870 ttl,
871 dns.rdataclass.IN,
872 dns.rdatatype.A,
873 '127.0.0.1')
874 response.answer.append(rrset)
875 # second query should NOT be served from the cache
876 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
877 self.assertTrue(receivedQuery)
878 self.assertTrue(receivedResponse)
879 receivedQuery.id = query.id
880 self.assertEqual(query, receivedQuery)
881 self.assertEqual(receivedResponse, response)
882
883 def testCacheSameCookieDifferentECS(self):
884 """
885 Cache: The content of cookies should NOT be ignored by the cache (cookieHashing=true), even with ECS there
886 """
887 ttl = 600
888 name = 'cache-different-cookies-different-ecs.cache-cookie-hashing.tests.powerdns.com.'
889 eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
890 ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
891 query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, options=[eco,ecso])
892 response = dns.message.make_response(query)
893 rrset = dns.rrset.from_text(name,
894 ttl,
895 dns.rdataclass.IN,
896 dns.rdatatype.AAAA,
897 '::1')
898 response.answer.append(rrset)
899
900 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
901 self.assertTrue(receivedQuery)
902 self.assertTrue(receivedResponse)
903 receivedQuery.id = query.id
904 self.assertEqual(query, receivedQuery)
905 self.assertEqual(receivedResponse, response)
906
907 eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
908 ecso = clientsubnetoption.ClientSubnetOption('192.0.2.2', 32)
909 query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, options=[eco,ecso])
910 response = dns.message.make_response(query)
911 rrset = dns.rrset.from_text(name,
912 ttl,
913 dns.rdataclass.IN,
914 dns.rdatatype.AAAA,
915 '::1')
916 response.answer.append(rrset)
917
918 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
919 self.assertTrue(receivedQuery)
920 self.assertTrue(receivedResponse)
921 receivedQuery.id = query.id
922 self.assertEqual(query, receivedQuery)
923 self.assertEqual(receivedResponse, response)
924
925class TestTempFailureCacheTTLAction(DNSDistTest):
926
927 _extraStartupSleep = 1
928 _config_template = """
929 pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
930 getPool(""):setCache(pc)
931 addAction("servfail.cache.tests.powerdns.com.", SetTempFailureCacheTTLAction(1))
932 newServer{address="127.0.0.1:%d"}
933 """
934
935 def testTempFailureCacheTTLAction(self):
936 """
937 Cache: When a TempFailure TTL is set, it should be honored
938
939 dnsdist is configured to cache packets, plus a specific qname is
940 set up with a lower TempFailure Cache TTL. we are sending one request
941 (cache miss) and verify that the cache is hit for the following query,
942 but the TTL then expires before the larger "good" packetcache TTL.
943 """
944 name = 'servfail.cache.tests.powerdns.com.'
945 query = dns.message.make_query(name, 'AAAA', 'IN')
946 response = dns.message.make_response(query)
947 response.set_rcode(dns.rcode.SERVFAIL)
948
949 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
950 self.assertTrue(receivedQuery)
951 self.assertTrue(receivedResponse)
952 receivedQuery.id = query.id
953 self.assertEqual(query, receivedQuery)
954 self.assertEqual(receivedResponse, response)
955
956 # next query should hit the cache
957 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
958 self.assertFalse(receivedQuery)
959 self.assertTrue(receivedResponse)
960 self.assertEqual(receivedResponse, response)
961
962 # now we wait a bit for the Failure-Cache TTL to expire
963 time.sleep(2)
964
965 # next query should NOT hit the cache
966 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
967 self.assertTrue(receivedQuery)
968 self.assertTrue(receivedResponse)
969 self.assertEqual(receivedResponse, response)
970
971class TestCachingWithExistingEDNS(DNSDistTest):
972
973 _config_template = """
974 pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
975 getPool(""):setCache(pc)
976 newServer{address="127.0.0.1:%d"}
977 """
978 def testCacheWithEDNS(self):
979 """
980 Cache: Cache should not match different EDNS value
981
982 dnsdist is configured to cache entries, we are sending one request
983 (cache miss) and verify that the same one with a different EDNS UDP
984 Payload size is not served from the cache.
985 """
986 misses = 0
987 name = 'cachedifferentedns.cache.tests.powerdns.com.'
988 query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=512)
989 response = dns.message.make_response(query)
990 rrset = dns.rrset.from_text(name,
991 3600,
992 dns.rdataclass.IN,
993 dns.rdatatype.A,
994 '127.0.0.1')
995 response.answer.append(rrset)
996
997 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
998 self.assertTrue(receivedQuery)
999 self.assertTrue(receivedResponse)
1000 receivedQuery.id = query.id
1001 self.assertEqual(query, receivedQuery)
1002 self.assertEqual(response, receivedResponse)
1003 misses += 1
1004
1005 query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
1006 response = dns.message.make_response(query)
1007 rrset = dns.rrset.from_text(name,
1008 3600,
1009 dns.rdataclass.IN,
1010 dns.rdatatype.A,
1011 '127.0.0.1')
1012 response.answer.append(rrset)
1013
1014 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1015 self.assertTrue(receivedQuery)
1016 self.assertTrue(receivedResponse)
1017 receivedQuery.id = query.id
1018 self.assertEqual(query, receivedQuery)
1019 self.assertEqual(response, receivedResponse)
1020 misses += 1
1021
1022 total = 0
1023 for key in self._responsesCounter:
1024 total += self._responsesCounter[key]
1025
1026 self.assertEqual(total, misses)
1027
1028class TestCachingCacheFull(DNSDistTest):
1029
1030 _config_template = """
1031 pc = newPacketCache(1, {maxTTL=86400, minTTL=1, numberOfShards=1})
1032 getPool(""):setCache(pc)
1033 newServer{address="127.0.0.1:%d"}
1034 """
1035 def testCacheFull(self):
1036 """
1037 Cache: No new entries are cached when the cache is full
1038
1039 """
1040 misses = 0
1041 name = 'cachenotfullyet.cache.tests.powerdns.com.'
1042 query = dns.message.make_query(name, 'A', 'IN')
1043 response = dns.message.make_response(query)
1044 rrset = dns.rrset.from_text(name,
1045 3600,
1046 dns.rdataclass.IN,
1047 dns.rdatatype.A,
1048 '127.0.0.1')
1049 response.answer.append(rrset)
1050
1051 # Miss
1052 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1053 self.assertTrue(receivedQuery)
1054 self.assertTrue(receivedResponse)
1055 receivedQuery.id = query.id
1056 self.assertEqual(query, receivedQuery)
1057 self.assertEqual(response, receivedResponse)
1058 misses += 1
1059
1060 # next queries should hit the cache
1061 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1062 self.assertEqual(receivedResponse, response)
1063
1064 # ok, now the cache is full, send another query
1065 name = 'cachefull.cache.tests.powerdns.com.'
1066 query = dns.message.make_query(name, 'AAAA', 'IN')
1067 response = dns.message.make_response(query)
1068 rrset = dns.rrset.from_text(name,
1069 3600,
1070 dns.rdataclass.IN,
1071 dns.rdatatype.AAAA,
1072 '::1')
1073 response.answer.append(rrset)
1074
1075 # Miss
1076 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1077 self.assertTrue(receivedQuery)
1078 self.assertTrue(receivedResponse)
1079 receivedQuery.id = query.id
1080 self.assertEqual(query, receivedQuery)
1081 self.assertEqual(response, receivedResponse)
1082 misses += 1
1083
1084 # next queries should NOT hit the cache
1085 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1086 self.assertTrue(receivedQuery)
1087 self.assertTrue(receivedResponse)
1088 receivedQuery.id = query.id
1089 self.assertEqual(query, receivedQuery)
1090 self.assertEqual(response, receivedResponse)
1091 misses += 1
1092
1093 total = 0
1094 for key in self._responsesCounter:
1095 total += self._responsesCounter[key]
1096
1097 self.assertEqual(total, misses)
1098
1099class TestCachingNoStale(DNSDistTest):
1100
1101 _consoleKey = DNSDistTest.generateConsoleKey()
1102 _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
1103 _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
1104 _config_template = """
1105 pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
1106 getPool(""):setCache(pc)
1107 setKey("%s")
1108 controlSocket("127.0.0.1:%d")
1109 newServer{address="127.0.0.1:%d"}
1110 """
1111 def testCacheNoStale(self):
1112 """
1113 Cache: Cache entry, set backend down, we should not get a stale entry
1114
1115 """
1116 ttl = 2
1117 name = 'nostale.cache.tests.powerdns.com.'
1118 query = dns.message.make_query(name, 'A', 'IN')
1119 response = dns.message.make_response(query)
1120 rrset = dns.rrset.from_text(name,
1121 ttl,
1122 dns.rdataclass.IN,
1123 dns.rdatatype.A,
1124 '127.0.0.1')
1125 response.answer.append(rrset)
1126
1127 # Miss
1128 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1129 self.assertTrue(receivedQuery)
1130 self.assertTrue(receivedResponse)
1131 receivedQuery.id = query.id
1132 self.assertEqual(query, receivedQuery)
1133 self.assertEqual(response, receivedResponse)
1134
1135 # next queries should hit the cache
1136 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1137 self.assertEqual(receivedResponse, response)
1138
1139 # ok, we mark the backend as down
1140 self.sendConsoleCommand("getServer(0):setDown()")
1141 # and we wait for the entry to expire
1142 time.sleep(ttl + 1)
1143
1144 # we should NOT get a cached, stale, entry
1145 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1146 self.assertEqual(receivedResponse, None)
1147
1148
1149class TestCachingStale(DNSDistTest):
1150
1151 _consoleKey = DNSDistTest.generateConsoleKey()
1152 _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
1153 _staleCacheTTL = 60
1154 _config_params = ['_staleCacheTTL', '_consoleKeyB64', '_consolePort', '_testServerPort']
1155 _config_template = """
1156 pc = newPacketCache(100, {maxTTL=86400, minTTL=1, temporaryFailureTTL=0, staleTTL=%d})
1157 getPool(""):setCache(pc)
1158 setStaleCacheEntriesTTL(600)
1159 setKey("%s")
1160 controlSocket("127.0.0.1:%d")
1161 newServer{address="127.0.0.1:%d"}
1162 """
1163 def testCacheStale(self):
1164 """
1165 Cache: Cache entry, set backend down, get stale entry
1166
1167 """
1168 misses = 0
1169 ttl = 2
1170 name = 'stale.cache.tests.powerdns.com.'
1171 query = dns.message.make_query(name, 'A', 'IN')
1172 response = dns.message.make_response(query)
1173 rrset = dns.rrset.from_text(name,
1174 ttl,
1175 dns.rdataclass.IN,
1176 dns.rdatatype.A,
1177 '127.0.0.1')
1178 response.answer.append(rrset)
1179
1180 # Miss
1181 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1182 self.assertTrue(receivedQuery)
1183 self.assertTrue(receivedResponse)
1184 receivedQuery.id = query.id
1185 self.assertEqual(query, receivedQuery)
1186 self.assertEqual(response, receivedResponse)
1187 misses += 1
1188
1189 # next queries should hit the cache
1190 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1191 self.assertEqual(receivedResponse, response)
1192
1193 # ok, we mark the backend as down
1194 self.sendConsoleCommand("getServer(0):setDown()")
1195 # and we wait for the entry to expire
1196 time.sleep(ttl + 1)
1197
1198 # we should get a cached, stale, entry
1199 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1200 self.assertEqual(receivedResponse, response)
1201 for an in receivedResponse.answer:
1202 self.assertEqual(an.ttl, self._staleCacheTTL)
1203
1204 total = 0
1205 for key in self._responsesCounter:
1206 total += self._responsesCounter[key]
1207
1208 self.assertEqual(total, misses)
1209
1210class TestCachingStaleExpunged(DNSDistTest):
1211
1212 _consoleKey = DNSDistTest.generateConsoleKey()
1213 _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
1214 _staleCacheTTL = 60
1215 _config_params = ['_staleCacheTTL', '_consoleKeyB64', '_consolePort', '_testServerPort']
1216 _config_template = """
1217 pc = newPacketCache(100, {maxTTL=86400, minTTL=1, temporaryFailureTTL=0, staleTTL=%d})
1218 getPool(""):setCache(pc)
1219 setStaleCacheEntriesTTL(600)
1220 -- try to remove all expired entries
1221 setCacheCleaningPercentage(100)
1222 -- clean the cache every second
1223 setCacheCleaningDelay(1)
1224 setKey("%s")
1225 controlSocket("127.0.0.1:%d")
1226 newServer{address="127.0.0.1:%d"}
1227 """
1228 def testCacheStale(self):
1229 """
1230 Cache: Cache entry, set backend down, wait for the cache cleaning to run and remove the entry, get no entry
1231 """
1232 misses = 0
1233 drops = 0
1234 ttl = 2
1235 name = 'stale-but-expunged.cache.tests.powerdns.com.'
1236 query = dns.message.make_query(name, 'A', 'IN')
1237 response = dns.message.make_response(query)
1238 rrset = dns.rrset.from_text(name,
1239 ttl,
1240 dns.rdataclass.IN,
1241 dns.rdatatype.A,
1242 '127.0.0.1')
1243 response.answer.append(rrset)
1244
1245 # Miss
1246 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1247 self.assertTrue(receivedQuery)
1248 self.assertTrue(receivedResponse)
1249 receivedQuery.id = query.id
1250 self.assertEqual(query, receivedQuery)
1251 self.assertEqual(response, receivedResponse)
1252 misses += 1
1253 self.assertEqual(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"misses\"]").strip("\n")), misses + drops)
1254
1255 # next queries should hit the cache
1256 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1257 self.assertEqual(receivedResponse, response)
1258 # the cache should have one entry
1259 self.assertEqual(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"entries\"]").strip("\n")), 1)
1260 self.assertEqual(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"hits\"]").strip("\n")), 1)
1261
1262 # ok, we mark the backend as down
1263 self.sendConsoleCommand("getServer(0):setDown()")
1264 # and we wait for the entry to expire
1265 time.sleep(ttl + 1)
1266 # wait a bit more to be sure that the cache cleaning algo has been run
1267 time.sleep(1)
1268 # the cache should be empty now
1269 self.assertEqual(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"entries\"]").strip("\n")), 0)
1270
1271 # we should get a DROP (backend is down, nothing in the cache anymore)
1272 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1273 self.assertEqual(receivedResponse, None)
1274 drops += 1
1275
1276 self.assertEqual(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"misses\"]").strip("\n")), misses + drops)
1277 self.assertEqual(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"hits\"]").strip("\n")), 1)
1278
1279 total = 0
1280 for key in self._responsesCounter:
1281 total += self._responsesCounter[key]
1282
1283 self.assertEqual(total, misses)
1284
1285class TestCachingStaleExpungePrevented(DNSDistTest):
1286
1287 _consoleKey = DNSDistTest.generateConsoleKey()
1288 _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
1289 _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
1290 _config_template = """
1291 pc = newPacketCache(100, {maxTTL=86400, minTTL=1, temporaryFailureTTL=0, staleTTL=60, dontAge=false, numberOfShards=1, deferrableInsertLock=true, maxNegativeTTL=3600, parseECS=false, keepStaleData=true})
1292 getPool(""):setCache(pc)
1293 setStaleCacheEntriesTTL(600)
1294 -- try to remove all expired entries
1295 setCacheCleaningPercentage(100)
1296 -- clean the cache every second
1297 setCacheCleaningDelay(1)
1298 setKey("%s")
1299 controlSocket("127.0.0.1:%d")
1300 newServer{address="127.0.0.1:%d"}
1301 """
1302 def testCacheStale(self):
1303 """
1304 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
1305 """
1306 misses = 0
1307 ttl = 2
1308 name = 'stale-not-expunged.cache.tests.powerdns.com.'
1309 query = dns.message.make_query(name, 'A', 'IN')
1310 response = dns.message.make_response(query)
1311 rrset = dns.rrset.from_text(name,
1312 ttl,
1313 dns.rdataclass.IN,
1314 dns.rdatatype.A,
1315 '127.0.0.1')
1316 response.answer.append(rrset)
1317
1318 # Miss
1319 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1320 self.assertTrue(receivedQuery)
1321 self.assertTrue(receivedResponse)
1322 receivedQuery.id = query.id
1323 self.assertEqual(query, receivedQuery)
1324 self.assertEqual(response, receivedResponse)
1325 misses += 1
1326 self.assertEqual(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"misses\"]").strip("\n")), 1)
1327
1328 # next queries should hit the cache
1329 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1330 self.assertEqual(receivedResponse, response)
1331 # the cache should have one entry
1332 self.assertEqual(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"entries\"]").strip("\n")), 1)
1333 self.assertEqual(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"hits\"]").strip("\n")), 1)
1334
1335 # ok, we mark the backend as down
1336 self.sendConsoleCommand("getServer(0):setDown()")
1337 # and we wait for the entry to expire
1338 time.sleep(ttl + 1)
1339 # wait a bit more to be sure that the cache cleaning algo has been run
1340 time.sleep(1)
1341 # the cache should NOT be empty because the removal of the expired entry should have been prevented
1342 # since all backends for this pool are down
1343 self.assertEqual(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"entries\"]").strip("\n")), 1)
1344
1345 # we should get a HIT
1346 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1347 self.assertEqual(receivedResponse, response)
1348
1349 self.assertEqual(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"misses\"]").strip("\n")), 1)
1350 self.assertEqual(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"hits\"]").strip("\n")), 2)
1351
1352 total = 0
1353 for key in self._responsesCounter:
1354 total += self._responsesCounter[key]
1355
1356 self.assertEqual(total, misses)
1357
1358class TestCacheManagement(DNSDistTest):
1359
1360 _consoleKey = DNSDistTest.generateConsoleKey()
1361 _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
1362 _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
1363 _config_template = """
1364 pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
1365 getPool(""):setCache(pc)
1366 setKey("%s")
1367 controlSocket("127.0.0.1:%d")
1368 newServer{address="127.0.0.1:%d"}
1369 """
1370 def testCacheExpunge(self):
1371 """
1372 Cache: Expunge
1373
1374 """
1375 misses = 0
1376 ttl = 600
1377 name = 'expunge.cache.tests.powerdns.com.'
1378 query = dns.message.make_query(name, 'A', 'IN')
1379 response = dns.message.make_response(query)
1380 rrset = dns.rrset.from_text(name,
1381 ttl,
1382 dns.rdataclass.IN,
1383 dns.rdatatype.A,
1384 '127.0.0.1')
1385 response.answer.append(rrset)
1386
1387 # Miss
1388 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1389 self.assertTrue(receivedQuery)
1390 self.assertTrue(receivedResponse)
1391 receivedQuery.id = query.id
1392 self.assertEqual(query, receivedQuery)
1393 self.assertEqual(response, receivedResponse)
1394 misses += 1
1395
1396 # next queries should hit the cache
1397 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1398 self.assertEqual(receivedResponse, response)
1399
1400 # remove cached entries
1401 self.sendConsoleCommand("getPool(\"\"):getCache():expunge(0)")
1402
1403 # Miss
1404 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1405 self.assertTrue(receivedQuery)
1406 self.assertTrue(receivedResponse)
1407 receivedQuery.id = query.id
1408 self.assertEqual(query, receivedQuery)
1409 self.assertEqual(response, receivedResponse)
1410 misses += 1
1411
1412 # next queries should hit the cache again
1413 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1414 self.assertEqual(receivedResponse, response)
1415
1416 total = 0
1417 for key in self._responsesCounter:
1418 total += self._responsesCounter[key]
1419
1420 self.assertEqual(total, misses)
1421
1422 def testCacheExpungeByName(self):
1423 """
1424 Cache: Expunge by name
1425
1426 """
1427 misses = 0
1428 ttl = 600
1429 name = 'expungebyname.cache.tests.powerdns.com.'
1430 query = dns.message.make_query(name, 'A', 'IN')
1431 response = dns.message.make_response(query)
1432 rrset = dns.rrset.from_text(name,
1433 ttl,
1434 dns.rdataclass.IN,
1435 dns.rdatatype.A,
1436 '127.0.0.1')
1437 response.answer.append(rrset)
1438
1439 name2 = 'expungebynameother.cache.tests.powerdns.com.'
1440 query2 = dns.message.make_query(name2, 'A', 'IN')
1441 response2 = dns.message.make_response(query2)
1442 rrset2 = dns.rrset.from_text(name2,
1443 ttl,
1444 dns.rdataclass.IN,
1445 dns.rdatatype.A,
1446 '127.0.0.1')
1447 response2.answer.append(rrset2)
1448
1449 # Miss
1450 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1451 self.assertTrue(receivedQuery)
1452 self.assertTrue(receivedResponse)
1453 receivedQuery.id = query.id
1454 self.assertEqual(query, receivedQuery)
1455 self.assertEqual(response, receivedResponse)
1456 misses += 1
1457
1458 # next queries should hit the cache
1459 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1460 self.assertEqual(receivedResponse, response)
1461
1462 # cache another entry
1463 (receivedQuery, receivedResponse) = self.sendUDPQuery(query2, response2)
1464 self.assertTrue(receivedQuery)
1465 self.assertTrue(receivedResponse)
1466 receivedQuery.id = query2.id
1467 self.assertEqual(query2, receivedQuery)
1468 self.assertEqual(response2, receivedResponse)
1469 misses += 1
1470
1471 # queries for name and name 2 should hit the cache
1472 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1473 self.assertEqual(receivedResponse, response)
1474
1475 (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
1476 self.assertEqual(receivedResponse, response2)
1477
1478 # remove cached entries from name
1479 self.sendConsoleCommand("getPool(\"\"):getCache():expungeByName(newDNSName(\"" + name + "\"))")
1480
1481 # Miss for name
1482 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1483 self.assertTrue(receivedQuery)
1484 self.assertTrue(receivedResponse)
1485 receivedQuery.id = query.id
1486 self.assertEqual(query, receivedQuery)
1487 self.assertEqual(response, receivedResponse)
1488 misses += 1
1489
1490 # next queries for name should hit the cache again
1491 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1492 self.assertEqual(receivedResponse, response)
1493
1494 # queries for name2 should still hit the cache
1495 (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
1496 self.assertEqual(receivedResponse, response2)
1497
1498 total = 0
1499 for key in self._responsesCounter:
1500 total += self._responsesCounter[key]
1501
1502 self.assertEqual(total, misses)
1503
1504 def testCacheExpungeByNameAndType(self):
1505 """
1506 Cache: Expunge by name and type
1507
1508 """
1509 misses = 0
1510 ttl = 600
1511 name = 'expungebynameandtype.cache.tests.powerdns.com.'
1512 query = dns.message.make_query(name, 'A', 'IN')
1513 response = dns.message.make_response(query)
1514 rrset = dns.rrset.from_text(name,
1515 ttl,
1516 dns.rdataclass.IN,
1517 dns.rdatatype.A,
1518 '127.0.0.1')
1519 response.answer.append(rrset)
1520
1521 query2 = dns.message.make_query(name, 'AAAA', 'IN')
1522 response2 = dns.message.make_response(query2)
1523 rrset2 = dns.rrset.from_text(name,
1524 ttl,
1525 dns.rdataclass.IN,
1526 dns.rdatatype.AAAA,
1527 '::1')
1528 response2.answer.append(rrset2)
1529
1530 # Miss
1531 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1532 self.assertTrue(receivedQuery)
1533 self.assertTrue(receivedResponse)
1534 receivedQuery.id = query.id
1535 self.assertEqual(query, receivedQuery)
1536 self.assertEqual(response, receivedResponse)
1537 misses += 1
1538
1539 # next queries should hit the cache
1540 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1541 self.assertEqual(receivedResponse, response)
1542
1543 # cache another entry
1544 (receivedQuery, receivedResponse) = self.sendUDPQuery(query2, response2)
1545 self.assertTrue(receivedQuery)
1546 self.assertTrue(receivedResponse)
1547 receivedQuery.id = query2.id
1548 self.assertEqual(query2, receivedQuery)
1549 self.assertEqual(response2, receivedResponse)
1550 misses += 1
1551
1552 # queries for name A and AAAA should hit the cache
1553 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1554 self.assertEqual(receivedResponse, response)
1555
1556 (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
1557 self.assertEqual(receivedResponse, response2)
1558
1559 # remove cached entries from name A
1560 self.sendConsoleCommand("getPool(\"\"):getCache():expungeByName(newDNSName(\"" + name + "\"), DNSQType.A)")
1561
1562 # Miss for name A
1563 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1564 self.assertTrue(receivedQuery)
1565 self.assertTrue(receivedResponse)
1566 receivedQuery.id = query.id
1567 self.assertEqual(query, receivedQuery)
1568 self.assertEqual(response, receivedResponse)
1569 misses += 1
1570
1571 # next queries for name A should hit the cache again
1572 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1573 self.assertEqual(receivedResponse, response)
1574
1575 # queries for name AAAA should still hit the cache
1576 (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
1577 self.assertEqual(receivedResponse, response2)
1578
1579 total = 0
1580 for key in self._responsesCounter:
1581 total += self._responsesCounter[key]
1582 self.assertEqual(total, misses)
1583
1584 def testCacheExpungeByNameAndSuffix(self):
1585 """
1586 Cache: Expunge by name
1587
1588 """
1589 misses = 0
1590 ttl = 600
1591 name = 'expungebyname.suffix.cache.tests.powerdns.com.'
1592 query = dns.message.make_query(name, 'A', 'IN')
1593 response = dns.message.make_response(query)
1594 rrset = dns.rrset.from_text(name,
1595 ttl,
1596 dns.rdataclass.IN,
1597 dns.rdatatype.A,
1598 '127.0.0.1')
1599 response.answer.append(rrset)
1600
1601 name2 = 'expungebyname.suffixother.cache.tests.powerdns.com.'
1602 query2 = dns.message.make_query(name2, 'A', 'IN')
1603 response2 = dns.message.make_response(query2)
1604 rrset2 = dns.rrset.from_text(name2,
1605 ttl,
1606 dns.rdataclass.IN,
1607 dns.rdatatype.A,
1608 '127.0.0.1')
1609 response2.answer.append(rrset2)
1610
1611 # Miss
1612 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1613 self.assertTrue(receivedQuery)
1614 self.assertTrue(receivedResponse)
1615 receivedQuery.id = query.id
1616 self.assertEqual(query, receivedQuery)
1617 self.assertEqual(response, receivedResponse)
1618 misses += 1
1619
1620 # next queries should hit the cache
1621 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1622 self.assertEqual(receivedResponse, response)
1623
1624 # cache another entry
1625 (receivedQuery, receivedResponse) = self.sendUDPQuery(query2, response2)
1626 self.assertTrue(receivedQuery)
1627 self.assertTrue(receivedResponse)
1628 receivedQuery.id = query2.id
1629 self.assertEqual(query2, receivedQuery)
1630 self.assertEqual(response2, receivedResponse)
1631 misses += 1
1632
1633 # queries for name and name 2 should hit the cache
1634 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1635 self.assertEqual(receivedResponse, response)
1636
1637 (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
1638 self.assertEqual(receivedResponse, response2)
1639
1640 # remove cached entries from name
1641 self.sendConsoleCommand("getPool(\"\"):getCache():expungeByName(newDNSName(\"suffix.cache.tests.powerdns.com.\"), DNSQType.ANY, true)")
1642
1643 # Miss for name
1644 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1645 self.assertTrue(receivedQuery)
1646 self.assertTrue(receivedResponse)
1647 receivedQuery.id = query.id
1648 self.assertEqual(query, receivedQuery)
1649 self.assertEqual(response, receivedResponse)
1650 misses += 1
1651
1652 # next queries for name should hit the cache again
1653 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1654 self.assertEqual(receivedResponse, response)
1655
1656 # queries for name2 should still hit the cache
1657 (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
1658 self.assertEqual(receivedResponse, response2)
1659
1660 total = 0
1661 for key in self._responsesCounter:
1662 total += self._responsesCounter[key]
1663
1664 self.assertEqual(total, misses)
1665
1666 def testCacheExpungeByNameAndTypeAndSuffix(self):
1667 """
1668 Cache: Expunge by name and type
1669
1670 """
1671 misses = 0
1672 ttl = 600
1673 name = 'expungebynameandtype.suffixtype.cache.tests.powerdns.com.'
1674 query = dns.message.make_query(name, 'A', 'IN')
1675 response = dns.message.make_response(query)
1676 rrset = dns.rrset.from_text(name,
1677 ttl,
1678 dns.rdataclass.IN,
1679 dns.rdatatype.A,
1680 '127.0.0.1')
1681 response.answer.append(rrset)
1682
1683 query2 = dns.message.make_query(name, 'AAAA', 'IN')
1684 response2 = dns.message.make_response(query2)
1685 rrset2 = dns.rrset.from_text(name,
1686 ttl,
1687 dns.rdataclass.IN,
1688 dns.rdatatype.AAAA,
1689 '::1')
1690 response2.answer.append(rrset2)
1691
1692 # Miss
1693 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1694 self.assertTrue(receivedQuery)
1695 self.assertTrue(receivedResponse)
1696 receivedQuery.id = query.id
1697 self.assertEqual(query, receivedQuery)
1698 self.assertEqual(response, receivedResponse)
1699 misses += 1
1700
1701 # next queries should hit the cache
1702 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1703 self.assertEqual(receivedResponse, response)
1704
1705 # cache another entry
1706 (receivedQuery, receivedResponse) = self.sendUDPQuery(query2, response2)
1707 self.assertTrue(receivedQuery)
1708 self.assertTrue(receivedResponse)
1709 receivedQuery.id = query2.id
1710 self.assertEqual(query2, receivedQuery)
1711 self.assertEqual(response2, receivedResponse)
1712 misses += 1
1713
1714 # queries for name A and AAAA should hit the cache
1715 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1716 self.assertEqual(receivedResponse, response)
1717
1718 (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
1719 self.assertEqual(receivedResponse, response2)
1720
1721 # remove cached entries from name A
1722 self.sendConsoleCommand("getPool(\"\"):getCache():expungeByName(newDNSName(\"suffixtype.cache.tests.powerdns.com.\"), DNSQType.A, true)")
1723
1724 # Miss for name A
1725 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1726 self.assertTrue(receivedQuery)
1727 self.assertTrue(receivedResponse)
1728 receivedQuery.id = query.id
1729 self.assertEqual(query, receivedQuery)
1730 self.assertEqual(response, receivedResponse)
1731 misses += 1
1732
1733 # next queries for name A should hit the cache again
1734 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1735 self.assertEqual(receivedResponse, response)
1736
1737 # queries for name AAAA should still hit the cache
1738 (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
1739 self.assertEqual(receivedResponse, response2)
1740
1741 total = 0
1742 for key in self._responsesCounter:
1743 total += self._responsesCounter[key]
1744 self.assertEqual(total, misses)
1745
1746class TestCachingTTL(DNSDistTest):
1747
1748 _maxCacheTTL = 86400
1749 _minCacheTTL = 600
1750 _config_params = ['_maxCacheTTL', '_minCacheTTL', '_testServerPort']
1751 _config_template = """
1752 pc = newPacketCache(1000, {maxTTL=%d, minTTL=%d})
1753 getPool(""):setCache(pc)
1754 newServer{address="127.0.0.1:%d"}
1755 """
1756 def testCacheShortTTL(self):
1757 """
1758 Cache: Entries with a TTL shorter than minTTL
1759
1760 """
1761 misses = 0
1762 ttl = 60
1763 name = 'ttltooshort.cache.tests.powerdns.com.'
1764 query = dns.message.make_query(name, 'A', 'IN')
1765 response = dns.message.make_response(query)
1766 rrset = dns.rrset.from_text(name,
1767 ttl,
1768 dns.rdataclass.IN,
1769 dns.rdatatype.A,
1770 '127.0.0.1')
1771 response.answer.append(rrset)
1772
1773 # Miss
1774 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1775 self.assertTrue(receivedQuery)
1776 self.assertTrue(receivedResponse)
1777 receivedQuery.id = query.id
1778 self.assertEqual(query, receivedQuery)
1779 self.assertEqual(response, receivedResponse)
1780 for an in receivedResponse.answer:
1781 self.assertEqual(an.ttl, ttl)
1782 misses += 1
1783
1784 # We should not have been cached
1785 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1786 self.assertTrue(receivedQuery)
1787 self.assertTrue(receivedResponse)
1788 receivedQuery.id = query.id
1789 self.assertEqual(query, receivedQuery)
1790 self.assertEqual(response, receivedResponse)
1791 for an in receivedResponse.answer:
1792 self.assertEqual(an.ttl, ttl)
1793 misses += 1
1794
1795 total = 0
1796 for key in self._responsesCounter:
1797 total += self._responsesCounter[key]
1798
1799 self.assertEqual(total, misses)
1800
1801 def testCacheNXWithNoRR(self):
1802 """
1803 Cache: NX with no RR
1804
1805 """
1806 misses = 0
1807 name = 'nxwithnorr.cache.tests.powerdns.com.'
1808 query = dns.message.make_query(name, 'A', 'IN')
1809 response = dns.message.make_response(query)
1810 response.set_rcode(dns.rcode.NXDOMAIN)
1811
1812 # Miss
1813 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1814 self.assertTrue(receivedQuery)
1815 self.assertTrue(receivedResponse)
1816 receivedQuery.id = query.id
1817 self.assertEqual(query, receivedQuery)
1818 self.assertEqual(response, receivedResponse)
1819 misses += 1
1820
1821 # We should not have been cached
1822 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1823 self.assertTrue(receivedQuery)
1824 self.assertTrue(receivedResponse)
1825 receivedQuery.id = query.id
1826 self.assertEqual(query, receivedQuery)
1827 self.assertEqual(response, receivedResponse)
1828 misses += 1
1829
1830 total = 0
1831 for key in self._responsesCounter:
1832 total += self._responsesCounter[key]
1833
1834 self.assertEqual(total, misses)
1835
1836class TestCachingLongTTL(DNSDistTest):
1837
1838 _maxCacheTTL = 2
1839 _config_params = ['_maxCacheTTL', '_testServerPort']
1840 _config_template = """
1841 pc = newPacketCache(1000, {maxTTL=%d})
1842 getPool(""):setCache(pc)
1843 newServer{address="127.0.0.1:%d"}
1844 """
1845 def testCacheLongTTL(self):
1846 """
1847 Cache: Entries with a longer TTL than the maximum
1848
1849 """
1850 misses = 0
1851 ttl = 172800
1852 name = 'longttl.cache.tests.powerdns.com.'
1853 query = dns.message.make_query(name, 'A', 'IN')
1854 response = dns.message.make_response(query)
1855 rrset = dns.rrset.from_text(name,
1856 ttl,
1857 dns.rdataclass.IN,
1858 dns.rdatatype.A,
1859 '127.0.0.1')
1860 response.answer.append(rrset)
1861
1862 # Miss
1863 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1864 self.assertTrue(receivedQuery)
1865 self.assertTrue(receivedResponse)
1866 receivedQuery.id = query.id
1867 self.assertEqual(query, receivedQuery)
1868 self.assertEqual(response, receivedResponse)
1869 for an in receivedResponse.answer:
1870 self.assertEqual(an.ttl, ttl)
1871 misses += 1
1872
1873 # next queries should hit the cache
1874 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1875 self.assertEqual(receivedResponse, response)
1876 for an in receivedResponse.answer:
1877 self.assertTrue(an.ttl <= ttl)
1878
1879 time.sleep(self._maxCacheTTL + 1)
1880
1881 # we should not have cached for longer than max cache
1882 # so it should be a miss
1883 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1884 self.assertTrue(receivedQuery)
1885 self.assertTrue(receivedResponse)
1886 receivedQuery.id = query.id
1887 self.assertEqual(query, receivedQuery)
1888 self.assertEqual(response, receivedResponse)
1889 for an in receivedResponse.answer:
1890 self.assertEqual(an.ttl, ttl)
1891 misses += 1
1892
1893 total = 0
1894 for key in self._responsesCounter:
1895 total += self._responsesCounter[key]
1896
1897 self.assertEqual(total, misses)
1898
1899class TestCachingFailureTTL(DNSDistTest):
1900
1901 _failureCacheTTL = 2
1902 _config_params = ['_failureCacheTTL', '_testServerPort']
1903 _config_template = """
1904 pc = newPacketCache(1000, {maxTTL=86400, minTTL=0, temporaryFailureTTL=%d, staleTTL=60})
1905 getPool(""):setCache(pc)
1906 newServer{address="127.0.0.1:%d"}
1907 """
1908 def testCacheServFailTTL(self):
1909 """
1910 Cache: ServFail TTL
1911
1912 """
1913 misses = 0
1914 name = 'servfail.failure.cache.tests.powerdns.com.'
1915 query = dns.message.make_query(name, 'A', 'IN')
1916 response = dns.message.make_response(query)
1917 response.set_rcode(dns.rcode.SERVFAIL)
1918
1919 # Miss
1920 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1921 self.assertTrue(receivedQuery)
1922 self.assertTrue(receivedResponse)
1923 receivedQuery.id = query.id
1924 self.assertEqual(query, receivedQuery)
1925 self.assertEqual(response, receivedResponse)
1926 misses += 1
1927
1928 # next queries should hit the cache
1929 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1930 self.assertEqual(receivedResponse, response)
1931
1932 time.sleep(self._failureCacheTTL + 1)
1933
1934 # we should not have cached for longer than failure cache
1935 # so it should be a miss
1936 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1937 self.assertTrue(receivedQuery)
1938 self.assertTrue(receivedResponse)
1939 receivedQuery.id = query.id
1940 self.assertEqual(query, receivedQuery)
1941 self.assertEqual(response, receivedResponse)
1942 misses += 1
1943
1944 total = 0
1945 for key in self._responsesCounter:
1946 total += self._responsesCounter[key]
1947
1948 self.assertEqual(total, misses)
1949
1950 def testCacheRefusedTTL(self):
1951 """
1952 Cache: Refused TTL
1953
1954 """
1955 misses = 0
1956 name = 'refused.failure.cache.tests.powerdns.com.'
1957 query = dns.message.make_query(name, 'A', 'IN')
1958 response = dns.message.make_response(query)
1959 response.set_rcode(dns.rcode.REFUSED)
1960
1961 # Miss
1962 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1963 self.assertTrue(receivedQuery)
1964 self.assertTrue(receivedResponse)
1965 receivedQuery.id = query.id
1966 self.assertEqual(query, receivedQuery)
1967 self.assertEqual(response, receivedResponse)
1968 misses += 1
1969
1970 # next queries should hit the cache
1971 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1972 self.assertEqual(receivedResponse, response)
1973
1974 time.sleep(self._failureCacheTTL + 1)
1975
1976 # we should not have cached for longer than failure cache
1977 # so it should be a miss
1978 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1979 self.assertTrue(receivedQuery)
1980 self.assertTrue(receivedResponse)
1981 receivedQuery.id = query.id
1982 self.assertEqual(query, receivedQuery)
1983 self.assertEqual(response, receivedResponse)
1984 misses += 1
1985
1986 total = 0
1987 for key in self._responsesCounter:
1988 total += self._responsesCounter[key]
1989
1990 self.assertEqual(total, misses)
1991
1992 def testCacheHeaderOnlyRefusedTTL(self):
1993 """
1994 Cache: Header-Only Refused TTL
1995
1996 """
1997 misses = 0
1998 name = 'header-only-refused.failure.cache.tests.powerdns.com.'
1999 query = dns.message.make_query(name, 'A', 'IN')
2000 response = dns.message.make_response(query)
2001 response.set_rcode(dns.rcode.REFUSED)
2002 response.question = []
2003
2004 # Miss
2005 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
2006 self.assertTrue(receivedQuery)
2007 self.assertTrue(receivedResponse)
2008 receivedQuery.id = query.id
2009 self.assertEqual(query, receivedQuery)
2010 self.assertEqual(response, receivedResponse)
2011 misses += 1
2012
2013 # next queries should hit the cache
2014 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
2015 self.assertEqual(receivedResponse, response)
2016
2017 time.sleep(self._failureCacheTTL + 1)
2018
2019 # we should not have cached for longer than failure cache
2020 # so it should be a miss
2021 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
2022 self.assertTrue(receivedQuery)
2023 self.assertTrue(receivedResponse)
2024 receivedQuery.id = query.id
2025 self.assertEqual(query, receivedQuery)
2026 self.assertEqual(response, receivedResponse)
2027 misses += 1
2028
2029 total = 0
2030 for key in self._responsesCounter:
2031 total += self._responsesCounter[key]
2032
2033 self.assertEqual(total, misses)
2034
2035class TestCachingNegativeTTL(DNSDistTest):
2036
2037 _negCacheTTL = 2
2038 _config_params = ['_negCacheTTL', '_testServerPort']
2039 _config_template = """
2040 pc = newPacketCache(1000, {maxTTL=86400, minTTL=0, temporaryFailureTTL=60, staleTTL=60, dontAge=false, numberOfShards=1, deferrableInsertLock=true, maxNegativeTTL=%d})
2041 getPool(""):setCache(pc)
2042 newServer{address="127.0.0.1:%d"}
2043 """
2044
2045 def testCacheNegativeTTLNXDomain(self):
2046 """
2047 Cache: Negative TTL on NXDOMAIN
2048
2049 """
2050 misses = 0
2051 name = 'nxdomain.negativettl.cache.tests.powerdns.com.'
2052 query = dns.message.make_query(name, 'A', 'IN')
2053 response = dns.message.make_response(query)
2054 response.set_rcode(dns.rcode.NXDOMAIN)
2055 soa = dns.rrset.from_text(name,
2056 60,
2057 dns.rdataclass.IN,
2058 dns.rdatatype.SOA,
2059 'ns.' + name + ' hostmaster.' + name + ' 1 3600 3600 3600 60')
2060 response.authority.append(soa)
2061
2062 # Miss
2063 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
2064 self.assertTrue(receivedQuery)
2065 self.assertTrue(receivedResponse)
2066 receivedQuery.id = query.id
2067 self.assertEqual(query, receivedQuery)
2068 self.assertEqual(response, receivedResponse)
2069 misses += 1
2070
2071 # next queries should hit the cache
2072 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
2073 self.assertEqual(receivedResponse, response)
2074
2075 time.sleep(self._negCacheTTL + 1)
2076
2077 # we should not have cached for longer than the negative TTL
2078 # so it should be a miss
2079 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
2080 self.assertTrue(receivedQuery)
2081 self.assertTrue(receivedResponse)
2082 receivedQuery.id = query.id
2083 self.assertEqual(query, receivedQuery)
2084 self.assertEqual(response, receivedResponse)
2085 misses += 1
2086
2087 total = 0
2088 for key in self._responsesCounter:
2089 total += self._responsesCounter[key]
2090
2091 self.assertEqual(total, misses)
2092
2093 def testCacheNegativeTTLNoData(self):
2094 """
2095 Cache: Negative TTL on NoData
2096
2097 """
2098 misses = 0
2099 name = 'nodata.negativettl.cache.tests.powerdns.com.'
2100 query = dns.message.make_query(name, 'A', 'IN')
2101 response = dns.message.make_response(query)
2102 response.set_rcode(dns.rcode.NOERROR)
2103 soa = dns.rrset.from_text(name,
2104 60,
2105 dns.rdataclass.IN,
2106 dns.rdatatype.SOA,
2107 'ns.' + name + ' hostmaster.' + name + ' 1 3600 3600 3600 60')
2108 response.authority.append(soa)
2109
2110 # Miss
2111 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
2112 self.assertTrue(receivedQuery)
2113 self.assertTrue(receivedResponse)
2114 receivedQuery.id = query.id
2115 self.assertEqual(query, receivedQuery)
2116 self.assertEqual(response, receivedResponse)
2117 misses += 1
2118
2119 # next queries should hit the cache
2120 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
2121 self.assertEqual(receivedResponse, response)
2122
2123 time.sleep(self._negCacheTTL + 1)
2124
2125 # we should not have cached for longer than the negative TTL
2126 # so it should be a miss
2127 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
2128 self.assertTrue(receivedQuery)
2129 self.assertTrue(receivedResponse)
2130 receivedQuery.id = query.id
2131 self.assertEqual(query, receivedQuery)
2132 self.assertEqual(response, receivedResponse)
2133 misses += 1
2134
2135 total = 0
2136 for key in self._responsesCounter:
2137 total += self._responsesCounter[key]
2138
2139 self.assertEqual(total, misses)
2140
2141class TestCachingDontAge(DNSDistTest):
2142
2143 _config_template = """
2144 pc = newPacketCache(100, {maxTTL=86400, minTTL=0, temporaryFailureTTL=60, staleTTL=60, dontAge=true})
2145 getPool(""):setCache(pc)
2146 newServer{address="127.0.0.1:%d"}
2147 """
2148 def testCacheDoesntDecreaseTTL(self):
2149 """
2150 Cache: Cache doesn't decrease TTL with 'don't age' set
2151
2152 dnsdist is configured to cache entries but without aging the TTL,
2153 we are sending one request (cache miss) and verify that the cache
2154 hits don't have a decreasing TTL.
2155 """
2156 ttl = 600
2157 misses = 0
2158 name = 'cachedoesntdecreasettl.cache-dont-age.tests.powerdns.com.'
2159 query = dns.message.make_query(name, 'AAAA', 'IN')
2160 response = dns.message.make_response(query)
2161 rrset = dns.rrset.from_text(name,
2162 ttl,
2163 dns.rdataclass.IN,
2164 dns.rdatatype.AAAA,
2165 '::1')
2166 response.answer.append(rrset)
2167
2168 # first query to fill the cache
2169 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
2170 self.assertTrue(receivedQuery)
2171 self.assertTrue(receivedResponse)
2172 receivedQuery.id = query.id
2173 self.assertEqual(query, receivedQuery)
2174 self.assertEqual(receivedResponse, response)
2175 misses += 1
2176
2177 # next queries should hit the cache
2178 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
2179 self.assertEqual(receivedResponse, response)
2180 for an in receivedResponse.answer:
2181 self.assertTrue(an.ttl == ttl)
2182
2183 # now we wait a bit for the TTL to decrease
2184 time.sleep(1)
2185
2186 # next queries should hit the cache
2187 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
2188 self.assertEqual(receivedResponse, response)
2189 for an in receivedResponse.answer:
2190 self.assertTrue(an.ttl == ttl)
2191
2192 total = 0
2193 for key in self._responsesCounter:
2194 total += self._responsesCounter[key]
2195
2196 self.assertEqual(total, misses)
2197
2198class TestCachingECSWithoutPoolECS(DNSDistTest):
2199
2200 _consoleKey = DNSDistTest.generateConsoleKey()
2201 _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
2202 _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
2203 _config_template = """
2204 pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
2205 getPool(""):setCache(pc)
2206 setKey("%s")
2207 controlSocket("127.0.0.1:%d")
2208 newServer{address="127.0.0.1:%d", useClientSubnet=true}
2209 """
2210
2211 def testCached(self):
2212 """
2213 Cache: Cached entry with ECS is a miss when no backend are available
2214 """
2215 ttl = 600
2216 name = 'cached.cache-ecs-without-pool-ecs.tests.powerdns.com.'
2217 query = dns.message.make_query(name, 'AAAA', 'IN')
2218 response = dns.message.make_response(query)
2219 rrset = dns.rrset.from_text(name,
2220 ttl,
2221 dns.rdataclass.IN,
2222 dns.rdatatype.AAAA,
2223 '::1')
2224 response.answer.append(rrset)
2225
2226 # first query to fill the cache
2227 for method in ("sendUDPQuery", "sendTCPQuery"):
2228 sender = getattr(self, method)
2229 (receivedQuery, receivedResponse) = sender(query, response)
2230 self.assertTrue(receivedQuery)
2231 self.assertTrue(receivedResponse)
2232 receivedQuery.id = query.id
2233 self.assertEqual(query, receivedQuery)
2234 self.assertEqual(receivedResponse, response)
2235
2236 # next queries should hit the cache
2237 for method in ("sendUDPQuery", "sendTCPQuery"):
2238 sender = getattr(self, method)
2239 (_, receivedResponse) = sender(query, response=None, useQueue=False)
2240 self.assertEqual(receivedResponse, response)
2241
2242 # we mark the backend as down
2243 self.sendConsoleCommand("getServer(0):setDown()")
2244
2245 # we should NOT get a cached entry since it has ECS and we haven't asked the pool
2246 # to add ECS when no backend is up
2247 for method in ("sendUDPQuery", "sendTCPQuery"):
2248 sender = getattr(self, method)
2249 (_, receivedResponse) = sender(query, response=None, useQueue=False)
2250 self.assertEqual(receivedResponse, None)
2251
2252class TestCachingECSWithPoolECS(DNSDistTest):
2253
2254 _consoleKey = DNSDistTest.generateConsoleKey()
2255 _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
2256 _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
2257 _config_template = """
2258 pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
2259 getPool(""):setCache(pc)
2260 getPool(""):setECS(true)
2261 setKey("%s")
2262 controlSocket("127.0.0.1:%d")
2263 newServer{address="127.0.0.1:%d", useClientSubnet=true}
2264 """
2265
2266 def testCached(self):
2267 """
2268 Cache: Cached entry with ECS is a hit when no backend are available
2269 """
2270 ttl = 600
2271 name = 'cached.cache-ecs-with-pool-ecs.tests.powerdns.com.'
2272 query = dns.message.make_query(name, 'AAAA', 'IN')
2273 response = dns.message.make_response(query)
2274 rrset = dns.rrset.from_text(name,
2275 ttl,
2276 dns.rdataclass.IN,
2277 dns.rdatatype.AAAA,
2278 '::1')
2279 response.answer.append(rrset)
2280
2281 # first query to fill the cache
2282 for method in ("sendUDPQuery", "sendTCPQuery"):
2283 sender = getattr(self, method)
2284 (receivedQuery, receivedResponse) = sender(query, response)
2285 self.assertTrue(receivedQuery)
2286 self.assertTrue(receivedResponse)
2287 receivedQuery.id = query.id
2288 self.assertEqual(query, receivedQuery)
2289 self.assertEqual(receivedResponse, response)
2290
2291 # next queries should hit the cache
2292 for method in ("sendUDPQuery", "sendTCPQuery"):
2293 sender = getattr(self, method)
2294 (_, receivedResponse) = sender(query, response=None, useQueue=False)
2295 self.assertEqual(receivedResponse, response)
2296
2297 # we mark the backend as down
2298 self.sendConsoleCommand("getServer(0):setDown()")
2299
2300 # we should STILL get a cached entry since it has ECS and we have asked the pool
2301 # to add ECS when no backend is up
2302 for method in ("sendUDPQuery", "sendTCPQuery"):
2303 sender = getattr(self, method)
2304 (_, receivedResponse) = sender(query, response=None, useQueue=False)
2305 self.assertEqual(receivedResponse, response)
2306
2307class TestCachingCollisionNoECSParsing(DNSDistTest):
2308
2309 _config_template = """
2310 pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
2311 getPool(""):setCache(pc)
2312 newServer{address="127.0.0.1:%d"}
2313 """
2314
2315 def testCacheCollisionNoECSParsing(self):
2316 """
2317 Cache: Collision with no ECS parsing
2318 """
2319 name = 'collision-no-ecs-parsing.cache.tests.powerdns.com.'
2320 ecso = clientsubnetoption.ClientSubnetOption('10.0.226.63', 32)
2321 query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
2322 query.flags = dns.flags.RD
2323 response = dns.message.make_response(query)
2324 rrset = dns.rrset.from_text(name,
2325 3600,
2326 dns.rdataclass.IN,
2327 dns.rdatatype.AAAA,
2328 '::1')
2329 response.answer.append(rrset)
2330
2331 # first query should to fill the cache
2332 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
2333 self.assertTrue(receivedQuery)
2334 self.assertTrue(receivedResponse)
2335 receivedQuery.id = query.id
2336 self.assertEqual(query, receivedQuery)
2337 self.assertEqual(receivedResponse, response)
2338
2339 # second query will hash to the same key, triggering a collision which
2340 # will not be detected because the qname, qtype, qclass and flags will
2341 # match and EDNS Client Subnet parsing has not been enabled
2342 ecso2 = clientsubnetoption.ClientSubnetOption('10.1.60.19', 32)
2343 query2 = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso2], payload=512)
2344 query2.flags = dns.flags.RD
2345 (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
2346 receivedResponse.id = response.id
2347 self.assertEqual(receivedResponse, response)
2348
2349class TestCachingCollisionWithECSParsing(DNSDistTest):
2350
2351 _config_template = """
2352 pc = newPacketCache(100, {maxTTL=86400, minTTL=1, temporaryFailureTTL=60, staleTTL=60, dontAge=false, numberOfShards=1, deferrableInsertLock=true, maxNegativeTTL=3600, parseECS=true})
2353 getPool(""):setCache(pc)
2354 newServer{address="127.0.0.1:%d"}
2355 """
2356
2357 def testCacheCollisionWithECSParsing(self):
2358 """
2359 Cache: Collision with ECS parsing
2360 """
2361 name = 'collision-with-ecs-parsing.cache.tests.powerdns.com.'
2362 ecso = clientsubnetoption.ClientSubnetOption('10.0.150.206', 32)
2363 query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
2364 query.flags = dns.flags.RD
2365 response = dns.message.make_response(query)
2366 rrset = dns.rrset.from_text(name,
2367 3600,
2368 dns.rdataclass.IN,
2369 dns.rdatatype.AAAA,
2370 '::1')
2371 response.answer.append(rrset)
2372
2373 # first query should to fill the cache
2374 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
2375 self.assertTrue(receivedQuery)
2376 self.assertTrue(receivedResponse)
2377 receivedQuery.id = query.id
2378 self.assertEqual(query, receivedQuery)
2379 self.assertEqual(receivedResponse, response)
2380
2381 # second query will hash to the same key, triggering a collision which
2382 # _will_ be detected this time because the qname, qtype, qclass and flags will
2383 # match but EDNS Client Subnet parsing is now enabled and will detect the issue
2384 ecso2 = clientsubnetoption.ClientSubnetOption('10.0.212.51', 32)
2385 query2 = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso2], payload=512)
2386 query2.flags = dns.flags.RD
2387 response2 = dns.message.make_response(query2)
2388 rrset = dns.rrset.from_text(name,
2389 3600,
2390 dns.rdataclass.IN,
2391 dns.rdatatype.AAAA,
2392 '2001:DB8::1')
2393 response2.answer.append(rrset)
2394 (receivedQuery, receivedResponse) = self.sendUDPQuery(query2, response2)
2395 self.assertEqual(receivedResponse, response2)
2396
2397class TestCachingScopeZero(DNSDistTest):
2398
2399 _config_template = """
2400 -- Be careful to enable ECS parsing in the packet cache, otherwise scope zero is disabled
2401 pc = newPacketCache(100, {maxTTL=86400, minTTL=1, temporaryFailureTTL=60, staleTTL=60, dontAge=false, numberOfShards=1, deferrableInsertLock=true, maxNegativeTTL=3600, parseECS=true})
2402 getPool(""):setCache(pc)
2403 newServer{address="127.0.0.1:%d", useClientSubnet=true}
2404 -- to simulate a second client coming from a different IP address,
2405 -- we will force the ECS value added to the query if RD is set (note that we need
2406 -- to unset it using rules before the first cache lookup)
2407 addAction(RDRule(), SetECSAction("192.0.2.1/32"))
2408 addAction(RDRule(), SetNoRecurseAction())
2409 """
2410
2411 def testScopeZero(self):
2412 """
2413 Cache: Test the scope-zero feature, backend returns a scope of zero
2414 """
2415 ttl = 600
2416 name = 'scope-zero.cache.tests.powerdns.com.'
2417 query = dns.message.make_query(name, 'AAAA', 'IN')
2418 query.flags &= ~dns.flags.RD
2419 ecso = clientsubnetoption.ClientSubnetOption('127.0.0.0', 24)
2420 expectedQuery = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
2421 expectedQuery.flags &= ~dns.flags.RD
2422 ecsoResponse = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24, 0)
2423 expectedResponse = dns.message.make_response(query)
2424 scopedResponse = dns.message.make_response(query)
2425 scopedResponse.use_edns(edns=True, payload=4096, options=[ecsoResponse])
2426 rrset = dns.rrset.from_text(name,
2427 ttl,
2428 dns.rdataclass.IN,
2429 dns.rdatatype.AAAA,
2430 '::1')
2431 scopedResponse.answer.append(rrset)
2432 expectedResponse.answer.append(rrset)
2433
2434 for method in ("sendUDPQuery", "sendTCPQuery"):
2435 sender = getattr(self, method)
2436 (receivedQuery, receivedResponse) = sender(query, scopedResponse)
2437 receivedQuery.id = expectedQuery.id
2438 self.checkMessageEDNSWithECS(expectedQuery, receivedQuery)
2439 self.checkMessageNoEDNS(receivedResponse, expectedResponse)
2440
2441 # next query should hit the cache, nothing special about that
2442 for method in ("sendUDPQuery", "sendTCPQuery"):
2443 sender = getattr(self, method)
2444 (_, receivedResponse) = sender(query, response=None, useQueue=False)
2445 self.checkMessageNoEDNS(receivedResponse, expectedResponse)
2446
2447 query = dns.message.make_query(name, 'AAAA', 'IN')
2448 query.flags &= dns.flags.RD
2449 # next query FROM A DIFFERENT CLIENT since RD is now set should STILL hit the cache
2450 for method in ("sendUDPQuery", "sendTCPQuery"):
2451 sender = getattr(self, method)
2452 (_, receivedResponse) = sender(query, response=None, useQueue=False)
2453 receivedResponse.id = expectedResponse.id
2454 self.checkMessageNoEDNS(receivedResponse, expectedResponse)
2455
2456 name = 'scope-zero-with-ecs.cache.tests.powerdns.com.'
2457 ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
2458 query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
2459 query.flags &= ~dns.flags.RD
2460 expectedQuery = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
2461 expectedQuery.flags &= ~dns.flags.RD
2462 expectedResponse = dns.message.make_response(query)
2463 expectedResponse.use_edns(edns=True, payload=4096, options=[ecsoResponse])
2464 expectedResponse.answer.append(rrset)
2465 scopedResponse = dns.message.make_response(query)
2466 scopedResponse.use_edns(edns=True, payload=4096, options=[ecsoResponse])
2467 scopedResponse.answer.append(rrset)
2468 # this query has ECS, it should NOT be able to use the scope-zero cached entry since the hash will be
2469 # different
2470 for method in ("sendUDPQuery", "sendTCPQuery"):
2471 sender = getattr(self, method)
2472 (receivedQuery, receivedResponse) = sender(query, scopedResponse)
2473 receivedQuery.id = expectedQuery.id
2474 self.checkMessageEDNSWithECS(expectedQuery, receivedQuery)
2475 self.checkMessageEDNSWithECS(receivedResponse, expectedResponse)
2476
2477 # it should still have been cached, though, so the next query should be a hit
2478 for method in ("sendUDPQuery", "sendTCPQuery"):
2479 sender = getattr(self, method)
2480 (_, receivedResponse) = sender(query, response=None, useQueue=False)
2481 self.checkMessageEDNSWithECS(receivedResponse, expectedResponse)
2482
2483 def testScopeNotZero(self):
2484 """
2485 Cache: Test the scope-zero feature, backend returns a scope of non-zero
2486 """
2487 ttl = 600
2488 name = 'scope-not-zero.cache.tests.powerdns.com.'
2489 query = dns.message.make_query(name, 'AAAA', 'IN')
2490 query.flags &= ~dns.flags.RD
2491 ecso = clientsubnetoption.ClientSubnetOption('127.0.0.0', 24)
2492 expectedQuery = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
2493 expectedQuery.flags &= ~dns.flags.RD
2494 ecso2 = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
2495 expectedQuery2 = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso2], payload=512)
2496 expectedQuery2.flags &= ~dns.flags.RD
2497 ecsoResponse = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24, 24)
2498 ecsoResponse2 = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32, 24)
2499 rrset = dns.rrset.from_text(name,
2500 ttl,
2501 dns.rdataclass.IN,
2502 dns.rdatatype.AAAA,
2503 '::1')
2504 expectedResponse = dns.message.make_response(query)
2505 expectedResponse.answer.append(rrset)
2506 scopedResponse = dns.message.make_response(query)
2507 scopedResponse.use_edns(edns=True, payload=4096, options=[ecsoResponse])
2508 scopedResponse.answer.append(rrset)
2509 scopedResponse2 = dns.message.make_response(query)
2510 scopedResponse2.use_edns(edns=True, payload=4096, options=[ecsoResponse2])
2511 scopedResponse2.answer.append(rrset)
2512
2513 for method in ("sendUDPQuery", "sendTCPQuery"):
2514 sender = getattr(self, method)
2515 (receivedQuery, receivedResponse) = sender(query, scopedResponse)
2516 receivedQuery.id = expectedQuery.id
2517 self.checkMessageEDNSWithECS(expectedQuery, receivedQuery)
2518 self.checkMessageNoEDNS(receivedResponse, expectedResponse)
2519
2520 # next query should hit the cache, nothing special about that
2521 for method in ("sendUDPQuery", "sendTCPQuery"):
2522 sender = getattr(self, method)
2523 (_, receivedResponse) = sender(query, response=None, useQueue=False)
2524 self.checkMessageNoEDNS(receivedResponse, expectedResponse)
2525
2526 query = dns.message.make_query(name, 'AAAA', 'IN')
2527 query.flags &= dns.flags.RD
2528 expectedResponse = dns.message.make_response(query)
2529 expectedResponse.answer.append(rrset)
2530 # next query FROM A DIFFERENT CLIENT since RD is now set should NOT hit the cache
2531 for method in ("sendUDPQuery", "sendTCPQuery"):
2532 sender = getattr(self, method)
2533 (receivedQuery, receivedResponse) = sender(query, scopedResponse2)
2534 receivedQuery.id = expectedQuery2.id
2535 self.checkMessageEDNSWithECS(expectedQuery2, receivedQuery)
2536 self.checkMessageNoEDNS(receivedResponse, expectedResponse)
2537
2538 def testNoECS(self):
2539 """
2540 Cache: Test the scope-zero feature, backend returns no ECS at all
2541 """
2542 ttl = 600
2543 name = 'scope-zero-no-ecs.cache.tests.powerdns.com.'
2544 query = dns.message.make_query(name, 'AAAA', 'IN')
2545 query.flags &= ~dns.flags.RD
2546 ecso = clientsubnetoption.ClientSubnetOption('127.0.0.0', 24)
2547 expectedQuery = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
2548 expectedQuery.flags &= ~dns.flags.RD
2549 ecso2 = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
2550 expectedQuery2 = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso2], payload=512)
2551 expectedQuery2.flags &= ~dns.flags.RD
2552 rrset = dns.rrset.from_text(name,
2553 ttl,
2554 dns.rdataclass.IN,
2555 dns.rdatatype.AAAA,
2556 '::1')
2557 response = dns.message.make_response(query)
2558 response.answer.append(rrset)
2559
2560 for method in ("sendUDPQuery", "sendTCPQuery"):
2561 sender = getattr(self, method)
2562 (receivedQuery, receivedResponse) = sender(query, response)
2563 receivedQuery.id = expectedQuery.id
2564 self.checkMessageEDNSWithECS(expectedQuery, receivedQuery)
2565 self.checkMessageNoEDNS(receivedResponse, response)
2566
2567 # next query should hit the cache, nothing special about that
2568 for method in ("sendUDPQuery", "sendTCPQuery"):
2569 sender = getattr(self, method)
2570 (_, receivedResponse) = sender(query, response=None, useQueue=False)
2571 self.checkMessageNoEDNS(receivedResponse, response)
2572
2573 query = dns.message.make_query(name, 'AAAA', 'IN')
2574 query.flags &= dns.flags.RD
2575 response = dns.message.make_response(query)
2576 response.answer.append(rrset)
2577 # next query FROM A DIFFERENT CLIENT since RD is now set should NOT hit the cache
2578 for method in ("sendUDPQuery", "sendTCPQuery"):
2579 sender = getattr(self, method)
2580 (receivedQuery, receivedResponse) = sender(query, response)
2581 receivedQuery.id = expectedQuery2.id
2582 self.checkMessageEDNSWithECS(expectedQuery2, receivedQuery)
2583 self.checkMessageNoEDNS(receivedResponse, response)
2584
2585class TestCachingScopeZeroButNoSubnetcheck(DNSDistTest):
2586
2587 _config_template = """
2588 -- We disable ECS parsing in the packet cache, meaning scope zero is disabled
2589 pc = newPacketCache(100, {maxTTL=86400, minTTL=1, temporaryFailureTTL=60, staleTTL=60, dontAge=false, numberOfShards=1, deferrableInsertLock=true, maxNegativeTTL=3600, parseECS=false})
2590 getPool(""):setCache(pc)
2591 newServer{address="127.0.0.1:%d", useClientSubnet=true}
2592 -- to simulate a second client coming from a different IP address,
2593 -- we will force the ECS value added to the query if RD is set (note that we need
2594 -- to unset it using rules before the first cache lookup)
2595 addAction(RDRule(), SetECSAction("192.0.2.1/32"))
2596 addAction(RDRule(), SetNoRecurseAction())
2597 """
2598
2599 def testScopeZero(self):
2600 """
2601 Cache: Test that the scope-zero feature is disabled when ECS parsing is not enabled in the cache
2602 """
2603 ttl = 600
2604 name = 'scope-zero-no-subnet.cache.tests.powerdns.com.'
2605 query = dns.message.make_query(name, 'AAAA', 'IN')
2606 query.flags &= ~dns.flags.RD
2607 ecso = clientsubnetoption.ClientSubnetOption('127.0.0.0', 24)
2608 expectedQuery = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
2609 expectedQuery.flags &= ~dns.flags.RD
2610 ecso2 = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
2611 expectedQuery2 = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso2], payload=512)
2612 expectedQuery2.flags &= ~dns.flags.RD
2613 ecsoResponse = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24, 0)
2614 expectedResponse = dns.message.make_response(query)
2615 scopedResponse = dns.message.make_response(query)
2616 scopedResponse.use_edns(edns=True, payload=4096, options=[ecsoResponse])
2617 rrset = dns.rrset.from_text(name,
2618 ttl,
2619 dns.rdataclass.IN,
2620 dns.rdatatype.AAAA,
2621 '::1')
2622 scopedResponse.answer.append(rrset)
2623 expectedResponse.answer.append(rrset)
2624
2625 for method in ("sendUDPQuery", "sendTCPQuery"):
2626 sender = getattr(self, method)
2627 (receivedQuery, receivedResponse) = sender(query, scopedResponse)
2628 receivedQuery.id = expectedQuery.id
2629 self.checkMessageEDNSWithECS(expectedQuery, receivedQuery)
2630 self.checkMessageNoEDNS(receivedResponse, expectedResponse)
2631
2632 # next query should hit the cache, nothing special about that
2633 for method in ("sendUDPQuery", "sendTCPQuery"):
2634 sender = getattr(self, method)
2635 (_, receivedResponse) = sender(query, response=None, useQueue=False)
2636 self.checkMessageNoEDNS(receivedResponse, expectedResponse)
2637
2638 query = dns.message.make_query(name, 'AAAA', 'IN')
2639 query.flags &= dns.flags.RD
2640 response = dns.message.make_response(query)
2641 response.answer.append(rrset)
2642 # next query FROM A DIFFERENT CLIENT since RD is now set should NOT hit the cache
2643 for method in ("sendUDPQuery", "sendTCPQuery"):
2644 sender = getattr(self, method)
2645 (receivedQuery, receivedResponse) = sender(query, response)
2646 receivedQuery.id = expectedQuery2.id
2647 self.checkMessageEDNSWithECS(expectedQuery2, receivedQuery)
2648 self.checkMessageNoEDNS(receivedResponse, response)
2649
2650class TestCachingAlteredHeader(DNSDistTest):
2651
2652 _config_template = """
2653 pc = newPacketCache(100)
2654 getPool(""):setCache(pc)
2655 addAction("cache-set-rd.tests.powerdns.com.", SetNoRecurseAction())
2656 newServer{address="127.0.0.1:%d"}
2657 """
2658
2659 def testCachingAlteredHeader(self):
2660 """
2661 Cache: The header has been altered via a rule
2662 """
2663 name = 'cache-set-rd.tests.powerdns.com.'
2664 query = dns.message.make_query(name, 'A', 'IN')
2665 # the query reaching the backend will never have the RD flag set
2666 expectedQuery = dns.message.make_query(name, 'A', 'IN')
2667 expectedQuery.flags &= ~dns.flags.RD
2668 response = dns.message.make_response(query)
2669 response.flags &= ~dns.flags.RD
2670 rrset = dns.rrset.from_text(name,
2671 60,
2672 dns.rdataclass.IN,
2673 dns.rdatatype.A,
2674 '192.0.2.1')
2675 response.answer.append(rrset)
2676
2677 # first query has RD=1
2678 query.flags |= dns.flags.RD
2679 expectedResponse = dns.message.make_response(query)
2680 rrset = dns.rrset.from_text(name,
2681 60,
2682 dns.rdataclass.IN,
2683 dns.rdatatype.A,
2684 '192.0.2.1')
2685 expectedResponse.answer.append(rrset)
2686
2687 for method in ("sendUDPQuery", "sendTCPQuery"):
2688 sender = getattr(self, method)
2689 (receivedQuery, receivedResponse) = sender(query, response)
2690 self.assertTrue(receivedQuery)
2691 self.assertTrue(receivedResponse)
2692 receivedQuery.id = expectedQuery.id
2693 self.assertEqual(expectedQuery, receivedQuery)
2694 self.assertEqual(receivedResponse, expectedResponse)
2695
2696 # next query should hit the cache
2697 for method in ("sendUDPQuery", "sendTCPQuery"):
2698 sender = getattr(self, method)
2699 (receivedQuery, receivedResponse) = sender(query, response=None, useQueue=False)
2700
2701 # same query with RD=0, should hit the cache as well
2702 query.flags &= ~dns.flags.RD
2703 expectedResponse = dns.message.make_response(query)
2704 rrset = dns.rrset.from_text(name,
2705 60,
2706 dns.rdataclass.IN,
2707 dns.rdatatype.A,
2708 '192.0.2.1')
2709 expectedResponse.answer.append(rrset)
2710 for method in ("sendUDPQuery", "sendTCPQuery"):
2711 sender = getattr(self, method)
2712 (receivedQuery, receivedResponse) = sender(query, response=None, useQueue=False)
2713 self.assertFalse(receivedQuery)
2714 self.assertTrue(receivedResponse)
2715 self.assertEqual(receivedResponse, expectedResponse)
2716
2717class TestCachingBackendSettingRD(DNSDistTest):
2718
2719 _config_template = """
2720 pc = newPacketCache(100)
2721 getPool(""):setCache(pc)
2722 newServer{address="127.0.0.1:%d"}
2723 """
2724
2725 def testCachingBackendSetRD(self):
2726 """
2727 Cache: The backend sets RD=1 in the response even if the query had RD=0
2728 """
2729 name = 'backend-sets-rd.tests.powerdns.com.'
2730 query = dns.message.make_query(name, 'A', 'IN')
2731 query.flags &= ~dns.flags.RD
2732 expectedQuery = dns.message.make_query(name, 'A', 'IN')
2733 expectedQuery.flags &= ~dns.flags.RD
2734 response = dns.message.make_response(query)
2735 response.flags |= dns.flags.RD
2736 rrset = dns.rrset.from_text(name,
2737 60,
2738 dns.rdataclass.IN,
2739 dns.rdatatype.A,
2740 '192.0.2.1')
2741 response.answer.append(rrset)
2742
2743 expectedResponse = dns.message.make_response(query)
2744 expectedResponse.flags &= ~dns.flags.RD
2745 rrset = dns.rrset.from_text(name,
2746 60,
2747 dns.rdataclass.IN,
2748 dns.rdatatype.A,
2749 '192.0.2.1')
2750 expectedResponse.answer.append(rrset)
2751
2752 for method in ("sendUDPQuery", "sendTCPQuery"):
2753 sender = getattr(self, method)
2754 (receivedQuery, receivedResponse) = sender(query, response)
2755 self.assertTrue(receivedQuery)
2756 self.assertTrue(receivedResponse)
2757 receivedQuery.id = expectedQuery.id
2758 self.assertEqual(expectedQuery, receivedQuery)
2759 self.assertEqual(receivedResponse, expectedResponse)
2760
2761 # exact same query should be cached
2762 for method in ("sendUDPQuery", "sendTCPQuery"):
2763 sender = getattr(self, method)
2764 (receivedQuery, receivedResponse) = sender(query, response=None, useQueue=False)
2765 self.assertFalse(receivedQuery)
2766 self.assertTrue(receivedResponse)
2767 self.assertEqual(receivedResponse, expectedResponse)
2768
2769 # same query with RD=1, should NOT hit the cache
2770 query.flags |= dns.flags.RD
2771 expectedResponse = dns.message.make_response(query)
2772 rrset = dns.rrset.from_text(name,
2773 60,
2774 dns.rdataclass.IN,
2775 dns.rdatatype.A,
2776 '192.0.2.1')
2777 expectedResponse.answer.append(rrset)
2778
2779 for method in ("sendUDPQuery", "sendTCPQuery"):
2780 sender = getattr(self, method)
2781 (receivedQuery, receivedResponse) = sender(query, response)
2782 self.assertTrue(receivedQuery)
2783 self.assertTrue(receivedResponse)
2784 self.assertEqual(receivedResponse, expectedResponse)
2785
2786class TestAPICache(DNSDistTest):
2787 _webTimeout = 2.0
2788 _webServerPort = pickAvailablePort()
2789 _webServerBasicAuthPassword = 'secret'
2790 _webServerBasicAuthPasswordHashed = '$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM='
2791 _webServerAPIKey = 'apisecret'
2792 _webServerAPIKeyHashed = '$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso='
2793 _config_params = ['_testServerPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
2794 _config_template = """
2795 newServer{address="127.0.0.1:%s"}
2796 webserver("127.0.0.1:%s")
2797 setWebserverConfig({password="%s", apiKey="%s"})
2798 pc = newPacketCache(100)
2799 getPool(""):setCache(pc)
2800 getPool("pool-with-cache"):setCache(pc)
2801 getPool("pool-without-cache")
2802 """
2803
2804 def testCacheClearingViaAPI(self):
2805 """
2806 Cache: Clear cache via API
2807 """
2808 headers = {'x-api-key': self._webServerAPIKey}
2809 url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/cache'
2810 name = 'cache-api.cache.tests.powerdns.com.'
2811 query = dns.message.make_query(name, 'AAAA', 'IN')
2812 response = dns.message.make_response(query)
2813 rrset = dns.rrset.from_text(name,
2814 3600,
2815 dns.rdataclass.IN,
2816 dns.rdatatype.AAAA,
2817 '::1')
2818 response.answer.append(rrset)
2819
2820 # first query to fill the cache
2821 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
2822 self.assertTrue(receivedQuery)
2823 self.assertTrue(receivedResponse)
2824 receivedQuery.id = query.id
2825 self.assertEqual(query, receivedQuery)
2826 self.assertEqual(receivedResponse, response)
2827
2828 # second query should be a hit
2829 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
2830 self.assertEqual(receivedResponse, response)
2831
2832 # GET should on the cache API should yield a 400
2833 r = requests.get(url + '?pool=pool-without-cache&name=cache-api.cache.tests.powerdns.com.&type=AAAA', headers=headers, timeout=self._webTimeout)
2834 self.assertEqual(r.status_code, 400)
2835
2836 # different pool
2837 r = requests.delete(url + '?pool=pool-without-cache&name=cache-api.cache.tests.powerdns.com.&type=AAAA', headers=headers, timeout=self._webTimeout)
2838 self.assertEqual(r.status_code, 404)
2839
2840 # no 'pool'
2841 r = requests.delete(url + '?name=cache-api.cache.tests.powerdns.com.&type=AAAA', headers=headers, timeout=self._webTimeout)
2842 self.assertEqual(r.status_code, 400)
2843
2844 # no 'name'
2845 r = requests.delete(url + '?pool=pool-without-cache&type=AAAA', headers=headers, timeout=self._webTimeout)
2846 self.assertEqual(r.status_code, 400)
2847
2848 # invalid name (label is too long)
2849 r = requests.delete(url + '?pool=&name=' + 'a'*65, headers=headers, timeout=self._webTimeout)
2850 self.assertEqual(r.status_code, 400)
2851
2852 # different name
2853 r = requests.delete(url + '?pool=&name=not-cache-api.cache.tests.powerdns.com.', headers=headers, timeout=self._webTimeout)
2854 self.assertTrue(r)
2855 self.assertEqual(r.status_code, 200)
2856 content = r.json()
2857 self.assertIn('count', content)
2858 self.assertEqual(int(content['count']), 0)
2859
2860 # different type
2861 r = requests.delete(url + '?pool=&name=cache-api.cache.tests.powerdns.com.&type=A', headers=headers, timeout=self._webTimeout)
2862 self.assertTrue(r)
2863 self.assertEqual(r.status_code, 200)
2864 content = r.json()
2865 self.assertIn('count', content)
2866 self.assertEqual(int(content['count']), 0)
2867
2868 # should still be a hit
2869 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
2870 self.assertEqual(receivedResponse, response)
2871
2872 # remove
2873 r = requests.delete(url + '?pool=&name=cache-api.cache.tests.powerdns.com.&type=AAAA', headers=headers, timeout=self._webTimeout)
2874 self.assertTrue(r)
2875 self.assertEqual(r.status_code, 200)
2876 content = r.json()
2877 self.assertIn('count', content)
2878 self.assertEqual(int(content['count']), 1)
2879
2880 # should be a miss
2881 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
2882 self.assertTrue(receivedQuery)
2883 self.assertTrue(receivedResponse)
2884 receivedQuery.id = query.id
2885 self.assertEqual(query, receivedQuery)
2886 self.assertEqual(receivedResponse, response)
2887
2888 # remove all types
2889 r = requests.delete(url + '?pool=&name=cache-api.cache.tests.powerdns.com.', headers=headers, timeout=self._webTimeout)
2890 self.assertTrue(r)
2891 self.assertEqual(r.status_code, 200)
2892 content = r.json()
2893 self.assertIn('count', content)
2894 self.assertEqual(int(content['count']), 1)
2895
2896 # should be a miss
2897 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
2898 self.assertTrue(receivedQuery)
2899 self.assertTrue(receivedResponse)
2900 receivedQuery.id = query.id
2901 self.assertEqual(query, receivedQuery)
2902 self.assertEqual(receivedResponse, response)
2903
2904 # suffix removal
2905 r = requests.delete(url + '?pool=&name=cache.tests.powerdns.com.&suffix=true', headers=headers, timeout=self._webTimeout)
2906 self.assertTrue(r)
2907 self.assertEqual(r.status_code, 200)
2908 content = r.json()
2909 self.assertIn('count', content)
2910 self.assertEqual(int(content['count']), 1)
2911
2912 # should be a miss
2913 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
2914 self.assertTrue(receivedQuery)
2915 self.assertTrue(receivedResponse)
2916 receivedQuery.id = query.id
2917 self.assertEqual(query, receivedQuery)
2918 self.assertEqual(receivedResponse, response)
2919
2920class TestCachingOfVeryLargeAnswers(DNSDistTest):
2921
2922 _config_template = """
2923 pc = newPacketCache(100, {maxTTL=86400, minTTL=1, maximumEntrySize=8192})
2924 getPool(""):setCache(pc)
2925 newServer{address="127.0.0.1:%d"}
2926 """
2927
2928 def testVeryLargeAnswer(self):
2929 """
2930 Cache: Check that we can cache (and retrieve) VERY large answers
2931
2932 We should be able to get answers as large as 8192 bytes this time
2933 """
2934 numberOfQueries = 10
2935 name = 'very-large-answer.cache.tests.powerdns.com.'
2936 query = dns.message.make_query(name, 'TXT', 'IN')
2937 response = dns.message.make_response(query)
2938 # we prepare a large answer
2939 content = ''
2940 for i in range(31):
2941 if len(content) > 0:
2942 content = content + ' '
2943 content = content + 'A' * 255
2944 # pad up to 8192
2945 content = content + ' ' + 'B' * 183
2946
2947 rrset = dns.rrset.from_text(name,
2948 3600,
2949 dns.rdataclass.IN,
2950 dns.rdatatype.TXT,
2951 content)
2952 response.answer.append(rrset)
2953 self.assertEqual(len(response.to_wire()), 8192)
2954
2955 # # first query to fill the cache, over TCP
2956 (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
2957 self.assertTrue(receivedQuery)
2958 self.assertTrue(receivedResponse)
2959 receivedQuery.id = query.id
2960 self.assertEqual(query, receivedQuery)
2961 self.assertEqual(receivedResponse, response)
2962
2963 for _ in range(numberOfQueries):
2964 (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
2965 self.assertEqual(receivedResponse, response)
2966
2967 total = 0
2968 for key in self._responsesCounter:
2969 total += self._responsesCounter[key]
2970 TestCachingOfVeryLargeAnswers._responsesCounter[key] = 0
2971
2972 self.assertEqual(total, 1)
2973
2974 # UDP should not be cached, dnsdist has a hard limit to 4096 bytes for UDP
2975 # actually we will never get an answer, because dnsdist will not be able to get it from the backend
2976 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
2977 self.assertTrue(receivedQuery)
2978 self.assertFalse(receivedResponse)
2979 receivedQuery.id = query.id
2980 self.assertEqual(query, receivedQuery)