5 import clientsubnetoption
8 from dnsdisttests
import DNSDistTest
, pickAvailablePort
10 class TestCaching(DNSDistTest
):
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)
19 return DNSAction.None, ""
21 addAction("nocachevialua.cache.tests.powerdns.com.", LuaAction(skipViaLua))
22 newServer{address="127.0.0.1:%d"}
27 Cache: Served from cache
29 dnsdist is configured to cache entries, we are sending several
30 identical requests and checking that the backend only receive
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
,
42 response
.answer
.append(rrset
)
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
)
52 for _
in range(numberOfQueries
):
53 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
54 self
.assertEqual(receivedResponse
, response
)
57 for key
in self
._responsesCounter
:
58 total
+= self
._responsesCounter
[key
]
59 TestCaching
._responsesCounter
[key
] = 0
61 self
.assertEqual(total
, 1)
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
)
72 for _
in range(numberOfQueries
):
73 (_
, receivedResponse
) = self
.sendTCPQuery(query
, response
=None, useQueue
=False)
74 self
.assertEqual(receivedResponse
, response
)
77 for key
in self
._responsesCounter
:
78 total
+= self
._responsesCounter
[key
]
79 TestCaching
._responsesCounter
[key
] = 0
81 self
.assertEqual(total
, 1)
83 def testDOCached(self
):
85 Cache: Served from cache, query has DO bit set
87 dnsdist is configured to cache entries, we are sending several
88 identical requests and checking that the backend only receive
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
,
100 response
.answer
.append(rrset
)
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
)
110 for _
in range(numberOfQueries
):
111 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
112 self
.assertEqual(receivedResponse
, response
)
115 for key
in self
._responsesCounter
:
116 total
+= self
._responsesCounter
[key
]
117 TestCaching
._responsesCounter
[key
] = 0
119 self
.assertEqual(total
, 1)
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
)
130 for _
in range(numberOfQueries
):
131 (_
, receivedResponse
) = self
.sendTCPQuery(query
, response
=None, useQueue
=False)
132 self
.assertEqual(receivedResponse
, response
)
135 for key
in self
._responsesCounter
:
136 total
+= self
._responsesCounter
[key
]
137 TestCaching
._responsesCounter
[key
] = 0
139 self
.assertEqual(total
, 1)
141 def testSkipCache(self
):
143 Cache: SetSkipCacheAction
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.
148 name
= 'nocache.cache.tests.powerdns.com.'
150 query
= dns
.message
.make_query(name
, 'AAAA', 'IN')
151 response
= dns
.message
.make_response(query
)
152 rrset
= dns
.rrset
.from_text(name
,
157 response
.answer
.append(rrset
)
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
)
169 for key
in self
._responsesCounter
:
170 value
= self
._responsesCounter
[key
]
171 self
.assertEqual(value
, numberOfQueries
)
173 def testSkipCacheViaLua(self
):
175 Cache: SkipCache via Lua
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.
180 name
= 'nocachevialua.cache.tests.powerdns.com.'
182 query
= dns
.message
.make_query(name
, 'AAAA', 'IN')
183 response
= dns
.message
.make_response(query
)
184 rrset
= dns
.rrset
.from_text(name
,
189 response
.answer
.append(rrset
)
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
)
201 for key
in self
._responsesCounter
:
202 value
= self
._responsesCounter
[key
]
203 self
.assertEqual(value
, numberOfQueries
)
205 def testSkipCacheResponse(self
):
207 Cache: SetSkipCacheResponseAction
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.
212 name
= 'nocache-response.cache.tests.powerdns.com.'
214 query
= dns
.message
.make_query(name
, 'AAAA', 'IN')
215 response
= dns
.message
.make_response(query
)
216 rrset
= dns
.rrset
.from_text(name
,
221 response
.answer
.append(rrset
)
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
)
233 for key
in self
._responsesCounter
:
234 value
= self
._responsesCounter
[key
]
235 self
.assertEqual(value
, numberOfQueries
)
237 def testAXFRResponse(self
):
239 Cache: AXFR should not be cached
241 dnsdist should not cache responses to AXFR queries.
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
,
250 'ns.' + name
+ ' hostmaster.' + name
+ ' 1 3600 3600 3600 60')
251 response
.answer
.append(soa
)
252 response
.answer
.append(dns
.rrset
.from_text(name
,
257 response
.answer
.append(soa
)
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
)
270 for key
in self
._responsesCounter
:
271 value
= self
._responsesCounter
[key
]
272 self
.assertEqual(value
, numberOfQueries
)
274 def testIXFRResponse(self
):
276 Cache: IXFR should not be cached
278 dnsdist should not cache responses to IXFR queries.
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
,
287 'ns.' + name
+ ' hostmaster.' + name
+ ' 1 3600 3600 3600 60')
288 response
.answer
.append(soa
)
289 response
.answer
.append(dns
.rrset
.from_text(name
,
294 response
.answer
.append(soa
)
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
)
307 for key
in self
._responsesCounter
:
308 value
= self
._responsesCounter
[key
]
309 self
.assertEqual(value
, numberOfQueries
)
311 def testCacheExpiration(self
):
313 Cache: Cache expiration
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.
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
,
330 response
.answer
.append(rrset
)
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
)
341 # next queries should hit the cache
342 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
343 self
.assertEqual(receivedResponse
, response
)
345 # now we wait a bit for the cache entry to expire
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
)
357 # following queries should hit the cache again
358 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
359 self
.assertEqual(receivedResponse
, response
)
362 for key
in self
._responsesCounter
:
363 total
+= self
._responsesCounter
[key
]
365 self
.assertEqual(total
, misses
)
367 def testCacheExpirationDifferentSets(self
):
369 Cache: Cache expiration with different sets
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.
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
,
386 'cname.cacheexpirationdifferentsets.cache.tests.powerdns.com.')
387 response
.answer
.append(rrset
)
388 rrset
= dns
.rrset
.from_text('cname.cacheexpirationdifferentsets.cache.tests.powerdns.com.',
393 response
.additional
.append(rrset
)
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
)
404 # next queries should hit the cache
405 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
406 self
.assertEqual(receivedResponse
, response
)
408 # now we wait a bit for the cache entry to expire
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
)
420 # following queries should hit the cache again
421 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
422 self
.assertEqual(receivedResponse
, response
)
425 for key
in self
._responsesCounter
:
426 total
+= self
._responsesCounter
[key
]
428 self
.assertEqual(total
, misses
)
430 def testCacheDecreaseTTL(self
):
432 Cache: Cache decreases TTL
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.
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
,
447 response
.answer
.append(rrset
)
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
)
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
)
464 # now we wait a bit for the TTL to decrease
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
)
474 for key
in self
._responsesCounter
:
475 total
+= self
._responsesCounter
[key
]
477 self
.assertEqual(total
, misses
)
479 def testCacheDifferentCase(self
):
481 Cache: Cache matches different case
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
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
,
499 response
.answer
.append(rrset
)
500 differentCaseResponse
.answer
.append(rrset
)
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
)
510 # different case query should still hit the cache
511 (_
, receivedResponse
) = self
.sendUDPQuery(differentCaseQuery
, response
=None, useQueue
=False)
512 self
.assertEqual(receivedResponse
, differentCaseResponse
)
514 def testLargeAnswer(self
):
516 Cache: Check that we can cache (and retrieve) large answers
518 We should be able to get answers as large as 4096 bytes
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
528 content
= content
+ ', '
529 content
= content
+ (str(i
)*50)
531 content
= content
+ 'A'*42
533 rrset
= dns
.rrset
.from_text(name
,
538 response
.answer
.append(rrset
)
539 self
.assertEqual(len(response
.to_wire()), 4096)
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
)
549 for _
in range(numberOfQueries
):
550 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
551 self
.assertEqual(receivedResponse
, response
)
554 for key
in self
._responsesCounter
:
555 total
+= self
._responsesCounter
[key
]
556 TestCaching
._responsesCounter
[key
] = 0
558 self
.assertEqual(total
, 1)
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
)
569 for _
in range(numberOfQueries
):
570 (_
, receivedResponse
) = self
.sendTCPQuery(query
, response
=None, useQueue
=False)
571 self
.assertEqual(receivedResponse
, response
)
574 for key
in self
._responsesCounter
:
575 total
+= self
._responsesCounter
[key
]
576 TestCaching
._responsesCounter
[key
] = 0
578 self
.assertEqual(total
, 1)
580 def testCacheDifferentCookies(self
):
582 Cache: The content of cookies should be ignored by the cache
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
,
594 response
.answer
.append(rrset
)
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
)
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
)
611 def testCacheCookies(self
):
613 Cache: A query with a cookie should not match one without any cookie
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
,
625 response
.answer
.append(rrset
)
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
)
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
,
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
)
651 def testCacheSameCookieDifferentECS(self
):
653 Cache: The content of cookies should be ignored by the cache but not the ECS one
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
,
666 response
.answer
.append(rrset
)
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
)
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
,
684 response
.answer
.append(rrset
)
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
)
693 class TestCachingHashingOptions(DNSDistTest
):
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"}
701 def testCacheDifferentECSSameCookie(self
):
703 Cache: ECS should be ignored by the cache even if cookie is present
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
,
716 response
.answer
.append(rrset
)
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
)
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
)
734 class TestCachingHashingCookies(DNSDistTest
):
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"}
742 def testCached(self
):
744 Cache: Served from cache
746 dnsdist is configured to cache entries, we are sending several
747 identical requests and checking that the backend only receive
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
,
759 response
.answer
.append(rrset
)
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
)
769 for _
in range(numberOfQueries
):
770 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
771 self
.assertEqual(receivedResponse
, response
)
774 for key
in self
._responsesCounter
:
775 total
+= self
._responsesCounter
[key
]
776 TestCaching
._responsesCounter
[key
] = 0
778 self
.assertEqual(total
, 1)
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
)
789 for _
in range(numberOfQueries
):
790 (_
, receivedResponse
) = self
.sendTCPQuery(query
, response
=None, useQueue
=False)
791 self
.assertEqual(receivedResponse
, response
)
794 for key
in self
._responsesCounter
:
795 total
+= self
._responsesCounter
[key
]
796 TestCaching
._responsesCounter
[key
] = 0
798 self
.assertEqual(total
, 1)
801 def testCacheDifferentCookies(self
):
803 Cache: The content of cookies should NOT be ignored by the cache (cookieHashing is set)
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
,
815 response
.answer
.append(rrset
)
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
)
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
,
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
)
843 def testCacheCookies(self
):
845 Cache: A query with a cookie should not match one without any cookie (cookieHashing=true)
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
,
857 response
.answer
.append(rrset
)
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
)
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
,
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
)
883 def testCacheSameCookieDifferentECS(self
):
885 Cache: The content of cookies should NOT be ignored by the cache (cookieHashing=true), even with ECS there
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
,
898 response
.answer
.append(rrset
)
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
)
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
,
916 response
.answer
.append(rrset
)
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
)
925 class TestTempFailureCacheTTLAction(DNSDistTest
):
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"}
935 def testTempFailureCacheTTLAction(self
):
937 Cache: When a TempFailure TTL is set, it should be honored
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.
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
)
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
)
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
)
962 # now we wait a bit for the Failure-Cache TTL to expire
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
)
971 class TestCachingWithExistingEDNS(DNSDistTest
):
973 _config_template
= """
974 pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
975 getPool(""):setCache(pc)
976 newServer{address="127.0.0.1:%d"}
978 def testCacheWithEDNS(self
):
980 Cache: Cache should not match different EDNS value
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.
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
,
995 response
.answer
.append(rrset
)
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
)
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
,
1012 response
.answer
.append(rrset
)
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
)
1023 for key
in self
._responsesCounter
:
1024 total
+= self
._responsesCounter
[key
]
1026 self
.assertEqual(total
, misses
)
1028 class TestCachingCacheFull(DNSDistTest
):
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"}
1035 def testCacheFull(self
):
1037 Cache: No new entries are cached when the cache is full
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
,
1049 response
.answer
.append(rrset
)
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
)
1060 # next queries should hit the cache
1061 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
1062 self
.assertEqual(receivedResponse
, response
)
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
,
1073 response
.answer
.append(rrset
)
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
)
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
)
1094 for key
in self
._responsesCounter
:
1095 total
+= self
._responsesCounter
[key
]
1097 self
.assertEqual(total
, misses
)
1099 class TestCachingNoStale(DNSDistTest
):
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)
1108 controlSocket("127.0.0.1:%d")
1109 newServer{address="127.0.0.1:%d"}
1111 def testCacheNoStale(self
):
1113 Cache: Cache entry, set backend down, we should not get a stale entry
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
,
1125 response
.answer
.append(rrset
)
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
)
1135 # next queries should hit the cache
1136 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
1137 self
.assertEqual(receivedResponse
, response
)
1139 # ok, we mark the backend as down
1140 self
.sendConsoleCommand("getServer(0):setDown()")
1141 # and we wait for the entry to expire
1144 # we should NOT get a cached, stale, entry
1145 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
1146 self
.assertEqual(receivedResponse
, None)
1149 class TestCachingStale(DNSDistTest
):
1151 _consoleKey
= DNSDistTest
.generateConsoleKey()
1152 _consoleKeyB64
= base64
.b64encode(_consoleKey
).decode('ascii')
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)
1160 controlSocket("127.0.0.1:%d")
1161 newServer{address="127.0.0.1:%d"}
1163 def testCacheStale(self
):
1165 Cache: Cache entry, set backend down, get stale entry
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
,
1178 response
.answer
.append(rrset
)
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
)
1189 # next queries should hit the cache
1190 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
1191 self
.assertEqual(receivedResponse
, response
)
1193 # ok, we mark the backend as down
1194 self
.sendConsoleCommand("getServer(0):setDown()")
1195 # and we wait for the entry to expire
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
)
1205 for key
in self
._responsesCounter
:
1206 total
+= self
._responsesCounter
[key
]
1208 self
.assertEqual(total
, misses
)
1210 class TestCachingStaleExpunged(DNSDistTest
):
1212 _consoleKey
= DNSDistTest
.generateConsoleKey()
1213 _consoleKeyB64
= base64
.b64encode(_consoleKey
).decode('ascii')
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)
1225 controlSocket("127.0.0.1:%d")
1226 newServer{address="127.0.0.1:%d"}
1228 def testCacheStale(self
):
1230 Cache: Cache entry, set backend down, wait for the cache cleaning to run and remove the entry, get no entry
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
,
1243 response
.answer
.append(rrset
)
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
)
1253 self
.assertEqual(int(self
.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"misses\"]").strip("\n")), misses
+ drops
)
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)
1262 # ok, we mark the backend as down
1263 self
.sendConsoleCommand("getServer(0):setDown()")
1264 # and we wait for the entry to expire
1266 # wait a bit more to be sure that the cache cleaning algo has been run
1268 # the cache should be empty now
1269 self
.assertEqual(int(self
.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"entries\"]").strip("\n")), 0)
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)
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)
1280 for key
in self
._responsesCounter
:
1281 total
+= self
._responsesCounter
[key
]
1283 self
.assertEqual(total
, misses
)
1285 class TestCachingStaleExpungePrevented(DNSDistTest
):
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)
1299 controlSocket("127.0.0.1:%d")
1300 newServer{address="127.0.0.1:%d"}
1302 def testCacheStale(self
):
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
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
,
1316 response
.answer
.append(rrset
)
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
)
1326 self
.assertEqual(int(self
.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"misses\"]").strip("\n")), 1)
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)
1335 # ok, we mark the backend as down
1336 self
.sendConsoleCommand("getServer(0):setDown()")
1337 # and we wait for the entry to expire
1339 # wait a bit more to be sure that the cache cleaning algo has been run
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)
1345 # we should get a HIT
1346 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
1347 self
.assertEqual(receivedResponse
, response
)
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)
1353 for key
in self
._responsesCounter
:
1354 total
+= self
._responsesCounter
[key
]
1356 self
.assertEqual(total
, misses
)
1358 class TestCacheManagement(DNSDistTest
):
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)
1367 controlSocket("127.0.0.1:%d")
1368 newServer{address="127.0.0.1:%d"}
1370 def testCacheExpunge(self
):
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
,
1385 response
.answer
.append(rrset
)
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
)
1396 # next queries should hit the cache
1397 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
1398 self
.assertEqual(receivedResponse
, response
)
1400 # remove cached entries
1401 self
.sendConsoleCommand("getPool(\"\"):getCache():expunge(0)")
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
)
1412 # next queries should hit the cache again
1413 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
1414 self
.assertEqual(receivedResponse
, response
)
1417 for key
in self
._responsesCounter
:
1418 total
+= self
._responsesCounter
[key
]
1420 self
.assertEqual(total
, misses
)
1422 def testCacheExpungeByName(self
):
1424 Cache: Expunge by name
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
,
1437 response
.answer
.append(rrset
)
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
,
1447 response2
.answer
.append(rrset2
)
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
)
1458 # next queries should hit the cache
1459 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
1460 self
.assertEqual(receivedResponse
, response
)
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
)
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
)
1475 (_
, receivedResponse
) = self
.sendUDPQuery(query2
, response
=None, useQueue
=False)
1476 self
.assertEqual(receivedResponse
, response2
)
1478 # remove cached entries from name
1479 self
.sendConsoleCommand("getPool(\"\"):getCache():expungeByName(newDNSName(\"" + 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
)
1490 # next queries for name should hit the cache again
1491 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
1492 self
.assertEqual(receivedResponse
, response
)
1494 # queries for name2 should still hit the cache
1495 (_
, receivedResponse
) = self
.sendUDPQuery(query2
, response
=None, useQueue
=False)
1496 self
.assertEqual(receivedResponse
, response2
)
1499 for key
in self
._responsesCounter
:
1500 total
+= self
._responsesCounter
[key
]
1502 self
.assertEqual(total
, misses
)
1504 def testCacheExpungeByNameAndType(self
):
1506 Cache: Expunge by name and type
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
,
1519 response
.answer
.append(rrset
)
1521 query2
= dns
.message
.make_query(name
, 'AAAA', 'IN')
1522 response2
= dns
.message
.make_response(query2
)
1523 rrset2
= dns
.rrset
.from_text(name
,
1528 response2
.answer
.append(rrset2
)
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
)
1539 # next queries should hit the cache
1540 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
1541 self
.assertEqual(receivedResponse
, response
)
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
)
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
)
1556 (_
, receivedResponse
) = self
.sendUDPQuery(query2
, response
=None, useQueue
=False)
1557 self
.assertEqual(receivedResponse
, response2
)
1559 # remove cached entries from name A
1560 self
.sendConsoleCommand("getPool(\"\"):getCache():expungeByName(newDNSName(\"" + name
+ "\"), DNSQType.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
)
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
)
1575 # queries for name AAAA should still hit the cache
1576 (_
, receivedResponse
) = self
.sendUDPQuery(query2
, response
=None, useQueue
=False)
1577 self
.assertEqual(receivedResponse
, response2
)
1580 for key
in self
._responsesCounter
:
1581 total
+= self
._responsesCounter
[key
]
1582 self
.assertEqual(total
, misses
)
1584 def testCacheExpungeByNameAndSuffix(self
):
1586 Cache: Expunge by name
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
,
1599 response
.answer
.append(rrset
)
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
,
1609 response2
.answer
.append(rrset2
)
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
)
1620 # next queries should hit the cache
1621 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
1622 self
.assertEqual(receivedResponse
, response
)
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
)
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
)
1637 (_
, receivedResponse
) = self
.sendUDPQuery(query2
, response
=None, useQueue
=False)
1638 self
.assertEqual(receivedResponse
, response2
)
1640 # remove cached entries from name
1641 self
.sendConsoleCommand("getPool(\"\"):getCache():expungeByName(newDNSName(\"suffix.cache.tests.powerdns.com.\"), DNSQType.ANY, true)")
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
)
1652 # next queries for name should hit the cache again
1653 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
1654 self
.assertEqual(receivedResponse
, response
)
1656 # queries for name2 should still hit the cache
1657 (_
, receivedResponse
) = self
.sendUDPQuery(query2
, response
=None, useQueue
=False)
1658 self
.assertEqual(receivedResponse
, response2
)
1661 for key
in self
._responsesCounter
:
1662 total
+= self
._responsesCounter
[key
]
1664 self
.assertEqual(total
, misses
)
1666 def testCacheExpungeByNameAndTypeAndSuffix(self
):
1668 Cache: Expunge by name and type
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
,
1681 response
.answer
.append(rrset
)
1683 query2
= dns
.message
.make_query(name
, 'AAAA', 'IN')
1684 response2
= dns
.message
.make_response(query2
)
1685 rrset2
= dns
.rrset
.from_text(name
,
1690 response2
.answer
.append(rrset2
)
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
)
1701 # next queries should hit the cache
1702 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
1703 self
.assertEqual(receivedResponse
, response
)
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
)
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
)
1718 (_
, receivedResponse
) = self
.sendUDPQuery(query2
, response
=None, useQueue
=False)
1719 self
.assertEqual(receivedResponse
, response2
)
1721 # remove cached entries from name A
1722 self
.sendConsoleCommand("getPool(\"\"):getCache():expungeByName(newDNSName(\"suffixtype.cache.tests.powerdns.com.\"), DNSQType.A, true)")
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
)
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
)
1737 # queries for name AAAA should still hit the cache
1738 (_
, receivedResponse
) = self
.sendUDPQuery(query2
, response
=None, useQueue
=False)
1739 self
.assertEqual(receivedResponse
, response2
)
1742 for key
in self
._responsesCounter
:
1743 total
+= self
._responsesCounter
[key
]
1744 self
.assertEqual(total
, misses
)
1746 class TestCachingTTL(DNSDistTest
):
1748 _maxCacheTTL
= 86400
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"}
1756 def testCacheShortTTL(self
):
1758 Cache: Entries with a TTL shorter than minTTL
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
,
1771 response
.answer
.append(rrset
)
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
)
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
)
1796 for key
in self
._responsesCounter
:
1797 total
+= self
._responsesCounter
[key
]
1799 self
.assertEqual(total
, misses
)
1801 def testCacheNXWithNoRR(self
):
1803 Cache: NX with no RR
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
)
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
)
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
)
1831 for key
in self
._responsesCounter
:
1832 total
+= self
._responsesCounter
[key
]
1834 self
.assertEqual(total
, misses
)
1836 class TestCachingLongTTL(DNSDistTest
):
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"}
1845 def testCacheLongTTL(self
):
1847 Cache: Entries with a longer TTL than the maximum
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
,
1860 response
.answer
.append(rrset
)
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
)
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
)
1879 time
.sleep(self
._maxCacheTTL
+ 1)
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
)
1894 for key
in self
._responsesCounter
:
1895 total
+= self
._responsesCounter
[key
]
1897 self
.assertEqual(total
, misses
)
1899 class TestCachingFailureTTL(DNSDistTest
):
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"}
1908 def testCacheServFailTTL(self
):
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
)
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
)
1928 # next queries should hit the cache
1929 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
1930 self
.assertEqual(receivedResponse
, response
)
1932 time
.sleep(self
._failureCacheTTL
+ 1)
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
)
1945 for key
in self
._responsesCounter
:
1946 total
+= self
._responsesCounter
[key
]
1948 self
.assertEqual(total
, misses
)
1950 def testCacheRefusedTTL(self
):
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
)
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
)
1970 # next queries should hit the cache
1971 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
1972 self
.assertEqual(receivedResponse
, response
)
1974 time
.sleep(self
._failureCacheTTL
+ 1)
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
)
1987 for key
in self
._responsesCounter
:
1988 total
+= self
._responsesCounter
[key
]
1990 self
.assertEqual(total
, misses
)
1992 def testCacheHeaderOnlyRefusedTTL(self
):
1994 Cache: Header-Only Refused TTL
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
= []
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
)
2013 # next queries should hit the cache
2014 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
2015 self
.assertEqual(receivedResponse
, response
)
2017 time
.sleep(self
._failureCacheTTL
+ 1)
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
)
2030 for key
in self
._responsesCounter
:
2031 total
+= self
._responsesCounter
[key
]
2033 self
.assertEqual(total
, misses
)
2035 class TestCachingNegativeTTL(DNSDistTest
):
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"}
2045 def testCacheNegativeTTLNXDomain(self
):
2047 Cache: Negative TTL on NXDOMAIN
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
,
2059 'ns.' + name
+ ' hostmaster.' + name
+ ' 1 3600 3600 3600 60')
2060 response
.authority
.append(soa
)
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
)
2071 # next queries should hit the cache
2072 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
2073 self
.assertEqual(receivedResponse
, response
)
2075 time
.sleep(self
._negCacheTTL
+ 1)
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
)
2088 for key
in self
._responsesCounter
:
2089 total
+= self
._responsesCounter
[key
]
2091 self
.assertEqual(total
, misses
)
2093 def testCacheNegativeTTLNoData(self
):
2095 Cache: Negative TTL on NoData
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
,
2107 'ns.' + name
+ ' hostmaster.' + name
+ ' 1 3600 3600 3600 60')
2108 response
.authority
.append(soa
)
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
)
2119 # next queries should hit the cache
2120 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
2121 self
.assertEqual(receivedResponse
, response
)
2123 time
.sleep(self
._negCacheTTL
+ 1)
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
)
2136 for key
in self
._responsesCounter
:
2137 total
+= self
._responsesCounter
[key
]
2139 self
.assertEqual(total
, misses
)
2141 class TestCachingDontAge(DNSDistTest
):
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"}
2148 def testCacheDoesntDecreaseTTL(self
):
2150 Cache: Cache doesn't decrease TTL with 'don't age' set
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.
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
,
2166 response
.answer
.append(rrset
)
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
)
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
)
2183 # now we wait a bit for the TTL to decrease
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
)
2193 for key
in self
._responsesCounter
:
2194 total
+= self
._responsesCounter
[key
]
2196 self
.assertEqual(total
, misses
)
2198 class TestCachingECSWithoutPoolECS(DNSDistTest
):
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)
2207 controlSocket("127.0.0.1:%d")
2208 newServer{address="127.0.0.1:%d", useClientSubnet=true}
2211 def testCached(self
):
2213 Cache: Cached entry with ECS is a miss when no backend are available
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
,
2224 response
.answer
.append(rrset
)
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
)
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
)
2242 # we mark the backend as down
2243 self
.sendConsoleCommand("getServer(0):setDown()")
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)
2252 class TestCachingECSWithPoolECS(DNSDistTest
):
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)
2262 controlSocket("127.0.0.1:%d")
2263 newServer{address="127.0.0.1:%d", useClientSubnet=true}
2266 def testCached(self
):
2268 Cache: Cached entry with ECS is a hit when no backend are available
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
,
2279 response
.answer
.append(rrset
)
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
)
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
)
2297 # we mark the backend as down
2298 self
.sendConsoleCommand("getServer(0):setDown()")
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
)
2307 class TestCachingCollisionNoECSParsing(DNSDistTest
):
2309 _config_template
= """
2310 pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
2311 getPool(""):setCache(pc)
2312 newServer{address="127.0.0.1:%d"}
2315 def testCacheCollisionNoECSParsing(self
):
2317 Cache: Collision with no ECS parsing
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
,
2329 response
.answer
.append(rrset
)
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
)
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
)
2349 class TestCachingCollisionWithECSParsing(DNSDistTest
):
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"}
2357 def testCacheCollisionWithECSParsing(self
):
2359 Cache: Collision with ECS parsing
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
,
2371 response
.answer
.append(rrset
)
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
)
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
,
2393 response2
.answer
.append(rrset
)
2394 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query2
, response2
)
2395 self
.assertEqual(receivedResponse
, response2
)
2397 class TestCachingScopeZero(DNSDistTest
):
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())
2411 def testScopeZero(self
):
2413 Cache: Test the scope-zero feature, backend returns a scope of zero
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
,
2431 scopedResponse
.answer
.append(rrset
)
2432 expectedResponse
.answer
.append(rrset
)
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
)
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
)
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
)
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
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
)
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
)
2483 def testScopeNotZero(self
):
2485 Cache: Test the scope-zero feature, backend returns a scope of non-zero
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
,
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
)
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
)
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
)
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
)
2538 def testNoECS(self
):
2540 Cache: Test the scope-zero feature, backend returns no ECS at all
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
,
2557 response
= dns
.message
.make_response(query
)
2558 response
.answer
.append(rrset
)
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
)
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
)
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
)
2585 class TestCachingScopeZeroButNoSubnetcheck(DNSDistTest
):
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())
2599 def testScopeZero(self
):
2601 Cache: Test that the scope-zero feature is disabled when ECS parsing is not enabled in the cache
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
,
2622 scopedResponse
.answer
.append(rrset
)
2623 expectedResponse
.answer
.append(rrset
)
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
)
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
)
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
)
2650 class TestCachingAlteredHeader(DNSDistTest
):
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"}
2659 def testCachingAlteredHeader(self
):
2661 Cache: The header has been altered via a rule
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
,
2675 response
.answer
.append(rrset
)
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
,
2685 expectedResponse
.answer
.append(rrset
)
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
)
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)
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
,
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
)
2717 class TestCachingBackendSettingRD(DNSDistTest
):
2719 _config_template
= """
2720 pc = newPacketCache(100)
2721 getPool(""):setCache(pc)
2722 newServer{address="127.0.0.1:%d"}
2725 def testCachingBackendSetRD(self
):
2727 Cache: The backend sets RD=1 in the response even if the query had RD=0
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
,
2741 response
.answer
.append(rrset
)
2743 expectedResponse
= dns
.message
.make_response(query
)
2744 expectedResponse
.flags
&= ~dns
.flags
.RD
2745 rrset
= dns
.rrset
.from_text(name
,
2750 expectedResponse
.answer
.append(rrset
)
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
)
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
)
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
,
2777 expectedResponse
.answer
.append(rrset
)
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
)
2786 class TestAPICache(DNSDistTest
):
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")
2804 def testCacheClearingViaAPI(self
):
2806 Cache: Clear cache via API
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
,
2818 response
.answer
.append(rrset
)
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
)
2828 # second query should be a hit
2829 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
2830 self
.assertEqual(receivedResponse
, response
)
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)
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)
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)
2845 r
= requests
.delete(url
+ '?pool=pool-without-cache&type=AAAA', headers
=headers
, timeout
=self
._webTimeout
)
2846 self
.assertEqual(r
.status_code
, 400)
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)
2853 r
= requests
.delete(url
+ '?pool=&name=not-cache-api.cache.tests.powerdns.com.', headers
=headers
, timeout
=self
._webTimeout
)
2855 self
.assertEqual(r
.status_code
, 200)
2857 self
.assertIn('count', content
)
2858 self
.assertEqual(int(content
['count']), 0)
2861 r
= requests
.delete(url
+ '?pool=&name=cache-api.cache.tests.powerdns.com.&type=A', headers
=headers
, timeout
=self
._webTimeout
)
2863 self
.assertEqual(r
.status_code
, 200)
2865 self
.assertIn('count', content
)
2866 self
.assertEqual(int(content
['count']), 0)
2868 # should still be a hit
2869 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
2870 self
.assertEqual(receivedResponse
, response
)
2873 r
= requests
.delete(url
+ '?pool=&name=cache-api.cache.tests.powerdns.com.&type=AAAA', headers
=headers
, timeout
=self
._webTimeout
)
2875 self
.assertEqual(r
.status_code
, 200)
2877 self
.assertIn('count', content
)
2878 self
.assertEqual(int(content
['count']), 1)
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
)
2889 r
= requests
.delete(url
+ '?pool=&name=cache-api.cache.tests.powerdns.com.', headers
=headers
, timeout
=self
._webTimeout
)
2891 self
.assertEqual(r
.status_code
, 200)
2893 self
.assertIn('count', content
)
2894 self
.assertEqual(int(content
['count']), 1)
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
)
2905 r
= requests
.delete(url
+ '?pool=&name=cache.tests.powerdns.com.&suffix=true', headers
=headers
, timeout
=self
._webTimeout
)
2907 self
.assertEqual(r
.status_code
, 200)
2909 self
.assertIn('count', content
)
2910 self
.assertEqual(int(content
['count']), 1)
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
)