1 #define BOOST_TEST_DYN_LINK
2 #define BOOST_TEST_NO_MAIN
3 #include <boost/test/unit_test.hpp>
6 #include "dnsrecords.hh"
9 static recordsAndSignatures
genRecsAndSigs(const DNSName
& name
, const uint16_t qtype
, const string
& content
, bool sigs
)
11 recordsAndSignatures ret
;
17 rec
.d_place
= DNSResourceRecord::AUTHORITY
;
18 rec
.d_content
= DNSRecordContent::mastermake(qtype
, QClass::IN
, content
);
20 ret
.records
.push_back(rec
);
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
);
31 static NegCache::NegCacheEntry
genNegCacheEntry(const DNSName
& name
, const DNSName
& auth
, const struct timeval
& now
, const uint16_t qtype
= 0)
33 NegCache::NegCacheEntry ret
;
36 ret
.d_qtype
= QType(qtype
);
38 ret
.d_ttd
= now
.tv_sec
+ 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);
46 BOOST_AUTO_TEST_SUITE(negcache_cc
)
48 BOOST_AUTO_TEST_CASE(test_get_entry
)
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
53 DNSName
qname("www2.powerdns.com");
54 DNSName
auth("powerdns.com");
57 Utility::gettimeofday(&now
, 0);
60 cache
.add(genNegCacheEntry(qname
, auth
, now
));
62 BOOST_CHECK_EQUAL(cache
.size(), 1U);
64 NegCache::NegCacheEntry ne
;
65 bool ret
= cache
.get(qname
, QType(1), now
, ne
);
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
);
73 BOOST_AUTO_TEST_CASE(test_get_entry2038
)
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
78 DNSName
qname("www2.powerdns.com");
79 DNSName
auth("powerdns.com");
81 timeval now
{INT_MAX
- 300, 0};
84 cache
.add(genNegCacheEntry(qname
, auth
, now
));
86 BOOST_CHECK_EQUAL(cache
.size(), 1U);
88 NegCache::NegCacheEntry ne
;
89 bool ret
= cache
.get(qname
, QType(QType::A
), now
, ne
);
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
);
97 BOOST_AUTO_TEST_CASE(test_get_entry_exact_type
)
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.
102 DNSName
qname("www2.powerdns.com");
103 DNSName
auth("powerdns.com");
106 Utility::gettimeofday(&now
, 0);
109 cache
.add(genNegCacheEntry(qname
, auth
, now
));
111 BOOST_CHECK_EQUAL(cache
.size(), 1U);
113 NegCache::NegCacheEntry ne
;
114 bool ret
= cache
.get(qname
, QType(1), now
, ne
, true);
116 BOOST_CHECK_EQUAL(ret
, false);
119 BOOST_AUTO_TEST_CASE(test_get_NODATA_entry
)
121 DNSName
qname("www2.powerdns.com");
122 DNSName
auth("powerdns.com");
125 Utility::gettimeofday(&now
, 0);
128 cache
.add(genNegCacheEntry(qname
, auth
, now
, 1));
130 BOOST_CHECK_EQUAL(cache
.size(), 1U);
132 NegCache::NegCacheEntry ne
;
133 bool ret
= cache
.get(qname
, QType(1), now
, ne
);
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
);
140 NegCache::NegCacheEntry ne2
;
141 ret
= cache
.get(qname
, QType(16), now
, ne2
);
142 BOOST_CHECK_EQUAL(ret
, false);
145 BOOST_AUTO_TEST_CASE(test_getRootNXTrust_entry
)
147 DNSName
qname("com");
151 Utility::gettimeofday(&now
, 0);
154 cache
.add(genNegCacheEntry(qname
, auth
, now
));
156 BOOST_CHECK_EQUAL(cache
.size(), 1U);
158 NegCache::NegCacheEntry ne
;
159 bool ret
= cache
.getRootNXTrust(qname
, now
, ne
, false, false);
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
);
167 BOOST_AUTO_TEST_CASE(test_add_and_get_expired_entry
)
169 DNSName
qname("www2.powerdns.com");
170 DNSName
auth("powerdns.com");
173 Utility::gettimeofday(&now
, 0);
177 cache
.add(genNegCacheEntry(qname
, auth
, now
));
179 BOOST_CHECK_EQUAL(cache
.size(), 1U);
181 NegCache::NegCacheEntry ne
;
184 bool ret
= cache
.get(qname
, QType(1), now
, ne
);
186 BOOST_CHECK_EQUAL(ret
, false);
189 BOOST_AUTO_TEST_CASE(test_getRootNXTrust_expired_entry
)
191 DNSName
qname("com");
195 Utility::gettimeofday(&now
, 0);
199 cache
.add(genNegCacheEntry(qname
, auth
, now
));
201 BOOST_CHECK_EQUAL(cache
.size(), 1U);
203 NegCache::NegCacheEntry ne
;
206 bool ret
= cache
.getRootNXTrust(qname
, now
, ne
, false, false);
208 BOOST_CHECK_EQUAL(ret
, false);
211 BOOST_AUTO_TEST_CASE(test_add_updated_entry
)
213 DNSName
qname("www2.powerdns.com");
214 DNSName
auth("powerdns.com");
215 DNSName
auth2("com");
218 Utility::gettimeofday(&now
, 0);
221 cache
.add(genNegCacheEntry(qname
, auth
, now
));
222 // Should override the existing entry for www2.powerdns.com
223 cache
.add(genNegCacheEntry(qname
, auth2
, now
));
225 BOOST_CHECK_EQUAL(cache
.size(), 1U);
227 NegCache::NegCacheEntry ne
;
228 bool ret
= cache
.get(qname
, QType(1), now
, ne
);
231 BOOST_CHECK_EQUAL(ne
.d_name
, qname
);
232 BOOST_CHECK_EQUAL(ne
.d_auth
, auth2
);
235 BOOST_AUTO_TEST_CASE(test_getRootNXTrust
)
237 DNSName
qname("www2.powerdns.com");
238 DNSName
auth("powerdns.com");
239 DNSName
qname2("com");
243 Utility::gettimeofday(&now
, 0);
246 cache
.add(genNegCacheEntry(qname
, auth
, now
));
247 cache
.add(genNegCacheEntry(qname2
, auth2
, now
));
249 NegCache::NegCacheEntry ne
;
250 bool ret
= cache
.getRootNXTrust(qname
, now
, ne
, false, false);
253 BOOST_CHECK_EQUAL(ne
.d_name
, qname2
);
254 BOOST_CHECK_EQUAL(ne
.d_auth
, auth2
);
257 BOOST_AUTO_TEST_CASE(test_getRootNXTrust_full_domain_only
)
259 DNSName
qname("www2.powerdns.com");
260 DNSName
auth("powerdns.com");
261 DNSName
qname2("com");
265 Utility::gettimeofday(&now
, 0);
268 cache
.add(genNegCacheEntry(qname
, auth
, now
));
269 cache
.add(genNegCacheEntry(qname2
, auth2
, now
, 1)); // Add the denial for COM|A
271 NegCache::NegCacheEntry ne
;
272 bool ret
= cache
.getRootNXTrust(qname
, now
, ne
, false, false);
274 BOOST_CHECK_EQUAL(ret
, false);
277 BOOST_AUTO_TEST_CASE(test_prune
)
279 string
qname(".powerdns.com");
280 DNSName
auth("powerdns.com");
283 Utility::gettimeofday(&now
, 0);
286 NegCache::NegCacheEntry ne
;
287 for (int i
= 0; i
< 400; i
++) {
288 ne
= genNegCacheEntry(DNSName(std::to_string(i
) + qname
), auth
, now
);
292 BOOST_CHECK_EQUAL(cache
.size(), 400U);
296 BOOST_CHECK_EQUAL(cache
.size(), 100U);
299 BOOST_AUTO_TEST_CASE(test_prune_many_shards
)
301 string
qname(".powerdns.com");
302 DNSName
auth("powerdns.com");
305 Utility::gettimeofday(&now
, 0);
308 NegCache::NegCacheEntry ne
;
309 for (int i
= 0; i
< 400; i
++) {
310 ne
= genNegCacheEntry(DNSName(std::to_string(i
) + qname
), auth
, now
);
314 BOOST_CHECK_EQUAL(cache
.size(), 400U);
318 BOOST_CHECK_EQUAL(cache
.size(), 100U);
321 BOOST_AUTO_TEST_CASE(test_prune_valid_entries
)
323 DNSName
power1("powerdns.com.");
324 DNSName
power2("powerdns-1.com.");
325 DNSName
auth("com.");
328 Utility::gettimeofday(&now
, 0);
331 NegCache::NegCacheEntry ne
;
333 /* insert power1 then power2 */
334 ne
= genNegCacheEntry(power1
, auth
, now
);
336 ne
= genNegCacheEntry(power2
, auth
, now
);
339 BOOST_CHECK_EQUAL(cache
.size(), 2U);
341 /* power2 has been inserted more recently, so it should be
344 BOOST_CHECK_EQUAL(cache
.size(), 1U);
346 NegCache::NegCacheEntry got
;
347 bool ret
= cache
.get(power2
, QType(1), now
, got
);
349 BOOST_CHECK_EQUAL(got
.d_name
, power2
);
350 BOOST_CHECK_EQUAL(got
.d_auth
, auth
);
352 /* insert power1 back */
353 ne
= genNegCacheEntry(power1
, auth
, now
);
355 BOOST_CHECK_EQUAL(cache
.size(), 2U);
357 /* replace the entry for power2 */
358 ne
= genNegCacheEntry(power2
, auth
, now
);
361 BOOST_CHECK_EQUAL(cache
.size(), 2U);
363 /* power2 has been updated more recently, so it should be
367 BOOST_CHECK_EQUAL(cache
.size(), 1U);
368 got
= NegCache::NegCacheEntry();
369 ret
= cache
.get(power2
, QType(1), now
, got
);
371 BOOST_CHECK_EQUAL(got
.d_name
, power2
);
372 BOOST_CHECK_EQUAL(got
.d_auth
, auth
);
375 BOOST_AUTO_TEST_CASE(test_wipe_single
)
377 string
qname(".powerdns.com");
378 DNSName
auth("powerdns.com");
381 Utility::gettimeofday(&now
, 0);
384 NegCache::NegCacheEntry ne
;
385 ne
= genNegCacheEntry(auth
, auth
, now
);
388 for (int i
= 0; i
< 400; i
++) {
389 ne
= genNegCacheEntry(DNSName(std::to_string(i
) + qname
), auth
, now
);
393 BOOST_CHECK_EQUAL(cache
.size(), 401U);
395 // Should only wipe the powerdns.com entry
397 BOOST_CHECK_EQUAL(cache
.size(), 400U);
399 NegCache::NegCacheEntry ne2
;
400 bool ret
= cache
.get(auth
, QType(1), now
, ne2
);
402 BOOST_CHECK_EQUAL(ret
, false);
404 cache
.wipe(DNSName("1.powerdns.com"));
405 BOOST_CHECK_EQUAL(cache
.size(), 399U);
407 NegCache::NegCacheEntry ne3
;
408 ret
= cache
.get(auth
, QType(1), now
, ne3
);
410 BOOST_CHECK_EQUAL(ret
, false);
413 BOOST_AUTO_TEST_CASE(test_wipe_subtree
)
415 string
qname(".powerdns.com");
416 string
qname2("powerdns.org");
417 DNSName
auth("powerdns.com");
420 Utility::gettimeofday(&now
, 0);
423 NegCache::NegCacheEntry ne
;
424 ne
= genNegCacheEntry(auth
, auth
, now
);
427 for (int i
= 0; i
< 400; i
++) {
428 ne
= genNegCacheEntry(DNSName(std::to_string(i
) + qname
), auth
, now
);
430 ne
= genNegCacheEntry(DNSName(std::to_string(i
) + qname2
), auth
, now
);
434 BOOST_CHECK_EQUAL(cache
.size(), 801U);
436 // Should wipe all the *.powerdns.com and powerdns.com entries
437 cache
.wipe(auth
, true);
438 BOOST_CHECK_EQUAL(cache
.size(), 400U);
441 BOOST_AUTO_TEST_CASE(test_wipe_typed
)
443 string
qname(".powerdns.com");
444 DNSName
auth("powerdns.com");
447 Utility::gettimeofday(&now
, 0);
450 NegCache::NegCacheEntry ne
;
451 ne
= genNegCacheEntry(auth
, auth
, now
, QType::A
);
454 for (int i
= 0; i
< 400; i
++) {
455 ne
= genNegCacheEntry(DNSName(std::to_string(i
) + qname
), auth
, now
, QType::A
);
459 BOOST_CHECK_EQUAL(cache
.size(), 401U);
461 // Should only wipe the powerdns.com entry
462 cache
.wipeTyped(auth
, QType::A
);
463 BOOST_CHECK_EQUAL(cache
.size(), 400U);
465 NegCache::NegCacheEntry ne2
;
466 bool ret
= cache
.get(auth
, QType(1), now
, ne2
);
468 BOOST_CHECK_EQUAL(ret
, false);
470 cache
.wipeTyped(DNSName("1.powerdns.com"), QType::A
);
471 BOOST_CHECK_EQUAL(cache
.size(), 399U);
473 NegCache::NegCacheEntry ne3
;
474 ret
= cache
.get(auth
, QType(1), now
, ne3
);
476 BOOST_CHECK_EQUAL(ret
, false);
479 BOOST_AUTO_TEST_CASE(test_clear
)
481 string
qname(".powerdns.com");
482 DNSName
auth("powerdns.com");
485 Utility::gettimeofday(&now
, 0);
488 NegCache::NegCacheEntry ne
;
490 for (int i
= 0; i
< 400; i
++) {
491 ne
= genNegCacheEntry(DNSName(std::to_string(i
) + qname
), auth
, now
);
495 BOOST_CHECK_EQUAL(cache
.size(), 400U);
497 BOOST_CHECK_EQUAL(cache
.size(), 0U);
500 BOOST_AUTO_TEST_CASE(test_dumpToFile
)
503 vector
<string
> expected
= {
504 "; negcache dump follows\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"};
520 Utility::gettimeofday(&now
, 0);
522 cache
.add(genNegCacheEntry(DNSName("www1.powerdns.com"), DNSName("powerdns.com"), now
));
523 cache
.add(genNegCacheEntry(DNSName("www2.powerdns.com"), DNSName("powerdns.com"), now
));
525 auto fp
= std::unique_ptr
<FILE, int (*)(FILE*)>(tmpfile(), fclose
);
527 BOOST_FAIL("Temporary file could not be opened");
529 cache
.doDump(fileno(fp
.get()), 0);
532 char* line
= nullptr;
536 for (auto str
: expected
) {
537 read
= getline(&line
, &len
, fp
.get());
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
);
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. */
550 BOOST_AUTO_TEST_CASE(test_count
)
552 string
qname(".powerdns.com");
553 string
qname2("powerdns.org");
554 DNSName
auth("powerdns.com");
557 Utility::gettimeofday(&now
, 0);
560 NegCache::NegCacheEntry ne
;
561 ne
= genNegCacheEntry(auth
, auth
, now
);
564 for (int i
= 0; i
< 400; i
++) {
565 ne
= genNegCacheEntry(DNSName(std::to_string(i
) + qname
), auth
, now
);
567 ne
= genNegCacheEntry(DNSName(std::to_string(i
) + qname2
), auth
, now
);
572 count
= cache
.count(auth
);
573 BOOST_CHECK_EQUAL(count
, 1U);
574 count
= cache
.count(auth
, QType(1));
575 BOOST_CHECK_EQUAL(count
, 0U);
578 BOOST_AUTO_TEST_SUITE_END()