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