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