]> git.ipfire.org Git - thirdparty/pdns.git/blame - regression-tests.dnsdist/test_Caching.py
Merge pull request #7713 from Habbie/auth-rec-ta
[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
405
a2c4a339
CH
406class TestTempFailureCacheTTLAction(DNSDistTest):
407
408 _config_template = """
7d294573 409 pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
a2c4a339
CH
410 getPool(""):setCache(pc)
411 addAction("servfail.cache.tests.powerdns.com.", TempFailureCacheTTLAction(1))
c1b81381 412 newServer{address="127.0.0.1:%d"}
a2c4a339
CH
413 """
414
415 def testTempFailureCacheTTLAction(self):
416 """
417 Cache: When a TempFailure TTL is set, it should be honored
418
419 dnsdist is configured to cache packets, plus a specific qname is
420 set up with a lower TempFailure Cache TTL. we are sending one request
421 (cache miss) and verify that the cache is hit for the following query,
422 but the TTL then expires before the larger "good" packetcache TTL.
423 """
424 name = 'servfail.cache.tests.powerdns.com.'
425 query = dns.message.make_query(name, 'AAAA', 'IN')
426 response = dns.message.make_response(query)
427 response.set_rcode(dns.rcode.SERVFAIL)
428
429 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
430 self.assertTrue(receivedQuery)
431 self.assertTrue(receivedResponse)
432 receivedQuery.id = query.id
433 self.assertEquals(query, receivedQuery)
434 self.assertEquals(receivedResponse, response)
435
436 # next query should hit the cache
45fc7a50 437 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
a2c4a339
CH
438 self.assertFalse(receivedQuery)
439 self.assertTrue(receivedResponse)
440 self.assertEquals(receivedResponse, response)
441
442 # now we wait a bit for the Failure-Cache TTL to expire
443 time.sleep(2)
444
445 # next query should NOT hit the cache
446 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
447 self.assertTrue(receivedQuery)
448 self.assertTrue(receivedResponse)
449 self.assertEquals(receivedResponse, response)
450
451
903853f4
RG
452class TestCachingWithExistingEDNS(DNSDistTest):
453
454 _config_template = """
7d294573 455 pc = newPacketCache(5, {maxTTL=86400, minTTL=1})
903853f4 456 getPool(""):setCache(pc)
c1b81381 457 newServer{address="127.0.0.1:%d"}
903853f4
RG
458 """
459 def testCacheWithEDNS(self):
460 """
461 Cache: Cache should not match different EDNS value
462
463 dnsdist is configured to cache entries, we are sending one request
464 (cache miss) and verify that the same one with a different EDNS UDP
465 Payload size is not served from the cache.
466 """
467 misses = 0
468 name = 'cachedifferentedns.cache.tests.powerdns.com.'
469 query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=512)
470 response = dns.message.make_response(query)
471 rrset = dns.rrset.from_text(name,
472 3600,
473 dns.rdataclass.IN,
474 dns.rdatatype.A,
475 '127.0.0.1')
476 response.answer.append(rrset)
477
478 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
479 self.assertTrue(receivedQuery)
480 self.assertTrue(receivedResponse)
481 receivedQuery.id = query.id
482 self.assertEquals(query, receivedQuery)
483 self.assertEquals(response, receivedResponse)
484 misses += 1
485
486 query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
487 response = dns.message.make_response(query)
488 rrset = dns.rrset.from_text(name,
489 3600,
490 dns.rdataclass.IN,
491 dns.rdatatype.A,
492 '127.0.0.1')
493 response.answer.append(rrset)
494
495 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
496 self.assertTrue(receivedQuery)
497 self.assertTrue(receivedResponse)
498 receivedQuery.id = query.id
499 self.assertEquals(query, receivedQuery)
500 self.assertEquals(response, receivedResponse)
501 misses += 1
502
503 total = 0
02bbf9eb
RG
504 for key in self._responsesCounter:
505 total += self._responsesCounter[key]
903853f4
RG
506
507 self.assertEquals(total, misses)
fe1c60f2
RG
508
509class TestCachingCacheFull(DNSDistTest):
510
511 _config_template = """
7d294573 512 pc = newPacketCache(1, {maxTTL=86400, minTTL=1})
fe1c60f2 513 getPool(""):setCache(pc)
c1b81381 514 newServer{address="127.0.0.1:%d"}
fe1c60f2
RG
515 """
516 def testCacheFull(self):
517 """
518 Cache: No new entries are cached when the cache is full
519
520 """
521 misses = 0
522 name = 'cachenotfullyet.cache.tests.powerdns.com.'
523 query = dns.message.make_query(name, 'A', 'IN')
524 response = dns.message.make_response(query)
525 rrset = dns.rrset.from_text(name,
526 3600,
527 dns.rdataclass.IN,
528 dns.rdatatype.A,
529 '127.0.0.1')
530 response.answer.append(rrset)
531
532 # Miss
533 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
534 self.assertTrue(receivedQuery)
535 self.assertTrue(receivedResponse)
536 receivedQuery.id = query.id
537 self.assertEquals(query, receivedQuery)
538 self.assertEquals(response, receivedResponse)
539 misses += 1
540
541 # next queries should hit the cache
542 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
543 self.assertEquals(receivedResponse, response)
544
545 # ok, now the cache is full, send another query
546 name = 'cachefull.cache.tests.powerdns.com.'
547 query = dns.message.make_query(name, 'AAAA', 'IN')
548 response = dns.message.make_response(query)
549 rrset = dns.rrset.from_text(name,
550 3600,
551 dns.rdataclass.IN,
552 dns.rdatatype.AAAA,
553 '::1')
554 response.answer.append(rrset)
555
556 # Miss
557 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
558 self.assertTrue(receivedQuery)
559 self.assertTrue(receivedResponse)
560 receivedQuery.id = query.id
561 self.assertEquals(query, receivedQuery)
562 self.assertEquals(response, receivedResponse)
563 misses += 1
564
565 # next queries should NOT hit the cache
566 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
567 self.assertTrue(receivedQuery)
568 self.assertTrue(receivedResponse)
569 receivedQuery.id = query.id
570 self.assertEquals(query, receivedQuery)
571 self.assertEquals(response, receivedResponse)
572 misses += 1
573
574 total = 0
02bbf9eb
RG
575 for key in self._responsesCounter:
576 total += self._responsesCounter[key]
fe1c60f2
RG
577
578 self.assertEquals(total, misses)
1ea747c0
RG
579
580class TestCachingNoStale(DNSDistTest):
581
582 _consoleKey = DNSDistTest.generateConsoleKey()
b4f23783 583 _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
1ea747c0
RG
584 _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
585 _config_template = """
7d294573 586 pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
1ea747c0
RG
587 getPool(""):setCache(pc)
588 setKey("%s")
c1b81381
RG
589 controlSocket("127.0.0.1:%d")
590 newServer{address="127.0.0.1:%d"}
1ea747c0
RG
591 """
592 def testCacheNoStale(self):
593 """
594 Cache: Cache entry, set backend down, we should not get a stale entry
595
596 """
b7c8f9a8 597 ttl = 2
1ea747c0
RG
598 name = 'nostale.cache.tests.powerdns.com.'
599 query = dns.message.make_query(name, 'A', 'IN')
600 response = dns.message.make_response(query)
601 rrset = dns.rrset.from_text(name,
602 1,
603 dns.rdataclass.IN,
604 dns.rdatatype.A,
605 '127.0.0.1')
606 response.answer.append(rrset)
607
608 # Miss
609 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
610 self.assertTrue(receivedQuery)
611 self.assertTrue(receivedResponse)
612 receivedQuery.id = query.id
613 self.assertEquals(query, receivedQuery)
614 self.assertEquals(response, receivedResponse)
615
616 # next queries should hit the cache
617 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
618 self.assertEquals(receivedResponse, response)
619
620 # ok, we mark the backend as down
621 self.sendConsoleCommand("getServer(0):setDown()")
622 # and we wait for the entry to expire
623 time.sleep(ttl + 1)
624
625 # we should NOT get a cached, stale, entry
626 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
627 self.assertEquals(receivedResponse, None)
628
629
630class TestCachingStale(DNSDistTest):
631
632 _consoleKey = DNSDistTest.generateConsoleKey()
b4f23783 633 _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
1ea747c0
RG
634 _staleCacheTTL = 60
635 _config_params = ['_staleCacheTTL', '_consoleKeyB64', '_consolePort', '_testServerPort']
636 _config_template = """
7d294573 637 pc = newPacketCache(100, {maxTTL=86400, minTTL=1, temporaryFailureTTL=0, staleTTL=%d})
1ea747c0
RG
638 getPool(""):setCache(pc)
639 setStaleCacheEntriesTTL(600)
640 setKey("%s")
c1b81381
RG
641 controlSocket("127.0.0.1:%d")
642 newServer{address="127.0.0.1:%d"}
1ea747c0
RG
643 """
644 def testCacheStale(self):
645 """
646 Cache: Cache entry, set backend down, get stale entry
647
648 """
649 misses = 0
b7c8f9a8 650 ttl = 2
1ea747c0
RG
651 name = 'stale.cache.tests.powerdns.com.'
652 query = dns.message.make_query(name, 'A', 'IN')
653 response = dns.message.make_response(query)
654 rrset = dns.rrset.from_text(name,
655 ttl,
656 dns.rdataclass.IN,
657 dns.rdatatype.A,
658 '127.0.0.1')
659 response.answer.append(rrset)
660
661 # Miss
662 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
663 self.assertTrue(receivedQuery)
664 self.assertTrue(receivedResponse)
665 receivedQuery.id = query.id
666 self.assertEquals(query, receivedQuery)
667 self.assertEquals(response, receivedResponse)
668 misses += 1
669
670 # next queries should hit the cache
671 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
672 self.assertEquals(receivedResponse, response)
673
674 # ok, we mark the backend as down
675 self.sendConsoleCommand("getServer(0):setDown()")
676 # and we wait for the entry to expire
677 time.sleep(ttl + 1)
678
679 # we should get a cached, stale, entry
680 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
681 self.assertEquals(receivedResponse, response)
682 for an in receivedResponse.answer:
683 self.assertEquals(an.ttl, self._staleCacheTTL)
684
685 total = 0
02bbf9eb
RG
686 for key in self._responsesCounter:
687 total += self._responsesCounter[key]
1ea747c0
RG
688
689 self.assertEquals(total, misses)
690
c1b81381
RG
691class TestCachingStaleExpunged(DNSDistTest):
692
693 _consoleKey = DNSDistTest.generateConsoleKey()
694 _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
695 _staleCacheTTL = 60
696 _config_params = ['_staleCacheTTL', '_consoleKeyB64', '_consolePort', '_testServerPort']
697 _config_template = """
7d294573 698 pc = newPacketCache(100, {maxTTL=86400, minTTL=1, temporaryFailureTTL=0, staleTTL=%d})
c1b81381
RG
699 getPool(""):setCache(pc)
700 setStaleCacheEntriesTTL(600)
701 -- try to remove all expired entries
702 setCacheCleaningPercentage(100)
703 -- clean the cache every second
704 setCacheCleaningDelay(1)
705 setKey("%s")
706 controlSocket("127.0.0.1:%d")
707 newServer{address="127.0.0.1:%d"}
708 """
709 def testCacheStale(self):
710 """
711 Cache: Cache entry, set backend down, wait for the cache cleaning to run and remove the entry, get no entry
712 """
713 misses = 0
714 drops = 0
b7c8f9a8 715 ttl = 2
c1b81381
RG
716 name = 'stale-but-expunged.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 self.assertEquals(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"misses\"]").strip("\n")), misses + drops)
735
736 # next queries should hit the cache
737 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
738 self.assertEquals(receivedResponse, response)
739 # the cache should have one entry
740 self.assertEquals(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"entries\"]").strip("\n")), 1)
741 self.assertEquals(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"hits\"]").strip("\n")), 1)
742
743 # ok, we mark the backend as down
744 self.sendConsoleCommand("getServer(0):setDown()")
745 # and we wait for the entry to expire
746 time.sleep(ttl + 1)
747 # wait a bit more to be sure that the cache cleaning algo has been run
748 time.sleep(1)
749 # the cache should be empty now
750 self.assertEquals(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"entries\"]").strip("\n")), 0)
751
752 # we should get a DROP (backend is down, nothing in the cache anymore)
753 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
754 self.assertEquals(receivedResponse, None)
755 drops += 1
756
757 self.assertEquals(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"misses\"]").strip("\n")), misses + drops)
758 self.assertEquals(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"hits\"]").strip("\n")), 1)
759
760 total = 0
761 for key in self._responsesCounter:
762 total += self._responsesCounter[key]
763
764 self.assertEquals(total, misses)
765
766class TestCachingStaleExpungePrevented(DNSDistTest):
767
768 _consoleKey = DNSDistTest.generateConsoleKey()
769 _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
770 _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
771 _config_template = """
7d294573 772 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
773 getPool(""):setCache(pc)
774 setStaleCacheEntriesTTL(600)
775 -- try to remove all expired entries
776 setCacheCleaningPercentage(100)
777 -- clean the cache every second
778 setCacheCleaningDelay(1)
779 setKey("%s")
780 controlSocket("127.0.0.1:%d")
781 newServer{address="127.0.0.1:%d"}
782 """
783 def testCacheStale(self):
784 """
785 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
786 """
787 misses = 0
b7c8f9a8 788 ttl = 2
c1b81381
RG
789 name = 'stale-not-expunged.cache.tests.powerdns.com.'
790 query = dns.message.make_query(name, 'A', 'IN')
791 response = dns.message.make_response(query)
792 rrset = dns.rrset.from_text(name,
793 ttl,
794 dns.rdataclass.IN,
795 dns.rdatatype.A,
796 '127.0.0.1')
797 response.answer.append(rrset)
798
799 # Miss
800 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
801 self.assertTrue(receivedQuery)
802 self.assertTrue(receivedResponse)
803 receivedQuery.id = query.id
804 self.assertEquals(query, receivedQuery)
805 self.assertEquals(response, receivedResponse)
806 misses += 1
807 self.assertEquals(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"misses\"]").strip("\n")), 1)
808
809 # next queries should hit the cache
810 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
811 self.assertEquals(receivedResponse, response)
812 # the cache should have one entry
813 self.assertEquals(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"entries\"]").strip("\n")), 1)
814 self.assertEquals(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"hits\"]").strip("\n")), 1)
815
816 # ok, we mark the backend as down
817 self.sendConsoleCommand("getServer(0):setDown()")
818 # and we wait for the entry to expire
819 time.sleep(ttl + 1)
820 # wait a bit more to be sure that the cache cleaning algo has been run
821 time.sleep(1)
822 # the cache should NOT be empty because the removal of the expired entry should have been prevented
823 # since all backends for this pool are down
824 self.assertEquals(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"entries\"]").strip("\n")), 1)
825
826 # we should get a HIT
827 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
828 self.assertEquals(receivedResponse, response)
829
830 self.assertEquals(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"misses\"]").strip("\n")), 1)
831 self.assertEquals(int(self.sendConsoleCommand("getPool(\"\"):getCache():getStats()[\"hits\"]").strip("\n")), 2)
832
833 total = 0
834 for key in self._responsesCounter:
835 total += self._responsesCounter[key]
836
837 self.assertEquals(total, misses)
838
1ea747c0
RG
839class TestCacheManagement(DNSDistTest):
840
841 _consoleKey = DNSDistTest.generateConsoleKey()
b4f23783 842 _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
1ea747c0
RG
843 _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
844 _config_template = """
7d294573 845 pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
1ea747c0
RG
846 getPool(""):setCache(pc)
847 setKey("%s")
c1b81381
RG
848 controlSocket("127.0.0.1:%d")
849 newServer{address="127.0.0.1:%d"}
1ea747c0
RG
850 """
851 def testCacheExpunge(self):
852 """
853 Cache: Expunge
854
855 """
856 misses = 0
857 ttl = 600
858 name = 'expunge.cache.tests.powerdns.com.'
859 query = dns.message.make_query(name, 'A', 'IN')
860 response = dns.message.make_response(query)
861 rrset = dns.rrset.from_text(name,
862 ttl,
863 dns.rdataclass.IN,
864 dns.rdatatype.A,
865 '127.0.0.1')
866 response.answer.append(rrset)
867
868 # Miss
869 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
870 self.assertTrue(receivedQuery)
871 self.assertTrue(receivedResponse)
872 receivedQuery.id = query.id
873 self.assertEquals(query, receivedQuery)
874 self.assertEquals(response, receivedResponse)
875 misses += 1
876
877 # next queries should hit the cache
878 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
879 self.assertEquals(receivedResponse, response)
880
881 # remove cached entries
882 self.sendConsoleCommand("getPool(\"\"):getCache():expunge(0)")
883
884 # Miss
885 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
886 self.assertTrue(receivedQuery)
887 self.assertTrue(receivedResponse)
888 receivedQuery.id = query.id
889 self.assertEquals(query, receivedQuery)
890 self.assertEquals(response, receivedResponse)
891 misses += 1
892
893 # next queries should hit the cache again
894 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
895 self.assertEquals(receivedResponse, response)
896
897 total = 0
02bbf9eb
RG
898 for key in self._responsesCounter:
899 total += self._responsesCounter[key]
1ea747c0
RG
900
901 self.assertEquals(total, misses)
902
903 def testCacheExpungeByName(self):
904 """
905 Cache: Expunge by name
906
907 """
908 misses = 0
909 ttl = 600
910 name = 'expungebyname.cache.tests.powerdns.com.'
911 query = dns.message.make_query(name, 'A', 'IN')
912 response = dns.message.make_response(query)
913 rrset = dns.rrset.from_text(name,
914 ttl,
915 dns.rdataclass.IN,
916 dns.rdatatype.A,
917 '127.0.0.1')
918 response.answer.append(rrset)
919
920 name2 = 'expungebynameother.cache.tests.powerdns.com.'
921 query2 = dns.message.make_query(name2, 'A', 'IN')
922 response2 = dns.message.make_response(query2)
923 rrset2 = dns.rrset.from_text(name2,
924 ttl,
925 dns.rdataclass.IN,
926 dns.rdatatype.A,
927 '127.0.0.1')
928 response2.answer.append(rrset2)
929
930 # Miss
931 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
932 self.assertTrue(receivedQuery)
933 self.assertTrue(receivedResponse)
934 receivedQuery.id = query.id
935 self.assertEquals(query, receivedQuery)
936 self.assertEquals(response, receivedResponse)
937 misses += 1
938
939 # next queries should hit the cache
940 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
941 self.assertEquals(receivedResponse, response)
942
943 # cache another entry
944 (receivedQuery, receivedResponse) = self.sendUDPQuery(query2, response2)
945 self.assertTrue(receivedQuery)
946 self.assertTrue(receivedResponse)
947 receivedQuery.id = query2.id
948 self.assertEquals(query2, receivedQuery)
949 self.assertEquals(response2, receivedResponse)
950 misses += 1
951
952 # queries for name and name 2 should hit the cache
953 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
954 self.assertEquals(receivedResponse, response)
955
956 (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
957 self.assertEquals(receivedResponse, response2)
958
959 # remove cached entries from name
960 self.sendConsoleCommand("getPool(\"\"):getCache():expungeByName(newDNSName(\"" + name + "\"))")
961
962 # Miss for name
963 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
964 self.assertTrue(receivedQuery)
965 self.assertTrue(receivedResponse)
966 receivedQuery.id = query.id
967 self.assertEquals(query, receivedQuery)
968 self.assertEquals(response, receivedResponse)
969 misses += 1
970
971 # next queries for name should hit the cache again
972 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
973 self.assertEquals(receivedResponse, response)
974
975 # queries for name2 should still hit the cache
976 (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
977 self.assertEquals(receivedResponse, response2)
978
979 total = 0
02bbf9eb
RG
980 for key in self._responsesCounter:
981 total += self._responsesCounter[key]
1ea747c0
RG
982
983 self.assertEquals(total, misses)
984
985 def testCacheExpungeByNameAndType(self):
986 """
987 Cache: Expunge by name and type
988
989 """
990 misses = 0
991 ttl = 600
992 name = 'expungebynameandtype.cache.tests.powerdns.com.'
993 query = dns.message.make_query(name, 'A', 'IN')
994 response = dns.message.make_response(query)
995 rrset = dns.rrset.from_text(name,
996 ttl,
997 dns.rdataclass.IN,
998 dns.rdatatype.A,
999 '127.0.0.1')
1000 response.answer.append(rrset)
1001
1002 query2 = dns.message.make_query(name, 'AAAA', 'IN')
1003 response2 = dns.message.make_response(query2)
1004 rrset2 = dns.rrset.from_text(name,
1005 ttl,
1006 dns.rdataclass.IN,
1007 dns.rdatatype.AAAA,
1008 '::1')
1009 response2.answer.append(rrset2)
1010
1011 # Miss
1012 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1013 self.assertTrue(receivedQuery)
1014 self.assertTrue(receivedResponse)
1015 receivedQuery.id = query.id
1016 self.assertEquals(query, receivedQuery)
1017 self.assertEquals(response, receivedResponse)
1018 misses += 1
1019
1020 # next queries should hit the cache
1021 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1022 self.assertEquals(receivedResponse, response)
1023
1024 # cache another entry
1025 (receivedQuery, receivedResponse) = self.sendUDPQuery(query2, response2)
1026 self.assertTrue(receivedQuery)
1027 self.assertTrue(receivedResponse)
1028 receivedQuery.id = query2.id
1029 self.assertEquals(query2, receivedQuery)
1030 self.assertEquals(response2, receivedResponse)
1031 misses += 1
1032
1033 # queries for name A and AAAA should hit the cache
1034 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1035 self.assertEquals(receivedResponse, response)
1036
1037 (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
1038 self.assertEquals(receivedResponse, response2)
1039
1040 # remove cached entries from name A
d3ec24f9 1041 self.sendConsoleCommand("getPool(\"\"):getCache():expungeByName(newDNSName(\"" + name + "\"), DNSQType.A)")
1ea747c0
RG
1042
1043 # Miss for name A
1044 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1045 self.assertTrue(receivedQuery)
1046 self.assertTrue(receivedResponse)
1047 receivedQuery.id = query.id
1048 self.assertEquals(query, receivedQuery)
1049 self.assertEquals(response, receivedResponse)
1050 misses += 1
1051
1052 # next queries for name A should hit the cache again
1053 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1054 self.assertEquals(receivedResponse, response)
1055
1056 # queries for name AAAA should still hit the cache
1057 (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
490dc586
RG
1058 self.assertEquals(receivedResponse, response2)
1059
1060 total = 0
1061 for key in self._responsesCounter:
1062 total += self._responsesCounter[key]
1063 self.assertEquals(total, misses)
1064
1065 def testCacheExpungeByNameAndSuffix(self):
1066 """
1067 Cache: Expunge by name
1068
1069 """
1070 misses = 0
1071 ttl = 600
1072 name = 'expungebyname.suffix.cache.tests.powerdns.com.'
1073 query = dns.message.make_query(name, 'A', 'IN')
1074 response = dns.message.make_response(query)
1075 rrset = dns.rrset.from_text(name,
1076 ttl,
1077 dns.rdataclass.IN,
1078 dns.rdatatype.A,
1079 '127.0.0.1')
1080 response.answer.append(rrset)
1081
1082 name2 = 'expungebyname.suffixother.cache.tests.powerdns.com.'
1083 query2 = dns.message.make_query(name2, 'A', 'IN')
1084 response2 = dns.message.make_response(query2)
1085 rrset2 = dns.rrset.from_text(name2,
1086 ttl,
1087 dns.rdataclass.IN,
1088 dns.rdatatype.A,
1089 '127.0.0.1')
1090 response2.answer.append(rrset2)
1091
1092 # Miss
1093 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1094 self.assertTrue(receivedQuery)
1095 self.assertTrue(receivedResponse)
1096 receivedQuery.id = query.id
1097 self.assertEquals(query, receivedQuery)
1098 self.assertEquals(response, receivedResponse)
1099 misses += 1
1100
1101 # next queries should hit the cache
1102 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1103 self.assertEquals(receivedResponse, response)
1104
1105 # cache another entry
1106 (receivedQuery, receivedResponse) = self.sendUDPQuery(query2, response2)
1107 self.assertTrue(receivedQuery)
1108 self.assertTrue(receivedResponse)
1109 receivedQuery.id = query2.id
1110 self.assertEquals(query2, receivedQuery)
1111 self.assertEquals(response2, receivedResponse)
1112 misses += 1
1113
1114 # queries for name and name 2 should hit the cache
1115 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1116 self.assertEquals(receivedResponse, response)
1117
1118 (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
1119 self.assertEquals(receivedResponse, response2)
1120
1121 # remove cached entries from name
d3ec24f9 1122 self.sendConsoleCommand("getPool(\"\"):getCache():expungeByName(newDNSName(\"suffix.cache.tests.powerdns.com.\"), DNSQType.ANY, true)")
490dc586
RG
1123
1124 # Miss for name
1125 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1126 self.assertTrue(receivedQuery)
1127 self.assertTrue(receivedResponse)
1128 receivedQuery.id = query.id
1129 self.assertEquals(query, receivedQuery)
1130 self.assertEquals(response, receivedResponse)
1131 misses += 1
1132
1133 # next queries for name should hit the cache again
1134 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1135 self.assertEquals(receivedResponse, response)
1136
1137 # queries for name2 should still hit the cache
1138 (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
1139 self.assertEquals(receivedResponse, response2)
1140
1141 total = 0
1142 for key in self._responsesCounter:
1143 total += self._responsesCounter[key]
1144
1145 self.assertEquals(total, misses)
1146
1147 def testCacheExpungeByNameAndTypeAndSuffix(self):
1148 """
1149 Cache: Expunge by name and type
1150
1151 """
1152 misses = 0
1153 ttl = 600
1154 name = 'expungebynameandtype.suffixtype.cache.tests.powerdns.com.'
1155 query = dns.message.make_query(name, 'A', 'IN')
1156 response = dns.message.make_response(query)
1157 rrset = dns.rrset.from_text(name,
1158 ttl,
1159 dns.rdataclass.IN,
1160 dns.rdatatype.A,
1161 '127.0.0.1')
1162 response.answer.append(rrset)
1163
1164 query2 = dns.message.make_query(name, 'AAAA', 'IN')
1165 response2 = dns.message.make_response(query2)
1166 rrset2 = dns.rrset.from_text(name,
1167 ttl,
1168 dns.rdataclass.IN,
1169 dns.rdatatype.AAAA,
1170 '::1')
1171 response2.answer.append(rrset2)
1172
1173 # Miss
1174 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1175 self.assertTrue(receivedQuery)
1176 self.assertTrue(receivedResponse)
1177 receivedQuery.id = query.id
1178 self.assertEquals(query, receivedQuery)
1179 self.assertEquals(response, receivedResponse)
1180 misses += 1
1181
1182 # next queries should hit the cache
1183 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1184 self.assertEquals(receivedResponse, response)
1185
1186 # cache another entry
1187 (receivedQuery, receivedResponse) = self.sendUDPQuery(query2, response2)
1188 self.assertTrue(receivedQuery)
1189 self.assertTrue(receivedResponse)
1190 receivedQuery.id = query2.id
1191 self.assertEquals(query2, receivedQuery)
1192 self.assertEquals(response2, receivedResponse)
1193 misses += 1
1194
1195 # queries for name A and AAAA should hit the cache
1196 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1197 self.assertEquals(receivedResponse, response)
1198
1199 (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
1200 self.assertEquals(receivedResponse, response2)
1201
1202 # remove cached entries from name A
d3ec24f9 1203 self.sendConsoleCommand("getPool(\"\"):getCache():expungeByName(newDNSName(\"suffixtype.cache.tests.powerdns.com.\"), DNSQType.A, true)")
490dc586
RG
1204
1205 # Miss for name A
1206 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1207 self.assertTrue(receivedQuery)
1208 self.assertTrue(receivedResponse)
1209 receivedQuery.id = query.id
1210 self.assertEquals(query, receivedQuery)
1211 self.assertEquals(response, receivedResponse)
1212 misses += 1
1213
1214 # next queries for name A should hit the cache again
1215 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1216 self.assertEquals(receivedResponse, response)
1217
1218 # queries for name AAAA should still hit the cache
1219 (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
1ea747c0
RG
1220 self.assertEquals(receivedResponse, response2)
1221
1222 total = 0
02bbf9eb
RG
1223 for key in self._responsesCounter:
1224 total += self._responsesCounter[key]
1ea747c0 1225 self.assertEquals(total, misses)
cc8cefe1
RG
1226
1227class TestCachingTTL(DNSDistTest):
1228
1229 _maxCacheTTL = 86400
1230 _minCacheTTL = 600
1231 _config_params = ['_maxCacheTTL', '_minCacheTTL', '_testServerPort']
1232 _config_template = """
7d294573 1233 pc = newPacketCache(1000, {maxTTL=%d, minTTL=%d})
cc8cefe1 1234 getPool(""):setCache(pc)
c1b81381 1235 newServer{address="127.0.0.1:%d"}
cc8cefe1
RG
1236 """
1237 def testCacheShortTTL(self):
1238 """
1239 Cache: Entries with a TTL shorter than minTTL
1240
1241 """
1242 misses = 0
1243 ttl = 60
1244 name = 'ttltooshort.cache.tests.powerdns.com.'
1245 query = dns.message.make_query(name, 'A', 'IN')
1246 response = dns.message.make_response(query)
1247 rrset = dns.rrset.from_text(name,
1248 ttl,
1249 dns.rdataclass.IN,
1250 dns.rdatatype.A,
1251 '127.0.0.1')
1252 response.answer.append(rrset)
1253
1254 # Miss
1255 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1256 self.assertTrue(receivedQuery)
1257 self.assertTrue(receivedResponse)
1258 receivedQuery.id = query.id
1259 self.assertEquals(query, receivedQuery)
1260 self.assertEquals(response, receivedResponse)
1261 for an in receivedResponse.answer:
1262 self.assertEquals(an.ttl, ttl)
1263 misses += 1
1264
1265 # We should not have been cached
1266 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1267 self.assertTrue(receivedQuery)
1268 self.assertTrue(receivedResponse)
1269 receivedQuery.id = query.id
1270 self.assertEquals(query, receivedQuery)
1271 self.assertEquals(response, receivedResponse)
1272 for an in receivedResponse.answer:
1273 self.assertEquals(an.ttl, ttl)
1274 misses += 1
1275
1276 total = 0
1277 for key in self._responsesCounter:
1278 total += self._responsesCounter[key]
1279
1280 self.assertEquals(total, misses)
1281
a3824e43
RG
1282 def testCacheNXWithNoRR(self):
1283 """
1284 Cache: NX with no RR
1285
1286 """
1287 misses = 0
1288 name = 'nxwithnorr.cache.tests.powerdns.com.'
1289 query = dns.message.make_query(name, 'A', 'IN')
1290 response = dns.message.make_response(query)
1291 response.set_rcode(dns.rcode.NXDOMAIN)
1292
1293 # Miss
1294 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1295 self.assertTrue(receivedQuery)
1296 self.assertTrue(receivedResponse)
1297 receivedQuery.id = query.id
1298 self.assertEquals(query, receivedQuery)
1299 self.assertEquals(response, receivedResponse)
1300 misses += 1
1301
1302 # We should not have been cached
1303 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1304 self.assertTrue(receivedQuery)
1305 self.assertTrue(receivedResponse)
1306 receivedQuery.id = query.id
1307 self.assertEquals(query, receivedQuery)
1308 self.assertEquals(response, receivedResponse)
1309 misses += 1
1310
1311 total = 0
1312 for key in self._responsesCounter:
1313 total += self._responsesCounter[key]
1314
1315 self.assertEquals(total, misses)
1316
cc8cefe1
RG
1317class TestCachingLongTTL(DNSDistTest):
1318
1319 _maxCacheTTL = 2
1320 _config_params = ['_maxCacheTTL', '_testServerPort']
1321 _config_template = """
7d294573 1322 pc = newPacketCache(1000, {maxTTL=%d})
cc8cefe1 1323 getPool(""):setCache(pc)
c1b81381 1324 newServer{address="127.0.0.1:%d"}
cc8cefe1
RG
1325 """
1326 def testCacheLongTTL(self):
1327 """
1328 Cache: Entries with a longer TTL than the maximum
1329
1330 """
1331 misses = 0
1332 ttl = 172800
1333 name = 'longttl.cache.tests.powerdns.com.'
1334 query = dns.message.make_query(name, 'A', 'IN')
1335 response = dns.message.make_response(query)
1336 rrset = dns.rrset.from_text(name,
1337 ttl,
1338 dns.rdataclass.IN,
1339 dns.rdatatype.A,
1340 '127.0.0.1')
1341 response.answer.append(rrset)
1342
1343 # Miss
1344 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1345 self.assertTrue(receivedQuery)
1346 self.assertTrue(receivedResponse)
1347 receivedQuery.id = query.id
1348 self.assertEquals(query, receivedQuery)
1349 self.assertEquals(response, receivedResponse)
1350 for an in receivedResponse.answer:
1351 self.assertEquals(an.ttl, ttl)
1352 misses += 1
1353
1354 # next queries should hit the cache
1355 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1356 self.assertEquals(receivedResponse, response)
1357 for an in receivedResponse.answer:
1358 self.assertTrue(an.ttl <= ttl)
1359
1360 time.sleep(self._maxCacheTTL + 1)
1361
1362 # we should not have cached for longer than max cache
1363 # so it should be a miss
1364 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1365 self.assertTrue(receivedQuery)
1366 self.assertTrue(receivedResponse)
1367 receivedQuery.id = query.id
1368 self.assertEquals(query, receivedQuery)
1369 self.assertEquals(response, receivedResponse)
1370 for an in receivedResponse.answer:
1371 self.assertEquals(an.ttl, ttl)
1372 misses += 1
1373
1374 total = 0
1375 for key in self._responsesCounter:
1376 total += self._responsesCounter[key]
1377
1378 self.assertEquals(total, misses)
2714396e
RG
1379
1380class TestCachingFailureTTL(DNSDistTest):
1381
1382 _failureCacheTTL = 2
1383 _config_params = ['_failureCacheTTL', '_testServerPort']
1384 _config_template = """
7d294573 1385 pc = newPacketCache(1000, {maxTTL=86400, minTTL=0, temporaryFailureTTL=%d, staleTTL=60})
2714396e 1386 getPool(""):setCache(pc)
c1b81381 1387 newServer{address="127.0.0.1:%d"}
2714396e
RG
1388 """
1389 def testCacheServFailTTL(self):
1390 """
1391 Cache: ServFail TTL
1392
1393 """
1394 misses = 0
1395 name = 'servfail.failure.cache.tests.powerdns.com.'
1396 query = dns.message.make_query(name, 'A', 'IN')
1397 response = dns.message.make_response(query)
1398 response.set_rcode(dns.rcode.SERVFAIL)
1399
1400 # Miss
1401 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1402 self.assertTrue(receivedQuery)
1403 self.assertTrue(receivedResponse)
1404 receivedQuery.id = query.id
1405 self.assertEquals(query, receivedQuery)
1406 self.assertEquals(response, receivedResponse)
1407 misses += 1
1408
1409 # next queries should hit the cache
1410 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1411 self.assertEquals(receivedResponse, response)
1412
1413 time.sleep(self._failureCacheTTL + 1)
1414
1415 # we should not have cached for longer than failure cache
1416 # so it should be a miss
1417 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1418 self.assertTrue(receivedQuery)
1419 self.assertTrue(receivedResponse)
1420 receivedQuery.id = query.id
1421 self.assertEquals(query, receivedQuery)
1422 self.assertEquals(response, receivedResponse)
1423 misses += 1
1424
1425 total = 0
1426 for key in self._responsesCounter:
1427 total += self._responsesCounter[key]
1428
1429 self.assertEquals(total, misses)
1430
1431 def testCacheRefusedTTL(self):
1432 """
1433 Cache: Refused TTL
1434
1435 """
1436 misses = 0
1437 name = 'refused.failure.cache.tests.powerdns.com.'
1438 query = dns.message.make_query(name, 'A', 'IN')
1439 response = dns.message.make_response(query)
1440 response.set_rcode(dns.rcode.REFUSED)
1441
1442 # Miss
1443 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1444 self.assertTrue(receivedQuery)
1445 self.assertTrue(receivedResponse)
1446 receivedQuery.id = query.id
1447 self.assertEquals(query, receivedQuery)
1448 self.assertEquals(response, receivedResponse)
1449 misses += 1
1450
1451 # next queries should hit the cache
1452 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1453 self.assertEquals(receivedResponse, response)
1454
1455 time.sleep(self._failureCacheTTL + 1)
1456
1457 # we should not have cached for longer than failure cache
1458 # so it should be a miss
1459 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1460 self.assertTrue(receivedQuery)
1461 self.assertTrue(receivedResponse)
1462 receivedQuery.id = query.id
1463 self.assertEquals(query, receivedQuery)
1464 self.assertEquals(response, receivedResponse)
1465 misses += 1
1466
1467 total = 0
1468 for key in self._responsesCounter:
1469 total += self._responsesCounter[key]
1470
1471 self.assertEquals(total, misses)
1472
1473 def testCacheHeaderOnlyRefusedTTL(self):
1474 """
1475 Cache: Header-Only Refused TTL
1476
1477 """
1478 misses = 0
1479 name = 'header-only-refused.failure.cache.tests.powerdns.com.'
1480 query = dns.message.make_query(name, 'A', 'IN')
1481 response = dns.message.make_response(query)
1482 response.set_rcode(dns.rcode.REFUSED)
1483 response.question = []
1484
1485 # Miss
1486 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1487 self.assertTrue(receivedQuery)
1488 self.assertTrue(receivedResponse)
1489 receivedQuery.id = query.id
1490 self.assertEquals(query, receivedQuery)
1491 self.assertEquals(response, receivedResponse)
1492 misses += 1
1493
1494 # next queries should hit the cache
1495 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1496 self.assertEquals(receivedResponse, response)
1497
1498 time.sleep(self._failureCacheTTL + 1)
1499
1500 # we should not have cached for longer than failure cache
1501 # so it should be a miss
1502 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1503 self.assertTrue(receivedQuery)
1504 self.assertTrue(receivedResponse)
1505 receivedQuery.id = query.id
1506 self.assertEquals(query, receivedQuery)
1507 self.assertEquals(response, receivedResponse)
1508 misses += 1
1509
1510 total = 0
1511 for key in self._responsesCounter:
1512 total += self._responsesCounter[key]
1513
1514 self.assertEquals(total, misses)
2b67180c 1515
47698274
RG
1516class TestCachingNegativeTTL(DNSDistTest):
1517
1518 _negCacheTTL = 1
1519 _config_params = ['_negCacheTTL', '_testServerPort']
1520 _config_template = """
7d294573 1521 pc = newPacketCache(1000, {maxTTL=86400, minTTL=0, temporaryFailureTTL=60, staleTTL=60, dontAge=false, numberOfShards=1, deferrableInsertLock=true, maxNegativeTTL=%d})
47698274 1522 getPool(""):setCache(pc)
c1b81381 1523 newServer{address="127.0.0.1:%d"}
47698274
RG
1524 """
1525
1526 def testCacheNegativeTTLNXDomain(self):
1527 """
1528 Cache: Negative TTL on NXDOMAIN
1529
1530 """
1531 misses = 0
1532 name = 'nxdomain.negativettl.cache.tests.powerdns.com.'
1533 query = dns.message.make_query(name, 'A', 'IN')
1534 response = dns.message.make_response(query)
1535 response.set_rcode(dns.rcode.NXDOMAIN)
1536 soa = dns.rrset.from_text(name,
1537 60,
1538 dns.rdataclass.IN,
1539 dns.rdatatype.SOA,
1540 'ns.' + name + ' hostmaster.' + name + ' 1 3600 3600 3600 60')
1541 response.authority.append(soa)
1542
1543 # Miss
1544 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1545 self.assertTrue(receivedQuery)
1546 self.assertTrue(receivedResponse)
1547 receivedQuery.id = query.id
1548 self.assertEquals(query, receivedQuery)
1549 self.assertEquals(response, receivedResponse)
1550 misses += 1
1551
1552 # next queries should hit the cache
1553 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1554 self.assertEquals(receivedResponse, response)
1555
1556 time.sleep(self._negCacheTTL + 1)
1557
b7c8f9a8 1558 # we should not have cached for longer than the negative TTL
47698274
RG
1559 # so it should be a miss
1560 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1561 self.assertTrue(receivedQuery)
1562 self.assertTrue(receivedResponse)
1563 receivedQuery.id = query.id
1564 self.assertEquals(query, receivedQuery)
1565 self.assertEquals(response, receivedResponse)
1566 misses += 1
1567
1568 total = 0
1569 for key in self._responsesCounter:
1570 total += self._responsesCounter[key]
1571
1572 self.assertEquals(total, misses)
1573
1574 def testCacheNegativeTTLNoData(self):
1575 """
1576 Cache: Negative TTL on NoData
1577
1578 """
1579 misses = 0
1580 name = 'nodata.negativettl.cache.tests.powerdns.com.'
1581 query = dns.message.make_query(name, 'A', 'IN')
1582 response = dns.message.make_response(query)
1583 response.set_rcode(dns.rcode.NOERROR)
1584 soa = dns.rrset.from_text(name,
1585 60,
1586 dns.rdataclass.IN,
1587 dns.rdatatype.SOA,
1588 'ns.' + name + ' hostmaster.' + name + ' 1 3600 3600 3600 60')
1589 response.authority.append(soa)
1590
1591 # Miss
1592 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1593 self.assertTrue(receivedQuery)
1594 self.assertTrue(receivedResponse)
1595 receivedQuery.id = query.id
1596 self.assertEquals(query, receivedQuery)
1597 self.assertEquals(response, receivedResponse)
1598 misses += 1
1599
1600 # next queries should hit the cache
1601 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1602 self.assertEquals(receivedResponse, response)
1603
1604 time.sleep(self._negCacheTTL + 1)
1605
1606 # we should not have cached for longer than the negativel TTL
1607 # so it should be a miss
1608 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1609 self.assertTrue(receivedQuery)
1610 self.assertTrue(receivedResponse)
1611 receivedQuery.id = query.id
1612 self.assertEquals(query, receivedQuery)
1613 self.assertEquals(response, receivedResponse)
1614 misses += 1
1615
1616 total = 0
1617 for key in self._responsesCounter:
1618 total += self._responsesCounter[key]
1619
1620 self.assertEquals(total, misses)
1621
2b67180c
RG
1622class TestCachingDontAge(DNSDistTest):
1623
1624 _config_template = """
7d294573 1625 pc = newPacketCache(100, {maxTTL=86400, minTTL=0, temporaryFailureTTL=60, staleTTL=60, dontAge=true})
2b67180c 1626 getPool(""):setCache(pc)
c1b81381 1627 newServer{address="127.0.0.1:%d"}
2b67180c
RG
1628 """
1629 def testCacheDoesntDecreaseTTL(self):
1630 """
1631 Cache: Cache doesn't decrease TTL with 'don't age' set
1632
1633 dnsdist is configured to cache entries but without aging the TTL,
1634 we are sending one request (cache miss) and verify that the cache
1635 hits don't have a decreasing TTL.
1636 """
1637 ttl = 600
1638 misses = 0
1639 name = 'cachedoesntdecreasettl.cache-dont-age.tests.powerdns.com.'
1640 query = dns.message.make_query(name, 'AAAA', 'IN')
1641 response = dns.message.make_response(query)
1642 rrset = dns.rrset.from_text(name,
1643 ttl,
1644 dns.rdataclass.IN,
1645 dns.rdatatype.AAAA,
1646 '::1')
1647 response.answer.append(rrset)
1648
1649 # first query to fill the cache
1650 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1651 self.assertTrue(receivedQuery)
1652 self.assertTrue(receivedResponse)
1653 receivedQuery.id = query.id
1654 self.assertEquals(query, receivedQuery)
1655 self.assertEquals(receivedResponse, response)
1656 misses += 1
1657
1658 # next queries should hit the cache
1659 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1660 self.assertEquals(receivedResponse, response)
1661 for an in receivedResponse.answer:
1662 self.assertTrue(an.ttl == ttl)
1663
1664 # now we wait a bit for the TTL to decrease
1665 time.sleep(1)
1666
1667 # next queries should hit the cache
1668 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
1669 self.assertEquals(receivedResponse, response)
1670 for an in receivedResponse.answer:
1671 self.assertTrue(an.ttl == ttl)
1672
1673 total = 0
1674 for key in self._responsesCounter:
1675 total += self._responsesCounter[key]
1676
1677 self.assertEquals(total, misses)
7e687744
RG
1678
1679class TestCachingECSWithoutPoolECS(DNSDistTest):
1680
1681 _consoleKey = DNSDistTest.generateConsoleKey()
1682 _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
1683 _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
1684 _config_template = """
7d294573 1685 pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
7e687744
RG
1686 getPool(""):setCache(pc)
1687 setKey("%s")
1688 controlSocket("127.0.0.1:%d")
1689 newServer{address="127.0.0.1:%d", useClientSubnet=true}
1690 """
1691
1692 def testCached(self):
1693 """
1694 Cache: Cached entry with ECS is a miss when no backend are available
1695 """
1696 ttl = 600
1697 name = 'cached.cache-ecs-without-pool-ecs.tests.powerdns.com.'
1698 query = dns.message.make_query(name, 'AAAA', 'IN')
1699 response = dns.message.make_response(query)
1700 rrset = dns.rrset.from_text(name,
1701 ttl,
1702 dns.rdataclass.IN,
1703 dns.rdatatype.AAAA,
1704 '::1')
1705 response.answer.append(rrset)
1706
1707 # first query to fill the cache
6ca2e796
RG
1708 for method in ("sendUDPQuery", "sendTCPQuery"):
1709 sender = getattr(self, method)
1710 (receivedQuery, receivedResponse) = sender(query, response)
1711 self.assertTrue(receivedQuery)
1712 self.assertTrue(receivedResponse)
1713 receivedQuery.id = query.id
1714 self.assertEquals(query, receivedQuery)
1715 self.assertEquals(receivedResponse, response)
7e687744
RG
1716
1717 # next queries should hit the cache
6ca2e796
RG
1718 for method in ("sendUDPQuery", "sendTCPQuery"):
1719 sender = getattr(self, method)
1720 (_, receivedResponse) = sender(query, response=None, useQueue=False)
1721 self.assertEquals(receivedResponse, response)
7e687744
RG
1722
1723 # we mark the backend as down
1724 self.sendConsoleCommand("getServer(0):setDown()")
1725
1726 # we should NOT get a cached entry since it has ECS and we haven't asked the pool
1727 # to add ECS when no backend is up
6ca2e796
RG
1728 for method in ("sendUDPQuery", "sendTCPQuery"):
1729 sender = getattr(self, method)
1730 (_, receivedResponse) = sender(query, response=None, useQueue=False)
1731 self.assertEquals(receivedResponse, None)
7e687744
RG
1732
1733class TestCachingECSWithPoolECS(DNSDistTest):
1734
1735 _consoleKey = DNSDistTest.generateConsoleKey()
1736 _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
1737 _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort']
1738 _config_template = """
7d294573 1739 pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
7e687744
RG
1740 getPool(""):setCache(pc)
1741 getPool(""):setECS(true)
1742 setKey("%s")
1743 controlSocket("127.0.0.1:%d")
1744 newServer{address="127.0.0.1:%d", useClientSubnet=true}
1745 """
1746
1747 def testCached(self):
1748 """
1749 Cache: Cached entry with ECS is a hit when no backend are available
1750 """
1751 ttl = 600
1752 name = 'cached.cache-ecs-with-pool-ecs.tests.powerdns.com.'
1753 query = dns.message.make_query(name, 'AAAA', 'IN')
1754 response = dns.message.make_response(query)
1755 rrset = dns.rrset.from_text(name,
1756 ttl,
1757 dns.rdataclass.IN,
1758 dns.rdatatype.AAAA,
1759 '::1')
1760 response.answer.append(rrset)
1761
1762 # first query to fill the cache
6ca2e796
RG
1763 for method in ("sendUDPQuery", "sendTCPQuery"):
1764 sender = getattr(self, method)
1765 (receivedQuery, receivedResponse) = sender(query, response)
1766 self.assertTrue(receivedQuery)
1767 self.assertTrue(receivedResponse)
1768 receivedQuery.id = query.id
1769 self.assertEquals(query, receivedQuery)
1770 self.assertEquals(receivedResponse, response)
7e687744
RG
1771
1772 # next queries should hit the cache
6ca2e796
RG
1773 for method in ("sendUDPQuery", "sendTCPQuery"):
1774 sender = getattr(self, method)
1775 (_, receivedResponse) = sender(query, response=None, useQueue=False)
1776 self.assertEquals(receivedResponse, response)
7e687744
RG
1777
1778 # we mark the backend as down
1779 self.sendConsoleCommand("getServer(0):setDown()")
1780
1781 # we should STILL get a cached entry since it has ECS and we have asked the pool
1782 # to add ECS when no backend is up
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)
78e3ac9e
RG
1787
1788class TestCachingCollisionNoECSParsing(DNSDistTest):
1789
1790 _config_template = """
7d294573 1791 pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
78e3ac9e 1792 getPool(""):setCache(pc)
c1b81381 1793 newServer{address="127.0.0.1:%d"}
78e3ac9e
RG
1794 """
1795
1796 def testCacheCollisionNoECSParsing(self):
1797 """
1798 Cache: Collision with no ECS parsing
1799 """
1800 name = 'collision-no-ecs-parsing.cache.tests.powerdns.com.'
1801 ecso = clientsubnetoption.ClientSubnetOption('10.0.188.3', 32)
1802 query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
1803 query.flags = dns.flags.RD
1804 response = dns.message.make_response(query)
1805 rrset = dns.rrset.from_text(name,
1806 3600,
1807 dns.rdataclass.IN,
1808 dns.rdatatype.AAAA,
1809 '::1')
1810 response.answer.append(rrset)
1811
1812 # first query should to fill the cache
1813 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1814 self.assertTrue(receivedQuery)
1815 self.assertTrue(receivedResponse)
1816 receivedQuery.id = query.id
1817 self.assertEquals(query, receivedQuery)
1818 self.assertEquals(receivedResponse, response)
1819
1820 # second query will hash to the same key, triggering a collision which
1821 # will not be detected because the qname, qtype, qclass and flags will
1822 # match and EDNS Client Subnet parsing has not been enabled
1823 ecso2 = clientsubnetoption.ClientSubnetOption('10.0.192.138', 32)
1824 query2 = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso2], payload=512)
1825 query2.flags = dns.flags.RD
1826 (_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
1827 receivedResponse.id = response.id
1828 self.assertEquals(receivedResponse, response)
1829
1830class TestCachingCollisionWithECSParsing(DNSDistTest):
1831
1832 _config_template = """
7d294573 1833 pc = newPacketCache(100, {maxTTL=86400, minTTL=1, temporaryFailureTTL=60, staleTTL=60, dontAge=false, numberOfShards=1, deferrableInsertLock=true, maxNegativeTTL=3600, parseECS=true})
78e3ac9e 1834 getPool(""):setCache(pc)
c1b81381 1835 newServer{address="127.0.0.1:%d"}
78e3ac9e
RG
1836 """
1837
1838 def testCacheCollisionWithECSParsing(self):
1839 """
1840 Cache: Collision with ECS parsing
1841 """
1842 name = 'collision-with-ecs-parsing.cache.tests.powerdns.com.'
1843 ecso = clientsubnetoption.ClientSubnetOption('10.0.115.61', 32)
1844 query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
1845 query.flags = dns.flags.RD
1846 response = dns.message.make_response(query)
1847 rrset = dns.rrset.from_text(name,
1848 3600,
1849 dns.rdataclass.IN,
1850 dns.rdatatype.AAAA,
1851 '::1')
1852 response.answer.append(rrset)
1853
1854 # first query should to fill the cache
1855 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
1856 self.assertTrue(receivedQuery)
1857 self.assertTrue(receivedResponse)
1858 receivedQuery.id = query.id
1859 self.assertEquals(query, receivedQuery)
1860 self.assertEquals(receivedResponse, response)
1861
1862 # second query will hash to the same key, triggering a collision which
1863 # _will_ be detected this time because the qname, qtype, qclass and flags will
1864 # match but EDNS Client Subnet parsing is now enabled and will detect the issue
1865 ecso2 = clientsubnetoption.ClientSubnetOption('10.0.143.21', 32)
1866 query2 = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso2], payload=512)
1867 query2.flags = dns.flags.RD
1868 response2 = dns.message.make_response(query2)
1869 rrset = dns.rrset.from_text(name,
1870 3600,
1871 dns.rdataclass.IN,
1872 dns.rdatatype.AAAA,
1873 '2001:DB8::1')
1874 response2.answer.append(rrset)
1875 (receivedQuery, receivedResponse) = self.sendUDPQuery(query2, response2)
1876 self.assertEquals(receivedResponse, response2)
21d2b4c0
RG
1877
1878class TestCachingScopeZero(DNSDistTest):
1879
1880 _config_template = """
1881 -- Be careful to enable ECS parsing in the packet cache, otherwise scope zero is disabled
7d294573 1882 pc = newPacketCache(100, {maxTTL=86400, minTTL=1, temporaryFailureTTL=60, staleTTL=60, dontAge=false, numberOfShards=1, deferrableInsertLock=true, maxNegativeTTL=3600, parseECS=true})
21d2b4c0
RG
1883 getPool(""):setCache(pc)
1884 newServer{address="127.0.0.1:%d", useClientSubnet=true}
1885 -- to simulate a second client coming from a different IP address,
1886 -- we will force the ECS value added to the query if RD is set (note that we need
1887 -- to unset it using rules before the first cache lookup)
1888 addAction(RDRule(), SetECSAction("192.0.2.1/32"))
1889 addAction(RDRule(), NoRecurseAction())
1890 """
1891
1892 def testScopeZero(self):
1893 """
1894 Cache: Test the scope-zero feature, backend returns a scope of zero
1895 """
1896 ttl = 600
1897 name = 'scope-zero.cache.tests.powerdns.com.'
1898 query = dns.message.make_query(name, 'AAAA', 'IN')
1899 query.flags &= ~dns.flags.RD
1900 ecso = clientsubnetoption.ClientSubnetOption('127.0.0.0', 24)
1901 expectedQuery = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
1902 expectedQuery.flags &= ~dns.flags.RD
1903 ecsoResponse = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24, 0)
1904 expectedResponse = dns.message.make_response(query)
1905 scopedResponse = dns.message.make_response(query)
1906 scopedResponse.use_edns(edns=True, payload=4096, options=[ecsoResponse])
1907 rrset = dns.rrset.from_text(name,
1908 ttl,
1909 dns.rdataclass.IN,
1910 dns.rdatatype.AAAA,
1911 '::1')
1912 scopedResponse.answer.append(rrset)
1913 expectedResponse.answer.append(rrset)
1914
1915 for method in ("sendUDPQuery", "sendTCPQuery"):
1916 sender = getattr(self, method)
1917 (receivedQuery, receivedResponse) = sender(query, scopedResponse)
1918 receivedQuery.id = expectedQuery.id
1919 self.checkMessageEDNSWithECS(expectedQuery, receivedQuery)
1920 self.checkMessageNoEDNS(receivedResponse, expectedResponse)
1921
1922 # next query should hit the cache, nothing special about that
1923 for method in ("sendUDPQuery", "sendTCPQuery"):
1924 sender = getattr(self, method)
1925 (_, receivedResponse) = sender(query, response=None, useQueue=False)
1926 self.checkMessageNoEDNS(receivedResponse, expectedResponse)
1927
1928 query = dns.message.make_query(name, 'AAAA', 'IN')
1929 query.flags &= dns.flags.RD
1930 # next query FROM A DIFFERENT CLIENT since RD is now set should STILL hit the cache
1931 for method in ("sendUDPQuery", "sendTCPQuery"):
1932 sender = getattr(self, method)
1933 (_, receivedResponse) = sender(query, response=None, useQueue=False)
1934 receivedResponse.id = expectedResponse.id
1935 self.checkMessageNoEDNS(receivedResponse, expectedResponse)
1936
1937 name = 'scope-zero-with-ecs.cache.tests.powerdns.com.'
1938 ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
1939 query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
1940 query.flags &= ~dns.flags.RD
1941 expectedQuery = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
1942 expectedQuery.flags &= ~dns.flags.RD
1943 expectedResponse = dns.message.make_response(query)
1944 expectedResponse.use_edns(edns=True, payload=4096, options=[ecsoResponse])
1945 expectedResponse.answer.append(rrset)
1946 scopedResponse = dns.message.make_response(query)
1947 scopedResponse.use_edns(edns=True, payload=4096, options=[ecsoResponse])
1948 scopedResponse.answer.append(rrset)
1949 # this query has ECS, it should NOT be able to use the scope-zero cached entry since the hash will be
1950 # different
1951 for method in ("sendUDPQuery", "sendTCPQuery"):
1952 sender = getattr(self, method)
1953 (receivedQuery, receivedResponse) = sender(query, scopedResponse)
1954 receivedQuery.id = expectedQuery.id
1955 self.checkMessageEDNSWithECS(expectedQuery, receivedQuery)
21d2b4c0
RG
1956 self.checkMessageEDNSWithECS(receivedResponse, expectedResponse)
1957
1958 # it should still have been cached, though, so the next query should be a hit
1959 for method in ("sendUDPQuery", "sendTCPQuery"):
1960 sender = getattr(self, method)
1961 (_, receivedResponse) = sender(query, response=None, useQueue=False)
1962 self.checkMessageEDNSWithECS(receivedResponse, expectedResponse)
1963
1964 def testScopeNotZero(self):
1965 """
1966 Cache: Test the scope-zero feature, backend returns a scope of non-zero
1967 """
1968 ttl = 600
1969 name = 'scope-not-zero.cache.tests.powerdns.com.'
1970 query = dns.message.make_query(name, 'AAAA', 'IN')
1971 query.flags &= ~dns.flags.RD
1972 ecso = clientsubnetoption.ClientSubnetOption('127.0.0.0', 24)
1973 expectedQuery = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
1974 expectedQuery.flags &= ~dns.flags.RD
1975 ecso2 = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
1976 expectedQuery2 = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso2], payload=512)
1977 expectedQuery2.flags &= ~dns.flags.RD
1978 ecsoResponse = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24, 24)
1979 ecsoResponse2 = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32, 24)
1980 rrset = dns.rrset.from_text(name,
1981 ttl,
1982 dns.rdataclass.IN,
1983 dns.rdatatype.AAAA,
1984 '::1')
1985 expectedResponse = dns.message.make_response(query)
1986 expectedResponse.answer.append(rrset)
1987 scopedResponse = dns.message.make_response(query)
1988 scopedResponse.use_edns(edns=True, payload=4096, options=[ecsoResponse])
1989 scopedResponse.answer.append(rrset)
1990 scopedResponse2 = dns.message.make_response(query)
1991 scopedResponse2.use_edns(edns=True, payload=4096, options=[ecsoResponse2])
1992 scopedResponse2.answer.append(rrset)
1993
1994 for method in ("sendUDPQuery", "sendTCPQuery"):
1995 sender = getattr(self, method)
1996 (receivedQuery, receivedResponse) = sender(query, scopedResponse)
1997 receivedQuery.id = expectedQuery.id
1998 self.checkMessageEDNSWithECS(expectedQuery, receivedQuery)
1999 self.checkMessageNoEDNS(receivedResponse, expectedResponse)
2000
2001 # next query should hit the cache, nothing special about that
2002 for method in ("sendUDPQuery", "sendTCPQuery"):
2003 sender = getattr(self, method)
2004 (_, receivedResponse) = sender(query, response=None, useQueue=False)
2005 self.checkMessageNoEDNS(receivedResponse, expectedResponse)
2006
2007 query = dns.message.make_query(name, 'AAAA', 'IN')
2008 query.flags &= dns.flags.RD
2009 expectedResponse = dns.message.make_response(query)
2010 expectedResponse.answer.append(rrset)
2011 # next query FROM A DIFFERENT CLIENT since RD is now set should NOT hit the cache
2012 for method in ("sendUDPQuery", "sendTCPQuery"):
2013 sender = getattr(self, method)
2014 (receivedQuery, receivedResponse) = sender(query, scopedResponse2)
2015 receivedQuery.id = expectedQuery2.id
2016 self.checkMessageEDNSWithECS(expectedQuery2, receivedQuery)
2017 self.checkMessageNoEDNS(receivedResponse, expectedResponse)
2018
2019 def testNoECS(self):
2020 """
2021 Cache: Test the scope-zero feature, backend returns no ECS at all
2022 """
2023 ttl = 600
2024 name = 'scope-zero-no-ecs.cache.tests.powerdns.com.'
2025 query = dns.message.make_query(name, 'AAAA', 'IN')
2026 query.flags &= ~dns.flags.RD
2027 ecso = clientsubnetoption.ClientSubnetOption('127.0.0.0', 24)
2028 expectedQuery = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
2029 expectedQuery.flags &= ~dns.flags.RD
2030 ecso2 = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
2031 expectedQuery2 = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso2], payload=512)
2032 expectedQuery2.flags &= ~dns.flags.RD
2033 rrset = dns.rrset.from_text(name,
2034 ttl,
2035 dns.rdataclass.IN,
2036 dns.rdatatype.AAAA,
2037 '::1')
2038 response = dns.message.make_response(query)
2039 response.answer.append(rrset)
2040
2041 for method in ("sendUDPQuery", "sendTCPQuery"):
2042 sender = getattr(self, method)
2043 (receivedQuery, receivedResponse) = sender(query, response)
2044 receivedQuery.id = expectedQuery.id
2045 self.checkMessageEDNSWithECS(expectedQuery, receivedQuery)
2046 self.checkMessageNoEDNS(receivedResponse, response)
2047
2048 # next query should hit the cache, nothing special about that
2049 for method in ("sendUDPQuery", "sendTCPQuery"):
2050 sender = getattr(self, method)
2051 (_, receivedResponse) = sender(query, response=None, useQueue=False)
2052 self.checkMessageNoEDNS(receivedResponse, response)
2053
2054 query = dns.message.make_query(name, 'AAAA', 'IN')
2055 query.flags &= dns.flags.RD
2056 response = dns.message.make_response(query)
2057 response.answer.append(rrset)
2058 # next query FROM A DIFFERENT CLIENT since RD is now set should NOT hit the cache
2059 for method in ("sendUDPQuery", "sendTCPQuery"):
2060 sender = getattr(self, method)
2061 (receivedQuery, receivedResponse) = sender(query, response)
2062 receivedQuery.id = expectedQuery2.id
2063 self.checkMessageEDNSWithECS(expectedQuery2, receivedQuery)
2064 self.checkMessageNoEDNS(receivedResponse, response)
2065
2066class TestCachingScopeZeroButNoSubnetcheck(DNSDistTest):
2067
2068 _config_template = """
2069 -- We disable ECS parsing in the packet cache, meaning scope zero is disabled
7d294573 2070 pc = newPacketCache(100, {maxTTL=86400, minTTL=1, temporaryFailureTTL=60, staleTTL=60, dontAge=false, numberOfShards=1, deferrableInsertLock=true, maxNegativeTTL=3600, parseECS=false})
21d2b4c0
RG
2071 getPool(""):setCache(pc)
2072 newServer{address="127.0.0.1:%d", useClientSubnet=true}
2073 -- to simulate a second client coming from a different IP address,
2074 -- we will force the ECS value added to the query if RD is set (note that we need
2075 -- to unset it using rules before the first cache lookup)
2076 addAction(RDRule(), SetECSAction("192.0.2.1/32"))
2077 addAction(RDRule(), NoRecurseAction())
2078 """
2079
2080 def testScopeZero(self):
2081 """
2082 Cache: Test that the scope-zero feature is disabled when ECS parsing is not enabled in the cache
2083 """
2084 ttl = 600
2085 name = 'scope-zero-no-subnet.cache.tests.powerdns.com.'
2086 query = dns.message.make_query(name, 'AAAA', 'IN')
2087 query.flags &= ~dns.flags.RD
2088 ecso = clientsubnetoption.ClientSubnetOption('127.0.0.0', 24)
2089 expectedQuery = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
2090 expectedQuery.flags &= ~dns.flags.RD
2091 ecso2 = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
2092 expectedQuery2 = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso2], payload=512)
2093 expectedQuery2.flags &= ~dns.flags.RD
2094 ecsoResponse = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24, 0)
2095 expectedResponse = dns.message.make_response(query)
2096 scopedResponse = dns.message.make_response(query)
2097 scopedResponse.use_edns(edns=True, payload=4096, options=[ecsoResponse])
2098 rrset = dns.rrset.from_text(name,
2099 ttl,
2100 dns.rdataclass.IN,
2101 dns.rdatatype.AAAA,
2102 '::1')
2103 scopedResponse.answer.append(rrset)
2104 expectedResponse.answer.append(rrset)
2105
2106 for method in ("sendUDPQuery", "sendTCPQuery"):
2107 sender = getattr(self, method)
2108 (receivedQuery, receivedResponse) = sender(query, scopedResponse)
2109 receivedQuery.id = expectedQuery.id
2110 self.checkMessageEDNSWithECS(expectedQuery, receivedQuery)
2111 self.checkMessageNoEDNS(receivedResponse, expectedResponse)
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, expectedResponse)
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)