]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/recursordist/test-negcache_cc.cc
Call the right wipe function for negcache
[thirdparty/pdns.git] / pdns / recursordist / test-negcache_cc.cc
1 #define BOOST_TEST_DYN_LINK
2 #define BOOST_TEST_NO_MAIN
3 #include <boost/test/unit_test.hpp>
4
5 #include "negcache.hh"
6 #include "dnsrecords.hh"
7 #include "utility.hh"
8
9 static recordsAndSignatures genRecsAndSigs(const DNSName& name, const uint16_t qtype, const string& content, bool sigs)
10 {
11 recordsAndSignatures ret;
12
13 DNSRecord rec;
14 rec.d_name = name;
15 rec.d_type = qtype;
16 rec.d_ttl = 600;
17 rec.d_place = DNSResourceRecord::AUTHORITY;
18 rec.d_content = DNSRecordContent::mastermake(qtype, QClass::IN, content);
19
20 ret.records.push_back(rec);
21
22 if (sigs) {
23 rec.d_type = QType::RRSIG;
24 rec.d_content = std::make_shared<RRSIGRecordContent>(QType(qtype).toString() + " 5 3 600 2037010100000000 2037010100000000 24567 dummy data");
25 ret.signatures.push_back(rec);
26 }
27
28 return ret;
29 }
30
31 static NegCache::NegCacheEntry genNegCacheEntry(const DNSName& name, const DNSName& auth, const struct timeval& now, const uint16_t qtype = 0)
32 {
33 NegCache::NegCacheEntry ret;
34
35 ret.d_name = name;
36 ret.d_qtype = QType(qtype);
37 ret.d_auth = auth;
38 ret.d_ttd = now.tv_sec + 600;
39 ret.d_orig_ttl = 600;
40 ret.authoritySOA = genRecsAndSigs(auth, QType::SOA, "ns1 hostmaster 1 2 3 4 5", true);
41 ret.DNSSECRecords = genRecsAndSigs(auth, QType::NSEC, "deadbeef", true);
42
43 return ret;
44 }
45
46 BOOST_AUTO_TEST_SUITE(negcache_cc)
47
48 BOOST_AUTO_TEST_CASE(test_get_entry)
49 {
50 /* Add a full name negative entry to the cache and attempt to get an entry for
51 * the A record. Should yield the full name does not exist entry
52 */
53 DNSName qname("www2.powerdns.com");
54 DNSName auth("powerdns.com");
55
56 struct timeval now;
57 Utility::gettimeofday(&now, 0);
58
59 NegCache cache;
60 cache.add(genNegCacheEntry(qname, auth, now));
61
62 BOOST_CHECK_EQUAL(cache.size(), 1U);
63
64 NegCache::NegCacheEntry ne;
65 bool ret = cache.get(qname, QType(1), now, ne);
66
67 BOOST_CHECK(ret);
68 BOOST_CHECK_EQUAL(ne.d_name, qname);
69 BOOST_CHECK_EQUAL(ne.d_qtype.toString(), QType(0).toString());
70 BOOST_CHECK_EQUAL(ne.d_auth, auth);
71 }
72
73 BOOST_AUTO_TEST_CASE(test_get_entry2038)
74 {
75 /* Add a full name negative entry to the cache and attempt to get an entry for
76 * the A record. Should yield the full name does not exist entry
77 */
78 DNSName qname("www2.powerdns.com");
79 DNSName auth("powerdns.com");
80
81 timeval now{INT_MAX - 300, 0};
82
83 NegCache cache;
84 cache.add(genNegCacheEntry(qname, auth, now));
85
86 BOOST_CHECK_EQUAL(cache.size(), 1U);
87
88 NegCache::NegCacheEntry ne;
89 bool ret = cache.get(qname, QType(QType::A), now, ne);
90
91 BOOST_CHECK(ret);
92 BOOST_CHECK_EQUAL(ne.d_name, qname);
93 BOOST_CHECK_EQUAL(ne.d_qtype.toString(), QType(0).toString());
94 BOOST_CHECK_EQUAL(ne.d_auth, auth);
95 }
96
97 BOOST_AUTO_TEST_CASE(test_get_entry_exact_type)
98 {
99 /* Add a full name negative entry to the cache and attempt to get an entry for
100 * the A record, asking only for an exact match.
101 */
102 DNSName qname("www2.powerdns.com");
103 DNSName auth("powerdns.com");
104
105 struct timeval now;
106 Utility::gettimeofday(&now, 0);
107
108 NegCache cache;
109 cache.add(genNegCacheEntry(qname, auth, now));
110
111 BOOST_CHECK_EQUAL(cache.size(), 1U);
112
113 NegCache::NegCacheEntry ne;
114 bool ret = cache.get(qname, QType(1), now, ne, true);
115
116 BOOST_CHECK_EQUAL(ret, false);
117 }
118
119 BOOST_AUTO_TEST_CASE(test_get_NODATA_entry)
120 {
121 DNSName qname("www2.powerdns.com");
122 DNSName auth("powerdns.com");
123
124 struct timeval now;
125 Utility::gettimeofday(&now, 0);
126
127 NegCache cache;
128 cache.add(genNegCacheEntry(qname, auth, now, 1));
129
130 BOOST_CHECK_EQUAL(cache.size(), 1U);
131
132 NegCache::NegCacheEntry ne;
133 bool ret = cache.get(qname, QType(1), now, ne);
134
135 BOOST_CHECK(ret);
136 BOOST_CHECK_EQUAL(ne.d_name, qname);
137 BOOST_CHECK_EQUAL(ne.d_qtype.toString(), QType(1).toString());
138 BOOST_CHECK_EQUAL(ne.d_auth, auth);
139
140 NegCache::NegCacheEntry ne2;
141 ret = cache.get(qname, QType(16), now, ne2);
142 BOOST_CHECK_EQUAL(ret, false);
143 }
144
145 BOOST_AUTO_TEST_CASE(test_getRootNXTrust_entry)
146 {
147 DNSName qname("com");
148 DNSName auth(".");
149
150 struct timeval now;
151 Utility::gettimeofday(&now, 0);
152
153 NegCache cache;
154 cache.add(genNegCacheEntry(qname, auth, now));
155
156 BOOST_CHECK_EQUAL(cache.size(), 1U);
157
158 NegCache::NegCacheEntry ne;
159 bool ret = cache.getRootNXTrust(qname, now, ne, false, false);
160
161 BOOST_CHECK(ret);
162 BOOST_CHECK_EQUAL(ne.d_name, qname);
163 BOOST_CHECK_EQUAL(ne.d_qtype.toString(), QType(0).toString());
164 BOOST_CHECK_EQUAL(ne.d_auth, auth);
165 }
166
167 BOOST_AUTO_TEST_CASE(test_add_and_get_expired_entry)
168 {
169 DNSName qname("www2.powerdns.com");
170 DNSName auth("powerdns.com");
171
172 struct timeval now;
173 Utility::gettimeofday(&now, 0);
174 now.tv_sec -= 1000;
175
176 NegCache cache;
177 cache.add(genNegCacheEntry(qname, auth, now));
178
179 BOOST_CHECK_EQUAL(cache.size(), 1U);
180
181 NegCache::NegCacheEntry ne;
182
183 now.tv_sec += 1000;
184 bool ret = cache.get(qname, QType(1), now, ne);
185
186 BOOST_CHECK_EQUAL(ret, false);
187 }
188
189 BOOST_AUTO_TEST_CASE(test_getRootNXTrust_expired_entry)
190 {
191 DNSName qname("com");
192 DNSName auth(".");
193
194 struct timeval now;
195 Utility::gettimeofday(&now, 0);
196 now.tv_sec -= 1000;
197
198 NegCache cache;
199 cache.add(genNegCacheEntry(qname, auth, now));
200
201 BOOST_CHECK_EQUAL(cache.size(), 1U);
202
203 NegCache::NegCacheEntry ne;
204
205 now.tv_sec += 1000;
206 bool ret = cache.getRootNXTrust(qname, now, ne, false, false);
207
208 BOOST_CHECK_EQUAL(ret, false);
209 }
210
211 BOOST_AUTO_TEST_CASE(test_add_updated_entry)
212 {
213 DNSName qname("www2.powerdns.com");
214 DNSName auth("powerdns.com");
215 DNSName auth2("com");
216
217 struct timeval now;
218 Utility::gettimeofday(&now, 0);
219
220 NegCache cache;
221 cache.add(genNegCacheEntry(qname, auth, now));
222 // Should override the existing entry for www2.powerdns.com
223 cache.add(genNegCacheEntry(qname, auth2, now));
224
225 BOOST_CHECK_EQUAL(cache.size(), 1U);
226
227 NegCache::NegCacheEntry ne;
228 bool ret = cache.get(qname, QType(1), now, ne);
229
230 BOOST_CHECK(ret);
231 BOOST_CHECK_EQUAL(ne.d_name, qname);
232 BOOST_CHECK_EQUAL(ne.d_auth, auth2);
233 }
234
235 BOOST_AUTO_TEST_CASE(test_getRootNXTrust)
236 {
237 DNSName qname("www2.powerdns.com");
238 DNSName auth("powerdns.com");
239 DNSName qname2("com");
240 DNSName auth2(".");
241
242 struct timeval now;
243 Utility::gettimeofday(&now, 0);
244
245 NegCache cache;
246 cache.add(genNegCacheEntry(qname, auth, now));
247 cache.add(genNegCacheEntry(qname2, auth2, now));
248
249 NegCache::NegCacheEntry ne;
250 bool ret = cache.getRootNXTrust(qname, now, ne, false, false);
251
252 BOOST_CHECK(ret);
253 BOOST_CHECK_EQUAL(ne.d_name, qname2);
254 BOOST_CHECK_EQUAL(ne.d_auth, auth2);
255 }
256
257 BOOST_AUTO_TEST_CASE(test_getRootNXTrust_full_domain_only)
258 {
259 DNSName qname("www2.powerdns.com");
260 DNSName auth("powerdns.com");
261 DNSName qname2("com");
262 DNSName auth2(".");
263
264 struct timeval now;
265 Utility::gettimeofday(&now, 0);
266
267 NegCache cache;
268 cache.add(genNegCacheEntry(qname, auth, now));
269 cache.add(genNegCacheEntry(qname2, auth2, now, 1)); // Add the denial for COM|A
270
271 NegCache::NegCacheEntry ne;
272 bool ret = cache.getRootNXTrust(qname, now, ne, false, false);
273
274 BOOST_CHECK_EQUAL(ret, false);
275 }
276
277 BOOST_AUTO_TEST_CASE(test_prune)
278 {
279 string qname(".powerdns.com");
280 DNSName auth("powerdns.com");
281
282 struct timeval now;
283 Utility::gettimeofday(&now, 0);
284
285 NegCache cache(1);
286 NegCache::NegCacheEntry ne;
287 for (int i = 0; i < 400; i++) {
288 ne = genNegCacheEntry(DNSName(std::to_string(i) + qname), auth, now);
289 cache.add(ne);
290 }
291
292 BOOST_CHECK_EQUAL(cache.size(), 400U);
293
294 cache.prune(100);
295
296 BOOST_CHECK_EQUAL(cache.size(), 100U);
297 }
298
299 BOOST_AUTO_TEST_CASE(test_prune_many_shards)
300 {
301 string qname(".powerdns.com");
302 DNSName auth("powerdns.com");
303
304 struct timeval now;
305 Utility::gettimeofday(&now, 0);
306
307 NegCache cache;
308 NegCache::NegCacheEntry ne;
309 for (int i = 0; i < 400; i++) {
310 ne = genNegCacheEntry(DNSName(std::to_string(i) + qname), auth, now);
311 cache.add(ne);
312 }
313
314 BOOST_CHECK_EQUAL(cache.size(), 400U);
315
316 cache.prune(100);
317
318 BOOST_CHECK_EQUAL(cache.size(), 100U);
319 }
320
321 BOOST_AUTO_TEST_CASE(test_prune_valid_entries)
322 {
323 DNSName power1("powerdns.com.");
324 DNSName power2("powerdns-1.com.");
325 DNSName auth("com.");
326
327 struct timeval now;
328 Utility::gettimeofday(&now, 0);
329
330 NegCache cache;
331 NegCache::NegCacheEntry ne;
332
333 /* insert power1 then power2 */
334 ne = genNegCacheEntry(power1, auth, now);
335 cache.add(ne);
336 ne = genNegCacheEntry(power2, auth, now);
337 cache.add(ne);
338
339 BOOST_CHECK_EQUAL(cache.size(), 2U);
340
341 /* power2 has been inserted more recently, so it should be
342 removed last */
343 cache.prune(1);
344 BOOST_CHECK_EQUAL(cache.size(), 1U);
345
346 NegCache::NegCacheEntry got;
347 bool ret = cache.get(power2, QType(1), now, got);
348 BOOST_REQUIRE(ret);
349 BOOST_CHECK_EQUAL(got.d_name, power2);
350 BOOST_CHECK_EQUAL(got.d_auth, auth);
351
352 /* insert power1 back */
353 ne = genNegCacheEntry(power1, auth, now);
354 cache.add(ne);
355 BOOST_CHECK_EQUAL(cache.size(), 2U);
356
357 /* replace the entry for power2 */
358 ne = genNegCacheEntry(power2, auth, now);
359 cache.add(ne);
360
361 BOOST_CHECK_EQUAL(cache.size(), 2U);
362
363 /* power2 has been updated more recently, so it should be
364 removed last */
365 cache.prune(1);
366
367 BOOST_CHECK_EQUAL(cache.size(), 1U);
368 got = NegCache::NegCacheEntry();
369 ret = cache.get(power2, QType(1), now, got);
370 BOOST_REQUIRE(ret);
371 BOOST_CHECK_EQUAL(got.d_name, power2);
372 BOOST_CHECK_EQUAL(got.d_auth, auth);
373 }
374
375 BOOST_AUTO_TEST_CASE(test_wipe_single)
376 {
377 string qname(".powerdns.com");
378 DNSName auth("powerdns.com");
379
380 struct timeval now;
381 Utility::gettimeofday(&now, 0);
382
383 NegCache cache;
384 NegCache::NegCacheEntry ne;
385 ne = genNegCacheEntry(auth, auth, now);
386 cache.add(ne);
387
388 for (int i = 0; i < 400; i++) {
389 ne = genNegCacheEntry(DNSName(std::to_string(i) + qname), auth, now);
390 cache.add(ne);
391 }
392
393 BOOST_CHECK_EQUAL(cache.size(), 401U);
394
395 // Should only wipe the powerdns.com entry
396 cache.wipe(auth);
397 BOOST_CHECK_EQUAL(cache.size(), 400U);
398
399 NegCache::NegCacheEntry ne2;
400 bool ret = cache.get(auth, QType(1), now, ne2);
401
402 BOOST_CHECK_EQUAL(ret, false);
403
404 cache.wipe(DNSName("1.powerdns.com"));
405 BOOST_CHECK_EQUAL(cache.size(), 399U);
406
407 NegCache::NegCacheEntry ne3;
408 ret = cache.get(auth, QType(1), now, ne3);
409
410 BOOST_CHECK_EQUAL(ret, false);
411 }
412
413 BOOST_AUTO_TEST_CASE(test_wipe_subtree)
414 {
415 string qname(".powerdns.com");
416 string qname2("powerdns.org");
417 DNSName auth("powerdns.com");
418
419 struct timeval now;
420 Utility::gettimeofday(&now, 0);
421
422 NegCache cache;
423 NegCache::NegCacheEntry ne;
424 ne = genNegCacheEntry(auth, auth, now);
425 cache.add(ne);
426
427 for (int i = 0; i < 400; i++) {
428 ne = genNegCacheEntry(DNSName(std::to_string(i) + qname), auth, now);
429 cache.add(ne);
430 ne = genNegCacheEntry(DNSName(std::to_string(i) + qname2), auth, now);
431 cache.add(ne);
432 }
433
434 BOOST_CHECK_EQUAL(cache.size(), 801U);
435
436 // Should wipe all the *.powerdns.com and powerdns.com entries
437 cache.wipe(auth, true);
438 BOOST_CHECK_EQUAL(cache.size(), 400U);
439 }
440
441 BOOST_AUTO_TEST_CASE(test_wipe_typed)
442 {
443 string qname(".powerdns.com");
444 DNSName auth("powerdns.com");
445
446 struct timeval now;
447 Utility::gettimeofday(&now, 0);
448
449 NegCache cache;
450 NegCache::NegCacheEntry ne;
451 ne = genNegCacheEntry(auth, auth, now, QType::A);
452 cache.add(ne);
453
454 for (int i = 0; i < 400; i++) {
455 ne = genNegCacheEntry(DNSName(std::to_string(i) + qname), auth, now, QType::A);
456 cache.add(ne);
457 }
458
459 BOOST_CHECK_EQUAL(cache.size(), 401U);
460
461 // Should only wipe the powerdns.com entry
462 cache.wipeTyped(auth, QType::A);
463 BOOST_CHECK_EQUAL(cache.size(), 400U);
464
465 NegCache::NegCacheEntry ne2;
466 bool ret = cache.get(auth, QType(1), now, ne2);
467
468 BOOST_CHECK_EQUAL(ret, false);
469
470 cache.wipeTyped(DNSName("1.powerdns.com"), QType::A);
471 BOOST_CHECK_EQUAL(cache.size(), 399U);
472
473 NegCache::NegCacheEntry ne3;
474 ret = cache.get(auth, QType(1), now, ne3);
475
476 BOOST_CHECK_EQUAL(ret, false);
477 }
478
479 BOOST_AUTO_TEST_CASE(test_clear)
480 {
481 string qname(".powerdns.com");
482 DNSName auth("powerdns.com");
483
484 struct timeval now;
485 Utility::gettimeofday(&now, 0);
486
487 NegCache cache;
488 NegCache::NegCacheEntry ne;
489
490 for (int i = 0; i < 400; i++) {
491 ne = genNegCacheEntry(DNSName(std::to_string(i) + qname), auth, now);
492 cache.add(ne);
493 }
494
495 BOOST_CHECK_EQUAL(cache.size(), 400U);
496 cache.clear();
497 BOOST_CHECK_EQUAL(cache.size(), 0U);
498 }
499
500 BOOST_AUTO_TEST_CASE(test_dumpToFile)
501 {
502 NegCache cache(1);
503 vector<string> expected = {
504 "; negcache dump follows\n",
505 ";\n",
506 "; negcache shard 0; size 2\n",
507 "www1.powerdns.com. 600 IN TYPE0 VIA powerdns.com. ; (Indeterminate) origttl=600 ss=0\n",
508 "powerdns.com. 600 IN SOA ns1. hostmaster. 1 2 3 4 5 ; (Indeterminate)\n",
509 "powerdns.com. 600 IN RRSIG SOA 5 3 600 20370101000000 20370101000000 24567 dummy. data ;\n",
510 "powerdns.com. 600 IN NSEC deadbeef. ; (Indeterminate)\n",
511 "powerdns.com. 600 IN RRSIG NSEC 5 3 600 20370101000000 20370101000000 24567 dummy. data ;\n",
512 "www2.powerdns.com. 600 IN TYPE0 VIA powerdns.com. ; (Indeterminate) origttl=600 ss=0\n",
513 "powerdns.com. 600 IN SOA ns1. hostmaster. 1 2 3 4 5 ; (Indeterminate)\n",
514 "powerdns.com. 600 IN RRSIG SOA 5 3 600 20370101000000 20370101000000 24567 dummy. data ;\n",
515 "powerdns.com. 600 IN NSEC deadbeef. ; (Indeterminate)\n",
516 "powerdns.com. 600 IN RRSIG NSEC 5 3 600 20370101000000 20370101000000 24567 dummy. data ;\n",
517 "; negcache size: 2/0 shards: 1 min/max shard size: 2/2\n"};
518
519 struct timeval now;
520 Utility::gettimeofday(&now, 0);
521
522 cache.add(genNegCacheEntry(DNSName("www1.powerdns.com"), DNSName("powerdns.com"), now));
523 cache.add(genNegCacheEntry(DNSName("www2.powerdns.com"), DNSName("powerdns.com"), now));
524
525 auto fp = std::unique_ptr<FILE, int (*)(FILE*)>(tmpfile(), fclose);
526 if (!fp)
527 BOOST_FAIL("Temporary file could not be opened");
528
529 cache.doDump(fileno(fp.get()), 0);
530
531 rewind(fp.get());
532 char* line = nullptr;
533 size_t len = 0;
534 ssize_t read;
535
536 for (auto str : expected) {
537 read = getline(&line, &len, fp.get());
538 if (read == -1)
539 BOOST_FAIL("Unable to read a line from the temp file");
540 // The clock might have ticked so the 600 becomes 599
541 BOOST_CHECK_EQUAL(line, str);
542 }
543
544 /* getline() allocates a buffer when called with a nullptr,
545 then reallocates it when needed, but we need to free the
546 last allocation if any. */
547 free(line);
548 }
549
550 BOOST_AUTO_TEST_CASE(test_count)
551 {
552 string qname(".powerdns.com");
553 string qname2("powerdns.org");
554 DNSName auth("powerdns.com");
555
556 struct timeval now;
557 Utility::gettimeofday(&now, 0);
558
559 NegCache cache;
560 NegCache::NegCacheEntry ne;
561 ne = genNegCacheEntry(auth, auth, now);
562 cache.add(ne);
563
564 for (int i = 0; i < 400; i++) {
565 ne = genNegCacheEntry(DNSName(std::to_string(i) + qname), auth, now);
566 cache.add(ne);
567 ne = genNegCacheEntry(DNSName(std::to_string(i) + qname2), auth, now);
568 cache.add(ne);
569 }
570
571 uint64_t count;
572 count = cache.count(auth);
573 BOOST_CHECK_EQUAL(count, 1U);
574 count = cache.count(auth, QType(1));
575 BOOST_CHECK_EQUAL(count, 0U);
576 }
577
578 BOOST_AUTO_TEST_SUITE_END()