1 #ifndef BOOST_TEST_DYN_LINK
2 #define BOOST_TEST_DYN_LINK
5 #include <boost/test/unit_test.hpp>
7 #include "aggressive_nsec.hh"
8 #include "test-syncres_cc.hh"
10 BOOST_AUTO_TEST_SUITE(aggressive_nsec_cc
)
12 BOOST_AUTO_TEST_CASE(test_small_coverering_nsec3
)
14 AggressiveNSECCache::s_maxNSEC3CommonPrefix
= 1;
16 const std::vector
<std::tuple
<string
, string
, uint8_t, bool>> table
= {
17 {"gujhshp2lhmnpoo9qde4blg4gq3hgl99", "gujhshp2lhmnpoo9qde4blg4gq3hgl9a", 157, true},
18 {"gujhshp2lhmnpoo9qde4blg4gq3hgl99", "gujhshp2lhmnpoo9qde4blg4gq3hgl9a", 158, false},
19 {"0ujhshp2lhmnpoo9qde4blg4gq3hgl99", "vujhshp2lhmnpoo9qde4blg4gq3hgl9a", 0, false},
20 {"0ujhshp2lhmnpoo9qde4blg4gq3hgl99", "7ujhshp2lhmnpoo9qde4blg4gq3hgl9a", 1, true},
21 {"0ujhshp2lhmnpoo9qde4blg4gq3hgl99", "7ujhshp2lhmnpoo9qde4blg4gq3hgl9a", 2, false},
22 {"0ujhshp2lhmnpoo9qde4blg4gq3hgl99", "fujhshp2lhmnpoo9qde4blg4gq3hgl9a", 1, false},
23 {"0ujhshp2lhmnpoo9qde4blg4gq3hgl99", "8ujhshp2lhmnpoo9qde4blg4gq3hgl9a", 1, false},
24 {"8ujhshp2lhmnpoo9qde4blg4gq3hgl99", "8ujhshp2lhmnpoo9qde4blg4gq3hgl99", 0, false},
25 {"8ujhshp2lhmnpoo9qde4blg4gq3hgl99", "8ujhshp2lhmnpoo9qde4blg4gq3hgl99", 1, false},
26 {"8ujhshp2lhmnpoo9qde4blg4gq3hgl99", "8ujhshp2lhmnpoo9qde4blg4gq3hgl99", 157, false},
29 for (const auto& [owner
, next
, boundary
, result
] : table
) {
30 AggressiveNSECCache::s_maxNSEC3CommonPrefix
= boundary
;
31 BOOST_CHECK_EQUAL(AggressiveNSECCache::isSmallCoveringNSEC3(DNSName(owner
), fromBase32Hex(next
)), result
);
35 BOOST_AUTO_TEST_CASE(test_aggressive_nsec_nxdomain
)
37 std::unique_ptr
<SyncRes
> sr
;
39 g_aggressiveNSECCache
= make_unique
<AggressiveNSECCache
>(10000);
41 setDNSSECValidation(sr
, DNSSECMode::ValidateAll
);
44 /* we first ask b.powerdns.com., get a NXD, then check that the aggressive
45 NSEC cache will use the NSEC (a -> h) to prove that g.powerdns.com. does not exist
47 const DNSName
target1("b.powerdns.com.");
48 const DNSName
target2("g.powerdns.com.");
51 auto luaconfsCopy
= g_luaconfs
.getCopy();
52 luaconfsCopy
.dsAnchors
.clear();
53 generateKeyMaterial(g_rootdnsname
, DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
, luaconfsCopy
.dsAnchors
);
54 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
56 g_luaconfs
.setState(luaconfsCopy
);
58 size_t queriesCount
= 0;
60 sr
->setAsyncCallback([target1
, &queriesCount
, keys
](const ComboAddress
& ip
, const DNSName
& domain
, int type
, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval
* /* now */, boost::optional
<Netmask
>& /* srcmask */, boost::optional
<const ResolveContext
&> /* context */, LWResult
* res
, bool* /* chained */) {
63 if (type
== QType::DS
|| type
== QType::DNSKEY
) {
64 if (domain
!= DNSName("powerdns.com.") && domain
.isPartOf(DNSName("powerdns.com."))) {
65 /* no cut, NSEC, name does not exist (the generic version will generate an exact NSEC for the target, which we don't want) */
66 setLWResult(res
, RCode::NoError
, true, false, true);
68 addRecordToLW(res
, DNSName("powerdns.com."), QType::SOA
, "powerdns.com. powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY
, 3600);
69 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
70 /* no record for this name */
71 addNSECRecordToLW(DNSName("a.powerdns.com."), DNSName("h.powerdns.com."), {QType::A
, QType::TXT
, QType::RRSIG
}, 600, res
->d_records
);
72 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
73 /* no wildcard either */
74 addNSECRecordToLW(DNSName(").powerdns.com."), DNSName("a.powerdns.com."), {QType::AAAA
, QType::RRSIG
}, 600, res
->d_records
);
75 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com"), 300);
76 return LWResult::Result::Success
;
78 else if (domain
== DNSName("com.")) {
80 return genericDSAndDNSKEYHandler(res
, domain
, DNSName("."), type
, keys
, false);
82 else if (domain
== DNSName("powerdns.com.")) {
83 return genericDSAndDNSKEYHandler(res
, domain
, DNSName("."), type
, keys
);
87 return genericDSAndDNSKEYHandler(res
, domain
, domain
, type
, keys
);
91 if (isRootServer(ip
)) {
92 setLWResult(res
, 0, false, false, true);
93 addRecordToLW(res
, "powerdns.com.", QType::NS
, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY
, 3600);
94 addDS(DNSName("powerdns.com."), 300, res
->d_records
, keys
);
95 addRRSIG(keys
, res
->d_records
, DNSName("."), 300);
96 addRecordToLW(res
, "a.gtld-servers.com.", QType::A
, "192.0.2.1", DNSResourceRecord::ADDITIONAL
, 3600);
97 return LWResult::Result::Success
;
99 else if (ip
== ComboAddress("192.0.2.1:53")) {
100 if (domain
== target1
) {
101 setLWResult(res
, RCode::NXDomain
, true, false, true);
102 addRecordToLW(res
, DNSName("powerdns.com."), QType::SOA
, "powerdns.com. powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY
, 3600);
103 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
104 /* no record for this name */
105 addNSECRecordToLW(DNSName("a.powerdns.com."), DNSName("h.powerdns.com."), {QType::A
, QType::TXT
, QType::RRSIG
}, 600, res
->d_records
);
106 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
107 /* no wildcard either */
108 addNSECRecordToLW(DNSName(").powerdns.com."), DNSName("a.powerdns.com."), {QType::AAAA
, QType::RRSIG
}, 600, res
->d_records
);
109 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com"), 300);
110 return LWResult::Result::Success
;
115 return LWResult::Result::Timeout
;
118 vector
<DNSRecord
> ret
;
119 int res
= sr
->beginResolve(target1
, QType(QType::A
), QClass::IN
, ret
);
120 BOOST_CHECK_EQUAL(res
, RCode::NXDomain
);
121 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Secure
);
122 BOOST_REQUIRE_EQUAL(ret
.size(), 6U);
123 BOOST_CHECK_EQUAL(queriesCount
, 4U);
126 res
= sr
->beginResolve(target2
, QType(QType::A
), QClass::IN
, ret
);
127 BOOST_CHECK_EQUAL(res
, RCode::NXDomain
);
128 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Secure
);
129 BOOST_REQUIRE_EQUAL(ret
.size(), 6U);
130 BOOST_CHECK_EQUAL(queriesCount
, 4U);
133 BOOST_AUTO_TEST_CASE(test_aggressive_nsec_nodata
)
135 std::unique_ptr
<SyncRes
> sr
;
137 g_aggressiveNSECCache
= make_unique
<AggressiveNSECCache
>(10000);
139 setDNSSECValidation(sr
, DNSSECMode::ValidateAll
);
142 /* we first ask a.powerdns.com. | A, get a NODATA, then check that the aggressive
143 NSEC cache will use the NSEC to prove that the AAAA does not exist either */
144 const DNSName
target("a.powerdns.com.");
147 auto luaconfsCopy
= g_luaconfs
.getCopy();
148 luaconfsCopy
.dsAnchors
.clear();
149 generateKeyMaterial(g_rootdnsname
, DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
, luaconfsCopy
.dsAnchors
);
150 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
152 g_luaconfs
.setState(luaconfsCopy
);
154 size_t queriesCount
= 0;
156 sr
->setAsyncCallback([target
, &queriesCount
, keys
](const ComboAddress
& ip
, const DNSName
& domain
, int type
, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval
* /* now */, boost::optional
<Netmask
>& /* srcmask */, boost::optional
<const ResolveContext
&> /* context */, LWResult
* res
, bool* /* chained */) {
159 if (type
== QType::DS
|| type
== QType::DNSKEY
) {
160 if (domain
!= DNSName("powerdns.com.") && domain
.isPartOf(DNSName("powerdns.com."))) {
162 return genericDSAndDNSKEYHandler(res
, domain
, domain
, type
, keys
, false);
164 else if (domain
== DNSName("com.")) {
166 return genericDSAndDNSKEYHandler(res
, domain
, DNSName("."), type
, keys
, false);
168 else if (domain
== DNSName("powerdns.com.")) {
169 return genericDSAndDNSKEYHandler(res
, domain
, DNSName("."), type
, keys
);
173 return genericDSAndDNSKEYHandler(res
, domain
, domain
, type
, keys
);
177 if (isRootServer(ip
)) {
178 setLWResult(res
, 0, false, false, true);
179 addRecordToLW(res
, "powerdns.com.", QType::NS
, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY
, 3600);
180 addDS(DNSName("powerdns.com."), 300, res
->d_records
, keys
);
181 addRRSIG(keys
, res
->d_records
, DNSName("."), 300);
182 addRecordToLW(res
, "a.gtld-servers.com.", QType::A
, "192.0.2.1", DNSResourceRecord::ADDITIONAL
, 3600);
183 return LWResult::Result::Success
;
185 else if (ip
== ComboAddress("192.0.2.1:53")) {
186 if (domain
== target
&& type
== QType::A
) {
187 setLWResult(res
, RCode::NoError
, true, false, true);
189 addRecordToLW(res
, DNSName("powerdns.com."), QType::SOA
, "powerdns.com. powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY
, 3600);
190 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
191 /* no record for this name */
193 addNSECRecordToLW(DNSName("a.powerdns.com."), DNSName("powerdns.com."), {QType::TXT
, QType::RRSIG
}, 600, res
->d_records
);
194 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
195 /* no need for wildcard in that case */
196 return LWResult::Result::Success
;
201 return LWResult::Result::Timeout
;
204 vector
<DNSRecord
> ret
;
205 int res
= sr
->beginResolve(target
, QType(QType::A
), QClass::IN
, ret
);
206 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
207 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Secure
);
208 BOOST_REQUIRE_EQUAL(ret
.size(), 4U);
209 BOOST_CHECK_EQUAL(queriesCount
, 4U);
212 res
= sr
->beginResolve(target
, QType(QType::AAAA
), QClass::IN
, ret
);
213 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
214 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Secure
);
215 BOOST_REQUIRE_EQUAL(ret
.size(), 4U);
216 BOOST_CHECK_EQUAL(queriesCount
, 4U);
219 BOOST_AUTO_TEST_CASE(test_aggressive_nsec_nodata_wildcard
)
221 std::unique_ptr
<SyncRes
> sr
;
223 g_aggressiveNSECCache
= make_unique
<AggressiveNSECCache
>(10000);
225 setDNSSECValidation(sr
, DNSSECMode::ValidateAll
);
228 /* we first ask a.powerdns.com. | A, get a NODATA (no exact match but there is a wildcard match),
229 then check that the aggressive NSEC cache will use the NSEC to prove that the AAAA does not exist either */
230 const DNSName
target("a.powerdns.com.");
233 auto luaconfsCopy
= g_luaconfs
.getCopy();
234 luaconfsCopy
.dsAnchors
.clear();
235 generateKeyMaterial(g_rootdnsname
, DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
, luaconfsCopy
.dsAnchors
);
236 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
238 g_luaconfs
.setState(luaconfsCopy
);
240 size_t queriesCount
= 0;
242 sr
->setAsyncCallback([target
, &queriesCount
, keys
](const ComboAddress
& ip
, const DNSName
& domain
, int type
, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval
* /* now */, boost::optional
<Netmask
>& /* srcmask */, boost::optional
<const ResolveContext
&> /* context */, LWResult
* res
, bool* /* chained */) {
245 if (type
== QType::DS
|| type
== QType::DNSKEY
) {
246 if (domain
!= DNSName("powerdns.com.") && domain
.isPartOf(DNSName("powerdns.com."))) {
247 /* no cut, NSEC, name does not exist but there is a wildcard (the generic version will generate an exact NSEC for the target, which we don't want) */
248 setLWResult(res
, RCode::NoError
, true, false, true);
250 addRecordToLW(res
, DNSName("powerdns.com."), QType::SOA
, "powerdns.com. powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY
, 3600);
251 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
252 /* the name does not exist, a wildcard applies but does not have this type */
253 addNSECRecordToLW(DNSName("*.powerdns.com."), DNSName("z.powerdns.com."), {QType::TXT
, QType::RRSIG
}, 600, res
->d_records
);
254 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300, false, boost::none
, DNSName("*.powerdns.com"));
255 return LWResult::Result::Success
;
257 else if (domain
== DNSName("com.")) {
259 return genericDSAndDNSKEYHandler(res
, domain
, DNSName("."), type
, keys
, false);
261 else if (domain
== DNSName("powerdns.com.")) {
262 return genericDSAndDNSKEYHandler(res
, domain
, DNSName("."), type
, keys
);
266 return genericDSAndDNSKEYHandler(res
, domain
, domain
, type
, keys
);
270 if (isRootServer(ip
)) {
271 setLWResult(res
, 0, false, false, true);
272 addRecordToLW(res
, "powerdns.com.", QType::NS
, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY
, 3600);
273 addDS(DNSName("powerdns.com."), 300, res
->d_records
, keys
);
274 addRRSIG(keys
, res
->d_records
, DNSName("."), 300);
275 addRecordToLW(res
, "a.gtld-servers.com.", QType::A
, "192.0.2.1", DNSResourceRecord::ADDITIONAL
, 3600);
276 return LWResult::Result::Success
;
278 else if (ip
== ComboAddress("192.0.2.1:53")) {
279 if (domain
== target
&& type
== QType::A
) {
280 setLWResult(res
, RCode::NoError
, true, false, true);
282 addRecordToLW(res
, DNSName("powerdns.com."), QType::SOA
, "powerdns.com. powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY
, 3600);
283 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
284 /* the name does not exist, a wildcard applies but does not have this type */
285 addNSECRecordToLW(DNSName("*.powerdns.com."), DNSName("z.powerdns.com."), {QType::TXT
, QType::RRSIG
}, 600, res
->d_records
);
286 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300, false, boost::none
, DNSName("*.powerdns.com"));
287 return LWResult::Result::Success
;
292 return LWResult::Result::Timeout
;
295 vector
<DNSRecord
> ret
;
296 int res
= sr
->beginResolve(target
, QType(QType::A
), QClass::IN
, ret
);
297 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
298 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Secure
);
299 BOOST_REQUIRE_EQUAL(ret
.size(), 4U);
300 BOOST_CHECK_EQUAL(queriesCount
, 4U);
303 res
= sr
->beginResolve(target
, QType(QType::AAAA
), QClass::IN
, ret
);
304 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
305 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Secure
);
306 BOOST_REQUIRE_EQUAL(ret
.size(), 4U);
307 BOOST_CHECK_EQUAL(queriesCount
, 4U);
310 BOOST_AUTO_TEST_CASE(test_aggressive_nsec_ancestor
)
312 std::unique_ptr
<SyncRes
> sr
;
314 g_aggressiveNSECCache
= make_unique
<AggressiveNSECCache
>(10000);
316 setDNSSECValidation(sr
, DNSSECMode::ValidateAll
);
319 /* powerdns.com is signed, sub.powerdns.com. is not.
320 We first get a query for sub.powerdns.com. which leads to an ancestor NSEC covering sub.powerdns.com.|DS to be inserted
321 into the aggressive cache, check that we don't mistakenly use that later to prove that something else below that name
322 doesn't exist either. */
323 const DNSName
target("sub.powerdns.com.");
326 auto luaconfsCopy
= g_luaconfs
.getCopy();
327 luaconfsCopy
.dsAnchors
.clear();
328 generateKeyMaterial(g_rootdnsname
, DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
, luaconfsCopy
.dsAnchors
);
329 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
331 g_luaconfs
.setState(luaconfsCopy
);
333 size_t queriesCount
= 0;
335 sr
->setAsyncCallback([target
, &queriesCount
, keys
](const ComboAddress
& ip
, const DNSName
& domain
, int type
, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval
* /* now */, boost::optional
<Netmask
>& /* srcmask */, boost::optional
<const ResolveContext
&> /* context */, LWResult
* res
, bool* /* chained */) {
338 if (type
== QType::DS
|| type
== QType::DNSKEY
) {
339 if (domain
== DNSName("com.")) {
341 return genericDSAndDNSKEYHandler(res
, domain
, DNSName("."), type
, keys
, false);
345 return genericDSAndDNSKEYHandler(res
, domain
, domain
, type
, keys
);
349 if (isRootServer(ip
)) {
350 setLWResult(res
, 0, false, false, true);
351 addRecordToLW(res
, "powerdns.com.", QType::NS
, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY
, 3600);
352 addDS(DNSName("powerdns.com."), 300, res
->d_records
, keys
);
353 addRRSIG(keys
, res
->d_records
, DNSName("."), 300);
354 addRecordToLW(res
, "a.gtld-servers.com.", QType::A
, "192.0.2.1", DNSResourceRecord::ADDITIONAL
, 3600);
355 return LWResult::Result::Success
;
357 else if (ip
== ComboAddress("192.0.2.1:53")) {
358 if (domain
.isPartOf(DNSName("sub.powerdns.com."))) {
359 setLWResult(res
, 0, false, false, true);
360 addRecordToLW(res
, "sub.powerdns.com.", QType::NS
, "ns.sub.powerdns.com.", DNSResourceRecord::AUTHORITY
, 3600);
361 /* proof that the DS doesn't exist follows */
362 /* NSEC ancestor for sub.powerdns.com */
363 addNSECRecordToLW(DNSName("sub.powerdns.com."), DNSName("sub1.powerdns.com"), {QType::NS
}, 600, res
->d_records
);
364 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
365 addRecordToLW(res
, "ns.sub.powerdns.com.", QType::A
, "192.0.2.2", DNSResourceRecord::ADDITIONAL
, 3600);
366 return LWResult::Result::Success
;
368 else if (domain
== DNSName("sub16.powerdns.com.")) {
369 setLWResult(res
, RCode::NXDomain
, true, false, true);
370 addRecordToLW(res
, DNSName("powerdns.com."), QType::SOA
, "powerdns.com. powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY
, 3600);
371 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
372 addNSECRecordToLW(DNSName("sub15.powerdns.com."), DNSName("sub17.powerdns.com."), {QType::A
}, 600, res
->d_records
);
373 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
374 /* then the wildcard *.powerdns.com
375 next covers the wildcard *.sub.powerdns.com
377 addNSECRecordToLW(DNSName(").powerdns.com"), DNSName("+.sub.powerdns.com"), {QType::TXT
, QType::RRSIG
}, 600, res
->d_records
);
378 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
379 return LWResult::Result::Success
;
382 else if (ip
== ComboAddress("192.0.2.2:53")) {
383 if (domain
== target
&& type
== QType::A
) {
384 setLWResult(res
, RCode::NoError
, true, false, true);
385 addRecordToLW(res
, DNSName("sub.powerdns.com."), QType::A
, "192.0.2.42");
386 return LWResult::Result::Success
;
388 else if (domain
== DNSName("4.sub.powerdns.com.") && type
== QType::A
) {
389 setLWResult(res
, RCode::NoError
, true, false, true);
390 addRecordToLW(res
, DNSName("4.sub.powerdns.com."), QType::A
, "192.0.2.84");
391 return LWResult::Result::Success
;
396 return LWResult::Result::Timeout
;
399 vector
<DNSRecord
> ret
;
400 int res
= sr
->beginResolve(target
, QType(QType::A
), QClass::IN
, ret
);
401 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
402 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Insecure
);
403 BOOST_REQUIRE_EQUAL(ret
.size(), 1U);
404 BOOST_CHECK_EQUAL(queriesCount
, 5U);
406 /* now we query sub16.powerdns.com, to get a NSEC covering the wildcard for *.sub.powerdns.com */
408 res
= sr
->beginResolve(DNSName("sub16.powerdns.com."), QType(QType::A
), QClass::IN
, ret
);
409 BOOST_CHECK_EQUAL(res
, RCode::NXDomain
);
410 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Secure
);
411 BOOST_REQUIRE_EQUAL(ret
.size(), 6U);
412 BOOST_CHECK_EQUAL(queriesCount
, 6U);
414 /* now we query other2.sub.powerdns.com, we should NOT be able to use the NSECs we have
415 to prove that the name does not exist */
417 res
= sr
->beginResolve(DNSName("4.sub.powerdns.com"), QType(QType::DS
), QClass::IN
, ret
);
418 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
419 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Insecure
);
420 BOOST_REQUIRE_EQUAL(ret
.size(), 1U);
421 BOOST_CHECK_EQUAL(queriesCount
, 8U);
424 BOOST_AUTO_TEST_CASE(test_aggressive_nsec_wildcard_synthesis
)
426 std::unique_ptr
<SyncRes
> sr
;
428 g_aggressiveNSECCache
= make_unique
<AggressiveNSECCache
>(10000);
430 setDNSSECValidation(sr
, DNSSECMode::ValidateAll
);
433 /* we first ask a.powerdns.com. | A, get an answer synthesized from the wildcard.
434 We can use it yet because we need the SOA, so let's request a non-existing type
435 then check that the aggressive NSEC cache will use the wildcard to synthesize an answer
436 for b.powerdns.com */
437 const DNSName
target("a.powerdns.com.");
440 auto luaconfsCopy
= g_luaconfs
.getCopy();
441 luaconfsCopy
.dsAnchors
.clear();
442 generateKeyMaterial(g_rootdnsname
, DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
, luaconfsCopy
.dsAnchors
);
443 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
445 g_luaconfs
.setState(luaconfsCopy
);
447 size_t queriesCount
= 0;
449 sr
->setAsyncCallback([target
, &queriesCount
, keys
](const ComboAddress
& ip
, const DNSName
& domain
, int type
, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval
* /* now */, boost::optional
<Netmask
>& /* srcmask */, boost::optional
<const ResolveContext
&> /* context */, LWResult
* res
, bool* /* chained */) {
452 if (type
== QType::DS
|| type
== QType::DNSKEY
) {
453 if (domain
!= DNSName("powerdns.com.") && domain
.isPartOf(DNSName("powerdns.com."))) {
454 /* no cut, NSEC, name does not exist but there is a wildcard (the generic version will generate an exact NSEC for the target, which we don't want) */
455 setLWResult(res
, RCode::NoError
, true, false, true);
457 addRecordToLW(res
, DNSName("powerdns.com."), QType::SOA
, "powerdns.com. powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY
, 3600);
458 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
459 /* the name does not exist, a wildcard applies and have the requested type but no DS */
460 addNSECRecordToLW(DNSName("*.powerdns.com."), DNSName("z.powerdns.com."), {QType::A
, QType::RRSIG
}, 600, res
->d_records
);
461 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300, false, boost::none
, DNSName("*.powerdns.com"));
462 return LWResult::Result::Success
;
464 else if (domain
== DNSName("com.")) {
466 return genericDSAndDNSKEYHandler(res
, domain
, DNSName("."), type
, keys
, false);
468 else if (domain
== DNSName("powerdns.com.")) {
469 return genericDSAndDNSKEYHandler(res
, domain
, DNSName("."), type
, keys
);
473 return genericDSAndDNSKEYHandler(res
, domain
, domain
, type
, keys
);
477 if (isRootServer(ip
)) {
478 setLWResult(res
, 0, false, false, true);
479 addRecordToLW(res
, "powerdns.com.", QType::NS
, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY
, 3600);
480 addDS(DNSName("powerdns.com."), 300, res
->d_records
, keys
);
481 addRRSIG(keys
, res
->d_records
, DNSName("."), 300);
482 addRecordToLW(res
, "a.gtld-servers.com.", QType::A
, "192.0.2.1", DNSResourceRecord::ADDITIONAL
, 3600);
483 return LWResult::Result::Success
;
485 else if (ip
== ComboAddress("192.0.2.1:53")) {
486 if (type
== QType::A
) {
487 setLWResult(res
, RCode::NoError
, true, false, true);
488 addRecordToLW(res
, domain
, QType::A
, "192.0.2.1");
489 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300, false, boost::none
, DNSName("*.powerdns.com"));
490 /* the name does not exist, a wildcard applies and has the requested type */
491 addNSECRecordToLW(DNSName("*.powerdns.com."), DNSName("z.powerdns.com."), {QType::A
, QType::RRSIG
}, 600, res
->d_records
);
492 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300, false, boost::none
, DNSName("*.powerdns.com"));
493 return LWResult::Result::Success
;
495 else if (type
== QType::TXT
) {
496 setLWResult(res
, RCode::NoError
, true, false, true);
497 /* the name does not exist, a wildcard applies but does not have the requested type */
498 addRecordToLW(res
, DNSName("powerdns.com."), QType::SOA
, "powerdns.com. powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY
);
499 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
500 addNSECRecordToLW(DNSName("*.powerdns.com."), DNSName("z.powerdns.com."), {QType::A
, QType::RRSIG
}, 600, res
->d_records
);
501 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300, false, boost::none
, DNSName("*.powerdns.com"));
502 return LWResult::Result::Success
;
507 return LWResult::Result::Timeout
;
510 vector
<DNSRecord
> ret
;
511 int res
= sr
->beginResolve(target
, QType(QType::A
), QClass::IN
, ret
);
512 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
513 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Secure
);
514 BOOST_REQUIRE_EQUAL(ret
.size(), 4U);
515 BOOST_CHECK_EQUAL(ret
.at(0).d_name
, target
);
516 BOOST_CHECK_EQUAL(ret
.at(0).d_type
, QType(QType::A
).getCode());
517 BOOST_CHECK_EQUAL(queriesCount
, 4U);
519 /* request the TXT to get the SOA */
521 res
= sr
->beginResolve(target
, QType(QType::TXT
), QClass::IN
, ret
);
522 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
523 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Secure
);
524 BOOST_REQUIRE_EQUAL(ret
.size(), 4U);
525 BOOST_CHECK_EQUAL(ret
.at(0).d_name
, DNSName("powerdns.com."));
526 BOOST_CHECK_EQUAL(ret
.at(0).d_type
, QType(QType::SOA
).getCode());
527 BOOST_CHECK_EQUAL(queriesCount
, 5U);
530 res
= sr
->beginResolve(DNSName("b.powerdns.com."), QType(QType::A
), QClass::IN
, ret
);
531 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
532 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Secure
);
533 BOOST_REQUIRE_EQUAL(ret
.size(), 4U);
534 BOOST_CHECK_EQUAL(ret
.at(0).d_name
, DNSName("b.powerdns.com."));
535 BOOST_CHECK_EQUAL(ret
.at(0).d_type
, QType(QType::A
).getCode());
536 BOOST_CHECK_EQUAL(queriesCount
, 5U);
539 BOOST_AUTO_TEST_CASE(test_aggressive_nsec3_nxdomain
)
541 std::unique_ptr
<SyncRes
> sr
;
543 AggressiveNSECCache::s_maxNSEC3CommonPrefix
= 159;
544 g_aggressiveNSECCache
= make_unique
<AggressiveNSECCache
>(10000);
546 setDNSSECValidation(sr
, DNSSECMode::ValidateAll
);
549 /* we are lucky enough that our hashes will cover g.powerdns.com. as well,
550 so we first ask b.powerdns.com., get a NXD, then check that the aggressive
551 NSEC cache will use the NSEC3 to prove that g.powerdns.com. does not exist
553 const DNSName
target1("b.powerdns.com.");
554 const DNSName
target2("g.powerdns.com.");
557 auto luaconfsCopy
= g_luaconfs
.getCopy();
558 luaconfsCopy
.dsAnchors
.clear();
559 generateKeyMaterial(g_rootdnsname
, DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
, luaconfsCopy
.dsAnchors
);
560 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
562 g_luaconfs
.setState(luaconfsCopy
);
564 size_t queriesCount
= 0;
566 sr
->setAsyncCallback([target1
, &queriesCount
, keys
](const ComboAddress
& ip
, const DNSName
& domain
, int type
, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval
* /* now */, boost::optional
<Netmask
>& /* srcmask */, boost::optional
<const ResolveContext
&> /* context */, LWResult
* res
, bool* /* chained */) {
569 if (type
== QType::DS
|| type
== QType::DNSKEY
) {
570 if (domain
!= DNSName("powerdns.com.") && domain
.isPartOf(DNSName("powerdns.com."))) {
572 setLWResult(res
, RCode::NXDomain
, true, false, true);
573 addRecordToLW(res
, DNSName("powerdns.com."), QType::SOA
, "powerdns.com. powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY
, 3600);
574 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
575 /* no record for this name */
576 /* first the closest encloser */
577 addNSEC3UnhashedRecordToLW(DNSName("powerdns.com."), DNSName("powerdns.com."), "whatever", {QType::A
, QType::TXT
, QType::RRSIG
}, 600, res
->d_records
);
578 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
579 /* then the next closer */
580 addNSEC3UnhashedRecordToLW(DNSName("a.powerdns.com."), DNSName("powerdns.com."), "v", {QType::RRSIG
}, 600, res
->d_records
);
581 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
583 addNSEC3NarrowRecordToLW(DNSName("*.powerdns.com."), DNSName("powerdns.com."), {QType::AAAA
, QType::RRSIG
}, 600, res
->d_records
);
584 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com"), 300);
585 return LWResult::Result::Success
;
587 else if (domain
== DNSName("com.")) {
589 return genericDSAndDNSKEYHandler(res
, domain
, DNSName("."), type
, keys
, false);
591 else if (domain
== DNSName("powerdns.com.")) {
592 return genericDSAndDNSKEYHandler(res
, domain
, DNSName("."), type
, keys
);
596 return genericDSAndDNSKEYHandler(res
, domain
, domain
, type
, keys
);
600 if (isRootServer(ip
)) {
601 setLWResult(res
, 0, false, false, true);
602 addRecordToLW(res
, "powerdns.com.", QType::NS
, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY
, 3600);
603 addDS(DNSName("powerdns.com."), 300, res
->d_records
, keys
);
604 addRRSIG(keys
, res
->d_records
, DNSName("."), 300);
605 addRecordToLW(res
, "a.gtld-servers.com.", QType::A
, "192.0.2.1", DNSResourceRecord::ADDITIONAL
, 3600);
606 return LWResult::Result::Success
;
608 else if (ip
== ComboAddress("192.0.2.1:53")) {
609 if (domain
== target1
) {
610 setLWResult(res
, RCode::NXDomain
, true, false, true);
611 addRecordToLW(res
, DNSName("powerdns.com."), QType::SOA
, "powerdns.com. powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY
, 3600);
612 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
613 /* no record for this name */
614 /* first the closest encloser */
615 addNSEC3UnhashedRecordToLW(DNSName("powerdns.com."), DNSName("powerdns.com."), "whatever", {QType::A
, QType::TXT
, QType::RRSIG
}, 600, res
->d_records
);
616 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
617 /* then the next closer */
618 addNSEC3UnhashedRecordToLW(DNSName("a.powerdns.com."), DNSName("powerdns.com."), "v", {QType::RRSIG
}, 600, res
->d_records
);
619 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
621 addNSEC3NarrowRecordToLW(DNSName("*.powerdns.com."), DNSName("powerdns.com."), {QType::AAAA
, QType::RRSIG
}, 600, res
->d_records
);
622 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com"), 300);
623 return LWResult::Result::Success
;
628 return LWResult::Result::Timeout
;
631 vector
<DNSRecord
> ret
;
632 int res
= sr
->beginResolve(target1
, QType(QType::A
), QClass::IN
, ret
);
633 BOOST_CHECK_EQUAL(res
, RCode::NXDomain
);
634 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Secure
);
635 BOOST_REQUIRE_EQUAL(ret
.size(), 8U);
636 BOOST_CHECK_EQUAL(queriesCount
, 4U);
639 res
= sr
->beginResolve(target2
, QType(QType::A
), QClass::IN
, ret
);
640 BOOST_CHECK_EQUAL(res
, RCode::NXDomain
);
641 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Secure
);
642 BOOST_REQUIRE_EQUAL(ret
.size(), 8U);
643 BOOST_CHECK_EQUAL(queriesCount
, 4U);
646 BOOST_AUTO_TEST_CASE(test_aggressive_nsec3_nodata
)
648 std::unique_ptr
<SyncRes
> sr
;
650 g_aggressiveNSECCache
= make_unique
<AggressiveNSECCache
>(10000);
652 setDNSSECValidation(sr
, DNSSECMode::ValidateAll
);
655 /* we first ask a.powerdns.com. | A, get a NODATA, then check that the aggressive
656 NSEC cache will use the NSEC3 to prove that the AAAA does not exist either */
657 const DNSName
target("a.powerdns.com.");
660 auto luaconfsCopy
= g_luaconfs
.getCopy();
661 luaconfsCopy
.dsAnchors
.clear();
662 generateKeyMaterial(g_rootdnsname
, DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
, luaconfsCopy
.dsAnchors
);
663 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
665 g_luaconfs
.setState(luaconfsCopy
);
667 size_t queriesCount
= 0;
669 sr
->setAsyncCallback([target
, &queriesCount
, keys
](const ComboAddress
& ip
, const DNSName
& domain
, int type
, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval
* /* now */, boost::optional
<Netmask
>& /* srcmask */, boost::optional
<const ResolveContext
&> /* context */, LWResult
* res
, bool* /* chained */) {
672 if (type
== QType::DS
|| type
== QType::DNSKEY
) {
673 if (domain
!= DNSName("powerdns.com.") && domain
.isPartOf(DNSName("powerdns.com."))) {
675 return genericDSAndDNSKEYHandler(res
, domain
, domain
, type
, keys
, false, boost::none
, true);
677 else if (domain
== DNSName("com.")) {
679 return genericDSAndDNSKEYHandler(res
, domain
, DNSName("."), type
, keys
, false);
681 else if (domain
== DNSName("powerdns.com.")) {
682 return genericDSAndDNSKEYHandler(res
, domain
, DNSName("."), type
, keys
);
686 return genericDSAndDNSKEYHandler(res
, domain
, domain
, type
, keys
);
690 if (isRootServer(ip
)) {
691 setLWResult(res
, 0, false, false, true);
692 addRecordToLW(res
, "powerdns.com.", QType::NS
, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY
, 3600);
693 addDS(DNSName("powerdns.com."), 300, res
->d_records
, keys
);
694 addRRSIG(keys
, res
->d_records
, DNSName("."), 300);
695 addRecordToLW(res
, "a.gtld-servers.com.", QType::A
, "192.0.2.1", DNSResourceRecord::ADDITIONAL
, 3600);
696 return LWResult::Result::Success
;
698 else if (ip
== ComboAddress("192.0.2.1:53")) {
699 if (domain
== target
&& type
== QType::A
) {
700 setLWResult(res
, RCode::NoError
, true, false, true);
702 addRecordToLW(res
, DNSName("powerdns.com."), QType::SOA
, "powerdns.com. powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY
, 3600);
703 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
704 /* no record for this name */
706 addNSEC3UnhashedRecordToLW(DNSName("a.powerdns.com."), DNSName("powerdns.com."), "whatever", {QType::TXT
, QType::RRSIG
}, 600, res
->d_records
);
707 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
708 /* no need for next closer or wildcard in that case */
709 return LWResult::Result::Success
;
714 return LWResult::Result::Timeout
;
717 vector
<DNSRecord
> ret
;
718 int res
= sr
->beginResolve(target
, QType(QType::A
), QClass::IN
, ret
);
719 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
720 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Secure
);
721 BOOST_REQUIRE_EQUAL(ret
.size(), 4U);
722 BOOST_CHECK_EQUAL(queriesCount
, 4U);
725 res
= sr
->beginResolve(target
, QType(QType::AAAA
), QClass::IN
, ret
);
726 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
727 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Secure
);
728 BOOST_REQUIRE_EQUAL(ret
.size(), 4U);
729 BOOST_CHECK_EQUAL(queriesCount
, 4U);
732 BOOST_AUTO_TEST_CASE(test_aggressive_nsec3_nodata_wildcard
)
734 std::unique_ptr
<SyncRes
> sr
;
736 AggressiveNSECCache::s_maxNSEC3CommonPrefix
= 159;
737 g_aggressiveNSECCache
= make_unique
<AggressiveNSECCache
>(10000);
739 setDNSSECValidation(sr
, DNSSECMode::ValidateAll
);
742 /* we first ask a.powerdns.com. | A, get a NODATA (no exact match but there is a wildcard match),
743 then check that the aggressive NSEC cache will use the NSEC3 to prove that the AAAA does not exist either */
744 const DNSName
target("a.powerdns.com.");
747 auto luaconfsCopy
= g_luaconfs
.getCopy();
748 luaconfsCopy
.dsAnchors
.clear();
749 generateKeyMaterial(g_rootdnsname
, DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
, luaconfsCopy
.dsAnchors
);
750 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
752 g_luaconfs
.setState(luaconfsCopy
);
754 size_t queriesCount
= 0;
756 sr
->setAsyncCallback([target
, &queriesCount
, keys
](const ComboAddress
& ip
, const DNSName
& domain
, int type
, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval
* /* now */, boost::optional
<Netmask
>& /* srcmask */, boost::optional
<const ResolveContext
&> /* context */, LWResult
* res
, bool* /* chained */) {
759 if (type
== QType::DS
|| type
== QType::DNSKEY
) {
760 if (domain
!= DNSName("powerdns.com.") && domain
.isPartOf(DNSName("powerdns.com."))) {
761 /* no cut, NSEC3, name does not exist but there is a wildcard (the generic version will generate an exact NSEC3 for the target, which we don't want) */
762 setLWResult(res
, RCode::NoError
, true, false, true);
764 addRecordToLW(res
, DNSName("powerdns.com."), QType::SOA
, "powerdns.com. powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY
, 3600);
765 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
766 /* first the closest encloser */
767 addNSEC3UnhashedRecordToLW(DNSName("powerdns.com."), DNSName("powerdns.com."), "whatever", {QType::A
, QType::TXT
, QType::RRSIG
}, 600, res
->d_records
, 10);
768 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
769 /* then the next closer */
770 addNSEC3UnhashedRecordToLW(DNSName("+.powerdns.com."), DNSName("powerdns.com."), "v", {QType::RRSIG
}, 600, res
->d_records
, 10);
771 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
772 /* a wildcard applies but does not have this type */
773 addNSEC3UnhashedRecordToLW(DNSName("*.powerdns.com."), DNSName("powerdns.com."), "whatever", {QType::TXT
, QType::RRSIG
}, 600, res
->d_records
, 10);
774 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com"), 300, false, boost::none
, DNSName("*.powerdns.com"));
775 return LWResult::Result::Success
;
777 else if (domain
== DNSName("com.")) {
779 return genericDSAndDNSKEYHandler(res
, domain
, DNSName("."), type
, keys
, false);
781 else if (domain
== DNSName("powerdns.com.")) {
782 return genericDSAndDNSKEYHandler(res
, domain
, DNSName("."), type
, keys
);
786 return genericDSAndDNSKEYHandler(res
, domain
, domain
, type
, keys
);
790 if (isRootServer(ip
)) {
791 setLWResult(res
, 0, false, false, true);
792 addRecordToLW(res
, "powerdns.com.", QType::NS
, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY
, 3600);
793 addDS(DNSName("powerdns.com."), 300, res
->d_records
, keys
);
794 addRRSIG(keys
, res
->d_records
, DNSName("."), 300);
795 addRecordToLW(res
, "a.gtld-servers.com.", QType::A
, "192.0.2.1", DNSResourceRecord::ADDITIONAL
, 3600);
796 return LWResult::Result::Success
;
798 else if (ip
== ComboAddress("192.0.2.1:53")) {
799 if (domain
== target
&& type
== QType::A
) {
800 setLWResult(res
, RCode::NoError
, true, false, true);
802 addRecordToLW(res
, DNSName("powerdns.com."), QType::SOA
, "powerdns.com. powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY
, 3600);
803 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
804 /* first the closest encloser */
805 addNSEC3NoDataNarrowRecordToLW(DNSName("powerdns.com."), DNSName("powerdns.com."), {QType::A
, QType::TXT
, QType::RRSIG
}, 600, res
->d_records
, 10);
806 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
807 /* then the next closer */
808 addNSEC3NarrowRecordToLW(DNSName("a.powerdns.com."), DNSName("powerdns.com."), {QType::RRSIG
}, 600, res
->d_records
, 10);
809 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
810 /* a wildcard applies but does not have this type */
811 addNSEC3NoDataNarrowRecordToLW(DNSName("*.powerdns.com."), DNSName("powerdns.com."), {QType::TXT
, QType::RRSIG
}, 600, res
->d_records
, 10);
812 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com"), 300, false, boost::none
, DNSName("*.powerdns.com"));
813 return LWResult::Result::Success
;
818 return LWResult::Result::Timeout
;
821 vector
<DNSRecord
> ret
;
822 int res
= sr
->beginResolve(target
, QType(QType::A
), QClass::IN
, ret
);
823 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
824 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Secure
);
825 BOOST_REQUIRE_EQUAL(ret
.size(), 8U);
826 BOOST_CHECK_EQUAL(queriesCount
, 4U);
829 res
= sr
->beginResolve(target
, QType(QType::AAAA
), QClass::IN
, ret
);
830 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
831 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Secure
);
832 BOOST_REQUIRE_EQUAL(ret
.size(), 8U);
833 BOOST_CHECK_EQUAL(queriesCount
, 4U);
836 BOOST_AUTO_TEST_CASE(test_aggressive_nsec3_ancestor
)
838 std::unique_ptr
<SyncRes
> sr
;
840 g_aggressiveNSECCache
= make_unique
<AggressiveNSECCache
>(10000);
842 setDNSSECValidation(sr
, DNSSECMode::ValidateAll
);
845 /* powerdns.com is signed, sub.powerdns.com. is not.
846 We first get a query for sub.powerdns.com. which leads to an ancestor NSEC3 covering sub.powerdns.com.|DS to be inserted
847 into the aggressive cache, check that we don't mistakenly use that later to prove that something else below that name
848 doesn't exist either. */
849 const DNSName
target("sub.powerdns.com.");
852 auto luaconfsCopy
= g_luaconfs
.getCopy();
853 luaconfsCopy
.dsAnchors
.clear();
854 generateKeyMaterial(g_rootdnsname
, DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
, luaconfsCopy
.dsAnchors
);
855 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
857 g_luaconfs
.setState(luaconfsCopy
);
859 size_t queriesCount
= 0;
861 sr
->setAsyncCallback([target
, &queriesCount
, keys
](const ComboAddress
& ip
, const DNSName
& domain
, int type
, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval
* /* now */, boost::optional
<Netmask
>& /* srcmask */, boost::optional
<const ResolveContext
&> /* context */, LWResult
* res
, bool* /* chained */) {
864 if (type
== QType::DS
|| type
== QType::DNSKEY
) {
865 if (domain
== DNSName("com.")) {
867 return genericDSAndDNSKEYHandler(res
, domain
, DNSName("."), type
, keys
, false);
871 return genericDSAndDNSKEYHandler(res
, domain
, domain
, type
, keys
);
875 if (isRootServer(ip
)) {
876 setLWResult(res
, 0, false, false, true);
877 addRecordToLW(res
, "powerdns.com.", QType::NS
, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY
, 3600);
878 addDS(DNSName("powerdns.com."), 300, res
->d_records
, keys
);
879 addRRSIG(keys
, res
->d_records
, DNSName("."), 300);
880 addRecordToLW(res
, "a.gtld-servers.com.", QType::A
, "192.0.2.1", DNSResourceRecord::ADDITIONAL
, 3600);
881 return LWResult::Result::Success
;
883 else if (ip
== ComboAddress("192.0.2.1:53")) {
884 if (domain
.isPartOf(DNSName("sub.powerdns.com."))) {
885 setLWResult(res
, 0, false, false, true);
886 addRecordToLW(res
, "sub.powerdns.com.", QType::NS
, "ns.sub.powerdns.com.", DNSResourceRecord::AUTHORITY
, 3600);
887 /* proof that the DS doesn't exist follows */
888 /* NSEC3 ancestor for sub.powerdns.com (1 additional iteration, deadbeef as salt), : 7v5rgf7okrmumvb8rscop0t3j1m5o4mb
889 next is crafted to cover 4.sub.powerdns.com => 930v7tmju1s48fopjh5ktsp1jmagi20p */
890 addNSEC3RecordToLW(DNSName("7v5rgf7okrmumvb8rscop0t3j1m5o4mb.powerdns.com."), fromBase32Hex("930v7tmju1s48fopjh5ktsp1jmagi20q"), "deadbeef", 1, {QType::NS
}, 600, res
->d_records
);
891 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
892 addRecordToLW(res
, "ns.sub.powerdns.com.", QType::A
, "192.0.2.2", DNSResourceRecord::ADDITIONAL
, 3600);
893 return LWResult::Result::Success
;
895 else if (domain
== DNSName("sub16.powerdns.com.")) {
896 setLWResult(res
, RCode::NXDomain
, true, false, true);
897 addRecordToLW(res
, DNSName("powerdns.com."), QType::SOA
, "powerdns.com. powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY
, 3600);
898 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
899 /* first the closest encloser */
900 addNSEC3UnhashedRecordToLW(DNSName("powerdns.com."), DNSName("powerdns.com."), "whatever", {QType::SOA
, QType::NS
}, 600, res
->d_records
, 1);
901 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
902 /* then the next closer sub16.powerdns.com. */
903 addNSEC3NarrowRecordToLW(DNSName("sub16.powerdns.com."), DNSName("powerdns.com."), {QType::A
}, 600, res
->d_records
, 1);
904 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
905 /* then the wildcard *.powerdns.com: mtrrinpd8l9e7fmn8lp74o8dffnivs8i (minus one because NXD)
906 next is crafted to cover the wildcard *.sub.powerdns.com (ocgb0ilk3g1m3olpms0q1quhn18nncc0)
908 addNSEC3RecordToLW(DNSName("mtrrinpd8l9e7fmn8lp74o8dffnivs8h.powerdns.com."), fromBase32Hex("ocgb0ilk3g1m3olpms0q1quhn18nncc1"), "deadbeef", 1, {QType::TXT
, QType::RRSIG
}, 600, res
->d_records
);
909 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
910 return LWResult::Result::Success
;
913 else if (ip
== ComboAddress("192.0.2.2:53")) {
914 if (domain
== target
&& type
== QType::A
) {
915 setLWResult(res
, RCode::NoError
, true, false, true);
916 addRecordToLW(res
, DNSName("sub.powerdns.com."), QType::A
, "192.0.2.42");
917 return LWResult::Result::Success
;
919 else if (domain
== DNSName("4.sub.powerdns.com.") && type
== QType::A
) {
920 setLWResult(res
, RCode::NoError
, true, false, true);
921 addRecordToLW(res
, DNSName("4.sub.powerdns.com."), QType::A
, "192.0.2.84");
922 return LWResult::Result::Success
;
927 return LWResult::Result::Timeout
;
930 vector
<DNSRecord
> ret
;
931 int res
= sr
->beginResolve(target
, QType(QType::A
), QClass::IN
, ret
);
932 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
933 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Insecure
);
934 BOOST_REQUIRE_EQUAL(ret
.size(), 1U);
935 BOOST_CHECK_EQUAL(queriesCount
, 5U);
937 /* now we query sub16.powerdns.com, to get a hash covering the wildcard for
938 *.sub.powerdns.com */
940 res
= sr
->beginResolve(DNSName("sub16.powerdns.com."), QType(QType::A
), QClass::IN
, ret
);
941 BOOST_CHECK_EQUAL(res
, RCode::NXDomain
);
942 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Secure
);
943 BOOST_REQUIRE_EQUAL(ret
.size(), 8U);
944 BOOST_CHECK_EQUAL(queriesCount
, 6U);
946 /* now we query other2.sub.powerdns.com, we should NOT be able to use the NSEC3s we have
947 to prove that the name does not exist */
949 res
= sr
->beginResolve(DNSName("4.sub.powerdns.com"), QType(QType::DS
), QClass::IN
, ret
);
950 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
951 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Insecure
);
952 BOOST_REQUIRE_EQUAL(ret
.size(), 1U);
953 BOOST_CHECK_EQUAL(queriesCount
, 8U);
956 BOOST_AUTO_TEST_CASE(test_aggressive_nsec3_wildcard_synthesis
)
958 std::unique_ptr
<SyncRes
> sr
;
960 g_aggressiveNSECCache
= make_unique
<AggressiveNSECCache
>(10000);
962 setDNSSECValidation(sr
, DNSSECMode::ValidateAll
);
965 /* we first ask a.powerdns.com. | A, get an answer synthesized from the wildcard.
966 We can't use it right away because we don't have the SOA, so let's do a TXT query to get it,
967 then check that the aggressive NSEC cache will use the wildcard to synthesize an answer
968 for b.powerdns.com */
969 const DNSName
target("a.powerdns.com.");
972 auto luaconfsCopy
= g_luaconfs
.getCopy();
973 luaconfsCopy
.dsAnchors
.clear();
974 generateKeyMaterial(g_rootdnsname
, DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
, luaconfsCopy
.dsAnchors
);
975 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256
, DNSSECKeeper::DIGEST_SHA256
, keys
);
977 g_luaconfs
.setState(luaconfsCopy
);
979 size_t queriesCount
= 0;
981 sr
->setAsyncCallback([target
, &queriesCount
, keys
](const ComboAddress
& ip
, const DNSName
& domain
, int type
, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval
* /* now */, boost::optional
<Netmask
>& /* srcmask */, boost::optional
<const ResolveContext
&> /* context */, LWResult
* res
, bool* /* chained */) {
984 if (type
== QType::DS
|| type
== QType::DNSKEY
) {
985 if (domain
!= DNSName("powerdns.com.") && domain
.isPartOf(DNSName("powerdns.com."))) {
986 /* no cut, NSEC3, name does not exist but there is a wildcard (the generic version will generate an exact NSEC3 for the target, which we don't want) */
987 setLWResult(res
, RCode::NoError
, true, false, true);
989 addRecordToLW(res
, DNSName("powerdns.com."), QType::SOA
, "powerdns.com. powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY
, 3600);
990 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
991 /* first the closest encloser */
992 addNSEC3UnhashedRecordToLW(DNSName("powerdns.com."), DNSName("powerdns.com."), "whatever", {QType::A
, QType::TXT
, QType::RRSIG
}, 600, res
->d_records
, 10);
993 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
994 /* then the next closer */
995 addNSEC3UnhashedRecordToLW(DNSName("+.powerdns.com."), DNSName("powerdns.com."), "v", {QType::RRSIG
}, 600, res
->d_records
, 10);
996 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
997 /* a wildcard applies but does not have this type */
998 addNSEC3UnhashedRecordToLW(DNSName("*.powerdns.com."), DNSName("powerdns.com."), "whatever", {QType::A
, QType::RRSIG
}, 600, res
->d_records
, 10);
999 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com"), 300, false, boost::none
, DNSName("*.powerdns.com"));
1000 return LWResult::Result::Success
;
1002 else if (domain
== DNSName("com.")) {
1004 return genericDSAndDNSKEYHandler(res
, domain
, DNSName("."), type
, keys
, false);
1006 else if (domain
== DNSName("powerdns.com.")) {
1007 return genericDSAndDNSKEYHandler(res
, domain
, DNSName("."), type
, keys
);
1011 return genericDSAndDNSKEYHandler(res
, domain
, domain
, type
, keys
);
1015 if (isRootServer(ip
)) {
1016 setLWResult(res
, 0, false, false, true);
1017 addRecordToLW(res
, "powerdns.com.", QType::NS
, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY
, 3600);
1018 addDS(DNSName("powerdns.com."), 300, res
->d_records
, keys
);
1019 addRRSIG(keys
, res
->d_records
, DNSName("."), 300);
1020 addRecordToLW(res
, "a.gtld-servers.com.", QType::A
, "192.0.2.1", DNSResourceRecord::ADDITIONAL
, 3600);
1021 return LWResult::Result::Success
;
1023 else if (ip
== ComboAddress("192.0.2.1:53")) {
1024 if (type
== QType::A
) {
1025 setLWResult(res
, RCode::NoError
, true, false, true);
1026 addRecordToLW(res
, domain
, QType::A
, "192.0.2.1");
1027 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300, false, boost::none
, DNSName("*.powerdns.com"));
1028 /* no need for the closest encloser since we have a positive answer expanded from a wildcard */
1029 /* the next closer */
1030 addNSEC3UnhashedRecordToLW(DNSName("+.powerdns.com."), DNSName("powerdns.com."), "v", {QType::RRSIG
}, 600, res
->d_records
, 10);
1031 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
1032 /* and of course we don't deny the wildcard itself */
1033 return LWResult::Result::Success
;
1035 else if (type
== QType::TXT
) {
1036 setLWResult(res
, RCode::NoError
, true, false, true);
1037 /* the name does not exist, a wildcard applies but does not have the requested type */
1038 addRecordToLW(res
, DNSName("powerdns.com."), QType::SOA
, "powerdns.com. powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY
);
1039 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
1040 /* the closest encloser */
1041 addNSEC3UnhashedRecordToLW(DNSName("powerdns.com."), DNSName("powerdns.com."), "v", {QType::SOA
, QType::NS
, QType::NSEC3
, QType::DNSKEY
, QType::RRSIG
}, 600, res
->d_records
, 10);
1042 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
1043 /* the next closer */
1044 addNSEC3UnhashedRecordToLW(DNSName("+.powerdns.com."), DNSName("powerdns.com."), "v", {QType::RRSIG
}, 600, res
->d_records
, 10);
1045 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
1046 /* and the wildcard expanded unto itself */
1047 addNSEC3UnhashedRecordToLW(DNSName("*.powerdns.com."), DNSName("powerdns.com."), "v", {QType::A
}, 600, res
->d_records
, 10);
1048 addRRSIG(keys
, res
->d_records
, DNSName("powerdns.com."), 300);
1049 return LWResult::Result::Success
;
1054 return LWResult::Result::Timeout
;
1057 vector
<DNSRecord
> ret
;
1058 int res
= sr
->beginResolve(target
, QType(QType::A
), QClass::IN
, ret
);
1059 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
1060 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Secure
);
1061 BOOST_REQUIRE_EQUAL(ret
.size(), 4U);
1062 BOOST_CHECK_EQUAL(ret
.at(0).d_name
, target
);
1063 BOOST_CHECK_EQUAL(ret
.at(0).d_type
, QType(QType::A
).getCode());
1064 BOOST_CHECK_EQUAL(queriesCount
, 4U);
1067 res
= sr
->beginResolve(target
, QType(QType::TXT
), QClass::IN
, ret
);
1068 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
1069 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Secure
);
1070 BOOST_REQUIRE_EQUAL(ret
.size(), 8U);
1071 BOOST_CHECK_EQUAL(ret
.at(0).d_name
, DNSName("powerdns.com."));
1072 BOOST_CHECK_EQUAL(ret
.at(0).d_type
, QType(QType::SOA
).getCode());
1073 BOOST_CHECK_EQUAL(queriesCount
, 5U);
1076 res
= sr
->beginResolve(DNSName("b.powerdns.com."), QType(QType::A
), QClass::IN
, ret
);
1077 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
1078 BOOST_CHECK_EQUAL(sr
->getValidationState(), vState::Secure
);
1079 BOOST_REQUIRE_EQUAL(ret
.size(), 4U);
1080 BOOST_CHECK_EQUAL(ret
.at(0).d_name
, DNSName("b.powerdns.com."));
1081 BOOST_CHECK_EQUAL(ret
.at(0).d_type
, QType(QType::A
).getCode());
1082 BOOST_CHECK_EQUAL(queriesCount
, 5U);
1085 BOOST_AUTO_TEST_CASE(test_aggressive_nsec_replace
)
1087 const size_t testSize
= 10000;
1088 auto cache
= make_unique
<AggressiveNSECCache
>(testSize
);
1093 Utility::gettimeofday(&now
, nullptr);
1095 vector
<DNSName
> names
;
1096 names
.reserve(testSize
);
1097 for (size_t i
= 0; i
< testSize
; i
++) {
1098 names
.emplace_back(std::to_string(i
) + "powerdns.com");
1104 for (const auto& name
: names
) {
1107 rec
.d_type
= QType::NSEC3
;
1108 rec
.d_ttl
= now
.tv_sec
+ 10;
1109 rec
.setContent(getRecordContent(QType::NSEC3
, "1 0 500 ab HASG==== A RRSIG NSEC3"));
1110 auto rrsig
= std::make_shared
<RRSIGRecordContent
>("NSEC3 5 3 10 20370101000000 20370101000000 24567 dummy. data");
1111 cache
->insertNSEC(DNSName("powerdns.com"), rec
.d_name
, rec
, {rrsig
}, true);
1113 auto diff1
= time
.udiff(true);
1115 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), testSize
);
1116 for (const auto& name
: names
) {
1119 rec
.d_type
= QType::NSEC3
;
1120 rec
.d_ttl
= now
.tv_sec
+ 10;
1121 rec
.setContent(getRecordContent(QType::NSEC3
, "1 0 500 ab HASG==== A RRSIG NSEC3"));
1122 auto rrsig
= std::make_shared
<RRSIGRecordContent
>("NSEC 5 3 10 20370101000000 20370101000000 24567 dummy. data");
1123 cache
->insertNSEC(DNSName("powerdns.com"), rec
.d_name
, rec
, {rrsig
}, true);
1126 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), testSize
);
1128 auto diff2
= time
.udiff(true);
1129 // Check that replace is about equally fast as insert
1130 BOOST_CHECK(diff1
< diff2
* 2 && diff2
< diff1
* 2);
1133 BOOST_AUTO_TEST_CASE(test_aggressive_nsec_wiping
)
1135 auto cache
= make_unique
<AggressiveNSECCache
>(10000);
1138 Utility::gettimeofday(&now
, 0);
1141 rec
.d_name
= DNSName("www.powerdns.com");
1142 rec
.d_type
= QType::NSEC
;
1143 rec
.d_ttl
= now
.tv_sec
+ 10;
1144 rec
.setContent(getRecordContent(QType::NSEC
, "z.powerdns.com. A RRSIG NSEC"));
1145 auto rrsig
= std::make_shared
<RRSIGRecordContent
>("NSEC 5 3 10 20370101000000 20370101000000 24567 dummy. data");
1146 cache
->insertNSEC(DNSName("powerdns.com"), rec
.d_name
, rec
, {rrsig
}, false);
1148 rec
.d_name
= DNSName("z.powerdns.com");
1149 rec
.setContent(getRecordContent(QType::NSEC
, "zz.powerdns.com. AAAA RRSIG NSEC"));
1150 cache
->insertNSEC(DNSName("powerdns.com"), rec
.d_name
, rec
, {rrsig
}, false);
1152 rec
.d_name
= DNSName("www.powerdns.org");
1153 rec
.d_type
= QType::NSEC3
;
1154 rec
.d_ttl
= now
.tv_sec
+ 10;
1155 rec
.setContent(getRecordContent(QType::NSEC3
, "1 0 500 ab HASG==== A RRSIG NSEC3"));
1156 rrsig
= std::make_shared
<RRSIGRecordContent
>("NSEC3 5 3 10 20370101000000 20370101000000 24567 dummy. data");
1157 cache
->insertNSEC(DNSName("powerdns.org"), rec
.d_name
, rec
, {rrsig
}, true);
1159 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 3U);
1161 /* remove just that zone */
1162 cache
->removeZoneInfo(DNSName("powerdns.org"), false);
1163 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 2U);
1166 cache
->insertNSEC(DNSName("powerdns.org"), rec
.d_name
, rec
, {rrsig
}, true);
1167 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 3U);
1169 /* remove everything under .org (which should end up in the same way) */
1170 cache
->removeZoneInfo(DNSName("org."), true);
1171 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 2U);
1174 cache
->insertNSEC(DNSName("powerdns.org"), rec
.d_name
, rec
, {rrsig
}, true);
1175 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 3U);
1177 /* remove everything */
1178 cache
->removeZoneInfo(DNSName("."), true);
1179 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 0U);
1182 BOOST_AUTO_TEST_CASE(test_aggressive_nsec_pruning
)
1184 auto cache
= make_unique
<AggressiveNSECCache
>(2);
1187 Utility::gettimeofday(&now
, 0);
1190 rec
.d_name
= DNSName("www.powerdns.com");
1191 rec
.d_type
= QType::NSEC
;
1192 rec
.d_ttl
= now
.tv_sec
+ 10;
1193 rec
.setContent(getRecordContent(QType::NSEC
, "z.powerdns.com. A RRSIG NSEC"));
1194 auto rrsig
= std::make_shared
<RRSIGRecordContent
>("NSEC 5 3 10 20370101000000 20370101000000 24567 dummy. data");
1195 cache
->insertNSEC(DNSName("powerdns.com"), rec
.d_name
, rec
, {rrsig
}, false);
1197 rec
.d_name
= DNSName("z.powerdns.com");
1198 rec
.setContent(getRecordContent(QType::NSEC
, "zz.powerdns.com. AAAA RRSIG NSEC"));
1199 cache
->insertNSEC(DNSName("powerdns.com"), rec
.d_name
, rec
, {rrsig
}, false);
1201 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 2U);
1202 /* we are at the limit of the number of entries, so we will scan 1/5th of the entries,
1203 and prune the expired ones, which mean we should not remove anything */
1204 cache
->prune(now
.tv_sec
);
1205 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 2U);
1207 rec
.d_name
= DNSName("www.powerdns.org");
1208 rec
.d_type
= QType::NSEC3
;
1209 rec
.d_ttl
= now
.tv_sec
+ 20;
1210 rec
.setContent(getRecordContent(QType::NSEC3
, "1 0 500 ab HASG==== A RRSIG NSEC3"));
1211 rrsig
= std::make_shared
<RRSIGRecordContent
>("NSEC3 5 3 10 20370101000000 20370101000000 24567 dummy. data");
1212 cache
->insertNSEC(DNSName("powerdns.org"), rec
.d_name
, rec
, {rrsig
}, true);
1214 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 3U);
1216 /* we have set a upper bound to 2 entries, so we are above,
1217 and one entry is actually expired, so we will prune one entry
1218 to get below the limit */
1219 cache
->prune(now
.tv_sec
+ 15);
1220 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 2U);
1222 /* now we are at the limit, so we will scan 1/10th of all zones entries, rounded up,
1223 and prune the expired ones, which mean we will also be removing the remaining two */
1224 cache
->prune(now
.tv_sec
+ 600);
1225 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 0U);
1228 BOOST_AUTO_TEST_CASE(test_aggressive_nsec_dump
)
1230 auto cache
= make_unique
<AggressiveNSECCache
>(10000);
1232 std::vector
<std::string
> expected
;
1233 expected
.emplace_back("; Zone powerdns.com.\n");
1234 expected
.emplace_back("www.powerdns.com. 10 IN NSEC z.powerdns.com. A RRSIG NSEC\n");
1235 expected
.emplace_back("- RRSIG NSEC 5 3 10 20370101000000 20370101000000 24567 dummy. data\n");
1236 expected
.emplace_back("z.powerdns.com. 10 IN NSEC zz.powerdns.com. AAAA RRSIG NSEC\n");
1237 expected
.emplace_back("- RRSIG NSEC 5 3 10 20370101000000 20370101000000 24567 dummy. data\n");
1238 expected
.emplace_back("; Zone powerdns.org.\n");
1239 expected
.emplace_back("www.powerdns.org. 10 IN NSEC3 1 0 50 ab HASG==== A RRSIG NSEC3\n");
1240 expected
.emplace_back("- RRSIG NSEC3 5 3 10 20370101000000 20370101000000 24567 dummy. data\n");
1245 Utility::gettimeofday(&now
, nullptr);
1248 rec
.d_name
= DNSName("www.powerdns.com");
1249 rec
.d_type
= QType::NSEC
;
1250 rec
.d_ttl
= now
.tv_sec
+ 10;
1251 rec
.setContent(getRecordContent(QType::NSEC
, "z.powerdns.com. A RRSIG NSEC"));
1252 auto rrsig
= std::make_shared
<RRSIGRecordContent
>("NSEC 5 3 10 20370101000000 20370101000000 24567 dummy. data");
1253 cache
->insertNSEC(DNSName("powerdns.com"), rec
.d_name
, rec
, {rrsig
}, false);
1255 rec
.d_name
= DNSName("z.powerdns.com");
1256 rec
.setContent(getRecordContent(QType::NSEC
, "zz.powerdns.com. AAAA RRSIG NSEC"));
1257 cache
->insertNSEC(DNSName("powerdns.com"), rec
.d_name
, rec
, {rrsig
}, false);
1259 rec
.d_name
= DNSName("www.powerdns.org");
1260 rec
.d_type
= QType::NSEC3
;
1261 rec
.d_ttl
= now
.tv_sec
+ 10;
1262 rec
.setContent(getRecordContent(QType::NSEC3
, "1 0 50 ab HASG==== A RRSIG NSEC3"));
1263 rrsig
= std::make_shared
<RRSIGRecordContent
>("NSEC3 5 3 10 20370101000000 20370101000000 24567 dummy. data");
1264 cache
->insertNSEC(DNSName("powerdns.org"), rec
.d_name
, rec
, {rrsig
}, true);
1266 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 3U);
1268 auto filePtr
= std::unique_ptr
<FILE, int (*)(FILE*)>(tmpfile(), fclose
);
1270 BOOST_FAIL("Temporary file could not be opened");
1273 BOOST_CHECK_EQUAL(cache
->dumpToFile(filePtr
, now
), 3U);
1275 rewind(filePtr
.get());
1276 char* line
= nullptr;
1279 for (const auto& str
: expected
) {
1280 auto read
= getline(&line
, &len
, filePtr
.get());
1282 BOOST_FAIL("Unable to read a line from the temp file");
1284 BOOST_CHECK_EQUAL(line
, str
);
1288 expected
.emplace_back("; Zone powerdns.com.\n");
1289 expected
.emplace_back("www.powerdns.com. 10 IN NSEC z.powerdns.com. A RRSIG NSEC\n");
1290 expected
.emplace_back("- RRSIG NSEC 5 3 10 20370101000000 20370101000000 24567 dummy. data\n");
1291 expected
.emplace_back("z.powerdns.com. 30 IN NSEC zz.powerdns.com. AAAA RRSIG NSEC\n");
1292 expected
.emplace_back("- RRSIG NSEC 5 3 10 20370101000000 20370101000000 24567 dummy. data\n");
1293 expected
.emplace_back("; Zone powerdns.org.\n");
1294 expected
.emplace_back("www.powerdns.org. 10 IN NSEC3 1 0 50 ab HASG==== A RRSIG NSEC3\n");
1295 expected
.emplace_back("- RRSIG NSEC3 5 3 10 20370101000000 20370101000000 24567 dummy. data\n");
1297 rec
.d_name
= DNSName("z.powerdns.com");
1298 rec
.d_type
= QType::NSEC
;
1299 rec
.d_ttl
= now
.tv_sec
+ 30;
1300 rec
.setContent(getRecordContent(QType::NSEC
, "zz.powerdns.com. AAAA RRSIG NSEC"));
1301 rrsig
= std::make_shared
<RRSIGRecordContent
>("NSEC 5 3 10 20370101000000 20370101000000 24567 dummy. data");
1302 cache
->insertNSEC(DNSName("powerdns.com"), rec
.d_name
, rec
, {rrsig
}, false);
1304 rewind(filePtr
.get());
1305 BOOST_CHECK_EQUAL(cache
->dumpToFile(filePtr
, now
), 3U);
1307 rewind(filePtr
.get());
1309 for (const auto& str
: expected
) {
1310 auto read
= getline(&line
, &len
, filePtr
.get());
1312 BOOST_FAIL("Unable to read a line from the temp file");
1314 BOOST_CHECK_EQUAL(line
, str
);
1317 /* getline() allocates a buffer when called with a nullptr,
1318 then reallocates it when needed, but we need to free the
1319 last allocation if any. */
1320 free(line
); // NOLINT: it's the API.
1323 BOOST_AUTO_TEST_CASE(test_aggressive_nsec3_rollover
)
1325 /* test that we don't compare a hash using the wrong (former) salt or iterations count in case of a rollover,
1326 or when different servers use different parameters */
1327 AggressiveNSECCache::s_maxNSEC3CommonPrefix
= 159;
1328 auto cache
= make_unique
<AggressiveNSECCache
>(10000);
1329 g_recCache
= std::make_unique
<MemRecursorCache
>();
1331 const DNSName
zone("powerdns.com");
1332 time_t now
= time(nullptr);
1334 /* first we need a SOA */
1335 std::vector
<DNSRecord
> records
;
1336 time_t ttd
= now
+ 30;
1338 drSOA
.d_name
= zone
;
1339 drSOA
.d_type
= QType::SOA
;
1340 drSOA
.d_class
= QClass::IN
;
1341 drSOA
.setContent(std::make_shared
<SOARecordContent
>("pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600"));
1342 drSOA
.d_ttl
= static_cast<uint32_t>(ttd
); // XXX truncation
1343 drSOA
.d_place
= DNSResourceRecord::ANSWER
;
1344 records
.push_back(drSOA
);
1346 g_recCache
->replace(now
, zone
, QType(QType::SOA
), records
, {}, {}, true, zone
, boost::none
, boost::none
, vState::Secure
);
1347 BOOST_CHECK_EQUAL(g_recCache
->size(), 1U);
1349 std::string oldSalt
= "ab";
1350 std::string newSalt
= "cd";
1351 unsigned int oldIterationsCount
= 2;
1352 unsigned int newIterationsCount
= 1;
1353 DNSName
name("www.powerdns.com");
1354 std::string hashed
= hashQNameWithSalt(oldSalt
, oldIterationsCount
, name
);
1357 rec
.d_name
= DNSName(toBase32Hex(hashed
)) + zone
;
1358 rec
.d_type
= QType::NSEC3
;
1359 rec
.d_ttl
= now
+ 10;
1361 NSEC3RecordContent nrc
;
1362 nrc
.d_algorithm
= 1;
1364 nrc
.d_iterations
= oldIterationsCount
;
1365 nrc
.d_salt
= oldSalt
;
1366 nrc
.d_nexthash
= hashed
;
1367 incrementHash(nrc
.d_nexthash
);
1368 for (const auto& type
: {QType::A
}) {
1372 rec
.setContent(std::make_shared
<NSEC3RecordContent
>(nrc
));
1373 auto rrsig
= std::make_shared
<RRSIGRecordContent
>("NSEC3 5 3 10 20370101000000 20370101000000 24567 dummy. data");
1374 cache
->insertNSEC(zone
, rec
.d_name
, rec
, {rrsig
}, true);
1376 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 1U);
1379 std::vector
<DNSRecord
> results
;
1381 /* we can use the NSEC3s we have */
1383 BOOST_CHECK_EQUAL(cache
->getDenial(now
, name
, QType::AAAA
, results
, res
, ComboAddress("192.0.2.1"), boost::none
, true), true);
1385 DNSName
other("other.powerdns.com");
1386 /* now we insert a new NSEC3, with a different salt, changing that value for the zone */
1387 hashed
= hashQNameWithSalt(newSalt
, oldIterationsCount
, other
);
1388 rec
.d_name
= DNSName(toBase32Hex(hashed
)) + zone
;
1389 rec
.d_type
= QType::NSEC3
;
1390 rec
.d_ttl
= now
+ 10;
1391 nrc
.d_algorithm
= 1;
1393 nrc
.d_iterations
= oldIterationsCount
;
1394 nrc
.d_salt
= newSalt
;
1395 nrc
.d_nexthash
= hashed
;
1396 incrementHash(nrc
.d_nexthash
);
1397 for (const auto& type
: {QType::A
}) {
1401 rec
.setContent(std::make_shared
<NSEC3RecordContent
>(nrc
));
1402 rrsig
= std::make_shared
<RRSIGRecordContent
>("NSEC3 5 3 10 20370101000000 20370101000000 24567 dummy. data");
1403 cache
->insertNSEC(zone
, rec
.d_name
, rec
, {rrsig
}, true);
1405 /* the existing entries should have been cleared */
1406 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 1U);
1408 /* we should be able to find a direct match for that name */
1410 BOOST_CHECK_EQUAL(cache
->getDenial(now
, other
, QType::AAAA
, results
, res
, ComboAddress("192.0.2.1"), boost::none
, true), true);
1412 /* but we should not be able to use the other NSEC3s */
1413 BOOST_CHECK_EQUAL(cache
->getDenial(now
, name
, QType::AAAA
, results
, res
, ComboAddress("192.0.2.1"), boost::none
, true), false);
1415 /* and the same thing but this time updating the iterations count instead of the salt */
1416 DNSName
other2("other2.powerdns.com");
1417 hashed
= hashQNameWithSalt(newSalt
, newIterationsCount
, other2
);
1418 rec
.d_name
= DNSName(toBase32Hex(hashed
)) + zone
;
1419 rec
.d_type
= QType::NSEC3
;
1420 rec
.d_ttl
= now
+ 10;
1421 nrc
.d_algorithm
= 1;
1423 nrc
.d_iterations
= newIterationsCount
;
1424 nrc
.d_salt
= newSalt
;
1425 nrc
.d_nexthash
= hashed
;
1426 incrementHash(nrc
.d_nexthash
);
1427 for (const auto& type
: {QType::A
}) {
1431 rec
.setContent(std::make_shared
<NSEC3RecordContent
>(nrc
));
1432 rrsig
= std::make_shared
<RRSIGRecordContent
>("NSEC3 5 3 10 20370101000000 20370101000000 24567 dummy. data");
1433 cache
->insertNSEC(zone
, rec
.d_name
, rec
, {rrsig
}, true);
1435 /* the existing entries should have been cleared */
1436 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 1U);
1438 /* we should be able to find a direct match for that name */
1440 BOOST_CHECK_EQUAL(cache
->getDenial(now
, other2
, QType::AAAA
, results
, res
, ComboAddress("192.0.2.1"), boost::none
, true), true);
1442 /* but we should not be able to use the other NSEC3s */
1443 BOOST_CHECK_EQUAL(cache
->getDenial(now
, other
, QType::AAAA
, results
, res
, ComboAddress("192.0.2.1"), boost::none
, true), false);
1446 BOOST_AUTO_TEST_CASE(test_aggressive_nsec_ancestor_cases
)
1448 auto cache
= make_unique
<AggressiveNSECCache
>(10000);
1449 g_recCache
= std::make_unique
<MemRecursorCache
>();
1451 const DNSName
zone("powerdns.com");
1452 time_t now
= time(nullptr);
1454 /* first we need a SOA */
1455 std::vector
<DNSRecord
> records
;
1456 time_t ttd
= now
+ 30;
1458 drSOA
.d_name
= zone
;
1459 drSOA
.d_type
= QType::SOA
;
1460 drSOA
.d_class
= QClass::IN
;
1461 drSOA
.setContent(std::make_shared
<SOARecordContent
>("pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600"));
1462 drSOA
.d_ttl
= static_cast<uint32_t>(ttd
); // XXX truncation
1463 drSOA
.d_place
= DNSResourceRecord::ANSWER
;
1464 records
.push_back(drSOA
);
1466 g_recCache
->replace(now
, zone
, QType(QType::SOA
), records
, {}, {}, true, zone
, boost::none
, boost::none
, vState::Secure
);
1467 BOOST_CHECK_EQUAL(g_recCache
->size(), 1U);
1470 cache
= make_unique
<AggressiveNSECCache
>(10000);
1471 /* insert a NSEC matching the exact name (apex) */
1472 DNSName
name("sub.powerdns.com");
1475 rec
.d_type
= QType::NSEC
;
1476 rec
.d_ttl
= now
+ 10;
1478 NSECRecordContent nrc
;
1479 nrc
.d_next
= DNSName("sub1.powerdns.com");
1480 for (const auto& type
: {QType::A
}) {
1484 rec
.setContent(std::make_shared
<NSECRecordContent
>(nrc
));
1485 auto rrsig
= std::make_shared
<RRSIGRecordContent
>("NSEC 5 3 10 20370101000000 20370101000000 24567 sub.powerdns.com. data");
1486 cache
->insertNSEC(zone
, rec
.d_name
, rec
, {rrsig
}, false);
1488 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 1U);
1490 /* the cache should now be able to deny other types (except the DS) */
1492 std::vector
<DNSRecord
> results
;
1493 BOOST_CHECK_EQUAL(cache
->getDenial(now
, name
, QType::AAAA
, results
, res
, ComboAddress("192.0.2.1"), boost::none
, true), true);
1494 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
1495 BOOST_CHECK_EQUAL(results
.size(), 3U);
1496 /* but not the DS that lives in the parent zone */
1498 BOOST_CHECK_EQUAL(cache
->getDenial(now
, name
, QType::DS
, results
, res
, ComboAddress("192.0.2.1"), boost::none
, true), false);
1499 BOOST_CHECK_EQUAL(results
.size(), 0U);
1503 cache
= make_unique
<AggressiveNSECCache
>(10000);
1504 /* insert a NSEC matching the exact name, but it is an ancestor NSEC (delegation) */
1505 DNSName
name("sub.powerdns.com");
1508 rec
.d_type
= QType::NSEC
;
1509 rec
.d_ttl
= now
+ 10;
1511 NSECRecordContent nrc
;
1512 nrc
.d_next
= DNSName("sub1.powerdns.com");
1513 for (const auto& type
: {QType::NS
}) {
1517 rec
.setContent(std::make_shared
<NSECRecordContent
>(nrc
));
1518 auto rrsig
= std::make_shared
<RRSIGRecordContent
>("NSEC 5 3 10 20370101000000 20370101000000 24567 powerdns.com. data");
1519 cache
->insertNSEC(zone
, rec
.d_name
, rec
, {rrsig
}, false);
1521 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 1U);
1523 /* the cache should now be able to deny the DS */
1525 std::vector
<DNSRecord
> results
;
1526 BOOST_CHECK_EQUAL(cache
->getDenial(now
, name
, QType::DS
, results
, res
, ComboAddress("192.0.2.1"), boost::none
, true), true);
1527 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
1528 BOOST_CHECK_EQUAL(results
.size(), 3U);
1529 /* but not any type that lives in the child zone */
1531 BOOST_CHECK_EQUAL(cache
->getDenial(now
, name
, QType::AAAA
, results
, res
, ComboAddress("192.0.2.1"), boost::none
, true), false);
1535 cache
= make_unique
<AggressiveNSECCache
>(10000);
1536 /* insert a NSEC matching the exact name inside a zone (neither apex nor delegation point) */
1537 DNSName
name("sub.powerdns.com");
1540 rec
.d_type
= QType::NSEC
;
1541 rec
.d_ttl
= now
+ 10;
1543 NSECRecordContent nrc
;
1544 nrc
.d_next
= DNSName("sub1.powerdns.com");
1545 for (const auto& type
: {QType::A
}) {
1549 rec
.setContent(std::make_shared
<NSECRecordContent
>(nrc
));
1550 auto rrsig
= std::make_shared
<RRSIGRecordContent
>("NSEC 5 3 10 20370101000000 20370101000000 24567 powerdns.com. data");
1551 cache
->insertNSEC(zone
, rec
.d_name
, rec
, {rrsig
}, false);
1553 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 1U);
1555 /* the cache should now be able to deny other types */
1557 std::vector
<DNSRecord
> results
;
1558 BOOST_CHECK_EQUAL(cache
->getDenial(now
, name
, QType::AAAA
, results
, res
, ComboAddress("192.0.2.1"), boost::none
, true), true);
1559 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
1560 BOOST_CHECK_EQUAL(results
.size(), 3U);
1561 /* including the DS */
1563 BOOST_CHECK_EQUAL(cache
->getDenial(now
, name
, QType::DS
, results
, res
, ComboAddress("192.0.2.1"), boost::none
, true), true);
1564 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
1565 BOOST_CHECK_EQUAL(results
.size(), 3U);
1569 /* nxd inside a zone (neither apex nor delegation point) */
1570 cache
= make_unique
<AggressiveNSECCache
>(10000);
1571 /* insert NSEC proving that the name does not exist */
1572 DNSName
name("sub.powerdns.com.");
1573 DNSName
wc("*.powerdns.com.");
1577 rec
.d_name
= DNSName("sua.powerdns.com");
1578 rec
.d_type
= QType::NSEC
;
1579 rec
.d_ttl
= now
+ 10;
1581 NSECRecordContent nrc
;
1582 nrc
.d_next
= DNSName("suc.powerdns.com");
1583 for (const auto& type
: {QType::A
, QType::SOA
, QType::NS
}) {
1587 rec
.setContent(std::make_shared
<NSECRecordContent
>(nrc
));
1588 auto rrsig
= std::make_shared
<RRSIGRecordContent
>("NSEC 5 3 10 20370101000000 20370101000000 24567 powerdns.com. data");
1589 cache
->insertNSEC(zone
, rec
.d_name
, rec
, {rrsig
}, false);
1591 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 1U);
1596 rec
.d_name
= DNSName(").powerdns.com.");
1597 rec
.d_type
= QType::NSEC
;
1598 rec
.d_ttl
= now
+ 10;
1600 NSECRecordContent nrc
;
1601 nrc
.d_next
= DNSName("+.powerdns.com.");
1602 for (const auto& type
: {QType::NS
}) {
1606 rec
.setContent(std::make_shared
<NSECRecordContent
>(nrc
));
1607 auto rrsig
= std::make_shared
<RRSIGRecordContent
>("NSEC 5 3 10 20370101000000 20370101000000 24567 powerdns.com. data");
1608 cache
->insertNSEC(zone
, rec
.d_name
, rec
, {rrsig
}, false);
1610 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 2U);
1613 /* the cache should now be able to deny any type for the name */
1615 std::vector
<DNSRecord
> results
;
1616 BOOST_CHECK_EQUAL(cache
->getDenial(now
, name
, QType::AAAA
, results
, res
, ComboAddress("192.0.2.1"), boost::none
, true), true);
1617 BOOST_CHECK_EQUAL(res
, RCode::NXDomain
);
1618 BOOST_CHECK_EQUAL(results
.size(), 5U);
1620 /* including the DS, since we are not at the apex */
1622 BOOST_CHECK_EQUAL(cache
->getDenial(now
, name
, QType::DS
, results
, res
, ComboAddress("192.0.2.1"), boost::none
, true), true);
1623 BOOST_CHECK_EQUAL(res
, RCode::NXDomain
);
1624 BOOST_CHECK_EQUAL(results
.size(), 5U);
1628 BOOST_AUTO_TEST_CASE(test_aggressive_nsec3_ancestor_cases
)
1630 AggressiveNSECCache::s_maxNSEC3CommonPrefix
= 159;
1631 auto cache
= make_unique
<AggressiveNSECCache
>(10000);
1632 g_recCache
= std::make_unique
<MemRecursorCache
>();
1634 const DNSName
zone("powerdns.com");
1635 time_t now
= time(nullptr);
1637 /* first we need a SOA */
1638 std::vector
<DNSRecord
> records
;
1639 time_t ttd
= now
+ 30;
1641 drSOA
.d_name
= zone
;
1642 drSOA
.d_type
= QType::SOA
;
1643 drSOA
.d_class
= QClass::IN
;
1644 drSOA
.setContent(std::make_shared
<SOARecordContent
>("pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600"));
1645 drSOA
.d_ttl
= static_cast<uint32_t>(ttd
); // XXX truncation
1646 drSOA
.d_place
= DNSResourceRecord::ANSWER
;
1647 records
.push_back(drSOA
);
1649 g_recCache
->replace(now
, zone
, QType(QType::SOA
), records
, {}, {}, true, zone
, boost::none
, boost::none
, vState::Secure
);
1650 BOOST_CHECK_EQUAL(g_recCache
->size(), 1U);
1652 const std::string
salt("ab");
1653 const unsigned int iterationsCount
= 1;
1656 cache
= make_unique
<AggressiveNSECCache
>(10000);
1657 /* insert a NSEC3 matching the exact name (apex) */
1658 DNSName
name("sub.powerdns.com");
1659 std::string hashed
= hashQNameWithSalt(salt
, iterationsCount
, name
);
1661 rec
.d_name
= DNSName(toBase32Hex(hashed
)) + zone
;
1662 rec
.d_type
= QType::NSEC3
;
1663 rec
.d_ttl
= now
+ 10;
1665 NSEC3RecordContent nrc
;
1666 nrc
.d_algorithm
= 1;
1668 nrc
.d_iterations
= iterationsCount
;
1670 nrc
.d_nexthash
= hashed
;
1671 incrementHash(nrc
.d_nexthash
);
1672 for (const auto& type
: {QType::A
}) {
1676 rec
.setContent(std::make_shared
<NSEC3RecordContent
>(nrc
));
1677 auto rrsig
= std::make_shared
<RRSIGRecordContent
>("NSEC3 5 3 10 20370101000000 20370101000000 24567 sub.powerdns.com. data");
1678 cache
->insertNSEC(zone
, rec
.d_name
, rec
, {rrsig
}, true);
1680 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 1U);
1682 /* the cache should now be able to deny other types (except the DS) */
1684 std::vector
<DNSRecord
> results
;
1685 BOOST_CHECK_EQUAL(cache
->getDenial(now
, name
, QType::AAAA
, results
, res
, ComboAddress("192.0.2.1"), boost::none
, true), true);
1686 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
1687 BOOST_CHECK_EQUAL(results
.size(), 3U);
1688 /* but not the DS that lives in the parent zone */
1690 BOOST_CHECK_EQUAL(cache
->getDenial(now
, name
, QType::DS
, results
, res
, ComboAddress("192.0.2.1"), boost::none
, true), false);
1691 BOOST_CHECK_EQUAL(results
.size(), 0U);
1695 cache
= make_unique
<AggressiveNSECCache
>(10000);
1696 /* insert a NSEC3 matching the exact name, but it is an ancestor NSEC3 (delegation) */
1697 DNSName
name("sub.powerdns.com");
1698 std::string hashed
= hashQNameWithSalt(salt
, iterationsCount
, name
);
1700 rec
.d_name
= DNSName(toBase32Hex(hashed
)) + zone
;
1701 rec
.d_type
= QType::NSEC3
;
1702 rec
.d_ttl
= now
+ 10;
1704 NSEC3RecordContent nrc
;
1705 nrc
.d_algorithm
= 1;
1707 nrc
.d_iterations
= iterationsCount
;
1709 nrc
.d_nexthash
= hashed
;
1710 incrementHash(nrc
.d_nexthash
);
1711 for (const auto& type
: {QType::NS
}) {
1715 rec
.setContent(std::make_shared
<NSEC3RecordContent
>(nrc
));
1716 auto rrsig
= std::make_shared
<RRSIGRecordContent
>("NSEC3 5 3 10 20370101000000 20370101000000 24567 powerdns.com. data");
1717 cache
->insertNSEC(zone
, rec
.d_name
, rec
, {rrsig
}, true);
1719 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 1U);
1721 /* the cache should now be able to deny the DS */
1723 std::vector
<DNSRecord
> results
;
1724 BOOST_CHECK_EQUAL(cache
->getDenial(now
, name
, QType::DS
, results
, res
, ComboAddress("192.0.2.1"), boost::none
, true), true);
1725 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
1726 BOOST_CHECK_EQUAL(results
.size(), 3U);
1727 /* but not any type that lives in the child zone */
1729 BOOST_CHECK_EQUAL(cache
->getDenial(now
, name
, QType::AAAA
, results
, res
, ComboAddress("192.0.2.1"), boost::none
, true), false);
1733 cache
= make_unique
<AggressiveNSECCache
>(10000);
1734 /* insert a NSEC3 matching the exact name inside a zone (neither apex nor delegation point) */
1735 DNSName
name("sub.powerdns.com");
1736 std::string hashed
= hashQNameWithSalt(salt
, iterationsCount
, name
);
1738 rec
.d_name
= DNSName(toBase32Hex(hashed
)) + zone
;
1739 rec
.d_type
= QType::NSEC3
;
1740 rec
.d_ttl
= now
+ 10;
1742 NSEC3RecordContent nrc
;
1743 nrc
.d_algorithm
= 1;
1745 nrc
.d_iterations
= iterationsCount
;
1747 nrc
.d_nexthash
= hashed
;
1748 incrementHash(nrc
.d_nexthash
);
1749 for (const auto& type
: {QType::A
}) {
1753 rec
.setContent(std::make_shared
<NSEC3RecordContent
>(nrc
));
1754 auto rrsig
= std::make_shared
<RRSIGRecordContent
>("NSEC3 5 3 10 20370101000000 20370101000000 24567 powerdns.com. data");
1755 cache
->insertNSEC(zone
, rec
.d_name
, rec
, {rrsig
}, true);
1757 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 1U);
1759 /* the cache should now be able to deny other types */
1761 std::vector
<DNSRecord
> results
;
1762 BOOST_CHECK_EQUAL(cache
->getDenial(now
, name
, QType::AAAA
, results
, res
, ComboAddress("192.0.2.1"), boost::none
, true), true);
1763 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
1764 BOOST_CHECK_EQUAL(results
.size(), 3U);
1765 /* including the DS */
1767 BOOST_CHECK_EQUAL(cache
->getDenial(now
, name
, QType::DS
, results
, res
, ComboAddress("192.0.2.1"), boost::none
, true), true);
1768 BOOST_CHECK_EQUAL(res
, RCode::NoError
);
1769 BOOST_CHECK_EQUAL(results
.size(), 3U);
1773 /* nxd inside a zone (neither apex nor delegation point) */
1774 cache
= make_unique
<AggressiveNSECCache
>(10000);
1775 /* insert NSEC3s proving that the name does not exist */
1776 DNSName
name("sub.powerdns.com.");
1777 DNSName
closestEncloser("powerdns.com.");
1778 DNSName
nextCloser("sub.powerdns.com.");
1779 DNSName
wc("*.powerdns.com.");
1782 /* closest encloser */
1783 std::string hashed
= hashQNameWithSalt(salt
, iterationsCount
, closestEncloser
);
1785 rec
.d_name
= DNSName(toBase32Hex(hashed
)) + zone
;
1786 rec
.d_type
= QType::NSEC3
;
1787 rec
.d_ttl
= now
+ 10;
1789 NSEC3RecordContent nrc
;
1790 nrc
.d_algorithm
= 1;
1792 nrc
.d_iterations
= iterationsCount
;
1794 nrc
.d_nexthash
= hashed
;
1795 incrementHash(nrc
.d_nexthash
);
1796 for (const auto& type
: {QType::A
, QType::SOA
, QType::NS
}) {
1800 rec
.setContent(std::make_shared
<NSEC3RecordContent
>(nrc
));
1801 auto rrsig
= std::make_shared
<RRSIGRecordContent
>("NSEC3 5 3 10 20370101000000 20370101000000 24567 powerdns.com. data");
1802 cache
->insertNSEC(zone
, rec
.d_name
, rec
, {rrsig
}, true);
1804 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 1U);
1808 std::string hashed
= hashQNameWithSalt(salt
, iterationsCount
, nextCloser
);
1809 decrementHash(hashed
);
1812 rec
.d_name
= DNSName(toBase32Hex(hashed
)) + zone
;
1813 rec
.d_type
= QType::NSEC3
;
1814 rec
.d_ttl
= now
+ 10;
1816 NSEC3RecordContent nrc
;
1817 nrc
.d_algorithm
= 1;
1819 nrc
.d_iterations
= iterationsCount
;
1821 nrc
.d_nexthash
= hashed
;
1822 incrementHash(nrc
.d_nexthash
);
1823 incrementHash(nrc
.d_nexthash
);
1824 for (const auto& type
: {QType::NS
}) {
1828 rec
.setContent(std::make_shared
<NSEC3RecordContent
>(nrc
));
1829 auto rrsig
= std::make_shared
<RRSIGRecordContent
>("NSEC3 5 3 10 20370101000000 20370101000000 24567 powerdns.com. data");
1830 cache
->insertNSEC(zone
, rec
.d_name
, rec
, {rrsig
}, true);
1832 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 2U);
1836 std::string hashed
= hashQNameWithSalt(salt
, iterationsCount
, wc
);
1837 decrementHash(hashed
);
1840 rec
.d_name
= DNSName(toBase32Hex(hashed
)) + zone
;
1841 rec
.d_type
= QType::NSEC3
;
1842 rec
.d_ttl
= now
+ 10;
1844 NSEC3RecordContent nrc
;
1845 nrc
.d_algorithm
= 1;
1847 nrc
.d_iterations
= iterationsCount
;
1849 nrc
.d_nexthash
= hashed
;
1850 incrementHash(nrc
.d_nexthash
);
1851 incrementHash(nrc
.d_nexthash
);
1852 for (const auto& type
: {QType::NS
}) {
1856 rec
.setContent(std::make_shared
<NSEC3RecordContent
>(nrc
));
1857 auto rrsig
= std::make_shared
<RRSIGRecordContent
>("NSEC3 5 3 10 20370101000000 20370101000000 24567 powerdns.com. data");
1858 cache
->insertNSEC(zone
, rec
.d_name
, rec
, {rrsig
}, true);
1860 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 3U);
1863 /* the cache should now be able to deny any type for the name */
1865 std::vector
<DNSRecord
> results
;
1866 BOOST_CHECK_EQUAL(cache
->getDenial(now
, name
, QType::AAAA
, results
, res
, ComboAddress("192.0.2.1"), boost::none
, true), true);
1867 BOOST_CHECK_EQUAL(res
, RCode::NXDomain
);
1868 BOOST_CHECK_EQUAL(results
.size(), 7U);
1870 /* including the DS, since we are not at the apex */
1872 BOOST_CHECK_EQUAL(cache
->getDenial(now
, name
, QType::DS
, results
, res
, ComboAddress("192.0.2.1"), boost::none
, true), true);
1873 BOOST_CHECK_EQUAL(res
, RCode::NXDomain
);
1874 BOOST_CHECK_EQUAL(results
.size(), 7U);
1877 /* we insert NSEC3s coming from the parent zone that could look like a valid denial but are not */
1878 cache
= make_unique
<AggressiveNSECCache
>(10000);
1880 DNSName
name("www.sub.powerdns.com.");
1881 DNSName
closestEncloser("powerdns.com.");
1882 DNSName
nextCloser("sub.powerdns.com.");
1883 DNSName
wc("*.powerdns.com.");
1886 /* closest encloser */
1887 std::string hashed
= hashQNameWithSalt(salt
, iterationsCount
, closestEncloser
);
1889 rec
.d_name
= DNSName(toBase32Hex(hashed
)) + zone
;
1890 rec
.d_type
= QType::NSEC3
;
1891 rec
.d_ttl
= now
+ 10;
1893 NSEC3RecordContent nrc
;
1894 nrc
.d_algorithm
= 1;
1896 nrc
.d_iterations
= iterationsCount
;
1898 nrc
.d_nexthash
= hashed
;
1899 incrementHash(nrc
.d_nexthash
);
1901 for (const auto& type
: {QType::NS
}) {
1905 rec
.setContent(std::make_shared
<NSEC3RecordContent
>(nrc
));
1906 auto rrsig
= std::make_shared
<RRSIGRecordContent
>("NSEC3 5 3 10 20370101000000 20370101000000 24567 powerdns.com. data");
1907 cache
->insertNSEC(zone
, rec
.d_name
, rec
, {rrsig
}, true);
1909 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 1U);
1913 std::string hashed
= hashQNameWithSalt(salt
, iterationsCount
, nextCloser
);
1914 decrementHash(hashed
);
1917 rec
.d_name
= DNSName(toBase32Hex(hashed
)) + zone
;
1918 rec
.d_type
= QType::NSEC3
;
1919 rec
.d_ttl
= now
+ 10;
1921 NSEC3RecordContent nrc
;
1922 nrc
.d_algorithm
= 1;
1924 nrc
.d_iterations
= iterationsCount
;
1926 nrc
.d_nexthash
= hashed
;
1927 incrementHash(nrc
.d_nexthash
);
1928 incrementHash(nrc
.d_nexthash
);
1929 for (const auto& type
: {QType::A
}) {
1933 rec
.setContent(std::make_shared
<NSEC3RecordContent
>(nrc
));
1934 auto rrsig
= std::make_shared
<RRSIGRecordContent
>("NSEC3 5 3 10 20370101000000 20370101000000 24567 powerdns.com. data");
1935 cache
->insertNSEC(zone
, rec
.d_name
, rec
, {rrsig
}, true);
1937 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 2U);
1941 std::string hashed
= hashQNameWithSalt(salt
, iterationsCount
, wc
);
1942 decrementHash(hashed
);
1945 rec
.d_name
= DNSName(toBase32Hex(hashed
)) + zone
;
1946 rec
.d_type
= QType::NSEC3
;
1947 rec
.d_ttl
= now
+ 10;
1949 NSEC3RecordContent nrc
;
1950 nrc
.d_algorithm
= 1;
1952 nrc
.d_iterations
= iterationsCount
;
1954 nrc
.d_nexthash
= hashed
;
1955 incrementHash(nrc
.d_nexthash
);
1956 incrementHash(nrc
.d_nexthash
);
1957 for (const auto& type
: {QType::A
}) {
1961 rec
.setContent(std::make_shared
<NSEC3RecordContent
>(nrc
));
1962 auto rrsig
= std::make_shared
<RRSIGRecordContent
>("NSEC3 5 3 10 20370101000000 20370101000000 24567 powerdns.com. data");
1963 cache
->insertNSEC(zone
, rec
.d_name
, rec
, {rrsig
}, true);
1965 BOOST_CHECK_EQUAL(cache
->getEntriesCount(), 3U);
1968 /* the cache should NOT be able to deny the name */
1970 std::vector
<DNSRecord
> results
;
1971 BOOST_CHECK_EQUAL(cache
->getDenial(now
, name
, QType::AAAA
, results
, res
, ComboAddress("192.0.2.1"), boost::none
, true), false);
1972 BOOST_CHECK_EQUAL(results
.size(), 0U);
1974 /* and the same for the DS */
1976 BOOST_CHECK_EQUAL(cache
->getDenial(now
, name
, QType::DS
, results
, res
, ComboAddress("192.0.2.1"), boost::none
, true), false);
1977 BOOST_CHECK_EQUAL(results
.size(), 0U);
1981 BOOST_AUTO_TEST_SUITE_END()