1 #ifndef BOOST_TEST_DYN_LINK
2 #define BOOST_TEST_DYN_LINK
5 #include <boost/test/unit_test.hpp>
7 #include "test-syncres_cc.hh"
9 BOOST_AUTO_TEST_SUITE(syncres_cc8
)
11 BOOST_AUTO_TEST_CASE(test_nsec_denial_nowrap
)
16 generateKeyMaterial(DNSName("example.org."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
18 vector
<DNSRecord
> records
;
20 sortedRecords_t recordContents
;
21 vector
<shared_ptr
<const RRSIGRecordContent
>> signatureContents
;
25 a.example.org. -> d.example.org. denies the existence of b.example.org.
27 addNSECRecordToLW(DNSName("a.example.org."), DNSName("d.example.org"), {QType::A
, QType::TXT
, QType::RRSIG
, QType::NSEC
}, 600, records
);
28 recordContents
.insert(records
.at(0).getContent());
29 addRRSIG(keys
, records
, DNSName("example.org."), 300);
30 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
34 pair
.records
= recordContents
;
35 pair
.signatures
= signatureContents
;
37 denialMap
[std::pair(DNSName("a.example.org."), QType::NSEC
)] = pair
;
39 /* add wildcard denial */
40 recordContents
.clear();
41 signatureContents
.clear();
42 addNSECRecordToLW(DNSName("example.org."), DNSName("+.example.org"), {QType::A
, QType::TXT
, QType::RRSIG
, QType::NSEC
}, 600, records
);
43 recordContents
.insert(records
.at(0).getContent());
44 addRRSIG(keys
, records
, DNSName("example.org."), 300);
45 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
48 pair
.records
= recordContents
;
49 pair
.signatures
= signatureContents
;
50 denialMap
[std::pair(DNSName("example.org."), QType::NSEC
)] = pair
;
52 dState denialState
= getDenial(denialMap
, DNSName("b.example.org."), QType::A
, false, false);
53 BOOST_CHECK_EQUAL(denialState
, dState::NXDOMAIN
);
55 denialState
= getDenial(denialMap
, DNSName("d.example.org."), QType::A
, false, false);
56 /* let's check that d.example.org. is not denied by this proof */
57 BOOST_CHECK_EQUAL(denialState
, dState::NODENIAL
);
60 BOOST_AUTO_TEST_CASE(test_nsec_denial_wrap_case_1
)
65 generateKeyMaterial(DNSName("example.org."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
67 vector
<DNSRecord
> records
;
69 sortedRecords_t recordContents
;
70 vector
<shared_ptr
<const RRSIGRecordContent
>> signatureContents
;
73 Wrap case 1 test case:
74 z.example.org. -> b.example.org. denies the existence of a.example.org.
76 addNSECRecordToLW(DNSName("z.example.org."), DNSName("b.example.org"), {QType::A
, QType::TXT
, QType::RRSIG
, QType::NSEC
}, 600, records
);
77 recordContents
.insert(records
.at(0).getContent());
78 addRRSIG(keys
, records
, DNSName("example.org."), 300);
79 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
83 pair
.records
= recordContents
;
84 pair
.signatures
= signatureContents
;
86 denialMap
[std::pair(DNSName("z.example.org."), QType::NSEC
)] = pair
;
88 dState denialState
= getDenial(denialMap
, DNSName("a.example.org."), QType::A
, false, false);
89 BOOST_CHECK_EQUAL(denialState
, dState::NXDOMAIN
);
91 denialState
= getDenial(denialMap
, DNSName("d.example.org."), QType::A
, false, false);
92 /* let's check that d.example.org. is not denied by this proof */
93 BOOST_CHECK_EQUAL(denialState
, dState::NODENIAL
);
96 BOOST_AUTO_TEST_CASE(test_nsec_denial_wrap_case_2
)
101 generateKeyMaterial(DNSName("example.org."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
103 vector
<DNSRecord
> records
;
105 sortedRecords_t recordContents
;
106 vector
<shared_ptr
<const RRSIGRecordContent
>> signatureContents
;
109 Wrap case 2 test case:
110 y.example.org. -> a.example.org. denies the existence of z.example.org.
112 addNSECRecordToLW(DNSName("y.example.org."), DNSName("a.example.org"), {QType::A
, QType::TXT
, QType::RRSIG
, QType::NSEC
}, 600, records
);
113 recordContents
.insert(records
.at(0).getContent());
114 addRRSIG(keys
, records
, DNSName("example.org."), 300);
115 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
119 pair
.records
= recordContents
;
120 pair
.signatures
= signatureContents
;
122 denialMap
[std::pair(DNSName("y.example.org."), QType::NSEC
)] = pair
;
124 dState denialState
= getDenial(denialMap
, DNSName("z.example.org."), QType::A
, false, false);
125 BOOST_CHECK_EQUAL(denialState
, dState::NXDOMAIN
);
127 denialState
= getDenial(denialMap
, DNSName("d.example.org."), QType::A
, false, false);
128 /* let's check that d.example.org. is not denied by this proof */
129 BOOST_CHECK_EQUAL(denialState
, dState::NODENIAL
);
132 BOOST_AUTO_TEST_CASE(test_nsec_denial_only_one_nsec
)
137 generateKeyMaterial(DNSName("example.org."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
139 vector
<DNSRecord
> records
;
141 sortedRecords_t recordContents
;
142 vector
<shared_ptr
<const RRSIGRecordContent
>> signatureContents
;
145 Only one NSEC in the whole zone test case:
146 a.example.org. -> a.example.org. denies the existence of b.example.org.
148 addNSECRecordToLW(DNSName("a.example.org."), DNSName("a.example.org"), {QType::A
, QType::TXT
, QType::RRSIG
, QType::NSEC
}, 600, records
);
149 recordContents
.insert(records
.at(0).getContent());
150 addRRSIG(keys
, records
, DNSName("example.org."), 300);
151 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
155 pair
.records
= recordContents
;
156 pair
.signatures
= signatureContents
;
158 denialMap
[std::pair(DNSName("a.example.org."), QType::NSEC
)] = pair
;
160 dState denialState
= getDenial(denialMap
, DNSName("b.example.org."), QType::A
, false, false);
161 BOOST_CHECK_EQUAL(denialState
, dState::NXDOMAIN
);
163 denialState
= getDenial(denialMap
, DNSName("a.example.org."), QType::A
, false, false);
164 /* let's check that d.example.org. is not denied by this proof */
165 BOOST_CHECK_EQUAL(denialState
, dState::NODENIAL
);
168 BOOST_AUTO_TEST_CASE(test_nsec_root_nxd_denial
)
173 generateKeyMaterial(DNSName("."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
175 vector
<DNSRecord
> records
;
177 sortedRecords_t recordContents
;
178 vector
<shared_ptr
<const RRSIGRecordContent
>> signatureContents
;
181 The RRSIG from "." denies the existence of anything between a. and c.,
184 addNSECRecordToLW(DNSName("a."), DNSName("c."), {QType::NS
}, 600, records
);
185 recordContents
.insert(records
.at(0).getContent());
186 addRRSIG(keys
, records
, DNSName("."), 300);
187 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
191 pair
.records
= recordContents
;
192 pair
.signatures
= signatureContents
;
194 denialMap
[std::pair(DNSName("a."), QType::NSEC
)] = pair
;
196 /* add wildcard denial */
197 recordContents
.clear();
198 signatureContents
.clear();
199 addNSECRecordToLW(DNSName("."), DNSName("+"), {QType::A
, QType::TXT
, QType::RRSIG
, QType::NSEC
}, 600, records
);
200 recordContents
.insert(records
.at(0).getContent());
201 addRRSIG(keys
, records
, DNSName("."), 300);
202 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
205 pair
.records
= recordContents
;
206 pair
.signatures
= signatureContents
;
207 denialMap
[std::pair(DNSName("."), QType::NSEC
)] = pair
;
209 dState denialState
= getDenial(denialMap
, DNSName("b."), QType::A
, false, false);
210 BOOST_CHECK_EQUAL(denialState
, dState::NXDOMAIN
);
213 BOOST_AUTO_TEST_CASE(test_nsec_ancestor_nxqtype_denial
)
218 generateKeyMaterial(DNSName("."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
220 vector
<DNSRecord
> records
;
222 sortedRecords_t recordContents
;
223 vector
<shared_ptr
<const RRSIGRecordContent
>> signatureContents
;
226 The RRSIG from "." denies the existence of any type except NS at a.
227 However since it's an ancestor delegation NSEC (NS bit set, SOA bit clear,
228 signer field that is shorter than the owner name of the NSEC RR) it can't
229 be used to deny anything except the whole name (which does not make sense here)
232 addNSECRecordToLW(DNSName("a."), DNSName("b."), {QType::NS
}, 600, records
);
233 recordContents
.insert(records
.at(0).getContent());
234 addRRSIG(keys
, records
, DNSName("."), 300);
235 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
239 pair
.records
= recordContents
;
240 pair
.signatures
= signatureContents
;
242 denialMap
[std::pair(DNSName("a."), QType::NSEC
)] = pair
;
244 /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
245 Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
246 nonexistence of any RRs below that zone cut, which include all RRs at
247 that (original) owner name other than DS RRs, and all RRs below that
248 owner name regardless of type.
251 dState denialState
= getDenial(denialMap
, DNSName("a."), QType::A
, false, true);
252 /* no data means the qname/qtype is not denied, because an ancestor
253 delegation NSEC can only deny the DS */
254 BOOST_CHECK_EQUAL(denialState
, dState::NODENIAL
);
256 /* it can not be used to deny any RRs below that owner name either */
257 denialState
= getDenial(denialMap
, DNSName("sub.a."), QType::A
, false, false);
258 BOOST_CHECK_EQUAL(denialState
, dState::NODENIAL
);
260 denialState
= getDenial(denialMap
, DNSName("a."), QType::DS
, true, true);
261 BOOST_CHECK_EQUAL(denialState
, dState::NXQTYPE
);
264 BOOST_AUTO_TEST_CASE(test_nsec_ds_denial_from_child
)
269 generateKeyMaterial(DNSName("org."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
270 generateKeyMaterial(DNSName("example.org."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
272 vector
<DNSRecord
> records
;
274 sortedRecords_t recordContents
;
275 vector
<shared_ptr
<const RRSIGRecordContent
>> signatureContents
;
277 addNSECRecordToLW(DNSName("example.org."), DNSName("a.example.org"), {QType::A
, QType::TXT
, QType::RRSIG
, QType::NSEC
}, 600, records
);
278 recordContents
.insert(records
.at(0).getContent());
279 addRRSIG(keys
, records
, DNSName("example.org."), 300);
280 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
284 pair
.records
= recordContents
;
285 pair
.signatures
= signatureContents
;
287 denialMap
[std::pair(DNSName("example.org."), QType::NSEC
)] = pair
;
289 /* check that this NSEC from the child zone can deny a AAAA at the apex */
290 BOOST_CHECK_EQUAL(getDenial(denialMap
, DNSName("example.org."), QType::AAAA
, false, true, std::nullopt
, true), dState::NXQTYPE
);
292 /* but not that the DS does not exist, since we need the parent for that */
293 BOOST_CHECK_EQUAL(getDenial(denialMap
, DNSName("example.org."), QType::DS
, false, true, std::nullopt
, true), dState::NODENIAL
);
296 BOOST_AUTO_TEST_CASE(test_nsec_insecure_delegation_denial
)
301 generateKeyMaterial(DNSName("."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
303 vector
<DNSRecord
> records
;
305 sortedRecords_t recordContents
;
306 vector
<shared_ptr
<const RRSIGRecordContent
>> signatureContents
;
309 * RFC 5155 section 8.9:
310 * If there is an NSEC3 RR present in the response that matches the
311 * delegation name, then the validator MUST ensure that the NS bit is
312 * set and that the DS bit is not set in the Type Bit Maps field of the
316 The RRSIG from "." denies the existence of any type at a.
317 NS should be set if it was proving an insecure delegation, let's check that
318 we correctly detect that it's not.
320 addNSECRecordToLW(DNSName("a."), DNSName("b."), {}, 600, records
);
321 recordContents
.insert(records
.at(0).getContent());
322 addRRSIG(keys
, records
, DNSName("."), 300);
323 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
327 pair
.records
= recordContents
;
328 pair
.signatures
= signatureContents
;
330 denialMap
[std::pair(DNSName("a."), QType::NSEC
)] = pair
;
332 /* Insecure because the NS is not set, so while it does
333 denies the DS, it can't prove an insecure delegation */
334 dState denialState
= getDenial(denialMap
, DNSName("a."), QType::DS
, true, true);
335 BOOST_CHECK_EQUAL(denialState
, dState::NODENIAL
);
338 BOOST_AUTO_TEST_CASE(test_nsec_insecure_delegation_denial_soa
)
343 generateKeyMaterial(DNSName("."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
345 vector
<DNSRecord
> records
;
347 sortedRecords_t recordContents
;
348 vector
<shared_ptr
<const RRSIGRecordContent
>> signatureContents
;
351 * RFC 5155 section 8.9:
352 * If there is an NSEC3 RR present in the response that matches the
353 * delegation name, then the validator MUST ensure that the NS bit is
354 * set and that the DS bit is not set in the Type Bit Maps field of the
358 The RRSIG from "." denies the existence of any type at a except NS and SOA.
359 NS has to be set since it is proving an insecure delegation, but SOA should NOT!
361 addNSECRecordToLW(DNSName("a."), DNSName("b."), {QType::NS
, QType::SOA
}, 600, records
);
362 recordContents
.insert(records
.at(0).getContent());
363 addRRSIG(keys
, records
, DNSName("."), 300);
364 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
368 pair
.records
= recordContents
;
369 pair
.signatures
= signatureContents
;
371 denialMap
[std::pair(DNSName("a."), QType::NSEC
)] = pair
;
373 /* Insecure because both NS and SOA are set, so this is not a proper delegation */
374 dState denialState
= getDenial(denialMap
, DNSName("a."), QType::DS
, true, true);
375 BOOST_CHECK_EQUAL(denialState
, dState::NODENIAL
);
378 BOOST_AUTO_TEST_CASE(test_nsec_nxqtype_cname
)
383 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
385 vector
<DNSRecord
> records
;
387 sortedRecords_t recordContents
;
388 vector
<shared_ptr
<const RRSIGRecordContent
>> signatureContents
;
390 addNSECRecordToLW(DNSName("a.powerdns.com."), DNSName("a.c.powerdns.com."), {QType::CNAME
}, 600, records
);
391 recordContents
.insert(records
.at(0).getContent());
392 addRRSIG(keys
, records
, DNSName("powerdns.com."), 300);
393 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
397 pair
.records
= recordContents
;
398 pair
.signatures
= signatureContents
;
400 denialMap
[std::pair(DNSName("a.powerdns.com."), QType::NSEC
)] = pair
;
402 /* this NSEC is not valid to deny a.powerdns.com|A since it states that a CNAME exists */
403 dState denialState
= getDenial(denialMap
, DNSName("a.powerdns.com."), QType::A
, true, true);
404 BOOST_CHECK_EQUAL(denialState
, dState::NODENIAL
);
407 BOOST_AUTO_TEST_CASE(test_nsec3_nxqtype_ds
)
412 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
414 vector
<DNSRecord
> records
;
416 sortedRecords_t recordContents
;
417 vector
<shared_ptr
<const RRSIGRecordContent
>> signatureContents
;
419 addNSEC3UnhashedRecordToLW(DNSName("powerdns.com."), DNSName("powerdns.com."), "whatever", {QType::A
}, 600, records
);
420 recordContents
.insert(records
.at(0).getContent());
421 addRRSIG(keys
, records
, DNSName("powerdns.com."), 300);
422 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
425 pair
.records
= recordContents
;
426 pair
.signatures
= signatureContents
;
428 denialMap
[std::pair(records
.at(0).d_name
, records
.at(0).d_type
)] = pair
;
431 /* this NSEC3 is not valid to deny the DS since it is from the child zone */
432 BOOST_CHECK_EQUAL(getDenial(denialMap
, DNSName("powerdns.com."), QType::DS
, false, true), dState::NODENIAL
);
433 /* AAAA should be fine, though */
434 BOOST_CHECK_EQUAL(getDenial(denialMap
, DNSName("powerdns.com."), QType::AAAA
, false, true), dState::NXQTYPE
);
437 BOOST_AUTO_TEST_CASE(test_nsec3_nxqtype_cname
)
442 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
444 vector
<DNSRecord
> records
;
446 sortedRecords_t recordContents
;
447 vector
<shared_ptr
<const RRSIGRecordContent
>> signatureContents
;
449 addNSEC3UnhashedRecordToLW(DNSName("a.powerdns.com."), DNSName("powerdns.com."), "whatever", {QType::CNAME
}, 600, records
);
450 recordContents
.insert(records
.at(0).getContent());
451 addRRSIG(keys
, records
, DNSName("powerdns.com."), 300);
452 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
455 pair
.records
= recordContents
;
456 pair
.signatures
= signatureContents
;
458 denialMap
[std::pair(records
.at(0).d_name
, records
.at(0).d_type
)] = pair
;
461 /* this NSEC3 is not valid to deny a.powerdns.com|A since it states that a CNAME exists */
462 dState denialState
= getDenial(denialMap
, DNSName("a.powerdns.com."), QType::A
, false, true);
463 BOOST_CHECK_EQUAL(denialState
, dState::NODENIAL
);
466 BOOST_AUTO_TEST_CASE(test_nsec_nxdomain_denial_missing_wildcard
)
471 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
473 vector
<DNSRecord
> records
;
475 sortedRecords_t recordContents
;
476 vector
<shared_ptr
<const RRSIGRecordContent
>> signatureContents
;
478 addNSECRecordToLW(DNSName("a.powerdns.com."), DNSName("d.powerdns.com"), {QType::A
, QType::TXT
, QType::RRSIG
, QType::NSEC
}, 600, records
);
479 recordContents
.insert(records
.at(0).getContent());
480 addRRSIG(keys
, records
, DNSName("powerdns.com."), 300);
481 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
485 pair
.records
= recordContents
;
486 pair
.signatures
= signatureContents
;
488 denialMap
[std::pair(DNSName("a.powerdns.com."), QType::NSEC
)] = pair
;
490 dState denialState
= getDenial(denialMap
, DNSName("b.powerdns.com."), QType::A
, false, false);
491 BOOST_CHECK_EQUAL(denialState
, dState::NODENIAL
);
494 BOOST_AUTO_TEST_CASE(test_nsec3_nxdomain_denial_missing_wildcard
)
499 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
501 vector
<DNSRecord
> records
;
503 sortedRecords_t recordContents
;
504 vector
<shared_ptr
<const RRSIGRecordContent
>> signatureContents
;
506 addNSEC3NarrowRecordToLW(DNSName("a.powerdns.com."), DNSName("powerdns.com."), {QType::A
, QType::TXT
, QType::RRSIG
, QType::NSEC
}, 600, records
, 10);
507 recordContents
.insert(records
.at(0).getContent());
508 addRRSIG(keys
, records
, DNSName("powerdns.com."), 300);
509 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
512 pair
.records
= recordContents
;
513 pair
.signatures
= signatureContents
;
515 denialMap
[std::pair(records
.at(0).d_name
, records
.at(0).d_type
)] = pair
;
517 /* Add NSEC3 for the closest encloser */
518 recordContents
.clear();
519 signatureContents
.clear();
521 addNSEC3UnhashedRecordToLW(DNSName("powerdns.com."), DNSName("powerdns.com."), "whatever", {QType::A
, QType::TXT
, QType::RRSIG
, QType::NSEC
}, 600, records
, 10);
522 recordContents
.insert(records
.at(0).getContent());
523 addRRSIG(keys
, records
, DNSName("powerdns.com."), 300);
524 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
526 pair
.records
= recordContents
;
527 pair
.signatures
= signatureContents
;
528 denialMap
[std::pair(records
.at(0).d_name
, records
.at(0).d_type
)] = pair
;
530 dState denialState
= getDenial(denialMap
, DNSName("a.powerdns.com."), QType::A
, false, false);
531 BOOST_CHECK_EQUAL(denialState
, dState::NODENIAL
);
534 BOOST_AUTO_TEST_CASE(test_nsec_expanded_wildcard_proof
)
539 generateKeyMaterial(DNSName("example.org."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
541 vector
<DNSRecord
> records
;
543 sortedRecords_t recordContents
;
544 vector
<shared_ptr
<const RRSIGRecordContent
>> signatureContents
;
546 /* proves that a.example.com does exist, and has been generated from a wildcard (see the RRSIG below) */
547 addNSECRecordToLW(DNSName("a.example.org."), DNSName("d.example.org"), {QType::A
, QType::TXT
, QType::RRSIG
, QType::NSEC
}, 600, records
);
548 recordContents
.insert(records
.at(0).getContent());
549 addRRSIG(keys
, records
, DNSName("example.org."), 300, false, boost::none
, DNSName("example.org."));
550 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
554 pair
.records
= recordContents
;
555 pair
.signatures
= signatureContents
;
557 denialMap
[std::pair(DNSName("a.example.org."), QType::NSEC
)] = pair
;
559 /* This is an expanded wildcard proof, meaning that it does prove that the exact name
560 does not exist so the wildcard can apply */
561 dState denialState
= getDenial(denialMap
, DNSName("a.example.org."), QType(0).getCode(), false, false, std::nullopt
, false, /* normally retrieved from the RRSIG's d_labels */ 2);
562 BOOST_CHECK_EQUAL(denialState
, dState::NXDOMAIN
);
565 BOOST_AUTO_TEST_CASE(test_nsec_wildcard_with_cname
)
570 generateKeyMaterial(DNSName("example.org."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
572 vector
<DNSRecord
> records
;
574 sortedRecords_t recordContents
;
575 vector
<shared_ptr
<const RRSIGRecordContent
>> signatureContents
;
577 /* proves that b.example.com does not exist */
578 addNSECRecordToLW(DNSName("a.example.org."), DNSName("d.example.org"), {QType::A
, QType::TXT
, QType::RRSIG
, QType::NSEC
}, 600, records
);
579 recordContents
.insert(records
.at(0).getContent());
580 addRRSIG(keys
, records
, DNSName("example.org."), 300);
581 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
585 pair
.records
= recordContents
;
586 pair
.signatures
= signatureContents
;
588 denialMap
[std::pair(DNSName("a.example.org."), QType::NSEC
)] = pair
;
590 /* add a NSEC proving that a wildcard exists, without a CNAME type */
591 recordContents
.clear();
592 signatureContents
.clear();
593 addNSECRecordToLW(DNSName("*.example.org."), DNSName("+.example.org"), {QType::A
, QType::TXT
, QType::RRSIG
, QType::NSEC
}, 600, records
);
594 recordContents
.insert(records
.at(0).getContent());
595 addRRSIG(keys
, records
, DNSName("example.org."), 300);
596 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
599 pair
.records
= recordContents
;
600 pair
.signatures
= signatureContents
;
601 denialMap
[std::pair(DNSName("*.example.org."), QType::NSEC
)] = pair
;
603 /* A does exist at the wildcard, AAAA does not */
604 dState denialState
= getDenial(denialMap
, DNSName("b.example.org."), QType::A
, false, true);
605 BOOST_CHECK_EQUAL(denialState
, dState::NODENIAL
);
607 denialState
= getDenial(denialMap
, DNSName("b.example.org."), QType::AAAA
, false, true);
608 BOOST_CHECK_EQUAL(denialState
, dState::NXQTYPE
);
610 /* now we replace the wildcard by one with a CNAME */
611 recordContents
.clear();
612 signatureContents
.clear();
613 addNSECRecordToLW(DNSName("*.example.org."), DNSName("+.example.org"), {QType::CNAME
, QType::RRSIG
, QType::NSEC
}, 600, records
);
614 recordContents
.insert(records
.at(0).getContent());
615 addRRSIG(keys
, records
, DNSName("example.org."), 300);
616 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
619 pair
.records
= recordContents
;
620 pair
.signatures
= signatureContents
;
621 denialMap
[std::pair(DNSName("*.example.org."), QType::NSEC
)] = pair
;
623 /* A and AAAA do not exist but we have a CNAME so at the wildcard */
624 denialState
= getDenial(denialMap
, DNSName("b.example.org."), QType::A
, false, true);
625 BOOST_CHECK_EQUAL(denialState
, dState::NODENIAL
);
627 denialState
= getDenial(denialMap
, DNSName("b.example.org."), QType::AAAA
, false, true);
628 BOOST_CHECK_EQUAL(denialState
, dState::NODENIAL
);
631 BOOST_AUTO_TEST_CASE(test_nsec3_wildcard_with_cname
)
636 generateKeyMaterial(DNSName("example.org."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
638 vector
<DNSRecord
> records
;
640 sortedRecords_t recordContents
;
641 vector
<shared_ptr
<const RRSIGRecordContent
>> signatureContents
;
643 /* proves that b.example.com does not exist */
644 addNSEC3NarrowRecordToLW(DNSName("b.example.org"), DNSName("example.org."), {QType::A
, QType::TXT
, QType::RRSIG
, QType::NSEC3
}, 600, records
);
645 recordContents
.insert(records
.at(0).getContent());
646 addRRSIG(keys
, records
, DNSName("example.org."), 300);
647 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
650 pair
.records
= recordContents
;
651 pair
.signatures
= signatureContents
;
653 denialMap
[std::pair(records
.at(0).d_name
, records
.at(0).d_type
)] = pair
;
655 /* Add NSEC3 for the closest encloser */
656 recordContents
.clear();
657 signatureContents
.clear();
659 addNSEC3UnhashedRecordToLW(DNSName("example.org."), DNSName("example.org."), "whatever", {QType::A
, QType::TXT
, QType::RRSIG
, QType::NSEC
}, 600, records
);
660 recordContents
.insert(records
.at(0).getContent());
661 addRRSIG(keys
, records
, DNSName("example.org."), 300);
662 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
664 pair
.records
= recordContents
;
665 pair
.signatures
= signatureContents
;
666 denialMap
[std::pair(records
.at(0).d_name
, records
.at(0).d_type
)] = pair
;
668 /* add wildcard, without a CNAME type */
669 recordContents
.clear();
670 signatureContents
.clear();
672 addNSEC3UnhashedRecordToLW(DNSName("*.example.org."), DNSName("example.org"), "whatever", {QType::A
, QType::TXT
, QType::RRSIG
, QType::NSEC3
}, 600, records
);
673 recordContents
.insert(records
.at(0).getContent());
674 addRRSIG(keys
, records
, DNSName("example.org."), 300);
675 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
677 pair
.records
= recordContents
;
678 pair
.signatures
= signatureContents
;
679 denialMap
[std::pair(records
.at(0).d_name
, records
.at(0).d_type
)] = pair
;
681 /* A does exist at the wildcard, AAAA does not */
682 dState denialState
= getDenial(denialMap
, DNSName("b.example.org."), QType::A
, false, true);
683 BOOST_CHECK_EQUAL(denialState
, dState::NODENIAL
);
685 denialState
= getDenial(denialMap
, DNSName("b.example.org."), QType::AAAA
, false, true);
686 BOOST_CHECK_EQUAL(denialState
, dState::NXQTYPE
);
688 /* now we replace the wildcard by one with a CNAME */
689 recordContents
.clear();
690 signatureContents
.clear();
692 addNSEC3UnhashedRecordToLW(DNSName("*.example.org."), DNSName("example.org"), "whatever", {QType::CNAME
, QType::RRSIG
, QType::NSEC3
}, 600, records
);
693 recordContents
.insert(records
.at(0).getContent());
694 addRRSIG(keys
, records
, DNSName("example.org."), 300);
695 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
697 pair
.records
= recordContents
;
698 pair
.signatures
= signatureContents
;
699 denialMap
[std::pair(records
.at(0).d_name
, records
.at(0).d_type
)] = pair
;
701 /* A and AAAA do not exist but we have a CNAME so at the wildcard */
702 denialState
= getDenial(denialMap
, DNSName("b.example.org."), QType::A
, false, true);
703 BOOST_CHECK_EQUAL(denialState
, dState::NODENIAL
);
705 denialState
= getDenial(denialMap
, DNSName("b.example.org."), QType::AAAA
, false, true);
706 BOOST_CHECK_EQUAL(denialState
, dState::NODENIAL
);
709 BOOST_AUTO_TEST_CASE(test_nsec_ent_denial
)
714 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
716 vector
<DNSRecord
> records
;
718 sortedRecords_t recordContents
;
719 vector
<shared_ptr
<const RRSIGRecordContent
>> signatureContents
;
721 addNSECRecordToLW(DNSName("a.powerdns.com."), DNSName("a.c.powerdns.com."), {QType::A
}, 600, records
);
722 recordContents
.insert(records
.at(0).getContent());
723 addRRSIG(keys
, records
, DNSName("powerdns.com."), 300);
724 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
728 pair
.records
= recordContents
;
729 pair
.signatures
= signatureContents
;
731 denialMap
[std::pair(DNSName("a.powerdns.com."), QType::NSEC
)] = pair
;
733 /* this NSEC is valid to prove a NXQTYPE at c.powerdns.com because it proves that
735 dState denialState
= getDenial(denialMap
, DNSName("c.powerdns.com."), QType::AAAA
, true, true);
736 BOOST_CHECK_EQUAL(denialState
, dState::NXQTYPE
);
738 /* this NSEC is not valid to prove a NXQTYPE at b.powerdns.com,
739 it could prove a NXDOMAIN if it had an additional wildcard denial */
740 denialState
= getDenial(denialMap
, DNSName("b.powerdns.com."), QType::AAAA
, true, true);
741 BOOST_CHECK_EQUAL(denialState
, dState::NODENIAL
);
743 /* this NSEC is not valid to prove a NXQTYPE for QType::A at a.c.powerdns.com either */
744 denialState
= getDenial(denialMap
, DNSName("a.c.powerdns.com."), QType::A
, true, true);
745 BOOST_CHECK_EQUAL(denialState
, dState::NODENIAL
);
747 /* if we add the wildcard denial proof, we should get a NXDOMAIN proof for b.powerdns.com */
748 recordContents
.clear();
749 signatureContents
.clear();
750 addNSECRecordToLW(DNSName(").powerdns.com."), DNSName("+.powerdns.com."), {}, 600, records
);
751 recordContents
.insert(records
.at(0).getContent());
752 addRRSIG(keys
, records
, DNSName("powerdns.com."), 300);
753 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
755 pair
.records
= recordContents
;
756 pair
.signatures
= signatureContents
;
757 denialMap
[std::pair(DNSName(").powerdns.com."), QType::NSEC
)] = pair
;
759 denialState
= getDenial(denialMap
, DNSName("b.powerdns.com."), QType::A
, true, false);
760 BOOST_CHECK_EQUAL(denialState
, dState::NXDOMAIN
);
762 /* this NSEC is NOT valid to prove a NXDOMAIN at c.powerdns.com because it proves that
763 it exists and is an ENT */
764 denialState
= getDenial(denialMap
, DNSName("c.powerdns.com."), QType::AAAA
, true, false);
765 BOOST_CHECK_EQUAL(denialState
, dState::NODENIAL
);
768 BOOST_AUTO_TEST_CASE(test_nsec3_ancestor_nxqtype_denial
)
773 generateKeyMaterial(DNSName("."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
775 vector
<DNSRecord
> records
;
777 sortedRecords_t recordContents
;
778 vector
<shared_ptr
<const RRSIGRecordContent
>> signatureContents
;
781 The RRSIG from "." denies the existence of any type except NS at a.
782 However since it's an ancestor delegation NSEC (NS bit set, SOA bit clear,
783 signer field that is shorter than the owner name of the NSEC RR) it can't
784 be used to deny anything except the whole name or a DS.
786 addNSEC3UnhashedRecordToLW(DNSName("a."), DNSName("."), "whatever", {QType::NS
}, 600, records
);
787 recordContents
.insert(records
.at(0).getContent());
788 addRRSIG(keys
, records
, DNSName("."), 300);
789 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
792 pair
.records
= recordContents
;
793 pair
.signatures
= signatureContents
;
795 denialMap
[std::pair(records
.at(0).d_name
, records
.at(0).d_type
)] = pair
;
798 /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
799 Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
800 nonexistence of any RRs below that zone cut, which include all RRs at
801 that (original) owner name other than DS RRs, and all RRs below that
802 owner name regardless of type.
805 dState denialState
= getDenial(denialMap
, DNSName("a."), QType::A
, false, true);
806 /* no denial means the qname/qtype is not denied, because an ancestor
807 delegation NSEC3 can only deny the DS */
808 BOOST_CHECK_EQUAL(denialState
, dState::NODENIAL
);
810 denialState
= getDenial(denialMap
, DNSName("a."), QType::DS
, true, true);
811 BOOST_CHECK_EQUAL(denialState
, dState::NXQTYPE
);
813 /* it can not be used to deny any RRs below that owner name either */
814 /* Add NSEC3 for the next closer */
815 recordContents
.clear();
816 signatureContents
.clear();
818 addNSEC3NarrowRecordToLW(DNSName("sub.a."), DNSName("."), {QType::A
, QType::TXT
, QType::RRSIG
, QType::NSEC3
}, 600, records
);
819 recordContents
.insert(records
.at(0).getContent());
820 addRRSIG(keys
, records
, DNSName("."), 300);
821 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
823 pair
.records
= recordContents
;
824 pair
.signatures
= signatureContents
;
825 denialMap
[std::pair(records
.at(0).d_name
, records
.at(0).d_type
)] = pair
;
827 /* add wildcard denial */
828 recordContents
.clear();
829 signatureContents
.clear();
831 addNSEC3NarrowRecordToLW(DNSName("*.a."), DNSName("."), {QType::A
, QType::TXT
, QType::RRSIG
, QType::NSEC3
}, 600, records
);
832 recordContents
.insert(records
.at(0).getContent());
833 addRRSIG(keys
, records
, DNSName("."), 300);
834 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
836 pair
.records
= recordContents
;
837 pair
.signatures
= signatureContents
;
838 denialMap
[std::pair(records
.at(0).d_name
, records
.at(0).d_type
)] = pair
;
840 denialState
= getDenial(denialMap
, DNSName("sub.a."), QType::A
, false, true);
841 BOOST_CHECK_EQUAL(denialState
, dState::NODENIAL
);
843 /* not even the DS! */
844 denialState
= getDenial(denialMap
, DNSName("sub.a."), QType::DS
, false, true);
845 BOOST_CHECK_EQUAL(denialState
, dState::NODENIAL
);
848 BOOST_AUTO_TEST_CASE(test_nsec3_denial_too_many_iterations
)
853 generateKeyMaterial(DNSName("."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
855 vector
<DNSRecord
> records
;
857 sortedRecords_t recordContents
;
858 vector
<shared_ptr
<const RRSIGRecordContent
>> signatureContents
;
860 /* adding a NSEC3 with more iterations that we support */
861 addNSEC3UnhashedRecordToLW(DNSName("a."), DNSName("."), "whatever", {QType::AAAA
}, 600, records
, g_maxNSEC3Iterations
+ 100);
862 recordContents
.insert(records
.at(0).getContent());
863 addRRSIG(keys
, records
, DNSName("."), 300);
864 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
867 pair
.records
= recordContents
;
868 pair
.signatures
= signatureContents
;
870 denialMap
[std::pair(records
.at(0).d_name
, records
.at(0).d_type
)] = pair
;
873 dState denialState
= getDenial(denialMap
, DNSName("a."), QType::A
, false, true);
874 /* since we refuse to compute more than g_maxNSEC3Iterations iterations, it should be Insecure */
875 BOOST_CHECK_EQUAL(denialState
, dState::INSECURE
);
878 BOOST_AUTO_TEST_CASE(test_nsec3_insecure_delegation_denial
)
883 generateKeyMaterial(DNSName("."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
885 vector
<DNSRecord
> records
;
887 sortedRecords_t recordContents
;
888 vector
<shared_ptr
<const RRSIGRecordContent
>> signatureContents
;
891 * RFC 5155 section 8.9:
892 * If there is an NSEC3 RR present in the response that matches the
893 * delegation name, then the validator MUST ensure that the NS bit is
894 * set and that the DS bit is not set in the Type Bit Maps field of the
898 The RRSIG from "." denies the existence of any type at a.
899 NS should be set if it was proving an insecure delegation, let's check that
900 we correctly detect that it's not.
902 addNSEC3UnhashedRecordToLW(DNSName("a."), DNSName("."), "whatever", {}, 600, records
);
903 recordContents
.insert(records
.at(0).getContent());
904 addRRSIG(keys
, records
, DNSName("."), 300);
905 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
908 pair
.records
= recordContents
;
909 pair
.signatures
= signatureContents
;
911 denialMap
[std::pair(records
.at(0).d_name
, records
.at(0).d_type
)] = pair
;
914 /* Insecure because the NS is not set, so while it does
915 denies the DS, it can't prove an insecure delegation */
916 dState denialState
= getDenial(denialMap
, DNSName("a."), QType::DS
, true, true);
917 BOOST_CHECK_EQUAL(denialState
, dState::NODENIAL
);
920 BOOST_AUTO_TEST_CASE(test_nsec3_insecure_delegation_denial_soa
)
925 generateKeyMaterial(DNSName("."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
927 vector
<DNSRecord
> records
;
929 sortedRecords_t recordContents
;
930 vector
<shared_ptr
<const RRSIGRecordContent
>> signatureContents
;
933 * RFC 5155 section 8.9:
934 * If there is an NSEC3 RR present in the response that matches the
935 * delegation name, then the validator MUST ensure that the NS bit is
936 * set and that the DS bit is not set in the Type Bit Maps field of the
940 The RRSIG from "." denies the existence of any type at a except NS and SOA.
941 NS has to be set since it is proving an insecure delegation, but SOA should NOT!
943 addNSEC3UnhashedRecordToLW(DNSName("a."), DNSName("."), "whatever", {QType::NS
, QType::SOA
}, 600, records
);
944 recordContents
.insert(records
.at(0).getContent());
945 addRRSIG(keys
, records
, DNSName("."), 300);
946 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
949 pair
.records
= recordContents
;
950 pair
.signatures
= signatureContents
;
952 denialMap
[std::pair(records
.at(0).d_name
, records
.at(0).d_type
)] = pair
;
955 /* Insecure because both NS and SOA are set, so it is not a proper delegation */
956 dState denialState
= getDenial(denialMap
, DNSName("a."), QType::DS
, true, true);
957 BOOST_CHECK_EQUAL(denialState
, dState::NODENIAL
);
960 BOOST_AUTO_TEST_CASE(test_nsec3_ent_opt_out
)
965 generateKeyMaterial(DNSName("."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
967 vector
<DNSRecord
> records
;
969 sortedRecords_t recordContents
;
970 vector
<shared_ptr
<const RRSIGRecordContent
>> signatureContents
;
973 * RFC 7129 section 5.1:
974 * A recently discovered corner case (see RFC Errata ID 3441 [Err3441])
975 * shows that not only those delegations remain insecure but also the
976 * empty non-terminal space that is derived from those delegations.
979 We have a NSEC3 proving that was.here does exist, and a second
980 one proving that ent.was.here. does not,
981 There NSEC3 are opt-out, so the result should be insecure (and we don't need
984 addNSEC3UnhashedRecordToLW(DNSName("was.here."), DNSName("."), "whatever", {}, 600, records
, 10, true /* opt out */);
985 recordContents
.insert(records
.at(0).getContent());
986 addRRSIG(keys
, records
, DNSName("."), 300);
987 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
990 pair
.records
= recordContents
;
991 pair
.signatures
= signatureContents
;
993 denialMap
[std::pair(records
.at(0).d_name
, records
.at(0).d_type
)] = pair
;
995 /* it can not be used to deny any RRs below that owner name either */
996 /* Add NSEC3 for the next closer */
997 recordContents
.clear();
998 signatureContents
.clear();
1000 addNSEC3NarrowRecordToLW(DNSName("ent.was.here."), DNSName("."), {QType::RRSIG
, QType::NSEC3
}, 600, records
, 10, true /* opt-out */);
1001 recordContents
.insert(records
.at(0).getContent());
1002 addRRSIG(keys
, records
, DNSName("."), 300);
1003 signatureContents
.push_back(getRR
<RRSIGRecordContent
>(records
.at(1)));
1005 pair
.records
= recordContents
;
1006 pair
.signatures
= signatureContents
;
1007 denialMap
[std::pair(records
.at(0).d_name
, records
.at(0).d_type
)] = pair
;
1009 /* Insecure because the opt-out bit is set */
1010 dState denialState
= getDenial(denialMap
, DNSName("ent.was.here."), QType::A
, false, true);
1011 BOOST_CHECK_EQUAL(denialState
, dState::OPTOUT
);
1014 BOOST_AUTO_TEST_CASE(test_dnssec_rrsig_negcache_validity
)
1016 std::unique_ptr
<SyncRes
> sr
;
1019 setDNSSECValidation(sr
, DNSSECMode::ValidateAll
);
1022 const DNSName
target("com.");
1025 auto luaconfsCopy
= g_luaconfs
.getCopy();
1026 luaconfsCopy
.dsAnchors
.clear();
1027 generateKeyMaterial(g_rootdnsname
, DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
, luaconfsCopy
.dsAnchors
);
1028 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
1029 g_luaconfs
.setState(luaconfsCopy
);
1031 size_t queriesCount
= 0;
1032 const time_t fixedNow
= sr
->getNow().tv_sec
;
1034 sr
->setAsyncCallback([&](const ComboAddress
& /* ip */, const DNSName
& domain
, int type
, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval
* /* now */, boost::optional
<Netmask
>& /* srcmask */, const ResolveContext
& /* context */, LWResult
* res
, bool* /* chained */) {
1037 DNSName auth
= domain
;
1040 if (type
== QType::DS
|| type
== QType::DNSKEY
) {
1041 return genericDSAndDNSKEYHandler(res
, domain
, auth
, type
, keys
);
1044 setLWResult(res
, RCode::NoError
, true, false, true);
1045 addRecordToLW(res
, domain
, QType::SOA
, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY
, 3600);
1046 addRRSIG(keys
, res
->d_records
, domain
, 300);
1047 addNSECRecordToLW(domain
, DNSName("z."), {QType::NSEC
, QType::RRSIG
}, 600, res
->d_records
);
1048 addRRSIG(keys
, res
->d_records
, domain
, 1, false, boost::none
, boost::none
, fixedNow
);
1049 return LWResult::Result::Success
;
1052 return LWResult::Result::Timeout
;
1055 vector
<DNSRecord
> ret
;
1056 int res
= sr
->beginResolve(target
, QType(QType::A
), QClass::IN
, ret
);
1057 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
1058 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Secure
);
1059 BOOST_REQUIRE_EQUAL(ret
.size(), 4U);
1060 BOOST_CHECK_EQUAL(queriesCount
, 4U);
1062 /* check that the entry has not been negatively cached for longer than the RRSIG validity */
1063 NegCache::NegCacheEntry ne
;
1064 BOOST_CHECK_EQUAL(g_negCache
->size(), 1U);
1065 BOOST_REQUIRE_EQUAL(g_negCache
->get(target
, QType(QType::A
), sr
->getNow(), ne
), true);
1066 BOOST_CHECK_EQUAL(ne
.d_ttd
, fixedNow
+ 1);
1067 BOOST_CHECK_EQUAL(ne
.d_validationState
, vState::Secure
);
1068 BOOST_CHECK_EQUAL(ne
.authoritySOA
.records
.size(), 1U);
1069 BOOST_CHECK_EQUAL(ne
.authoritySOA
.signatures
.size(), 1U);
1070 BOOST_CHECK_EQUAL(ne
.DNSSECRecords
.records
.size(), 1U);
1071 BOOST_CHECK_EQUAL(ne
.DNSSECRecords
.signatures
.size(), 1U);
1073 /* again, to test the cache */
1075 res
= sr
->beginResolve(target
, QType(QType::A
), QClass::IN
, ret
);
1076 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
1077 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Secure
);
1078 BOOST_REQUIRE_EQUAL(ret
.size(), 4U);
1079 BOOST_CHECK_EQUAL(queriesCount
, 4U);
1082 BOOST_AUTO_TEST_CASE(test_dnssec_rrsig_negcache_bogus_validity
)
1084 std::unique_ptr
<SyncRes
> sr
;
1087 setDNSSECValidation(sr
, DNSSECMode::ValidateAll
);
1090 const DNSName
target("com.");
1093 auto luaconfsCopy
= g_luaconfs
.getCopy();
1094 luaconfsCopy
.dsAnchors
.clear();
1095 generateKeyMaterial(g_rootdnsname
, DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
, luaconfsCopy
.dsAnchors
);
1096 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
1097 g_luaconfs
.setState(luaconfsCopy
);
1099 size_t queriesCount
= 0;
1100 const time_t fixedNow
= sr
->getNow().tv_sec
;
1102 sr
->setAsyncCallback([&](const ComboAddress
& /* ip */, const DNSName
& domain
, int type
, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval
* /* now */, boost::optional
<Netmask
>& /* srcmask */, const ResolveContext
& /* context */, LWResult
* res
, bool* /* chained */) {
1105 DNSName auth
= domain
;
1108 if (type
== QType::DS
|| type
== QType::DNSKEY
) {
1109 return genericDSAndDNSKEYHandler(res
, domain
, auth
, type
, keys
);
1112 setLWResult(res
, RCode::NoError
, true, false, true);
1113 addRecordToLW(res
, domain
, QType::SOA
, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY
, 86400);
1114 addRRSIG(keys
, res
->d_records
, domain
, 86400);
1115 addNSECRecordToLW(domain
, DNSName("z."), {QType::NSEC
, QType::RRSIG
}, 86400, res
->d_records
);
1117 return LWResult::Result::Success
;
1120 return LWResult::Result::Timeout
;
1123 SyncRes::s_maxnegttl
= 3600;
1124 SyncRes::s_maxbogusttl
= 360;
1126 vector
<DNSRecord
> ret
;
1127 int res
= sr
->beginResolve(target
, QType(QType::A
), QClass::IN
, ret
);
1128 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
1129 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::BogusNoRRSIG
);
1130 BOOST_REQUIRE_EQUAL(ret
.size(), 3U);
1131 BOOST_CHECK_EQUAL(queriesCount
, 4U);
1133 /* check that the entry has been negatively cached but not longer than s_maxbogusttl */
1134 NegCache::NegCacheEntry ne
;
1135 BOOST_CHECK_EQUAL(g_negCache
->size(), 1U);
1136 BOOST_REQUIRE_EQUAL(g_negCache
->get(target
, QType(QType::A
), sr
->getNow(), ne
), true);
1137 BOOST_CHECK_EQUAL(ne
.d_ttd
, fixedNow
+ SyncRes::s_maxbogusttl
);
1138 BOOST_CHECK_EQUAL(ne
.d_validationState
, vState::BogusNoRRSIG
);
1139 BOOST_CHECK_EQUAL(ne
.authoritySOA
.records
.size(), 1U);
1140 BOOST_CHECK_EQUAL(ne
.authoritySOA
.signatures
.size(), 1U);
1141 BOOST_CHECK_EQUAL(ne
.DNSSECRecords
.records
.size(), 1U);
1142 BOOST_CHECK_EQUAL(ne
.DNSSECRecords
.signatures
.size(), 0U);
1144 /* again, to test the cache */
1146 res
= sr
->beginResolve(target
, QType(QType::A
), QClass::IN
, ret
);
1147 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
1148 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::BogusNoRRSIG
);
1149 BOOST_REQUIRE_EQUAL(ret
.size(), 3U);
1150 BOOST_CHECK_EQUAL(queriesCount
, 4U);
1153 BOOST_AUTO_TEST_CASE(test_dnssec_rrsig_cache_validity
)
1155 std::unique_ptr
<SyncRes
> sr
;
1158 setDNSSECValidation(sr
, DNSSECMode::ValidateAll
);
1161 const DNSName
target("com.");
1162 const ComboAddress
targetAddr("192.0.2.42");
1165 auto luaconfsCopy
= g_luaconfs
.getCopy();
1166 luaconfsCopy
.dsAnchors
.clear();
1167 generateKeyMaterial(g_rootdnsname
, DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
, luaconfsCopy
.dsAnchors
);
1168 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
1169 g_luaconfs
.setState(luaconfsCopy
);
1171 size_t queriesCount
= 0;
1172 const time_t tnow
= sr
->getNow().tv_sec
;
1174 sr
->setAsyncCallback([&](const ComboAddress
& /* ip */, const DNSName
& domain
, int type
, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval
* /* now */, boost::optional
<Netmask
>& /* srcmask */, const ResolveContext
& /* context */, LWResult
* res
, bool* /* chained */) {
1177 DNSName auth
= domain
;
1180 if (type
== QType::DS
|| type
== QType::DNSKEY
) {
1181 return genericDSAndDNSKEYHandler(res
, domain
, auth
, type
, keys
);
1184 setLWResult(res
, RCode::NoError
, true, false, true);
1185 addRecordToLW(res
, domain
, QType::A
, targetAddr
.toString(), DNSResourceRecord::ANSWER
, 3600);
1186 addRRSIG(keys
, res
->d_records
, domain
, 1, false, boost::none
, boost::none
, tnow
);
1187 return LWResult::Result::Success
;
1190 return LWResult::Result::Timeout
;
1193 vector
<DNSRecord
> ret
;
1194 int res
= sr
->beginResolve(target
, QType(QType::A
), QClass::IN
, ret
);
1195 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
1196 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Secure
);
1197 BOOST_REQUIRE_EQUAL(ret
.size(), 2U);
1198 BOOST_CHECK_EQUAL(queriesCount
, 4U);
1200 /* check that the entry has not been cached for longer than the RRSIG validity */
1201 const ComboAddress who
;
1202 vector
<DNSRecord
> cached
;
1203 vector
<std::shared_ptr
<const RRSIGRecordContent
>> signatures
;
1204 BOOST_REQUIRE_EQUAL(g_recCache
->get(tnow
, target
, QType(QType::A
), MemRecursorCache::RequireAuth
, &cached
, who
, boost::none
, &signatures
), 1);
1205 BOOST_REQUIRE_EQUAL(cached
.size(), 1U);
1206 BOOST_REQUIRE_EQUAL(signatures
.size(), 1U);
1207 BOOST_CHECK_EQUAL((cached
[0].d_ttl
- tnow
), 1);
1209 /* again, to test the cache */
1211 res
= sr
->beginResolve(target
, QType(QType::A
), QClass::IN
, ret
);
1212 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
1213 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Secure
);
1214 BOOST_REQUIRE_EQUAL(ret
.size(), 2U);
1215 BOOST_CHECK_EQUAL(queriesCount
, 4U);
1218 BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_cache_secure
)
1221 Validation is optional, and the first query does not ask for it,
1222 so the answer is cached as Indeterminate.
1223 The second query asks for validation, answer should be marked as
1224 Secure, after just-in-time validation.
1226 std::unique_ptr
<SyncRes
> sr
;
1229 setDNSSECValidation(sr
, DNSSECMode::Process
);
1232 const DNSName
target("com.");
1235 auto luaconfsCopy
= g_luaconfs
.getCopy();
1236 luaconfsCopy
.dsAnchors
.clear();
1237 generateKeyMaterial(g_rootdnsname
, DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
, luaconfsCopy
.dsAnchors
);
1238 g_luaconfs
.setState(luaconfsCopy
);
1240 size_t queriesCount
= 0;
1242 sr
->setAsyncCallback([&](const ComboAddress
& /* ip */, const DNSName
& domain
, int type
, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval
* /* now */, boost::optional
<Netmask
>& /* srcmask */, const ResolveContext
& /* context */, LWResult
* res
, bool* /* chained */) {
1245 if (type
== QType::DS
|| type
== QType::DNSKEY
) {
1246 return genericDSAndDNSKEYHandler(res
, domain
, domain
, type
, keys
, false);
1249 if (domain
== target
&& type
== QType::A
) {
1250 setLWResult(res
, 0, true, false, true);
1251 addRecordToLW(res
, target
, QType::A
, "192.0.2.1");
1252 addRRSIG(keys
, res
->d_records
, DNSName("."), 300);
1253 return LWResult::Result::Success
;
1257 return LWResult::Result::Timeout
;
1260 vector
<DNSRecord
> ret
;
1261 /* first query does not require validation */
1262 sr
->setDNSSECValidationRequested(false);
1263 int res
= sr
->beginResolve(target
, QType(QType::A
), QClass::IN
, ret
);
1264 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
1265 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Indeterminate
);
1266 BOOST_REQUIRE_EQUAL(ret
.size(), 2U);
1267 for (const auto& record
: ret
) {
1268 BOOST_CHECK(record
.d_type
== QType::A
|| record
.d_type
== QType::RRSIG
);
1270 BOOST_CHECK_EQUAL(queriesCount
, 1U);
1273 /* second one _does_ require validation */
1274 sr
->setDNSSECValidationRequested(true);
1275 res
= sr
->beginResolve(target
, QType(QType::A
), QClass::IN
, ret
);
1276 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
1277 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Secure
);
1278 BOOST_REQUIRE_EQUAL(ret
.size(), 2U);
1279 for (const auto& record
: ret
) {
1280 BOOST_CHECK(record
.d_type
== QType::A
|| record
.d_type
== QType::RRSIG
);
1282 BOOST_CHECK_EQUAL(queriesCount
, 2U);
1285 BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_cache_insecure
)
1288 Validation is optional, and the first query does not ask for it,
1289 so the answer is cached as Indeterminate.
1290 The second query asks for validation, answer should be marked as
1293 std::unique_ptr
<SyncRes
> sr
;
1296 setDNSSECValidation(sr
, DNSSECMode::Process
);
1299 const DNSName
target("com.");
1302 auto luaconfsCopy
= g_luaconfs
.getCopy();
1303 luaconfsCopy
.dsAnchors
.clear();
1304 g_luaconfs
.setState(luaconfsCopy
);
1306 size_t queriesCount
= 0;
1308 sr
->setAsyncCallback([&](const ComboAddress
& /* ip */, const DNSName
& domain
, int type
, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval
* /* now */, boost::optional
<Netmask
>& /* srcmask */, const ResolveContext
& /* context */, LWResult
* res
, bool* /* chained */) {
1311 if (type
== QType::DS
|| type
== QType::DNSKEY
) {
1312 return genericDSAndDNSKEYHandler(res
, domain
, domain
, type
, keys
, false);
1315 if (domain
== target
&& type
== QType::A
) {
1316 setLWResult(res
, 0, true, false, true);
1317 addRecordToLW(res
, target
, QType::A
, "192.0.2.1");
1318 return LWResult::Result::Success
;
1322 return LWResult::Result::Timeout
;
1325 vector
<DNSRecord
> ret
;
1326 /* first query does not require validation */
1327 sr
->setDNSSECValidationRequested(false);
1328 int res
= sr
->beginResolve(target
, QType(QType::A
), QClass::IN
, ret
);
1329 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
1330 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Indeterminate
);
1331 BOOST_REQUIRE_EQUAL(ret
.size(), 1U);
1332 for (const auto& record
: ret
) {
1333 BOOST_CHECK(record
.d_type
== QType::A
);
1335 BOOST_CHECK_EQUAL(queriesCount
, 1U);
1338 /* second one _does_ require validation */
1339 sr
->setDNSSECValidationRequested(true);
1340 res
= sr
->beginResolve(target
, QType(QType::A
), QClass::IN
, ret
);
1341 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
1342 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Insecure
);
1343 BOOST_REQUIRE_EQUAL(ret
.size(), 1U);
1344 for (const auto& record
: ret
) {
1345 BOOST_CHECK(record
.d_type
== QType::A
);
1347 BOOST_CHECK_EQUAL(queriesCount
, 1U);
1350 BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_cache_bogus
)
1353 Validation is optional, and the first query does not ask for it,
1354 so the answer is cached as Indeterminate.
1355 The second query asks for validation, answer should be marked as
1358 std::unique_ptr
<SyncRes
> sr
;
1361 setDNSSECValidation(sr
, DNSSECMode::Process
);
1364 const DNSName
target("com.");
1367 auto luaconfsCopy
= g_luaconfs
.getCopy();
1368 luaconfsCopy
.dsAnchors
.clear();
1369 generateKeyMaterial(g_rootdnsname
, DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
, luaconfsCopy
.dsAnchors
);
1370 g_luaconfs
.setState(luaconfsCopy
);
1372 size_t queriesCount
= 0;
1374 sr
->setAsyncCallback([&](const ComboAddress
& /* ip */, const DNSName
& domain
, int type
, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval
* /* now */, boost::optional
<Netmask
>& /* srcmask */, const ResolveContext
& /* context */, LWResult
* res
, bool* /* chained */) {
1377 if (type
== QType::DS
|| type
== QType::DNSKEY
) {
1378 return genericDSAndDNSKEYHandler(res
, domain
, domain
, type
, keys
, false);
1381 if (domain
== target
&& type
== QType::A
) {
1382 setLWResult(res
, 0, true, false, true);
1383 addRecordToLW(res
, target
, QType::A
, "192.0.2.1", DNSResourceRecord::ANSWER
, 86400);
1385 return LWResult::Result::Success
;
1389 return LWResult::Result::Timeout
;
1392 SyncRes::s_maxbogusttl
= 3600;
1394 vector
<DNSRecord
> ret
;
1395 /* first query does not require validation */
1396 sr
->setDNSSECValidationRequested(false);
1397 int res
= sr
->beginResolve(target
, QType(QType::A
), QClass::IN
, ret
);
1398 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
1399 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Indeterminate
);
1400 BOOST_REQUIRE_EQUAL(ret
.size(), 1U);
1401 for (const auto& record
: ret
) {
1402 BOOST_CHECK(record
.d_type
== QType::A
);
1403 BOOST_CHECK_EQUAL(record
.d_ttl
, 86400U);
1405 BOOST_CHECK_EQUAL(queriesCount
, 1U);
1408 /* second one _does_ require validation */
1409 sr
->setDNSSECValidationRequested(true);
1410 res
= sr
->beginResolve(target
, QType(QType::A
), QClass::IN
, ret
);
1411 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
1412 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::BogusNoRRSIG
);
1413 /* check that we correctly capped the TTD for a Bogus record after
1414 just-in-time validation */
1415 BOOST_REQUIRE_EQUAL(ret
.size(), 1U);
1416 for (const auto& record
: ret
) {
1417 BOOST_CHECK(record
.d_type
== QType::A
);
1418 BOOST_CHECK_EQUAL(record
.d_ttl
, SyncRes::s_maxbogusttl
);
1420 BOOST_CHECK_EQUAL(queriesCount
, 3U);
1423 /* third time also _does_ require validation, so we
1424 can check that the cache has been updated */
1425 sr
->setDNSSECValidationRequested(true);
1426 res
= sr
->beginResolve(target
, QType(QType::A
), QClass::IN
, ret
);
1427 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
1428 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::BogusNoRRSIG
);
1429 BOOST_REQUIRE_EQUAL(ret
.size(), 1U);
1430 for (const auto& record
: ret
) {
1431 BOOST_CHECK(record
.d_type
== QType::A
);
1432 BOOST_CHECK_EQUAL(record
.d_ttl
, SyncRes::s_maxbogusttl
);
1434 BOOST_CHECK_EQUAL(queriesCount
, 3U);
1437 BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_cache_secure_any
)
1440 Validation is optional, and the first two queries (A, AAAA) do not ask for it,
1441 so the answer are cached as Indeterminate.
1442 The third query asks for validation, and is for ANY, so the answer should be marked as
1443 Secure, after just-in-time validation.
1444 The last query also requests validation but is for AAAA only.
1446 std::unique_ptr
<SyncRes
> sr
;
1449 setDNSSECValidation(sr
, DNSSECMode::Process
);
1452 const DNSName
target("com.");
1455 auto luaconfsCopy
= g_luaconfs
.getCopy();
1456 luaconfsCopy
.dsAnchors
.clear();
1457 generateKeyMaterial(g_rootdnsname
, DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
, luaconfsCopy
.dsAnchors
);
1458 g_luaconfs
.setState(luaconfsCopy
);
1460 size_t queriesCount
= 0;
1462 sr
->setAsyncCallback([&](const ComboAddress
& /* ip */, const DNSName
& domain
, int type
, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval
* /* now */, boost::optional
<Netmask
>& /* srcmask */, const ResolveContext
& /* context */, LWResult
* res
, bool* /* chained */) {
1465 if (type
== QType::DS
|| type
== QType::DNSKEY
) {
1466 return genericDSAndDNSKEYHandler(res
, domain
, domain
, type
, keys
, false);
1469 if (domain
== target
&& type
== QType::A
) {
1470 setLWResult(res
, 0, true, false, true);
1471 addRecordToLW(res
, target
, QType::A
, "192.0.2.1");
1472 addRRSIG(keys
, res
->d_records
, DNSName("."), 300);
1473 return LWResult::Result::Success
;
1475 if (domain
== target
&& type
== QType::AAAA
) {
1476 setLWResult(res
, 0, true, false, true);
1477 addRecordToLW(res
, target
, QType::AAAA
, "2001:db8::1");
1478 addRRSIG(keys
, res
->d_records
, DNSName("."), 300);
1479 return LWResult::Result::Success
;
1483 return LWResult::Result::Timeout
;
1486 vector
<DNSRecord
> ret
;
1487 /* first query does not require validation */
1488 sr
->setDNSSECValidationRequested(false);
1489 int res
= sr
->beginResolve(target
, QType(QType::A
), QClass::IN
, ret
);
1490 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
1491 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Indeterminate
);
1492 BOOST_REQUIRE_EQUAL(ret
.size(), 2U);
1493 for (const auto& record
: ret
) {
1494 BOOST_CHECK(record
.d_type
== QType::A
|| record
.d_type
== QType::RRSIG
);
1496 BOOST_CHECK_EQUAL(queriesCount
, 1U);
1499 /* second query does not require validation either */
1500 sr
->setDNSSECValidationRequested(false);
1501 res
= sr
->beginResolve(target
, QType(QType::AAAA
), QClass::IN
, ret
);
1502 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
1503 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Indeterminate
);
1504 BOOST_REQUIRE_EQUAL(ret
.size(), 2U);
1505 for (const auto& record
: ret
) {
1506 BOOST_CHECK(record
.d_type
== QType::AAAA
|| record
.d_type
== QType::RRSIG
);
1508 BOOST_CHECK_EQUAL(queriesCount
, 2U);
1511 /* third one _does_ require validation */
1512 sr
->setDNSSECValidationRequested(true);
1513 res
= sr
->beginResolve(target
, QType(QType::ANY
), QClass::IN
, ret
);
1514 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
1515 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Secure
);
1516 BOOST_REQUIRE_EQUAL(ret
.size(), 4U);
1517 for (const auto& record
: ret
) {
1518 BOOST_CHECK(record
.d_type
== QType::A
|| record
.d_type
== QType::AAAA
|| record
.d_type
== QType::RRSIG
);
1520 BOOST_CHECK_EQUAL(queriesCount
, 3U);
1523 /* last one also requires validation */
1524 sr
->setDNSSECValidationRequested(true);
1525 res
= sr
->beginResolve(target
, QType(QType::AAAA
), QClass::IN
, ret
);
1526 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
1527 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Secure
);
1528 BOOST_REQUIRE_EQUAL(ret
.size(), 2U);
1529 for (const auto& record
: ret
) {
1530 BOOST_CHECK(record
.d_type
== QType::AAAA
|| record
.d_type
== QType::RRSIG
);
1532 BOOST_CHECK_EQUAL(queriesCount
, 3U);
1535 BOOST_AUTO_TEST_SUITE_END()