]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/recursordist/test-syncres_cc5.cc
rec: CVE-2023-50387 and CVE-2023-50868
[thirdparty/pdns.git] / pdns / recursordist / test-syncres_cc5.cc
1 #ifndef BOOST_TEST_DYN_LINK
2 #define BOOST_TEST_DYN_LINK
3 #endif
4
5 #include <boost/test/unit_test.hpp>
6
7 #include "test-syncres_cc.hh"
8
9 BOOST_AUTO_TEST_SUITE(syncres_cc5)
10
11 BOOST_AUTO_TEST_CASE(test_dnssec_secure_various_algos)
12 {
13 std::unique_ptr<SyncRes> sr;
14 initSR(sr, true);
15
16 setDNSSECValidation(sr, DNSSECMode::ValidateAll);
17
18 primeHints();
19 const DNSName target("powerdns.com.");
20 const ComboAddress targetAddr("192.0.2.42");
21 testkeysset_t keys;
22
23 auto luaconfsCopy = g_luaconfs.getCopy();
24 luaconfsCopy.dsAnchors.clear();
25 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::RSASHA512, DNSSECKeeper::DIGEST_SHA384, keys, luaconfsCopy.dsAnchors);
26 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
27 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA384, DNSSECKeeper::DIGEST_SHA384, keys);
28
29 g_luaconfs.setState(luaconfsCopy);
30
31 size_t queriesCount = 0;
32
33 /* make sure that the signature inception and validity times are computed
34 based on the SyncRes time, not the current one, in case the function
35 takes too long. */
36
37 const time_t fixedNow = sr->getNow().tv_sec;
38
39 sr->setAsyncCallback([&](const ComboAddress& address, 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 */) {
40 queriesCount++;
41
42 DNSName auth = domain;
43 if (domain == target) {
44 auth = DNSName("powerdns.com.");
45 }
46
47 if (type == QType::DS || type == QType::DNSKEY) {
48 return genericDSAndDNSKEYHandler(res, domain, auth, type, keys, true, fixedNow);
49 }
50
51 if (isRootServer(address)) {
52 setLWResult(res, 0, false, false, true);
53 addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
54 addDS(DNSName("com."), 300, res->d_records, keys);
55 addRRSIG(keys, res->d_records, DNSName("."), 300, false, boost::none, boost::none, fixedNow);
56 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
57 return LWResult::Result::Success;
58 }
59
60 if (address == ComboAddress("192.0.2.1:53")) {
61 if (domain == DNSName("com.")) {
62 setLWResult(res, 0, true, false, true);
63 addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
64 addRRSIG(keys, res->d_records, domain, 300, false, boost::none, boost::none, fixedNow);
65 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
66 addRRSIG(keys, res->d_records, domain, 300);
67 }
68 else {
69 setLWResult(res, 0, false, false, true);
70 addRecordToLW(res, auth, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
71 addDS(auth, 300, res->d_records, keys);
72 addRRSIG(keys, res->d_records, DNSName("com."), 300, false, boost::none, boost::none, fixedNow);
73 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
74 }
75 return LWResult::Result::Success;
76 }
77
78 if (address == ComboAddress("192.0.2.2:53")) {
79 if (type == QType::NS) {
80 setLWResult(res, 0, true, false, true);
81 addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
82 addRRSIG(keys, res->d_records, auth, 300, false, boost::none, boost::none, fixedNow);
83 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
84 addRRSIG(keys, res->d_records, auth, 300);
85 }
86 else {
87 setLWResult(res, RCode::NoError, true, false, true);
88 addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
89 addRRSIG(keys, res->d_records, auth, 300, false, boost::none, boost::none, fixedNow);
90 }
91 return LWResult::Result::Success;
92 }
93
94 return LWResult::Result::Timeout;
95 });
96
97 vector<DNSRecord> ret;
98 int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
99 BOOST_CHECK_EQUAL(res, RCode::NoError);
100 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
101 BOOST_REQUIRE_EQUAL(ret.size(), 2U);
102 BOOST_CHECK_EQUAL(queriesCount, 6U);
103
104 /* again, to test the cache */
105 ret.clear();
106 res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
107 BOOST_CHECK_EQUAL(res, RCode::NoError);
108 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
109 BOOST_REQUIRE_EQUAL(ret.size(), 2U);
110 BOOST_CHECK_EQUAL(queriesCount, 6U);
111 }
112
113 static void testFixedPointInTime(time_t fixedNow)
114 {
115 std::unique_ptr<SyncRes> sr;
116 initSR(sr, true, false, fixedNow);
117
118 setDNSSECValidation(sr, DNSSECMode::ValidateAll);
119
120 primeHints(fixedNow);
121 const DNSName target("powerdns.com.");
122 const ComboAddress targetAddr("192.0.2.42");
123 testkeysset_t keys;
124
125 auto luaconfsCopy = g_luaconfs.getCopy();
126 luaconfsCopy.dsAnchors.clear();
127 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::RSASHA512, DNSSECKeeper::DIGEST_SHA384, keys, luaconfsCopy.dsAnchors);
128 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
129 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA384, DNSSECKeeper::DIGEST_SHA384, keys);
130
131 g_luaconfs.setState(luaconfsCopy);
132
133 size_t queriesCount = 0;
134
135 sr->setAsyncCallback([&](const ComboAddress& address, 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 */) {
136 queriesCount++;
137
138 DNSName auth = domain;
139 if (domain == target) {
140 auth = DNSName("powerdns.com.");
141 }
142
143 if (type == QType::DS || type == QType::DNSKEY) {
144 return genericDSAndDNSKEYHandler(res, domain, auth, type, keys, true, fixedNow);
145 }
146
147 if (isRootServer(address)) {
148 setLWResult(res, 0, false, false, true);
149 addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
150 addDS(DNSName("com."), 300, res->d_records, keys);
151 addRRSIG(keys, res->d_records, DNSName("."), 300, false, boost::none, boost::none, fixedNow);
152 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
153 return LWResult::Result::Success;
154 }
155
156 if (address == ComboAddress("192.0.2.1:53")) {
157 if (domain == DNSName("com.")) {
158 setLWResult(res, 0, true, false, true);
159 addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
160 addRRSIG(keys, res->d_records, domain, 300, false, boost::none, boost::none, fixedNow);
161 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
162 addRRSIG(keys, res->d_records, domain, 300, false, boost::none, boost::none, fixedNow);
163 }
164 else {
165 setLWResult(res, 0, false, false, true);
166 addRecordToLW(res, auth, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
167 addDS(auth, 300, res->d_records, keys);
168 addRRSIG(keys, res->d_records, DNSName("com."), 300, false, boost::none, boost::none, fixedNow);
169 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
170 }
171 return LWResult::Result::Success;
172 }
173
174 if (address == ComboAddress("192.0.2.2:53")) {
175 if (type == QType::NS) {
176 setLWResult(res, 0, true, false, true);
177 addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
178 addRRSIG(keys, res->d_records, auth, 300, false, boost::none, boost::none, fixedNow);
179 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
180 addRRSIG(keys, res->d_records, auth, 300, false, boost::none, boost::none, fixedNow);
181 }
182 else {
183 setLWResult(res, RCode::NoError, true, false, true);
184 addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
185 addRRSIG(keys, res->d_records, auth, 300, false, boost::none, boost::none, fixedNow);
186 }
187 return LWResult::Result::Success;
188 }
189
190 return LWResult::Result::Timeout;
191 });
192
193 vector<DNSRecord> ret;
194 int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
195 BOOST_CHECK_EQUAL(res, RCode::NoError);
196 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
197 BOOST_REQUIRE_EQUAL(ret.size(), 2U);
198 BOOST_CHECK_EQUAL(queriesCount, 6U);
199 /* again, to test the cache */
200 ret.clear();
201 res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
202 BOOST_CHECK_EQUAL(res, RCode::NoError);
203 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
204 BOOST_REQUIRE_EQUAL(ret.size(), 2U);
205 BOOST_CHECK_EQUAL(queriesCount, 6U);
206 }
207
208 BOOST_AUTO_TEST_CASE(test_dnssec_secure_various_algos1970)
209 {
210 /* validity period in ye olde times */
211 const time_t fixedNow = 1800;
212 testFixedPointInTime(fixedNow);
213 }
214
215 BOOST_AUTO_TEST_CASE(test_dnssec_secure_various_algos2038)
216 {
217 /* validity period contains the wrapping point in 2038 */
218 const time_t fixedNow = INT_MAX - 1800;
219 testFixedPointInTime(fixedNow);
220 }
221
222 BOOST_AUTO_TEST_CASE(test_dnssec_secure_various_algos2041)
223 {
224 /* validity period completely after 2038 but not wrapping uint32_t*/
225 const time_t fixedNow = time_t(INT_MAX) + 100000000;
226 testFixedPointInTime(fixedNow);
227 }
228
229 #if 0
230 // Currently fails see validate.cc:isRRSIGNotExpired() and isRRSIGIncepted()
231 BOOST_AUTO_TEST_CASE(test_dnssec_secure_various_algos2106)
232 {
233 /* validity period beyond 2106 uint32_t wrapping point */
234 const time_t fixedNow = 2 * time_t(INT_MAX);
235 testFixedPointInTime(fixedNow);
236 }
237 #endif
238
239 BOOST_AUTO_TEST_CASE(test_dnssec_secure_a_then_ns)
240 {
241 std::unique_ptr<SyncRes> sr;
242 initSR(sr, true);
243
244 setDNSSECValidation(sr, DNSSECMode::ValidateAll);
245
246 primeHints();
247 const DNSName target("powerdns.com.");
248 const ComboAddress targetAddr("192.0.2.42");
249 testkeysset_t keys;
250
251 auto luaconfsCopy = g_luaconfs.getCopy();
252 luaconfsCopy.dsAnchors.clear();
253 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
254 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
255 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
256 g_luaconfs.setState(luaconfsCopy);
257
258 size_t queriesCount = 0;
259
260 sr->setAsyncCallback([&](const ComboAddress& address, 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 */) {
261 queriesCount++;
262
263 DNSName auth = domain;
264 if (domain == target) {
265 auth = DNSName("powerdns.com.");
266 }
267
268 if (type == QType::DS || type == QType::DNSKEY) {
269 return genericDSAndDNSKEYHandler(res, domain, auth, type, keys);
270 }
271
272 if (isRootServer(address)) {
273 setLWResult(res, 0, false, false, true);
274 addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
275 addDS(DNSName("com."), 300, res->d_records, keys);
276 addRRSIG(keys, res->d_records, DNSName("."), 300);
277 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
278 return LWResult::Result::Success;
279 }
280
281 if (address == ComboAddress("192.0.2.1:53")) {
282 if (domain == DNSName("com.")) {
283 setLWResult(res, 0, true, false, true);
284 addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
285 addRRSIG(keys, res->d_records, domain, 300);
286 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
287 addRRSIG(keys, res->d_records, domain, 300);
288 }
289 else {
290 setLWResult(res, 0, false, false, true);
291 addRecordToLW(res, auth, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
292 addDS(auth, 300, res->d_records, keys);
293 addRRSIG(keys, res->d_records, DNSName("com."), 300);
294 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
295 }
296 return LWResult::Result::Success;
297 }
298
299 if (address == ComboAddress("192.0.2.2:53")) {
300 if (type == QType::NS) {
301 setLWResult(res, 0, true, false, true);
302 addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
303 addRRSIG(keys, res->d_records, auth, 300);
304 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
305 addRRSIG(keys, res->d_records, auth, 300);
306 }
307 else {
308 setLWResult(res, RCode::NoError, true, false, true);
309 addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
310 addRRSIG(keys, res->d_records, auth, 300);
311 }
312 return LWResult::Result::Success;
313 }
314
315 return LWResult::Result::Timeout;
316 });
317
318 vector<DNSRecord> ret;
319 int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
320 BOOST_CHECK_EQUAL(res, RCode::NoError);
321 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
322 BOOST_REQUIRE_EQUAL(ret.size(), 2U);
323 BOOST_CHECK_EQUAL(queriesCount, 6U);
324
325 /* again, to test the cache */
326 ret.clear();
327 res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
328 BOOST_CHECK_EQUAL(res, RCode::NoError);
329 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
330 BOOST_REQUIRE_EQUAL(ret.size(), 2U);
331 BOOST_CHECK_EQUAL(queriesCount, 6U);
332
333 /* this time we ask for the NS that should be in the cache, to check
334 the validation status */
335 ret.clear();
336 res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
337 BOOST_CHECK_EQUAL(res, RCode::NoError);
338 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
339 BOOST_REQUIRE_EQUAL(ret.size(), 2U);
340 BOOST_CHECK_EQUAL(queriesCount, 7U);
341 }
342
343 BOOST_AUTO_TEST_CASE(test_dnssec_insecure_a_then_ns)
344 {
345 std::unique_ptr<SyncRes> sr;
346 initSR(sr, true);
347
348 setDNSSECValidation(sr, DNSSECMode::ValidateAll);
349
350 primeHints();
351 const DNSName target("powerdns.com.");
352 const ComboAddress targetAddr("192.0.2.42");
353 testkeysset_t keys;
354
355 auto luaconfsCopy = g_luaconfs.getCopy();
356 luaconfsCopy.dsAnchors.clear();
357 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
358 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
359 g_luaconfs.setState(luaconfsCopy);
360
361 size_t queriesCount = 0;
362
363 sr->setAsyncCallback([&](const ComboAddress& address, 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 */) {
364 queriesCount++;
365
366 DNSName auth = domain;
367 if (domain == target) {
368 auth = DNSName("powerdns.com.");
369 }
370
371 if (type == QType::DS || type == QType::DNSKEY) {
372 return genericDSAndDNSKEYHandler(res, domain, auth, type, keys);
373 }
374
375 if (isRootServer(address)) {
376 setLWResult(res, 0, false, false, true);
377 addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
378 addDS(DNSName("com."), 300, res->d_records, keys);
379 addRRSIG(keys, res->d_records, DNSName("."), 300);
380 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
381 return LWResult::Result::Success;
382 }
383
384 if (address == ComboAddress("192.0.2.1:53")) {
385 if (domain == DNSName("com.")) {
386 setLWResult(res, 0, true, false, true);
387 addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
388 addRRSIG(keys, res->d_records, domain, 300);
389 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
390 addRRSIG(keys, res->d_records, domain, 300);
391 }
392 else {
393 setLWResult(res, 0, false, false, true);
394 addRecordToLW(res, auth, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
395 /* no DS */
396 addNSECRecordToLW(domain, DNSName("z.powerdns.com."), {QType::NS}, 600, res->d_records);
397 addRRSIG(keys, res->d_records, DNSName("com."), 300);
398 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
399 }
400 return LWResult::Result::Success;
401 }
402
403 if (address == ComboAddress("192.0.2.2:53")) {
404 if (type == QType::NS) {
405 setLWResult(res, 0, true, false, true);
406 addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
407 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
408 }
409 else {
410 setLWResult(res, RCode::NoError, true, false, true);
411 addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
412 }
413 return LWResult::Result::Success;
414 }
415
416 return LWResult::Result::Timeout;
417 });
418
419 vector<DNSRecord> ret;
420 int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
421 BOOST_CHECK_EQUAL(res, RCode::NoError);
422 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
423 BOOST_REQUIRE_EQUAL(ret.size(), 1U);
424 BOOST_CHECK_EQUAL(queriesCount, 5U);
425
426 /* again, to test the cache */
427 ret.clear();
428 res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
429 BOOST_CHECK_EQUAL(res, RCode::NoError);
430 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
431 BOOST_REQUIRE_EQUAL(ret.size(), 1U);
432 BOOST_CHECK_EQUAL(queriesCount, 5U);
433
434 /* this time we ask for the NS that should be in the cache, to check
435 the validation status */
436 ret.clear();
437 res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
438 BOOST_CHECK_EQUAL(res, RCode::NoError);
439 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
440 BOOST_REQUIRE_EQUAL(ret.size(), 1U);
441 BOOST_CHECK_EQUAL(queriesCount, 6U);
442 }
443
444 BOOST_AUTO_TEST_CASE(test_dnssec_secure_with_nta)
445 {
446 std::unique_ptr<SyncRes> sr;
447 initSR(sr, true);
448
449 setDNSSECValidation(sr, DNSSECMode::ValidateAll);
450
451 primeHints();
452 const DNSName target("powerdns.com.");
453 const ComboAddress targetAddr("192.0.2.42");
454 testkeysset_t keys;
455
456 auto luaconfsCopy = g_luaconfs.getCopy();
457 luaconfsCopy.dsAnchors.clear();
458 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
459 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
460 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
461
462 /* Add a NTA for "powerdns.com" */
463 luaconfsCopy.negAnchors[target] = "NTA for PowerDNS.com";
464
465 g_luaconfs.setState(luaconfsCopy);
466
467 size_t queriesCount = 0;
468
469 sr->setAsyncCallback([&](const ComboAddress& address, 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 */) {
470 queriesCount++;
471
472 DNSName auth = domain;
473 if (domain == target) {
474 auth = DNSName("powerdns.com.");
475 }
476
477 if (type == QType::DS || type == QType::DNSKEY) {
478 return genericDSAndDNSKEYHandler(res, domain, auth, type, keys);
479 }
480
481 if (isRootServer(address)) {
482 setLWResult(res, 0, false, false, true);
483 addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
484 addDS(DNSName("com."), 300, res->d_records, keys);
485 addRRSIG(keys, res->d_records, DNSName("."), 300);
486 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
487 return LWResult::Result::Success;
488 }
489
490 if (address == ComboAddress("192.0.2.1:53")) {
491 if (domain == DNSName("com.")) {
492 setLWResult(res, 0, true, false, true);
493 addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
494 addRRSIG(keys, res->d_records, domain, 300);
495 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
496 addRRSIG(keys, res->d_records, domain, 300);
497 }
498 else {
499 setLWResult(res, 0, false, false, true);
500 addRecordToLW(res, auth, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
501 addDS(auth, 300, res->d_records, keys);
502 addRRSIG(keys, res->d_records, DNSName("com."), 300);
503 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
504 }
505 return LWResult::Result::Success;
506 }
507
508 if (address == ComboAddress("192.0.2.2:53")) {
509 if (type == QType::NS) {
510 setLWResult(res, 0, true, false, true);
511 addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
512 addRRSIG(keys, res->d_records, auth, 300);
513 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
514 addRRSIG(keys, res->d_records, auth, 300);
515 }
516 else {
517 setLWResult(res, RCode::NoError, true, false, true);
518 addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
519 addRRSIG(keys, res->d_records, auth, 300);
520 }
521 return LWResult::Result::Success;
522 }
523
524 return LWResult::Result::Timeout;
525 });
526
527 vector<DNSRecord> ret;
528 int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
529 BOOST_CHECK_EQUAL(res, RCode::NoError);
530 /* Should be insecure because of the NTA */
531 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
532 BOOST_REQUIRE_EQUAL(ret.size(), 2U);
533 BOOST_CHECK_EQUAL(queriesCount, 5U);
534
535 /* again, to test the cache */
536 ret.clear();
537 res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
538 BOOST_CHECK_EQUAL(res, RCode::NoError);
539 /* Should be insecure because of the NTA */
540 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
541 BOOST_REQUIRE_EQUAL(ret.size(), 2U);
542 BOOST_CHECK_EQUAL(queriesCount, 5U);
543 }
544
545 BOOST_AUTO_TEST_CASE(test_dnssec_bogus_with_nta)
546 {
547 std::unique_ptr<SyncRes> sr;
548 initSR(sr, true);
549
550 setDNSSECValidation(sr, DNSSECMode::ValidateAll);
551
552 primeHints();
553 const DNSName target("powerdns.com.");
554 const ComboAddress targetAddr("192.0.2.42");
555 testkeysset_t keys;
556
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("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
561 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
562
563 /* Add a NTA for "powerdns.com" */
564 luaconfsCopy.negAnchors[target] = "NTA for PowerDNS.com";
565
566 g_luaconfs.setState(luaconfsCopy);
567
568 size_t queriesCount = 0;
569
570 sr->setAsyncCallback([&](const ComboAddress& address, 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 */) {
571 queriesCount++;
572
573 if (type == QType::DS || type == QType::DNSKEY) {
574 setLWResult(res, 0, true, false, true);
575 addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
576 return LWResult::Result::Success;
577 }
578 {
579 if (isRootServer(address)) {
580 setLWResult(res, 0, false, false, true);
581 addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
582 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
583 return LWResult::Result::Success;
584 }
585 if (address == ComboAddress("192.0.2.1:53")) {
586 if (domain == DNSName("com.")) {
587 setLWResult(res, 0, true, false, true);
588 addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
589 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
590 }
591 else {
592 setLWResult(res, 0, false, false, true);
593 addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
594 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
595 }
596 return LWResult::Result::Success;
597 }
598 if (address == ComboAddress("192.0.2.2:53")) {
599 if (type == QType::NS) {
600 setLWResult(res, 0, true, false, true);
601 addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
602 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
603 }
604 else {
605 setLWResult(res, RCode::NoError, true, false, true);
606 addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
607 }
608 return LWResult::Result::Success;
609 }
610 }
611
612 return LWResult::Result::Timeout;
613 });
614
615 /* There is TA for root but no DS/DNSKEY/RRSIG, should be Bogus, but.. */
616 vector<DNSRecord> ret;
617 int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
618 BOOST_CHECK_EQUAL(res, RCode::NoError);
619 /* Should be insecure because of the NTA */
620 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
621 BOOST_REQUIRE_EQUAL(ret.size(), 1U);
622 BOOST_CHECK_EQUAL(queriesCount, 3U);
623
624 /* again, to test the cache */
625 ret.clear();
626 res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
627 BOOST_CHECK_EQUAL(res, RCode::NoError);
628 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
629 BOOST_REQUIRE_EQUAL(ret.size(), 1U);
630 BOOST_CHECK_EQUAL(queriesCount, 3U);
631 }
632
633 BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec)
634 {
635 std::unique_ptr<SyncRes> sr;
636 initSR(sr, true);
637
638 setDNSSECValidation(sr, DNSSECMode::ValidateAll);
639
640 primeHints();
641 const DNSName target("powerdns.com.");
642 testkeysset_t keys;
643
644 auto luaconfsCopy = g_luaconfs.getCopy();
645 luaconfsCopy.dsAnchors.clear();
646 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
647 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
648 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
649
650 g_luaconfs.setState(luaconfsCopy);
651
652 size_t queriesCount = 0;
653
654 sr->setAsyncCallback([&](const ComboAddress& address, 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 */) {
655 queriesCount++;
656
657 if (type == QType::DS || type == QType::DNSKEY) {
658 return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
659 }
660 {
661 if (isRootServer(address)) {
662 setLWResult(res, 0, false, false, true);
663 addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
664 addDS(DNSName("com."), 300, res->d_records, keys);
665 addRRSIG(keys, res->d_records, DNSName("."), 300);
666 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
667 return LWResult::Result::Success;
668 }
669 if (address == ComboAddress("192.0.2.1:53")) {
670 if (domain == DNSName("com.")) {
671 setLWResult(res, 0, true, false, true);
672 addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
673 addRRSIG(keys, res->d_records, domain, 300);
674 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
675 addRRSIG(keys, res->d_records, domain, 300);
676 }
677 else {
678 setLWResult(res, 0, false, false, true);
679 addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
680 addDS(domain, 300, res->d_records, keys);
681 addRRSIG(keys, res->d_records, DNSName("com."), 300);
682 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
683 }
684 return LWResult::Result::Success;
685 }
686 if (address == ComboAddress("192.0.2.2:53")) {
687 if (type == QType::NS) {
688 setLWResult(res, 0, true, false, true);
689 addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
690 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
691 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
692 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
693 }
694 else {
695 setLWResult(res, 0, true, false, true);
696 addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
697 addRRSIG(keys, res->d_records, domain, 300);
698 addNSECRecordToLW(domain, DNSName("z.powerdns.com."), {QType::NS, QType::DNSKEY}, 600, res->d_records);
699 addRRSIG(keys, res->d_records, domain, 300);
700 }
701 return LWResult::Result::Success;
702 }
703 }
704
705 return LWResult::Result::Timeout;
706 });
707
708 vector<DNSRecord> ret;
709 int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
710 BOOST_CHECK_EQUAL(res, RCode::NoError);
711 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
712 BOOST_REQUIRE_EQUAL(ret.size(), 4U);
713 BOOST_CHECK_EQUAL(queriesCount, 6U);
714
715 /* again, to test the cache */
716 ret.clear();
717 res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
718 BOOST_CHECK_EQUAL(res, RCode::NoError);
719 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
720 BOOST_REQUIRE_EQUAL(ret.size(), 4U);
721 BOOST_CHECK_EQUAL(queriesCount, 6U);
722 }
723
724 BOOST_AUTO_TEST_CASE(test_dnssec_validation_nxdomain_nsec)
725 {
726 std::unique_ptr<SyncRes> sr;
727 initSR(sr, true);
728
729 setDNSSECValidation(sr, DNSSECMode::ValidateAll);
730
731 primeHints();
732 const DNSName target("nx.powerdns.com.");
733 testkeysset_t keys;
734
735 auto luaconfsCopy = g_luaconfs.getCopy();
736 luaconfsCopy.dsAnchors.clear();
737 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
738 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
739 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
740
741 g_luaconfs.setState(luaconfsCopy);
742
743 size_t queriesCount = 0;
744
745 sr->setAsyncCallback([&](const ComboAddress& address, 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 */) {
746 queriesCount++;
747
748 DNSName auth = domain;
749 if (domain == target) {
750 auth = DNSName("powerdns.com.");
751 }
752 if (type == QType::DS || type == QType::DNSKEY) {
753 if (type == QType::DS && domain == target) {
754 setLWResult(res, RCode::NXDomain, true, false, true);
755 addRecordToLW(res, DNSName("powerdns.com."), QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
756 addRRSIG(keys, res->d_records, auth, 300);
757 addNSECRecordToLW(DNSName("nw.powerdns.com."), DNSName("ny.powerdns.com."), {QType::RRSIG, QType::NSEC}, 600, res->d_records);
758 addRRSIG(keys, res->d_records, auth, 300);
759 return LWResult::Result::Success;
760 }
761 return genericDSAndDNSKEYHandler(res, domain, auth, type, keys);
762 }
763 {
764 if (isRootServer(address)) {
765 setLWResult(res, 0, false, false, true);
766 addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
767 addDS(DNSName("com."), 300, res->d_records, keys);
768 addRRSIG(keys, res->d_records, DNSName("."), 300);
769 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
770 return LWResult::Result::Success;
771 }
772 if (address == ComboAddress("192.0.2.1:53")) {
773 if (domain == DNSName("com.")) {
774 setLWResult(res, 0, true, false, true);
775 addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
776 addRRSIG(keys, res->d_records, domain, 300);
777 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
778 addRRSIG(keys, res->d_records, domain, 300);
779 }
780 else {
781 setLWResult(res, 0, false, false, true);
782 addRecordToLW(res, auth, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
783 addDS(auth, 300, res->d_records, keys);
784 addRRSIG(keys, res->d_records, DNSName("com."), 300);
785 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
786 }
787 return LWResult::Result::Success;
788 }
789 if (address == ComboAddress("192.0.2.2:53")) {
790 if (type == QType::NS) {
791 setLWResult(res, 0, true, false, true);
792 if (domain == DNSName("powerdns.com.")) {
793 addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
794 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
795 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
796 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
797 }
798 else {
799 addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
800 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
801 addNSECRecordToLW(DNSName("nx.powerdns.com."), DNSName("nz.powerdns.com."), {QType::A, QType::NSEC, QType::RRSIG}, 600, res->d_records);
802 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
803 }
804 }
805 else {
806 setLWResult(res, RCode::NXDomain, true, false, true);
807 addRecordToLW(res, DNSName("powerdns.com."), QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
808 addRRSIG(keys, res->d_records, auth, 300);
809 addNSECRecordToLW(DNSName("nw.powerdns.com."), DNSName("ny.powerdns.com."), {QType::RRSIG, QType::NSEC}, 600, res->d_records);
810 addRRSIG(keys, res->d_records, auth, 300);
811 /* add wildcard denial */
812 addNSECRecordToLW(DNSName("powerdns.com."), DNSName("a.powerdns.com."), {QType::RRSIG, QType::NSEC}, 600, res->d_records);
813 addRRSIG(keys, res->d_records, auth, 300);
814 }
815 return LWResult::Result::Success;
816 }
817 }
818
819 return LWResult::Result::Timeout;
820 });
821
822 vector<DNSRecord> ret;
823 int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
824 BOOST_CHECK_EQUAL(res, RCode::NXDomain);
825 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
826 BOOST_REQUIRE_EQUAL(ret.size(), 6U);
827 BOOST_CHECK_EQUAL(queriesCount, 6U);
828
829 /* again, to test the cache */
830 ret.clear();
831 res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
832 BOOST_CHECK_EQUAL(res, RCode::NXDomain);
833 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
834 BOOST_REQUIRE_EQUAL(ret.size(), 6U);
835 BOOST_CHECK_EQUAL(queriesCount, 6U);
836 }
837
838 BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec_wildcard)
839 {
840 std::unique_ptr<SyncRes> sr;
841 initSR(sr, true);
842
843 setDNSSECValidation(sr, DNSSECMode::ValidateAll);
844
845 primeHints();
846 const DNSName target("www.powerdns.com.");
847 testkeysset_t keys;
848
849 auto luaconfsCopy = g_luaconfs.getCopy();
850 luaconfsCopy.dsAnchors.clear();
851 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
852 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
853 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
854
855 g_luaconfs.setState(luaconfsCopy);
856
857 size_t queriesCount = 0;
858
859 sr->setAsyncCallback([&](const ComboAddress& address, 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 */) {
860 queriesCount++;
861
862 if (type == QType::DS || type == QType::DNSKEY) {
863 if (type == QType::DS && domain == target) {
864 setLWResult(res, RCode::NoError, true, false, true);
865 addRecordToLW(res, DNSName("powerdns.com."), QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
866 addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300);
867 addNSECRecordToLW(DNSName("www.powerdns.com."), DNSName("wwz.powerdns.com."), {QType::A, QType::NSEC, QType::RRSIG}, 600, res->d_records);
868 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
869 return LWResult::Result::Success;
870 }
871 return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
872 }
873 {
874 if (isRootServer(address)) {
875 setLWResult(res, 0, false, false, true);
876 addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
877 addDS(DNSName("com."), 300, res->d_records, keys);
878 addRRSIG(keys, res->d_records, DNSName("."), 300);
879 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
880 return LWResult::Result::Success;
881 }
882 if (address == ComboAddress("192.0.2.1:53")) {
883 if (domain == DNSName("com.")) {
884 setLWResult(res, 0, true, false, true);
885 addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
886 addRRSIG(keys, res->d_records, domain, 300);
887 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
888 addRRSIG(keys, res->d_records, domain, 300);
889 }
890 else {
891 setLWResult(res, 0, false, false, true);
892 addRecordToLW(res, "powerdns.com.", QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
893 addDS(DNSName("powerdns.com."), 300, res->d_records, keys);
894 addRRSIG(keys, res->d_records, DNSName("com."), 300);
895 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
896 }
897 return LWResult::Result::Success;
898 }
899 if (address == ComboAddress("192.0.2.2:53")) {
900 setLWResult(res, 0, true, false, true);
901 if (type == QType::NS) {
902 if (domain == DNSName("powerdns.com.")) {
903 addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
904 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
905 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
906 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
907 }
908 else {
909 addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
910 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
911 addNSECRecordToLW(DNSName("www.powerdns.com."), DNSName("wwz.powerdns.com."), {QType::A, QType::NSEC, QType::RRSIG}, 600, res->d_records);
912 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
913 }
914 }
915 else {
916 addRecordToLW(res, domain, QType::A, "192.0.2.42", DNSResourceRecord::ANSWER, 600);
917 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300, false, boost::none, DNSName("*.powerdns.com"));
918 /* we need to add the proof that this name does not exist, so the wildcard may apply */
919 addNSECRecordToLW(DNSName("a.powerdns.com."), DNSName("wwz.powerdns.com."), {QType::A, QType::NSEC, QType::RRSIG}, 60, res->d_records);
920 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
921 }
922 return LWResult::Result::Success;
923 }
924 }
925
926 return LWResult::Result::Timeout;
927 });
928
929 vector<DNSRecord> ret;
930 int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
931 BOOST_CHECK_EQUAL(res, RCode::NoError);
932 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
933 BOOST_REQUIRE_EQUAL(ret.size(), 4U);
934 BOOST_CHECK_EQUAL(queriesCount, 6U);
935
936 /* again, to test the cache */
937 ret.clear();
938 res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
939 BOOST_CHECK_EQUAL(res, RCode::NoError);
940 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
941 BOOST_REQUIRE_EQUAL(ret.size(), 4U);
942 for (const auto& rec : ret) {
943 /* check that we applied the lowest TTL, here this is from the NSEC proving that the exact name did not exist */
944 BOOST_CHECK_LE(rec.d_ttl, 60U);
945 }
946 BOOST_CHECK_EQUAL(queriesCount, 6U);
947 }
948
949 BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec_wildcard_proof_before_rrsig)
950 {
951 /* this tests makes sure that we correctly detect that we need to gather
952 wildcard proof (since the answer is expanded from a wildcard, we need
953 to prove that the target name does not exist) even though the RRSIG which
954 allows us to detect that the answer is an expanded wildcard (from the label
955 count field of the RRSIG) comes _after_ the NSEC
956 */
957 std::unique_ptr<SyncRes> sr;
958 initSR(sr, true);
959
960 setDNSSECValidation(sr, DNSSECMode::ValidateAll);
961
962 primeHints();
963 const DNSName target("www.powerdns.com.");
964 testkeysset_t keys;
965
966 auto luaconfsCopy = g_luaconfs.getCopy();
967 luaconfsCopy.dsAnchors.clear();
968 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
969 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
970 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
971
972 g_luaconfs.setState(luaconfsCopy);
973
974 size_t queriesCount = 0;
975
976 sr->setAsyncCallback([&](const ComboAddress& address, 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 */) {
977 queriesCount++;
978
979 if (type == QType::DS || type == QType::DNSKEY) {
980 if (type == QType::DS && domain == target) {
981 setLWResult(res, RCode::NoError, true, false, true);
982 addRecordToLW(res, DNSName("powerdns.com."), QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
983 addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300);
984 addNSECRecordToLW(DNSName("www.powerdns.com."), DNSName("wwz.powerdns.com."), {QType::A, QType::NSEC, QType::RRSIG}, 600, res->d_records);
985 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
986 return LWResult::Result::Success;
987 }
988 return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
989 }
990 {
991 if (isRootServer(address)) {
992 setLWResult(res, 0, false, false, true);
993 addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
994 addDS(DNSName("com."), 300, res->d_records, keys);
995 addRRSIG(keys, res->d_records, DNSName("."), 300);
996 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
997 return LWResult::Result::Success;
998 }
999 if (address == ComboAddress("192.0.2.1:53")) {
1000 if (domain == DNSName("com.")) {
1001 setLWResult(res, 0, true, false, true);
1002 addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
1003 addRRSIG(keys, res->d_records, domain, 300);
1004 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
1005 addRRSIG(keys, res->d_records, domain, 300);
1006 }
1007 else {
1008 setLWResult(res, 0, false, false, true);
1009 addRecordToLW(res, "powerdns.com.", QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
1010 addDS(DNSName("powerdns.com."), 300, res->d_records, keys);
1011 addRRSIG(keys, res->d_records, DNSName("com."), 300);
1012 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
1013 }
1014 return LWResult::Result::Success;
1015 }
1016 if (address == ComboAddress("192.0.2.2:53")) {
1017 setLWResult(res, 0, true, false, true);
1018 if (type == QType::NS) {
1019 if (domain == DNSName("powerdns.com.")) {
1020 addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
1021 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
1022 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
1023 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
1024 }
1025 else {
1026 addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
1027 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
1028 addNSECRecordToLW(DNSName("www.powerdns.com."), DNSName("wwz.powerdns.com."), {QType::A, QType::NSEC, QType::RRSIG}, 600, res->d_records);
1029 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
1030 }
1031 }
1032 else {
1033 addRecordToLW(res, domain, QType::A, "192.0.2.42", DNSResourceRecord::ANSWER, 600);
1034 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300, false, boost::none, DNSName("*.powerdns.com"));
1035 /* we need to add the proof that this name does not exist, so the wildcard may apply */
1036 addNSECRecordToLW(DNSName("a.powerdns.com."), DNSName("wwz.powerdns.com."), {QType::A, QType::NSEC, QType::RRSIG}, 60, res->d_records);
1037 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
1038 /* now this is the important part! We are swapping the first RRSIG and the NSEC, to make sure we still gather the NSEC proof that the
1039 exact name does not exist even though we have not seen the RRSIG whose label count is smaller than the target name yet */
1040 std::swap(res->d_records.at(1), res->d_records.at(3));
1041 }
1042 return LWResult::Result::Success;
1043 }
1044 }
1045
1046 return LWResult::Result::Timeout;
1047 });
1048
1049 vector<DNSRecord> ret;
1050 int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
1051 BOOST_CHECK_EQUAL(res, RCode::NoError);
1052 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
1053 BOOST_REQUIRE_EQUAL(ret.size(), 4U);
1054 BOOST_CHECK_EQUAL(queriesCount, 6U);
1055
1056 /* again, to test the cache */
1057 ret.clear();
1058 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 for (const auto& rec : ret) {
1063 /* check that we applied the lowest TTL, here this is from the NSEC proving that the exact name did not exist */
1064 BOOST_CHECK_LE(rec.d_ttl, 60U);
1065 }
1066 BOOST_CHECK_EQUAL(queriesCount, 6U);
1067 }
1068
1069 BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec_nodata_nowildcard)
1070 {
1071 std::unique_ptr<SyncRes> sr;
1072 initSR(sr, true);
1073
1074 setDNSSECValidation(sr, DNSSECMode::ValidateAll);
1075
1076 primeHints();
1077 const DNSName target("www.com.");
1078 testkeysset_t keys;
1079
1080 auto luaconfsCopy = g_luaconfs.getCopy();
1081 luaconfsCopy.dsAnchors.clear();
1082 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
1083 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
1084
1085 g_luaconfs.setState(luaconfsCopy);
1086
1087 size_t queriesCount = 0;
1088
1089 sr->setAsyncCallback([&](const ComboAddress& address, 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 */) {
1090 queriesCount++;
1091
1092 if (type == QType::DS || type == QType::DNSKEY) {
1093 if (type == QType::DS && domain == target) {
1094 DNSName auth("com.");
1095 setLWResult(res, 0, true, false, true);
1096
1097 addRecordToLW(res, auth, QType::SOA, "foo. bar. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
1098 addRRSIG(keys, res->d_records, auth, 300);
1099 /* add a NSEC denying the DS AND the existence of a cut (no NS) */
1100 addNSECRecordToLW(domain, DNSName("z") + domain, {QType::NSEC}, 600, res->d_records);
1101 addRRSIG(keys, res->d_records, auth, 300);
1102 return LWResult::Result::Success;
1103 }
1104 return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
1105 }
1106 {
1107 if (isRootServer(address)) {
1108 setLWResult(res, 0, false, false, true);
1109 addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
1110 addDS(DNSName("com."), 300, res->d_records, keys);
1111 addRRSIG(keys, res->d_records, DNSName("."), 300);
1112 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
1113 return LWResult::Result::Success;
1114 }
1115 if (address == ComboAddress("192.0.2.1:53")) {
1116 setLWResult(res, 0, true, false, true);
1117 /* no data */
1118 addRecordToLW(res, DNSName("com."), QType::SOA, "com. com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
1119 addRRSIG(keys, res->d_records, DNSName("com."), 300);
1120 /* no record for this name */
1121 addNSECRecordToLW(DNSName("wwv.com."), DNSName("wwx.com."), {QType::NSEC, QType::RRSIG}, 600, res->d_records);
1122 addRRSIG(keys, res->d_records, DNSName("com."), 300);
1123 /* a wildcard matches but has no record for this type */
1124 addNSECRecordToLW(DNSName("*.com."), DNSName("com."), {QType::AAAA, QType::NSEC, QType::RRSIG}, 600, res->d_records);
1125 addRRSIG(keys, res->d_records, DNSName("com"), 300, false, boost::none, DNSName("*.com"));
1126 return LWResult::Result::Success;
1127 }
1128 }
1129
1130 return LWResult::Result::Timeout;
1131 });
1132
1133 vector<DNSRecord> ret;
1134 int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
1135 BOOST_CHECK_EQUAL(res, RCode::NoError);
1136 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
1137 BOOST_REQUIRE_EQUAL(ret.size(), 6U);
1138 BOOST_CHECK_EQUAL(queriesCount, 4U);
1139
1140 /* again, to test the cache */
1141 ret.clear();
1142 res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
1143 BOOST_CHECK_EQUAL(res, RCode::NoError);
1144 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
1145 BOOST_REQUIRE_EQUAL(ret.size(), 6U);
1146 BOOST_CHECK_EQUAL(queriesCount, 4U);
1147 }
1148
1149 BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec3_nodata_nowildcard)
1150 {
1151 std::unique_ptr<SyncRes> sr;
1152 initSR(sr, true);
1153
1154 setDNSSECValidation(sr, DNSSECMode::ValidateAll);
1155
1156 primeHints();
1157 const DNSName target("www.com.");
1158 testkeysset_t keys;
1159
1160 auto luaconfsCopy = g_luaconfs.getCopy();
1161 luaconfsCopy.dsAnchors.clear();
1162 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
1163 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
1164
1165 g_luaconfs.setState(luaconfsCopy);
1166
1167 size_t queriesCount = 0;
1168
1169 sr->setAsyncCallback([&](const ComboAddress& address, 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 */) {
1170 queriesCount++;
1171
1172 if (type == QType::DS || type == QType::DNSKEY) {
1173 if (type == QType::DS && domain == target) {
1174 DNSName auth("com.");
1175 setLWResult(res, 0, true, false, true);
1176
1177 addRecordToLW(res, auth, QType::SOA, "foo. bar. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
1178 addRRSIG(keys, res->d_records, auth, 300);
1179 /* add a NSEC3 denying the DS AND the existence of a cut (no NS) */
1180 /* first the closest encloser */
1181 addNSEC3UnhashedRecordToLW(DNSName("com."), auth, "whatever", {QType::A, QType::TXT, QType::RRSIG, QType::NSEC}, 600, res->d_records);
1182 addRRSIG(keys, res->d_records, auth, 300);
1183 /* then the next closer */
1184 addNSEC3NarrowRecordToLW(domain, DNSName("com."), {QType::RRSIG, QType::NSEC}, 600, res->d_records);
1185 addRRSIG(keys, res->d_records, auth, 300);
1186 /* a wildcard matches but has no record for this type */
1187 addNSEC3UnhashedRecordToLW(DNSName("*.com."), DNSName("com."), "whatever", {QType::AAAA, QType::NSEC, QType::RRSIG}, 600, res->d_records);
1188 addRRSIG(keys, res->d_records, DNSName("com"), 300, false, boost::none, DNSName("*.com"));
1189 return LWResult::Result::Success;
1190 }
1191 return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
1192 }
1193 {
1194 if (isRootServer(address)) {
1195 setLWResult(res, 0, false, false, true);
1196 addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
1197 addDS(DNSName("com."), 300, res->d_records, keys);
1198 addRRSIG(keys, res->d_records, DNSName("."), 300);
1199 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
1200 return LWResult::Result::Success;
1201 }
1202 if (address == ComboAddress("192.0.2.1:53")) {
1203 setLWResult(res, 0, true, false, true);
1204 /* no data */
1205 addRecordToLW(res, DNSName("com."), QType::SOA, "com. com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
1206 addRRSIG(keys, res->d_records, DNSName("com."), 300);
1207 /* no record for this name */
1208 /* first the closest encloser */
1209 addNSEC3UnhashedRecordToLW(DNSName("com."), DNSName("com."), "whatever", {QType::A, QType::TXT, QType::RRSIG, QType::NSEC}, 600, res->d_records);
1210 addRRSIG(keys, res->d_records, DNSName("com."), 300);
1211 /* then the next closer */
1212 addNSEC3NarrowRecordToLW(domain, DNSName("com."), {QType::RRSIG, QType::NSEC}, 600, res->d_records);
1213 addRRSIG(keys, res->d_records, DNSName("com."), 300);
1214 /* a wildcard matches but has no record for this type */
1215 addNSEC3UnhashedRecordToLW(DNSName("*.com."), DNSName("com."), "whatever", {QType::AAAA, QType::NSEC, QType::RRSIG}, 600, res->d_records);
1216 addRRSIG(keys, res->d_records, DNSName("com"), 300, false, boost::none, DNSName("*.com"));
1217 return LWResult::Result::Success;
1218 }
1219 }
1220
1221 return LWResult::Result::Timeout;
1222 });
1223
1224 vector<DNSRecord> ret;
1225 int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
1226 BOOST_CHECK_EQUAL(res, RCode::NoError);
1227 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
1228 BOOST_REQUIRE_EQUAL(ret.size(), 8U);
1229 BOOST_CHECK_EQUAL(queriesCount, 4U);
1230
1231 /* again, to test the cache */
1232 ret.clear();
1233 res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
1234 BOOST_CHECK_EQUAL(res, RCode::NoError);
1235 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
1236 BOOST_REQUIRE_EQUAL(ret.size(), 8U);
1237 BOOST_CHECK_EQUAL(queriesCount, 4U);
1238 }
1239
1240 BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec3_too_many_nsec3s)
1241 {
1242 std::unique_ptr<SyncRes> sr;
1243 initSR(sr, true);
1244
1245 setDNSSECValidation(sr, DNSSECMode::ValidateAll);
1246
1247 primeHints();
1248 const DNSName target("www.com.");
1249 testkeysset_t keys;
1250
1251 auto luaconfsCopy = g_luaconfs.getCopy();
1252 luaconfsCopy.dsAnchors.clear();
1253 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
1254 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
1255
1256 g_luaconfs.setState(luaconfsCopy);
1257
1258 size_t queriesCount = 0;
1259
1260 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 */) {
1261 queriesCount++;
1262
1263 if (type == QType::DS || type == QType::DNSKEY) {
1264 if (type == QType::DS && domain == target) {
1265 DNSName auth("com.");
1266 setLWResult(res, 0, true, false, true);
1267
1268 addRecordToLW(res, auth, QType::SOA, "foo. bar. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
1269 addRRSIG(keys, res->d_records, auth, 300);
1270 /* add a NSEC3 denying the DS AND the existence of a cut (no NS) */
1271 /* first the closest encloser */
1272 addNSEC3UnhashedRecordToLW(DNSName("com."), auth, "whatever", {QType::A, QType::TXT, QType::RRSIG, QType::NSEC}, 600, res->d_records);
1273 addRRSIG(keys, res->d_records, auth, 300);
1274 /* then the next closer */
1275 addNSEC3NarrowRecordToLW(domain, DNSName("com."), {QType::RRSIG, QType::NSEC}, 600, res->d_records);
1276 addRRSIG(keys, res->d_records, auth, 300);
1277 /* a wildcard matches but has no record for this type */
1278 addNSEC3UnhashedRecordToLW(DNSName("*.com."), DNSName("com."), "whatever", {QType::AAAA, QType::NSEC, QType::RRSIG}, 600, res->d_records);
1279 addRRSIG(keys, res->d_records, DNSName("com"), 300, false, boost::none, DNSName("*.com"));
1280 return LWResult::Result::Success;
1281 }
1282 return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
1283 }
1284 else {
1285 if (isRootServer(ip)) {
1286 setLWResult(res, 0, false, false, true);
1287 addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
1288 addDS(DNSName("com."), 300, res->d_records, keys);
1289 addRRSIG(keys, res->d_records, DNSName("."), 300);
1290 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
1291 return LWResult::Result::Success;
1292 }
1293 else if (ip == ComboAddress("192.0.2.1:53")) {
1294 setLWResult(res, 0, true, false, true);
1295 /* no data */
1296 addRecordToLW(res, DNSName("com."), QType::SOA, "com. com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
1297 addRRSIG(keys, res->d_records, DNSName("com."), 300);
1298 /* no record for this name */
1299 /* first the closest encloser */
1300 addNSEC3UnhashedRecordToLW(DNSName("com."), DNSName("com."), "whatever", {QType::A, QType::TXT, QType::RRSIG, QType::NSEC}, 600, res->d_records);
1301 addRRSIG(keys, res->d_records, DNSName("com."), 300);
1302 /* then the next closer */
1303 addNSEC3NarrowRecordToLW(domain, DNSName("com."), {QType::RRSIG, QType::NSEC}, 600, res->d_records);
1304 addRRSIG(keys, res->d_records, DNSName("com."), 300);
1305 /* a wildcard matches but has no record for this type */
1306 addNSEC3UnhashedRecordToLW(DNSName("*.com."), DNSName("com."), "whatever", {QType::AAAA, QType::NSEC, QType::RRSIG}, 600, res->d_records);
1307 addRRSIG(keys, res->d_records, DNSName("com"), 300, false, boost::none, DNSName("*.com"));
1308 return LWResult::Result::Success;
1309 }
1310 }
1311
1312 return LWResult::Result::Timeout;
1313 });
1314
1315 /* we allow at most 2 NSEC3s, but we need at least 3 of them to
1316 get a valid denial so we will go Bogus */
1317 g_maxNSEC3sPerRecordToConsider = 2;
1318
1319 vector<DNSRecord> ret;
1320 int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
1321 BOOST_CHECK_EQUAL(res, RCode::NoError);
1322 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::BogusInvalidDenial);
1323 BOOST_REQUIRE_EQUAL(ret.size(), 8U);
1324 BOOST_CHECK_EQUAL(queriesCount, 5U);
1325
1326 g_maxNSEC3sPerRecordToConsider = 0;
1327 }
1328
1329 BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec3_too_many_nsec3s_per_query)
1330 {
1331 SyncRes::s_maxnsec3iterationsperq = 20;
1332 std::unique_ptr<SyncRes> sr;
1333 initSR(sr, true);
1334
1335 setDNSSECValidation(sr, DNSSECMode::ValidateAll);
1336
1337 primeHints();
1338 const DNSName target("www.com.");
1339 testkeysset_t keys;
1340
1341 auto luaconfsCopy = g_luaconfs.getCopy();
1342 luaconfsCopy.dsAnchors.clear();
1343 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
1344 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
1345
1346 g_luaconfs.setState(luaconfsCopy);
1347
1348 size_t queriesCount = 0;
1349
1350 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 */) {
1351 queriesCount++;
1352
1353 if (type == QType::DS || type == QType::DNSKEY) {
1354 if (type == QType::DS && domain == target) {
1355 DNSName auth("com.");
1356 setLWResult(res, 0, true, false, true);
1357
1358 addRecordToLW(res, auth, QType::SOA, "foo. bar. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
1359 addRRSIG(keys, res->d_records, auth, 300);
1360 /* add a NSEC3 denying the DS AND the existence of a cut (no NS) */
1361 /* first the closest encloser */
1362 addNSEC3UnhashedRecordToLW(DNSName("com."), auth, "whatever", {QType::A, QType::TXT, QType::RRSIG, QType::NSEC}, 600, res->d_records);
1363 addRRSIG(keys, res->d_records, auth, 300);
1364 /* then the next closer */
1365 addNSEC3NarrowRecordToLW(domain, DNSName("com."), {QType::RRSIG, QType::NSEC}, 600, res->d_records);
1366 addRRSIG(keys, res->d_records, auth, 300);
1367 /* a wildcard matches but has no record for this type */
1368 addNSEC3UnhashedRecordToLW(DNSName("*.com."), DNSName("com."), "whatever", {QType::AAAA, QType::NSEC, QType::RRSIG}, 600, res->d_records);
1369 addRRSIG(keys, res->d_records, DNSName("com"), 300, false, boost::none, DNSName("*.com"));
1370 return LWResult::Result::Success;
1371 }
1372 return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
1373 }
1374 else {
1375 if (isRootServer(ip)) {
1376 setLWResult(res, 0, false, false, true);
1377 addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
1378 addDS(DNSName("com."), 300, res->d_records, keys);
1379 addRRSIG(keys, res->d_records, DNSName("."), 300);
1380 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
1381 return LWResult::Result::Success;
1382 }
1383 else if (ip == ComboAddress("192.0.2.1:53")) {
1384 setLWResult(res, 0, true, false, true);
1385 /* no data */
1386 addRecordToLW(res, DNSName("com."), QType::SOA, "com. com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
1387 addRRSIG(keys, res->d_records, DNSName("com."), 300);
1388 /* no record for this name */
1389 /* first the closest encloser */
1390 addNSEC3UnhashedRecordToLW(DNSName("com."), DNSName("com."), "whatever", {QType::A, QType::TXT, QType::RRSIG, QType::NSEC}, 600, res->d_records);
1391 addRRSIG(keys, res->d_records, DNSName("com."), 300);
1392 /* then the next closer */
1393 addNSEC3NarrowRecordToLW(domain, DNSName("com."), {QType::RRSIG, QType::NSEC}, 600, res->d_records);
1394 addRRSIG(keys, res->d_records, DNSName("com."), 300);
1395 /* a wildcard matches but has no record for this type */
1396 addNSEC3UnhashedRecordToLW(DNSName("*.com."), DNSName("com."), "whatever", {QType::AAAA, QType::NSEC, QType::RRSIG}, 600, res->d_records);
1397 addRRSIG(keys, res->d_records, DNSName("com"), 300, false, boost::none, DNSName("*.com"));
1398 return LWResult::Result::Success;
1399 }
1400 }
1401
1402 return LWResult::Result::Timeout;
1403 });
1404
1405 vector<DNSRecord> ret;
1406 BOOST_CHECK_THROW(sr->beginResolve(target, QType(QType::A), QClass::IN, ret), pdns::validation::TooManySEC3IterationsException);
1407
1408 SyncRes::s_maxnsec3iterationsperq = 0;
1409 }
1410
1411 BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec3_nodata_nowildcard_duplicated_nsec3)
1412 {
1413 std::unique_ptr<SyncRes> sr;
1414 initSR(sr, true);
1415
1416 setDNSSECValidation(sr, DNSSECMode::ValidateAll);
1417
1418 primeHints();
1419 const DNSName target("www.com.");
1420 testkeysset_t keys;
1421
1422 auto luaconfsCopy = g_luaconfs.getCopy();
1423 luaconfsCopy.dsAnchors.clear();
1424 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
1425 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
1426
1427 g_luaconfs.setState(luaconfsCopy);
1428
1429 size_t queriesCount = 0;
1430
1431 sr->setAsyncCallback([&](const ComboAddress& address, 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 */) {
1432 queriesCount++;
1433
1434 if (type == QType::DS || type == QType::DNSKEY) {
1435 if (type == QType::DS && domain == target) {
1436 DNSName auth("com.");
1437 setLWResult(res, 0, true, false, true);
1438
1439 addRecordToLW(res, auth, QType::SOA, "foo. bar. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
1440 addRRSIG(keys, res->d_records, auth, 300);
1441 /* add a NSEC3 denying the DS AND the existence of a cut (no NS) */
1442 /* first the closest encloser */
1443 addNSEC3UnhashedRecordToLW(DNSName("com."), auth, "whatever", {QType::A, QType::TXT, QType::RRSIG, QType::NSEC}, 600, res->d_records);
1444 addRRSIG(keys, res->d_records, auth, 300);
1445 /* then the next closer */
1446 addNSEC3NarrowRecordToLW(domain, DNSName("com."), {QType::RRSIG, QType::NSEC}, 600, res->d_records);
1447 addRRSIG(keys, res->d_records, auth, 300);
1448 /* a wildcard matches but has no record for this type */
1449 addNSEC3UnhashedRecordToLW(DNSName("*.com."), DNSName("com."), "whatever", {QType::AAAA, QType::NSEC, QType::RRSIG}, 600, res->d_records);
1450 addRRSIG(keys, res->d_records, DNSName("com"), 300, false, boost::none, DNSName("*.com"));
1451 return LWResult::Result::Success;
1452 }
1453 return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
1454 }
1455 {
1456 if (isRootServer(address)) {
1457 setLWResult(res, 0, false, false, true);
1458 addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
1459 addDS(DNSName("com."), 300, res->d_records, keys);
1460 addRRSIG(keys, res->d_records, DNSName("."), 300);
1461 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
1462 return LWResult::Result::Success;
1463 }
1464 if (address == ComboAddress("192.0.2.1:53")) {
1465 setLWResult(res, 0, true, false, true);
1466 /* no data */
1467 addRecordToLW(res, DNSName("com."), QType::SOA, "com. com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
1468 addRRSIG(keys, res->d_records, DNSName("com."), 300);
1469 /* no record for this name */
1470 /* first the closest encloser */
1471 addNSEC3UnhashedRecordToLW(DNSName("com."), DNSName("com."), "whatever", {QType::A, QType::TXT, QType::RRSIG, QType::NSEC}, 600, res->d_records);
1472 addRRSIG(keys, res->d_records, DNSName("com."), 300);
1473 /* !! we duplicate the NSEC3 on purpose, to check deduplication. The RRSIG will have been computed for a RRSET containing only one NSEC3 and should not be valid. */
1474 addNSEC3UnhashedRecordToLW(DNSName("com."), DNSName("com."), "whatever", {QType::A, QType::TXT, QType::RRSIG, QType::NSEC}, 600, res->d_records);
1475 /* then the next closer */
1476 addNSEC3NarrowRecordToLW(domain, DNSName("com."), {QType::RRSIG, QType::NSEC}, 600, res->d_records);
1477 addRRSIG(keys, res->d_records, DNSName("com."), 300);
1478 /* a wildcard matches but has no record for this type */
1479 addNSEC3UnhashedRecordToLW(DNSName("*.com."), DNSName("com."), "whatever", {QType::AAAA, QType::NSEC, QType::RRSIG}, 600, res->d_records);
1480 addRRSIG(keys, res->d_records, DNSName("com"), 300, false, boost::none, DNSName("*.com"));
1481 return LWResult::Result::Success;
1482 }
1483 }
1484
1485 return LWResult::Result::Timeout;
1486 });
1487
1488 vector<DNSRecord> ret;
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::Secure);
1492 /* because we pass along the duplicated NSEC3 */
1493 BOOST_REQUIRE_EQUAL(ret.size(), 9U);
1494 BOOST_CHECK_EQUAL(queriesCount, 4U);
1495
1496 /* again, to test the cache */
1497 ret.clear();
1498 res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
1499 BOOST_CHECK_EQUAL(res, RCode::NoError);
1500 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
1501 /* because we pass along the duplicated NSEC3 */
1502 BOOST_REQUIRE_EQUAL(ret.size(), 9U);
1503 BOOST_CHECK_EQUAL(queriesCount, 4U);
1504 }
1505
1506 BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec3_nodata_nowildcard_too_many_iterations)
1507 {
1508 std::unique_ptr<SyncRes> sr;
1509 initSR(sr, true);
1510
1511 setDNSSECValidation(sr, DNSSECMode::ValidateAll);
1512
1513 primeHints();
1514 const DNSName target("www.com.");
1515 testkeysset_t keys;
1516
1517 auto luaconfsCopy = g_luaconfs.getCopy();
1518 luaconfsCopy.dsAnchors.clear();
1519 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
1520 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
1521
1522 g_luaconfs.setState(luaconfsCopy);
1523
1524 size_t queriesCount = 0;
1525
1526 sr->setAsyncCallback([&](const ComboAddress& address, 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 */) {
1527 queriesCount++;
1528
1529 if (type == QType::DS || type == QType::DNSKEY) {
1530 if (type == QType::DS && domain == target) {
1531 DNSName auth("com.");
1532 setLWResult(res, 0, true, false, true);
1533
1534 addRecordToLW(res, auth, QType::SOA, "foo. bar. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
1535 addRRSIG(keys, res->d_records, auth, 300);
1536 /* add a NSEC3 denying the DS AND the existence of a cut (no NS) */
1537 /* first the closest encloser */
1538 addNSEC3UnhashedRecordToLW(DNSName("com."), auth, "whatever", {QType::A, QType::TXT, QType::RRSIG, QType::NSEC}, 600, res->d_records, g_maxNSEC3Iterations + 100);
1539 addRRSIG(keys, res->d_records, auth, 300);
1540 /* then the next closer */
1541 addNSEC3NarrowRecordToLW(domain, DNSName("com."), {QType::RRSIG, QType::NSEC}, 600, res->d_records, g_maxNSEC3Iterations + 100);
1542 addRRSIG(keys, res->d_records, auth, 300);
1543 /* a wildcard matches but has no record for this type */
1544 addNSEC3UnhashedRecordToLW(DNSName("*.com."), DNSName("com."), "whatever", {QType::AAAA, QType::NSEC, QType::RRSIG}, 600, res->d_records, g_maxNSEC3Iterations + 100);
1545 addRRSIG(keys, res->d_records, DNSName("com"), 300, false, boost::none, DNSName("*.com"));
1546 return LWResult::Result::Success;
1547 }
1548 return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
1549 }
1550 {
1551 if (isRootServer(address)) {
1552 setLWResult(res, 0, false, false, true);
1553 addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
1554 addDS(DNSName("com."), 300, res->d_records, keys);
1555 addRRSIG(keys, res->d_records, DNSName("."), 300);
1556 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
1557 return LWResult::Result::Success;
1558 }
1559 if (address == ComboAddress("192.0.2.1:53")) {
1560 setLWResult(res, 0, true, false, true);
1561 /* no data */
1562 addRecordToLW(res, DNSName("com."), QType::SOA, "com. com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
1563 addRRSIG(keys, res->d_records, DNSName("com."), 300);
1564 /* no record for this name */
1565 /* first the closest encloser */
1566 addNSEC3UnhashedRecordToLW(DNSName("com."), DNSName("com."), "whatever", {QType::A, QType::TXT, QType::RRSIG, QType::NSEC}, 600, res->d_records, g_maxNSEC3Iterations + 100);
1567 addRRSIG(keys, res->d_records, DNSName("com."), 300);
1568 /* then the next closer */
1569 addNSEC3NarrowRecordToLW(domain, DNSName("com."), {QType::RRSIG, QType::NSEC}, 600, res->d_records, g_maxNSEC3Iterations + 100);
1570 addRRSIG(keys, res->d_records, DNSName("com."), 300);
1571 /* a wildcard matches but has no record for this type */
1572 addNSEC3UnhashedRecordToLW(DNSName("*.com."), DNSName("com."), "whatever", {QType::AAAA, QType::NSEC, QType::RRSIG}, 600, res->d_records, g_maxNSEC3Iterations + 100);
1573 addRRSIG(keys, res->d_records, DNSName("com"), 300, false, boost::none, DNSName("*.com"));
1574 return LWResult::Result::Success;
1575 }
1576 }
1577
1578 return LWResult::Result::Timeout;
1579 });
1580
1581 /* we are generating NSEC3 with more iterations than we allow, so we should go Insecure */
1582 vector<DNSRecord> ret;
1583 int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
1584 BOOST_CHECK_EQUAL(res, RCode::NoError);
1585 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
1586 BOOST_REQUIRE_EQUAL(ret.size(), 8U);
1587 BOOST_CHECK_EQUAL(queriesCount, 4U);
1588
1589 /* again, to test the cache */
1590 ret.clear();
1591 res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
1592 BOOST_CHECK_EQUAL(res, RCode::NoError);
1593 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
1594 BOOST_REQUIRE_EQUAL(ret.size(), 8U);
1595 BOOST_CHECK_EQUAL(queriesCount, 4U);
1596 }
1597
1598 BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec3_wildcard)
1599 {
1600 std::unique_ptr<SyncRes> sr;
1601 initSR(sr, true);
1602
1603 setDNSSECValidation(sr, DNSSECMode::ValidateAll);
1604
1605 primeHints();
1606 const DNSName target("www.sub.powerdns.com.");
1607 testkeysset_t keys;
1608
1609 auto luaconfsCopy = g_luaconfs.getCopy();
1610 luaconfsCopy.dsAnchors.clear();
1611 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
1612 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
1613 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
1614
1615 g_luaconfs.setState(luaconfsCopy);
1616
1617 size_t queriesCount = 0;
1618
1619 sr->setAsyncCallback([&](const ComboAddress& address, 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 */) {
1620 queriesCount++;
1621
1622 if (type == QType::DS || type == QType::DNSKEY) {
1623 if (type == QType::DS && domain.isPartOf(DNSName("sub.powerdns.com"))) {
1624 setLWResult(res, RCode::NoError, true, false, true);
1625 addRecordToLW(res, DNSName("powerdns.com."), QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
1626 addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300);
1627 if (domain == DNSName("sub.powerdns.com")) {
1628 addNSECRecordToLW(DNSName("sub.powerdns.com."), DNSName("sud.powerdns.com."), {QType::A, QType::NSEC, QType::RRSIG}, 600, res->d_records);
1629 }
1630 else if (domain == target) {
1631 addNSECRecordToLW(DNSName("www.sub.powerdns.com."), DNSName("wwz.sub.powerdns.com."), {QType::A, QType::NSEC, QType::RRSIG}, 600, res->d_records);
1632 }
1633 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
1634 return LWResult::Result::Success;
1635 }
1636 return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
1637 }
1638 {
1639 if (isRootServer(address)) {
1640 setLWResult(res, 0, false, false, true);
1641 addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
1642 addDS(DNSName("com."), 300, res->d_records, keys);
1643 addRRSIG(keys, res->d_records, DNSName("."), 300);
1644 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
1645 return LWResult::Result::Success;
1646 }
1647 if (address == ComboAddress("192.0.2.1:53")) {
1648 if (domain == DNSName("com.")) {
1649 setLWResult(res, 0, true, false, true);
1650 addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
1651 addRRSIG(keys, res->d_records, domain, 300);
1652 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
1653 addRRSIG(keys, res->d_records, domain, 300);
1654 }
1655 else {
1656 setLWResult(res, 0, false, false, true);
1657 addRecordToLW(res, "powerdns.com.", QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
1658 addDS(DNSName("powerdns.com."), 300, res->d_records, keys);
1659 addRRSIG(keys, res->d_records, DNSName("com."), 300);
1660 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
1661 }
1662 return LWResult::Result::Success;
1663 }
1664 if (address == ComboAddress("192.0.2.2:53")) {
1665 setLWResult(res, 0, true, false, true);
1666 if (type == QType::NS) {
1667 if (domain == DNSName("powerdns.com.")) {
1668 addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
1669 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
1670 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
1671 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
1672 }
1673 else {
1674 addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
1675 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
1676 addNSECRecordToLW(DNSName("www.powerdns.com."), DNSName("wwz.powerdns.com."), {QType::A, QType::NSEC, QType::RRSIG}, 600, res->d_records);
1677 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
1678 }
1679 }
1680 else {
1681 addRecordToLW(res, domain, QType::A, "192.0.2.42", DNSResourceRecord::ANSWER, 600);
1682 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300, false, boost::none, DNSName("*.powerdns.com"));
1683 /* we need to add the proof that this name does not exist, so the wildcard may apply */
1684 /* first the closest encloser */
1685 addNSEC3UnhashedRecordToLW(DNSName("powerdns.com."), DNSName("powerdns.com."), "whatever", {QType::A, QType::TXT, QType::RRSIG, QType::NSEC}, 600, res->d_records);
1686 addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300);
1687 /* then the next closer */
1688 addNSEC3NarrowRecordToLW(DNSName("sub.powerdns.com."), DNSName("powerdns.com."), {QType::A, QType::TXT, QType::RRSIG, QType::NSEC}, 60, res->d_records);
1689 addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300);
1690 }
1691 return LWResult::Result::Success;
1692 }
1693 }
1694
1695 return LWResult::Result::Timeout;
1696 });
1697
1698 vector<DNSRecord> ret;
1699 int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
1700 BOOST_CHECK_EQUAL(res, RCode::NoError);
1701 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
1702 BOOST_REQUIRE_EQUAL(ret.size(), 6U);
1703 BOOST_CHECK_EQUAL(queriesCount, 6U);
1704
1705 /* again, to test the cache */
1706 ret.clear();
1707 res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
1708 BOOST_CHECK_EQUAL(res, RCode::NoError);
1709 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
1710 BOOST_REQUIRE_EQUAL(ret.size(), 6U);
1711 for (const auto& rec : ret) {
1712 /* check that we applied the lowest TTL, here this is from the NSEC3 proving that the exact name did not exist (next closer) */
1713 BOOST_CHECK_LE(rec.d_ttl, 60U);
1714 }
1715 BOOST_CHECK_EQUAL(queriesCount, 6U);
1716 }
1717
1718 BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec3_wildcard_too_many_iterations)
1719 {
1720 std::unique_ptr<SyncRes> sr;
1721 initSR(sr, true);
1722
1723 setDNSSECValidation(sr, DNSSECMode::ValidateAll);
1724
1725 primeHints();
1726 const DNSName target("www.powerdns.com.");
1727 testkeysset_t keys;
1728
1729 auto luaconfsCopy = g_luaconfs.getCopy();
1730 luaconfsCopy.dsAnchors.clear();
1731 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
1732 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
1733 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
1734
1735 g_luaconfs.setState(luaconfsCopy);
1736
1737 size_t queriesCount = 0;
1738
1739 sr->setAsyncCallback([&](const ComboAddress& address, 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 */) {
1740 queriesCount++;
1741
1742 if (type == QType::DS || type == QType::DNSKEY) {
1743 if (type == QType::DS && domain == target) {
1744 setLWResult(res, RCode::NoError, true, false, true);
1745 addRecordToLW(res, DNSName("powerdns.com."), QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
1746 addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300);
1747 addNSECRecordToLW(DNSName("www.powerdns.com."), DNSName("wwz.powerdns.com."), {QType::A, QType::NSEC, QType::RRSIG}, 600, res->d_records);
1748 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
1749 return LWResult::Result::Success;
1750 }
1751 return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
1752 }
1753 {
1754 if (isRootServer(address)) {
1755 setLWResult(res, 0, false, false, true);
1756 addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
1757 addDS(DNSName("com."), 300, res->d_records, keys);
1758 addRRSIG(keys, res->d_records, DNSName("."), 300);
1759 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
1760 return LWResult::Result::Success;
1761 }
1762 if (address == ComboAddress("192.0.2.1:53")) {
1763 if (domain == DNSName("com.")) {
1764 setLWResult(res, 0, true, false, true);
1765 addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
1766 addRRSIG(keys, res->d_records, domain, 300);
1767 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
1768 addRRSIG(keys, res->d_records, domain, 300);
1769 }
1770 else {
1771 setLWResult(res, 0, false, false, true);
1772 addRecordToLW(res, "powerdns.com.", QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
1773 addDS(DNSName("powerdns.com."), 300, res->d_records, keys);
1774 addRRSIG(keys, res->d_records, DNSName("com."), 300);
1775 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
1776 }
1777 return LWResult::Result::Success;
1778 }
1779 if (address == ComboAddress("192.0.2.2:53")) {
1780 setLWResult(res, 0, true, false, true);
1781 if (type == QType::NS) {
1782 if (domain == DNSName("powerdns.com.")) {
1783 addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
1784 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
1785 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
1786 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
1787 }
1788 else {
1789 addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
1790 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
1791 addNSECRecordToLW(DNSName("www.powerdns.com."), DNSName("wwz.powerdns.com."), {QType::A, QType::NSEC, QType::RRSIG}, 600, res->d_records);
1792 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
1793 }
1794 }
1795 else {
1796 addRecordToLW(res, domain, QType::A, "192.0.2.42");
1797 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300, false, boost::none, DNSName("*.powerdns.com"));
1798 /* we need to add the proof that this name does not exist, so the wildcard may apply */
1799 /* first the closest encloser */
1800 addNSEC3UnhashedRecordToLW(DNSName("powerdns.com."), DNSName("powerdns.com."), "whatever", {QType::A, QType::TXT, QType::RRSIG, QType::NSEC}, 600, res->d_records);
1801 addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300);
1802 /* then the next closer */
1803 addNSEC3NarrowRecordToLW(DNSName("www.powerdns.com."), DNSName("powerdns.com."), {QType::A, QType::TXT, QType::RRSIG, QType::NSEC}, 600, res->d_records, g_maxNSEC3Iterations + 100);
1804 addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300);
1805 }
1806 return LWResult::Result::Success;
1807 }
1808 }
1809
1810 return LWResult::Result::Timeout;
1811 });
1812
1813 /* the NSEC3 providing the denial of existence proof for the next closer has too many iterations,
1814 we should end up Insecure */
1815 vector<DNSRecord> ret;
1816 int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
1817 BOOST_CHECK_EQUAL(res, RCode::NoError);
1818 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
1819 BOOST_REQUIRE_EQUAL(ret.size(), 6U);
1820 BOOST_CHECK_EQUAL(queriesCount, 6U);
1821
1822 /* again, to test the cache */
1823 ret.clear();
1824 res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
1825 BOOST_CHECK_EQUAL(res, RCode::NoError);
1826 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
1827 BOOST_REQUIRE_EQUAL(ret.size(), 6U);
1828 BOOST_CHECK_EQUAL(queriesCount, 6U);
1829 }
1830
1831 BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec_wildcard_missing)
1832 {
1833 std::unique_ptr<SyncRes> sr;
1834 initSR(sr, true);
1835
1836 setDNSSECValidation(sr, DNSSECMode::ValidateAll);
1837
1838 primeHints();
1839 const DNSName target("www.powerdns.com.");
1840 testkeysset_t keys;
1841
1842 auto luaconfsCopy = g_luaconfs.getCopy();
1843 luaconfsCopy.dsAnchors.clear();
1844 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
1845 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
1846 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
1847
1848 g_luaconfs.setState(luaconfsCopy);
1849
1850 size_t queriesCount = 0;
1851
1852 sr->setAsyncCallback([&](const ComboAddress& address, 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 */) {
1853 queriesCount++;
1854
1855 if (type == QType::DS || type == QType::DNSKEY) {
1856 if (type == QType::DS && domain == target) {
1857 setLWResult(res, RCode::NoError, true, false, true);
1858 addRecordToLW(res, DNSName("powerdns.com."), QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
1859 addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300);
1860 addNSECRecordToLW(DNSName("www.powerdns.com."), DNSName("wwz.powerdns.com."), {QType::A, QType::NSEC, QType::RRSIG}, 600, res->d_records);
1861 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
1862 return LWResult::Result::Success;
1863 }
1864 return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
1865 }
1866 {
1867 if (isRootServer(address)) {
1868 setLWResult(res, 0, false, false, true);
1869 addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
1870 addDS(DNSName("com."), 300, res->d_records, keys);
1871 addRRSIG(keys, res->d_records, DNSName("."), 300);
1872 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
1873 return LWResult::Result::Success;
1874 }
1875 if (address == ComboAddress("192.0.2.1:53")) {
1876 if (domain == DNSName("com.")) {
1877 setLWResult(res, 0, true, false, true);
1878 addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
1879 addRRSIG(keys, res->d_records, domain, 300);
1880 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
1881 addRRSIG(keys, res->d_records, domain, 300);
1882 }
1883 else {
1884 setLWResult(res, 0, false, false, true);
1885 addRecordToLW(res, "powerdns.com.", QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
1886 addDS(DNSName("powerdns.com."), 300, res->d_records, keys);
1887 addRRSIG(keys, res->d_records, DNSName("com."), 300);
1888 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
1889 }
1890 return LWResult::Result::Success;
1891 }
1892 if (address == ComboAddress("192.0.2.2:53")) {
1893 setLWResult(res, 0, true, false, true);
1894 if (type == QType::NS) {
1895 if (domain == DNSName("powerdns.com.")) {
1896 addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
1897 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
1898 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
1899 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
1900 }
1901 else {
1902 addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
1903 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
1904 addNSECRecordToLW(DNSName("www.powerdns.com."), DNSName("wwz.powerdns.com."), {QType::A, QType::NSEC, QType::RRSIG}, 600, res->d_records);
1905 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
1906 }
1907 }
1908 else {
1909 addRecordToLW(res, domain, QType::A, "192.0.2.42");
1910 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300, false, boost::none, DNSName("*.powerdns.com"));
1911 }
1912 return LWResult::Result::Success;
1913 }
1914 }
1915
1916 return LWResult::Result::Timeout;
1917 });
1918
1919 vector<DNSRecord> ret;
1920 int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
1921 BOOST_CHECK_EQUAL(res, RCode::NoError);
1922 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::BogusInvalidDenial);
1923 BOOST_REQUIRE_EQUAL(ret.size(), 2U);
1924 BOOST_CHECK_EQUAL(queriesCount, 7U);
1925
1926 /* again, to test the cache */
1927 ret.clear();
1928 res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
1929 BOOST_CHECK_EQUAL(res, RCode::NoError);
1930 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::BogusInvalidDenial);
1931 BOOST_REQUIRE_EQUAL(ret.size(), 2U);
1932 BOOST_CHECK_EQUAL(queriesCount, 7U);
1933 }
1934
1935 BOOST_AUTO_TEST_CASE(test_dnssec_validation_wildcard_expanded_onto_itself)
1936 {
1937 std::unique_ptr<SyncRes> sr;
1938 initSR(sr, true);
1939
1940 setDNSSECValidation(sr, DNSSECMode::ValidateAll);
1941
1942 primeHints();
1943 const DNSName target("*.powerdns.com.");
1944 testkeysset_t keys;
1945
1946 auto luaconfsCopy = g_luaconfs.getCopy();
1947 luaconfsCopy.dsAnchors.clear();
1948 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
1949 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
1950 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
1951
1952 g_luaconfs.setState(luaconfsCopy);
1953
1954 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 */) {
1955 if (type == QType::DS || type == QType::DNSKEY) {
1956 if (domain == target) {
1957 const auto auth = DNSName("powerdns.com.");
1958 /* we don't want a cut there */
1959 setLWResult(res, 0, true, false, true);
1960 addRecordToLW(res, auth, QType::SOA, "foo. bar. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
1961 addRRSIG(keys, res->d_records, auth, 300);
1962 /* add a NSEC denying the DS */
1963 std::set<uint16_t> types = {QType::NSEC};
1964 addNSECRecordToLW(domain, DNSName("z") + domain, types, 600, res->d_records);
1965 addRRSIG(keys, res->d_records, auth, 300);
1966 return LWResult::Result::Success;
1967 }
1968 return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
1969 }
1970 {
1971 setLWResult(res, 0, true, false, true);
1972 addRecordToLW(res, domain, QType::A, "192.0.2.42");
1973 addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300, false, boost::none, DNSName("*.powerdns.com"));
1974 /* we don't _really_ need to add the proof that the exact name does not exist because it does,
1975 it's the wildcard itself, but let's do it so other validators don't choke on it */
1976 addNSECRecordToLW(DNSName("*.powerdns.com."), DNSName("wwz.powerdns.com."), {QType::A, QType::NSEC, QType::RRSIG}, 600, res->d_records);
1977 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300, false, boost::none, DNSName("*.powerdns.com"));
1978 return LWResult::Result::Success;
1979 }
1980 });
1981
1982 vector<DNSRecord> ret;
1983 int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
1984 BOOST_CHECK_EQUAL(res, RCode::NoError);
1985 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
1986 /* A + RRSIG, NSEC + RRSIG */
1987 BOOST_REQUIRE_EQUAL(ret.size(), 4U);
1988 }
1989
1990 BOOST_AUTO_TEST_CASE(test_dnssec_validation_wildcard_expanded_onto_itself_nodata)
1991 {
1992 std::unique_ptr<SyncRes> sr;
1993 initSR(sr, true);
1994
1995 setDNSSECValidation(sr, DNSSECMode::ValidateAll);
1996
1997 primeHints();
1998 const DNSName target("*.powerdns.com.");
1999 testkeysset_t keys;
2000
2001 auto luaconfsCopy = g_luaconfs.getCopy();
2002 luaconfsCopy.dsAnchors.clear();
2003 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
2004 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
2005 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
2006
2007 g_luaconfs.setState(luaconfsCopy);
2008
2009 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 */) {
2010 if (type == QType::DS || type == QType::DNSKEY) {
2011 if (domain == target) {
2012 const auto auth = DNSName("powerdns.com.");
2013 /* we don't want a cut there */
2014 setLWResult(res, 0, true, false, true);
2015 addRecordToLW(res, auth, QType::SOA, "foo. bar. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
2016 addRRSIG(keys, res->d_records, auth, 300);
2017 /* add a NSEC denying the DS */
2018 std::set<uint16_t> types = {QType::NSEC};
2019 addNSECRecordToLW(domain, DNSName("z") + domain, types, 600, res->d_records);
2020 addRRSIG(keys, res->d_records, auth, 300);
2021 return LWResult::Result::Success;
2022 }
2023 return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
2024 }
2025 {
2026 setLWResult(res, 0, true, false, true);
2027 addRecordToLW(res, domain, QType::SOA, "powerdns.com. bar. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
2028 addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300);
2029 /* add the proof that the exact name does exist but that this type does not */
2030 addNSECRecordToLW(DNSName("*.powerdns.com."), DNSName("\\000.*.powerdns.com."), {QType::AAAA, QType::NSEC, QType::RRSIG}, 600, res->d_records);
2031 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300, false, boost::none, DNSName("*.powerdns.com"));
2032 return LWResult::Result::Success;
2033 }
2034 });
2035
2036 vector<DNSRecord> ret;
2037 int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2038 BOOST_CHECK_EQUAL(res, RCode::NoError);
2039 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
2040 /* SOA + RRSIG, NSEC + RRSIG */
2041 BOOST_REQUIRE_EQUAL(ret.size(), 4U);
2042 }
2043
2044 BOOST_AUTO_TEST_CASE(test_dnssec_validation_wildcard_like_expanded_from_wildcard)
2045 {
2046 std::unique_ptr<SyncRes> sr;
2047 initSR(sr, true);
2048
2049 setDNSSECValidation(sr, DNSSECMode::ValidateAll);
2050
2051 primeHints();
2052 const DNSName target("*.sub.powerdns.com.");
2053 testkeysset_t keys;
2054
2055 auto luaconfsCopy = g_luaconfs.getCopy();
2056 luaconfsCopy.dsAnchors.clear();
2057 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
2058 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
2059 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
2060
2061 g_luaconfs.setState(luaconfsCopy);
2062
2063 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 */) {
2064 if (type == QType::DS || type == QType::DNSKEY) {
2065 if (domain == target) {
2066 const auto auth = DNSName("powerdns.com.");
2067 /* we don't want a cut there */
2068 setLWResult(res, 0, true, false, true);
2069 addRecordToLW(res, auth, QType::SOA, "foo. bar. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
2070 addRRSIG(keys, res->d_records, auth, 300);
2071 addNSECRecordToLW(DNSName("*.powerdns.com."), DNSName("wwz.powerdns.com."), {QType::A, QType::NSEC, QType::RRSIG}, 600, res->d_records);
2072 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300, false, boost::none, DNSName("*.powerdns.com"));
2073 return LWResult::Result::Success;
2074 }
2075 if (domain == DNSName("sub.powerdns.com.")) {
2076 const auto auth = DNSName("powerdns.com.");
2077 /* we don't want a cut there */
2078 setLWResult(res, 0, true, false, true);
2079 addRecordToLW(res, auth, QType::SOA, "foo. bar. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
2080 addRRSIG(keys, res->d_records, auth, 300);
2081 /* add a NSEC denying the DS */
2082 addNSECRecordToLW(DNSName("*.powerdns.com."), DNSName("wwz.powerdns.com."), {QType::A, QType::NSEC, QType::RRSIG}, 600, res->d_records);
2083 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300, false, boost::none, DNSName("*.powerdns.com"));
2084 return LWResult::Result::Success;
2085 }
2086 return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
2087 }
2088 {
2089 setLWResult(res, 0, true, false, true);
2090 addRecordToLW(res, domain, QType::A, "192.0.2.42");
2091 addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300, false, boost::none, DNSName("*.powerdns.com"));
2092 addNSECRecordToLW(DNSName("*.powerdns.com."), DNSName("wwz.powerdns.com."), {QType::A, QType::NSEC, QType::RRSIG}, 600, res->d_records);
2093 addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300, false, boost::none, DNSName("*.powerdns.com"));
2094 return LWResult::Result::Success;
2095 }
2096 });
2097
2098 vector<DNSRecord> ret;
2099 int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2100 BOOST_CHECK_EQUAL(res, RCode::NoError);
2101 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
2102 /* A + RRSIG, NSEC + RRSIG */
2103 BOOST_REQUIRE_EQUAL(ret.size(), 4U);
2104 }
2105
2106 // Tests PR 8648
2107 BOOST_AUTO_TEST_CASE(test_dnssec_incomplete_cache_zonecut_qm)
2108 {
2109 std::unique_ptr<SyncRes> sr;
2110 initSR(sr, true, false);
2111 sr->setQNameMinimization();
2112 setDNSSECValidation(sr, DNSSECMode::ValidateAll);
2113
2114 primeHints();
2115 testkeysset_t keys;
2116
2117 auto luaconfsCopy = g_luaconfs.getCopy();
2118 luaconfsCopy.dsAnchors.clear();
2119 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
2120 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2121 generateKeyMaterial(DNSName("net."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2122 generateKeyMaterial(DNSName("herokuapp.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2123 generateKeyMaterial(DNSName("nsone.net."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2124 g_luaconfs.setState(luaconfsCopy);
2125
2126 size_t queriesCount = 0;
2127
2128 sr->setAsyncCallback([&](const ComboAddress& address, 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 */) {
2129 queriesCount++;
2130
2131 DNSName auth(domain);
2132 DNSName com("com.");
2133 DNSName net("net.");
2134 DNSName nsone("nsone.net.");
2135 DNSName hero("herokuapp.com.");
2136 DNSName p03nsone("dns1.p03.nsone.net.");
2137
2138 // cerr << ip.toString() << ": " << domain << '|' << QType(type).toString() << endl;
2139 if (type == QType::DS || type == QType::DNSKEY) {
2140 return genericDSAndDNSKEYHandler(res, domain, auth, type, keys);
2141 }
2142
2143 if (isRootServer(address)) {
2144 if (domain == com) {
2145 setLWResult(res, 0, false, false, true);
2146 addRecordToLW(res, com, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 3600);
2147 addDS(com, 300, res->d_records, keys);
2148 addRRSIG(keys, res->d_records, g_rootdnsname, 300);
2149 addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2150 }
2151 else if (domain == net) {
2152 setLWResult(res, 0, false, false, true);
2153 addRecordToLW(res, net, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 3600);
2154 addDS(net, 300, res->d_records, keys);
2155 addRRSIG(keys, res->d_records, g_rootdnsname, 300);
2156 addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2157 }
2158 else if (domain == p03nsone && type == QType::A) {
2159 setLWResult(res, 0, false, false, true);
2160 addRecordToLW(res, nsone, QType::NS, "dns1.p01.nsone.net.", DNSResourceRecord::AUTHORITY, 3600);
2161 addNSECRecordToLW(nsone, DNSName("zzz.nsone.net."), {QType::NS, QType::SOA, QType::RRSIG, QType::DNSKEY}, 600, res->d_records);
2162 addRRSIG(keys, res->d_records, net, 300);
2163 addRecordToLW(res, "dns1.p01.nsone.net", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
2164 }
2165 else {
2166 BOOST_ASSERT(0);
2167 }
2168 return LWResult::Result::Success;
2169 }
2170
2171 if (address == ComboAddress("192.0.2.1:53")) {
2172 if (domain == hero && type == QType::NS) {
2173 setLWResult(res, 0, false, false, true);
2174 addRecordToLW(res, hero, QType::NS, "dns1.p03.nsone.net.", DNSResourceRecord::AUTHORITY, 3600);
2175 addDS(hero, 300, res->d_records, keys);
2176 addRRSIG(keys, res->d_records, com, 300);
2177 }
2178 else if (domain == nsone && type == QType::A) {
2179 setLWResult(res, 0, false, false, true);
2180 addRecordToLW(res, nsone, QType::NS, "dns1.p01.nsone.net.", DNSResourceRecord::AUTHORITY, 3600);
2181 addNSECRecordToLW(nsone, DNSName("zzz.nsone.net."), {QType::NS, QType::SOA, QType::RRSIG, QType::DNSKEY}, 600, res->d_records);
2182 addRRSIG(keys, res->d_records, net, 300);
2183 addRecordToLW(res, "dns1.p01.nsone.net", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
2184 }
2185 else {
2186 BOOST_ASSERT(0);
2187 }
2188 return LWResult::Result::Success;
2189 }
2190 if (address == ComboAddress("192.0.2.2:53")) {
2191 DNSName p01("p01.nsone.net.");
2192 DNSName p03("p03.nsone.net.");
2193 DNSName p01nsone("dns1.p01.nsone.net.");
2194 if (domain == hero && type == QType::NS) {
2195 setLWResult(res, 0, true, false, true);
2196 addRecordToLW(res, hero, QType::NS, "dns1.p03.nsone.net.", DNSResourceRecord::ANSWER, 3600);
2197 addRRSIG(keys, res->d_records, hero, 300);
2198 }
2199 else if (domain == p01nsone && type == QType::A) {
2200 setLWResult(res, 0, true, false, true);
2201 addRecordToLW(res, p01nsone, QType::A, "192.0.2.2", DNSResourceRecord::ANSWER, 3600);
2202 }
2203 else if (domain == p03nsone && type == QType::A) {
2204 setLWResult(res, 0, true, false, true);
2205 addRecordToLW(res, p03nsone, QType::A, "192.0.2.2", DNSResourceRecord::ANSWER, 3600);
2206 }
2207 else if (domain == p01 && type == QType::A) {
2208 setLWResult(res, 0, true, false, true);
2209 addRecordToLW(res, p01, QType::SOA, "dns1.p01.nsone.net. hostmaster.nsone.net. 123 43200 7200 1209600 10800", DNSResourceRecord::AUTHORITY, 3600);
2210 }
2211 else if (domain == p03 && type == QType::A) {
2212 setLWResult(res, 0, true, false, true);
2213 addRecordToLW(res, p03, QType::SOA, "dns1.p03.nsone.net. hostmaster.nsone.net. 123 43200 7200 1209600 10800", DNSResourceRecord::AUTHORITY, 3600);
2214 }
2215 else {
2216 BOOST_ASSERT(0);
2217 }
2218 return LWResult::Result::Success;
2219 }
2220 BOOST_ASSERT(0);
2221 return LWResult::Result::Timeout;
2222 });
2223
2224 vector<DNSRecord> ret;
2225 int res = sr->beginResolve(DNSName("herokuapp.com."), QType(QType::NS), QClass::IN, ret);
2226 BOOST_CHECK_EQUAL(res, RCode::NoError);
2227 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
2228 BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2229 BOOST_CHECK_EQUAL(queriesCount, 8U);
2230
2231 ret.clear();
2232 res = sr->beginResolve(DNSName("dns1.p03.nsone.net."), QType(QType::A), QClass::IN, ret);
2233 BOOST_CHECK_EQUAL(res, RCode::NoError);
2234 BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
2235 BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2236 BOOST_CHECK_EQUAL(queriesCount, 14U);
2237 }
2238
2239 BOOST_AUTO_TEST_CASE(test_dnssec_secure_servfail_ds)
2240 {
2241 std::unique_ptr<SyncRes> sr;
2242 initSR(sr, true);
2243
2244 setDNSSECValidation(sr, DNSSECMode::ValidateAll);
2245
2246 primeHints();
2247 const DNSName target("powerdns.com.");
2248 const ComboAddress targetAddr("192.0.2.42");
2249 testkeysset_t keys;
2250
2251 auto luaconfsCopy = g_luaconfs.getCopy();
2252 luaconfsCopy.dsAnchors.clear();
2253 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
2254 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2255 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2256
2257 g_luaconfs.setState(luaconfsCopy);
2258
2259 size_t queriesCount = 0;
2260
2261 /* make sure that the signature inception and validity times are computed
2262 based on the SyncRes time, not the current one, in case the function
2263 takes too long. */
2264
2265 const time_t fixedNow = sr->getNow().tv_sec;
2266
2267 sr->setAsyncCallback([&](const ComboAddress& address, 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 */) {
2268 queriesCount++;
2269
2270 DNSName auth = domain;
2271 if (domain == target) {
2272 auth = DNSName("powerdns.com.");
2273 }
2274
2275 if (type == QType::DS && domain == DNSName("powerdns.com.")) {
2276 /* time out */
2277 return LWResult::Result::Timeout;
2278 }
2279
2280 if (type == QType::DS || type == QType::DNSKEY) {
2281 return genericDSAndDNSKEYHandler(res, domain, auth, type, keys, true, fixedNow);
2282 }
2283
2284 if (isRootServer(address)) {
2285 setLWResult(res, 0, false, false, true);
2286 addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
2287 addDS(DNSName("com."), 300, res->d_records, keys);
2288 addRRSIG(keys, res->d_records, DNSName("."), 300, false, boost::none, boost::none, fixedNow);
2289 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2290 return LWResult::Result::Success;
2291 }
2292
2293 if (address == ComboAddress("192.0.2.1:53")) {
2294 if (domain == DNSName("com.")) {
2295 setLWResult(res, 0, true, false, true);
2296 addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
2297 addRRSIG(keys, res->d_records, domain, 300, false, boost::none, boost::none, fixedNow);
2298 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2299 addRRSIG(keys, res->d_records, domain, 300);
2300 }
2301 else {
2302 setLWResult(res, 0, false, false, true);
2303 addRecordToLW(res, auth, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
2304 /* do NOT include the DS here */
2305 //addDS(auth, 300, res->d_records, keys);
2306 //addRRSIG(keys, res->d_records, DNSName("com."), 300, false, boost::none, boost::none, fixedNow);
2307 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
2308 }
2309 return LWResult::Result::Success;
2310 }
2311
2312 if (address == ComboAddress("192.0.2.2:53")) {
2313 if (type == QType::NS) {
2314 setLWResult(res, 0, true, false, true);
2315 addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
2316 addRRSIG(keys, res->d_records, auth, 300, false, boost::none, boost::none, fixedNow);
2317 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
2318 addRRSIG(keys, res->d_records, auth, 300);
2319 }
2320 else {
2321 setLWResult(res, RCode::NoError, true, false, true);
2322 addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
2323 addRRSIG(keys, res->d_records, auth, 300, false, boost::none, boost::none, fixedNow);
2324 }
2325 return LWResult::Result::Success;
2326 }
2327
2328 return LWResult::Result::Timeout;
2329 });
2330
2331 vector<DNSRecord> ret;
2332 try {
2333 sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2334 BOOST_CHECK(false);
2335 }
2336 catch (const ImmediateServFailException& e) {
2337 BOOST_CHECK(e.reason.find("Server Failure while retrieving DS records for powerdns.com") != string::npos);
2338 }
2339
2340 /* and a second time to check nothing was cached */
2341 try {
2342 sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2343 BOOST_CHECK(false);
2344 }
2345 catch (const ImmediateServFailException& e) {
2346 BOOST_CHECK(e.reason.find("Server Failure while retrieving DS records for powerdns.com") != string::npos);
2347 }
2348 }
2349
2350 static void dnssec_secure_servfail_dnskey(DNSSECMode mode, vState /* expectedValidationResult */)
2351 {
2352 std::unique_ptr<SyncRes> sr;
2353 initSR(sr, true);
2354
2355 setDNSSECValidation(sr, mode);
2356
2357 primeHints();
2358 const DNSName target("powerdns.com.");
2359 const ComboAddress targetAddr("192.0.2.42");
2360 testkeysset_t keys;
2361
2362 auto luaconfsCopy = g_luaconfs.getCopy();
2363 luaconfsCopy.dsAnchors.clear();
2364 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
2365 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2366 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2367
2368 g_luaconfs.setState(luaconfsCopy);
2369
2370 size_t queriesCount = 0;
2371
2372 /* make sure that the signature inception and validity times are computed
2373 based on the SyncRes time, not the current one, in case the function
2374 takes too long. */
2375
2376 const time_t fixedNow = sr->getNow().tv_sec;
2377
2378 sr->setAsyncCallback([&](const ComboAddress& address, 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 */) {
2379 queriesCount++;
2380
2381 DNSName auth = domain;
2382 if (domain == target) {
2383 auth = DNSName("powerdns.com.");
2384 }
2385
2386 if (type == QType::DNSKEY && domain == DNSName("powerdns.com.")) {
2387 /* time out */
2388 return LWResult::Result::Timeout;
2389 }
2390
2391 if (type == QType::DS || type == QType::DNSKEY) {
2392 return genericDSAndDNSKEYHandler(res, domain, auth, type, keys, true, fixedNow);
2393 }
2394
2395 if (isRootServer(address)) {
2396 setLWResult(res, 0, false, false, true);
2397 addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
2398 addDS(DNSName("com."), 300, res->d_records, keys);
2399 addRRSIG(keys, res->d_records, DNSName("."), 300, false, boost::none, boost::none, fixedNow);
2400 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2401 return LWResult::Result::Success;
2402 }
2403
2404 if (address == ComboAddress("192.0.2.1:53")) {
2405 if (domain == DNSName("com.")) {
2406 setLWResult(res, 0, true, false, true);
2407 addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
2408 addRRSIG(keys, res->d_records, domain, 300, false, boost::none, boost::none, fixedNow);
2409 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2410 addRRSIG(keys, res->d_records, domain, 300);
2411 }
2412 else {
2413 setLWResult(res, 0, false, false, true);
2414 addRecordToLW(res, auth, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
2415 addDS(auth, 300, res->d_records, keys);
2416 addRRSIG(keys, res->d_records, DNSName("com."), 300, false, boost::none, boost::none, fixedNow);
2417 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
2418 }
2419 return LWResult::Result::Success;
2420 }
2421
2422 if (address == ComboAddress("192.0.2.2:53")) {
2423 if (type == QType::NS) {
2424 setLWResult(res, 0, true, false, true);
2425 addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
2426 addRRSIG(keys, res->d_records, auth, 300, false, boost::none, boost::none, fixedNow);
2427 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
2428 addRRSIG(keys, res->d_records, auth, 300);
2429 }
2430 else {
2431 setLWResult(res, RCode::NoError, true, false, true);
2432 addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
2433 addRRSIG(keys, res->d_records, auth, 300, false, boost::none, boost::none, fixedNow);
2434 }
2435 return LWResult::Result::Success;
2436 }
2437
2438 return LWResult::Result::Timeout;
2439 });
2440
2441 vector<DNSRecord> ret;
2442 try {
2443 sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2444 BOOST_CHECK(false);
2445 }
2446 catch (const ImmediateServFailException& e) {
2447 BOOST_CHECK(e.reason.find("Server Failure while retrieving DNSKEY records for powerdns.com") != string::npos);
2448 }
2449
2450 /* and a second time to check nothing was cached */
2451 try {
2452 sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2453 BOOST_CHECK(false);
2454 }
2455 catch (const ImmediateServFailException& e) {
2456 BOOST_CHECK(e.reason.find("Server Failure while retrieving DNSKEY records for powerdns.com") != string::npos);
2457 }
2458 }
2459
2460 BOOST_AUTO_TEST_CASE(test_dnssec_secure_servfail_dnskey)
2461 {
2462 dnssec_secure_servfail_dnskey(DNSSECMode::ValidateAll, vState::Indeterminate);
2463 dnssec_secure_servfail_dnskey(DNSSECMode::Off, vState::Indeterminate);
2464 }
2465
2466 // Same test as above but powerdns.com is now Insecure according to parent, so failure to retrieve DNSSKEYs
2467 // should be mostly harmless.
2468 static void dnssec_secure_servfail_dnskey_insecure(DNSSECMode mode, vState expectedValidationResult)
2469 {
2470 std::unique_ptr<SyncRes> sr;
2471 initSR(sr, true);
2472
2473 setDNSSECValidation(sr, mode);
2474
2475 primeHints();
2476 const DNSName target("powerdns.com.");
2477 const ComboAddress targetAddr("192.0.2.42");
2478
2479 // We use two sets of keys, as powerdns.com is Insecure according to parent but returns signed results,
2480 // triggering a (failing) DNSKEY retrieval.
2481 testkeysset_t keys;
2482 testkeysset_t pdnskeys;
2483
2484 auto luaconfsCopy = g_luaconfs.getCopy();
2485 luaconfsCopy.dsAnchors.clear();
2486 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
2487 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2488 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, pdnskeys);
2489
2490 g_luaconfs.setState(luaconfsCopy);
2491
2492 size_t queriesCount = 0;
2493
2494 /* make sure that the signature inception and validity times are computed
2495 based on the SyncRes time, not the current one, in case the function
2496 takes too long. */
2497
2498 const time_t fixedNow = sr->getNow().tv_sec;
2499
2500 sr->setAsyncCallback([&](const ComboAddress& address, 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 */) {
2501 queriesCount++;
2502
2503 DNSName auth = domain;
2504 if (domain == target) {
2505 auth = DNSName("powerdns.com.");
2506 }
2507
2508 if (type == QType::DNSKEY && domain == DNSName("powerdns.com.")) {
2509 /* time out */
2510 return LWResult::Result::Timeout;
2511 }
2512
2513 if (type == QType::DS || type == QType::DNSKEY) {
2514 // This one does not know about pdnskeys, so it will declare powerdns.com as Insecure
2515 return genericDSAndDNSKEYHandler(res, domain, auth, type, keys, true, fixedNow);
2516 }
2517
2518 if (isRootServer(address)) {
2519 setLWResult(res, 0, false, false, true);
2520 addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
2521 addDS(DNSName("com."), 300, res->d_records, keys);
2522 addRRSIG(keys, res->d_records, DNSName("."), 300, false, boost::none, boost::none, fixedNow);
2523 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2524 return LWResult::Result::Success;
2525 }
2526
2527 if (address == ComboAddress("192.0.2.1:53")) {
2528 if (domain == DNSName("com.")) {
2529 setLWResult(res, 0, true, false, true);
2530 addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
2531 addRRSIG(keys, res->d_records, domain, 300, false, boost::none, boost::none, fixedNow);
2532 addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2533 addRRSIG(keys, res->d_records, domain, 300);
2534 }
2535 else {
2536 setLWResult(res, 0, false, false, true);
2537 addRecordToLW(res, auth, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
2538 addDS(auth, 300, res->d_records, keys);
2539 addRRSIG(keys, res->d_records, DNSName("com."), 300, false, boost::none, boost::none, fixedNow);
2540 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
2541 }
2542 return LWResult::Result::Success;
2543 }
2544
2545 if (address == ComboAddress("192.0.2.2:53")) {
2546 if (type == QType::NS) {
2547 setLWResult(res, 0, true, false, true);
2548 addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
2549 addRRSIG(pdnskeys, res->d_records, auth, 300, false, boost::none, boost::none, fixedNow);
2550 addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
2551 addRRSIG(pdnskeys, res->d_records, auth, 300);
2552 }
2553 else {
2554 setLWResult(res, RCode::NoError, true, false, true);
2555 addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
2556 addRRSIG(pdnskeys, res->d_records, auth, 300, false, boost::none, boost::none, fixedNow);
2557 }
2558 return LWResult::Result::Success;
2559 }
2560
2561 return LWResult::Result::Timeout;
2562 });
2563
2564 vector<DNSRecord> ret;
2565 auto res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2566 BOOST_CHECK_EQUAL(res, 0);
2567 BOOST_CHECK_EQUAL(sr->getValidationState(), expectedValidationResult);
2568 BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2569 }
2570
2571 BOOST_AUTO_TEST_CASE(test_dnssec_secure_servfail_dnskey_insecure)
2572 {
2573 dnssec_secure_servfail_dnskey_insecure(DNSSECMode::ValidateAll, vState::Insecure);
2574 dnssec_secure_servfail_dnskey_insecure(DNSSECMode::Off, vState::Insecure);
2575 }
2576
2577 BOOST_AUTO_TEST_SUITE_END()