]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/recursordist/test-syncres_cc8.cc
Merge pull request #9237 from rgacogne/rec-nxd-ent-denial
[thirdparty/pdns.git] / pdns / recursordist / test-syncres_cc8.cc
1 #define BOOST_TEST_DYN_LINK
2 #include <boost/test/unit_test.hpp>
3
4 #include "test-syncres_cc.hh"
5
6 BOOST_AUTO_TEST_SUITE(syncres_cc8)
7
8 BOOST_AUTO_TEST_CASE(test_nsec_denial_nowrap)
9 {
10 initSR();
11
12 testkeysset_t keys;
13 generateKeyMaterial(DNSName("example.org."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
14
15 vector<DNSRecord> records;
16
17 sortedRecords_t recordContents;
18 vector<shared_ptr<RRSIGRecordContent>> signatureContents;
19
20 /*
21 No wrap test case:
22 a.example.org. -> d.example.org. denies the existence of b.example.org.
23 */
24 addNSECRecordToLW(DNSName("a.example.org."), DNSName("d.example.org"), {QType::A, QType::TXT, QType::RRSIG, QType::NSEC}, 600, records);
25 recordContents.insert(records.at(0).d_content);
26 addRRSIG(keys, records, DNSName("example.org."), 300);
27 signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
28 records.clear();
29
30 ContentSigPair pair;
31 pair.records = recordContents;
32 pair.signatures = signatureContents;
33 cspmap_t denialMap;
34 denialMap[std::make_pair(DNSName("a.example.org."), QType::NSEC)] = pair;
35
36 /* add wildcard denial */
37 recordContents.clear();
38 signatureContents.clear();
39 addNSECRecordToLW(DNSName("example.org."), DNSName("+.example.org"), {QType::A, QType::TXT, QType::RRSIG, QType::NSEC}, 600, records);
40 recordContents.insert(records.at(0).d_content);
41 addRRSIG(keys, records, DNSName("example.org."), 300);
42 signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
43 records.clear();
44
45 pair.records = recordContents;
46 pair.signatures = signatureContents;
47 denialMap[std::make_pair(DNSName("example.org."), QType::NSEC)] = pair;
48
49 dState denialState = getDenial(denialMap, DNSName("b.example.org."), QType::A, false, false);
50 BOOST_CHECK_EQUAL(denialState, NXDOMAIN);
51
52 denialState = getDenial(denialMap, DNSName("d.example.org."), QType::A, false, false);
53 /* let's check that d.example.org. is not denied by this proof */
54 BOOST_CHECK_EQUAL(denialState, NODATA);
55 }
56
57 BOOST_AUTO_TEST_CASE(test_nsec_denial_wrap_case_1)
58 {
59 initSR();
60
61 testkeysset_t keys;
62 generateKeyMaterial(DNSName("example.org."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
63
64 vector<DNSRecord> records;
65
66 sortedRecords_t recordContents;
67 vector<shared_ptr<RRSIGRecordContent>> signatureContents;
68
69 /*
70 Wrap case 1 test case:
71 z.example.org. -> b.example.org. denies the existence of a.example.org.
72 */
73 addNSECRecordToLW(DNSName("z.example.org."), DNSName("b.example.org"), {QType::A, QType::TXT, QType::RRSIG, QType::NSEC}, 600, records);
74 recordContents.insert(records.at(0).d_content);
75 addRRSIG(keys, records, DNSName("example.org."), 300);
76 signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
77 records.clear();
78
79 ContentSigPair pair;
80 pair.records = recordContents;
81 pair.signatures = signatureContents;
82 cspmap_t denialMap;
83 denialMap[std::make_pair(DNSName("z.example.org."), QType::NSEC)] = pair;
84
85 dState denialState = getDenial(denialMap, DNSName("a.example.org."), QType::A, false, false);
86 BOOST_CHECK_EQUAL(denialState, NXDOMAIN);
87
88 denialState = getDenial(denialMap, DNSName("d.example.org."), QType::A, false, false);
89 /* let's check that d.example.org. is not denied by this proof */
90 BOOST_CHECK_EQUAL(denialState, NODATA);
91 }
92
93 BOOST_AUTO_TEST_CASE(test_nsec_denial_wrap_case_2)
94 {
95 initSR();
96
97 testkeysset_t keys;
98 generateKeyMaterial(DNSName("example.org."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
99
100 vector<DNSRecord> records;
101
102 sortedRecords_t recordContents;
103 vector<shared_ptr<RRSIGRecordContent>> signatureContents;
104
105 /*
106 Wrap case 2 test case:
107 y.example.org. -> a.example.org. denies the existence of z.example.org.
108 */
109 addNSECRecordToLW(DNSName("y.example.org."), DNSName("a.example.org"), {QType::A, QType::TXT, QType::RRSIG, QType::NSEC}, 600, records);
110 recordContents.insert(records.at(0).d_content);
111 addRRSIG(keys, records, DNSName("example.org."), 300);
112 signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
113 records.clear();
114
115 ContentSigPair pair;
116 pair.records = recordContents;
117 pair.signatures = signatureContents;
118 cspmap_t denialMap;
119 denialMap[std::make_pair(DNSName("y.example.org."), QType::NSEC)] = pair;
120
121 dState denialState = getDenial(denialMap, DNSName("z.example.org."), QType::A, false, false);
122 BOOST_CHECK_EQUAL(denialState, NXDOMAIN);
123
124 denialState = getDenial(denialMap, DNSName("d.example.org."), QType::A, false, false);
125 /* let's check that d.example.org. is not denied by this proof */
126 BOOST_CHECK_EQUAL(denialState, NODATA);
127 }
128
129 BOOST_AUTO_TEST_CASE(test_nsec_denial_only_one_nsec)
130 {
131 initSR();
132
133 testkeysset_t keys;
134 generateKeyMaterial(DNSName("example.org."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
135
136 vector<DNSRecord> records;
137
138 sortedRecords_t recordContents;
139 vector<shared_ptr<RRSIGRecordContent>> signatureContents;
140
141 /*
142 Only one NSEC in the whole zone test case:
143 a.example.org. -> a.example.org. denies the existence of b.example.org.
144 */
145 addNSECRecordToLW(DNSName("a.example.org."), DNSName("a.example.org"), {QType::A, QType::TXT, QType::RRSIG, QType::NSEC}, 600, records);
146 recordContents.insert(records.at(0).d_content);
147 addRRSIG(keys, records, DNSName("example.org."), 300);
148 signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
149 records.clear();
150
151 ContentSigPair pair;
152 pair.records = recordContents;
153 pair.signatures = signatureContents;
154 cspmap_t denialMap;
155 denialMap[std::make_pair(DNSName("a.example.org."), QType::NSEC)] = pair;
156
157 dState denialState = getDenial(denialMap, DNSName("b.example.org."), QType::A, false, false);
158 BOOST_CHECK_EQUAL(denialState, NXDOMAIN);
159
160 denialState = getDenial(denialMap, DNSName("a.example.org."), QType::A, false, false);
161 /* let's check that d.example.org. is not denied by this proof */
162 BOOST_CHECK_EQUAL(denialState, NODATA);
163 }
164
165 BOOST_AUTO_TEST_CASE(test_nsec_root_nxd_denial)
166 {
167 initSR();
168
169 testkeysset_t keys;
170 generateKeyMaterial(DNSName("."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
171
172 vector<DNSRecord> records;
173
174 sortedRecords_t recordContents;
175 vector<shared_ptr<RRSIGRecordContent>> signatureContents;
176
177 /*
178 The RRSIG from "." denies the existence of anything between a. and c.,
179 including b.
180 */
181 addNSECRecordToLW(DNSName("a."), DNSName("c."), {QType::NS}, 600, records);
182 recordContents.insert(records.at(0).d_content);
183 addRRSIG(keys, records, DNSName("."), 300);
184 signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
185 records.clear();
186
187 ContentSigPair pair;
188 pair.records = recordContents;
189 pair.signatures = signatureContents;
190 cspmap_t denialMap;
191 denialMap[std::make_pair(DNSName("a."), QType::NSEC)] = pair;
192
193 /* add wildcard denial */
194 recordContents.clear();
195 signatureContents.clear();
196 addNSECRecordToLW(DNSName("."), DNSName("+"), {QType::A, QType::TXT, QType::RRSIG, QType::NSEC}, 600, records);
197 recordContents.insert(records.at(0).d_content);
198 addRRSIG(keys, records, DNSName("."), 300);
199 signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
200 records.clear();
201
202 pair.records = recordContents;
203 pair.signatures = signatureContents;
204 denialMap[std::make_pair(DNSName("."), QType::NSEC)] = pair;
205
206 dState denialState = getDenial(denialMap, DNSName("b."), QType::A, false, false);
207 BOOST_CHECK_EQUAL(denialState, NXDOMAIN);
208 }
209
210 BOOST_AUTO_TEST_CASE(test_nsec_ancestor_nxqtype_denial)
211 {
212 initSR();
213
214 testkeysset_t keys;
215 generateKeyMaterial(DNSName("."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
216
217 vector<DNSRecord> records;
218
219 sortedRecords_t recordContents;
220 vector<shared_ptr<RRSIGRecordContent>> signatureContents;
221
222 /*
223 The RRSIG from "." denies the existence of any type except NS at a.
224 However since it's an ancestor delegation NSEC (NS bit set, SOA bit clear,
225 signer field that is shorter than the owner name of the NSEC RR) it can't
226 be used to deny anything except the whole name or a DS.
227 */
228 addNSECRecordToLW(DNSName("a."), DNSName("b."), {QType::NS}, 600, records);
229 recordContents.insert(records.at(0).d_content);
230 addRRSIG(keys, records, DNSName("."), 300);
231 signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
232 records.clear();
233
234 ContentSigPair pair;
235 pair.records = recordContents;
236 pair.signatures = signatureContents;
237 cspmap_t denialMap;
238 denialMap[std::make_pair(DNSName("a."), QType::NSEC)] = pair;
239
240 /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
241 Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
242 nonexistence of any RRs below that zone cut, which include all RRs at
243 that (original) owner name other than DS RRs, and all RRs below that
244 owner name regardless of type.
245 */
246
247 dState denialState = getDenial(denialMap, DNSName("a."), QType::A, false, false);
248 /* no data means the qname/qtype is not denied, because an ancestor
249 delegation NSEC can only deny the DS */
250 BOOST_CHECK_EQUAL(denialState, NODATA);
251
252 /* it can not be used to deny any RRs below that owner name either */
253 denialState = getDenial(denialMap, DNSName("sub.a."), QType::A, false, false);
254 BOOST_CHECK_EQUAL(denialState, NODATA);
255
256 denialState = getDenial(denialMap, DNSName("a."), QType::DS, true, true);
257 BOOST_CHECK_EQUAL(denialState, NXQTYPE);
258 }
259
260 BOOST_AUTO_TEST_CASE(test_nsec_insecure_delegation_denial)
261 {
262 initSR();
263
264 testkeysset_t keys;
265 generateKeyMaterial(DNSName("."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
266
267 vector<DNSRecord> records;
268
269 sortedRecords_t recordContents;
270 vector<shared_ptr<RRSIGRecordContent>> signatureContents;
271
272 /*
273 * RFC 5155 section 8.9:
274 * If there is an NSEC3 RR present in the response that matches the
275 * delegation name, then the validator MUST ensure that the NS bit is
276 * set and that the DS bit is not set in the Type Bit Maps field of the
277 * NSEC3 RR.
278 */
279 /*
280 The RRSIG from "." denies the existence of any type at a.
281 NS should be set if it was proving an insecure delegation, let's check that
282 we correctly detect that it's not.
283 */
284 addNSECRecordToLW(DNSName("a."), DNSName("b."), {}, 600, records);
285 recordContents.insert(records.at(0).d_content);
286 addRRSIG(keys, records, DNSName("."), 300);
287 signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
288 records.clear();
289
290 ContentSigPair pair;
291 pair.records = recordContents;
292 pair.signatures = signatureContents;
293 cspmap_t denialMap;
294 denialMap[std::make_pair(DNSName("a."), QType::NSEC)] = pair;
295
296 /* Insecure because the NS is not set, so while it does
297 denies the DS, it can't prove an insecure delegation */
298 dState denialState = getDenial(denialMap, DNSName("a."), QType::DS, true, true);
299 BOOST_CHECK_EQUAL(denialState, NODATA);
300 }
301
302 BOOST_AUTO_TEST_CASE(test_nsec_nxqtype_cname)
303 {
304 initSR();
305
306 testkeysset_t keys;
307 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
308
309 vector<DNSRecord> records;
310
311 sortedRecords_t recordContents;
312 vector<shared_ptr<RRSIGRecordContent>> signatureContents;
313
314 addNSECRecordToLW(DNSName("a.powerdns.com."), DNSName("a.c.powerdns.com."), {QType::CNAME}, 600, records);
315 recordContents.insert(records.at(0).d_content);
316 addRRSIG(keys, records, DNSName("powerdns.com."), 300);
317 signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
318 records.clear();
319
320 ContentSigPair pair;
321 pair.records = recordContents;
322 pair.signatures = signatureContents;
323 cspmap_t denialMap;
324 denialMap[std::make_pair(DNSName("a.powerdns.com."), QType::NSEC)] = pair;
325
326 /* this NSEC is not valid to deny a.powerdns.com|A since it states that a CNAME exists */
327 dState denialState = getDenial(denialMap, DNSName("a.powerdns.com."), QType::A, true, true);
328 BOOST_CHECK_EQUAL(denialState, NODATA);
329 }
330
331 BOOST_AUTO_TEST_CASE(test_nsec3_nxqtype_cname)
332 {
333 initSR();
334
335 testkeysset_t keys;
336 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
337
338 vector<DNSRecord> records;
339
340 sortedRecords_t recordContents;
341 vector<shared_ptr<RRSIGRecordContent>> signatureContents;
342
343 addNSEC3UnhashedRecordToLW(DNSName("a.powerdns.com."), DNSName("powerdns.com."), "whatever", {QType::CNAME}, 600, records);
344 recordContents.insert(records.at(0).d_content);
345 addRRSIG(keys, records, DNSName("powerdns.com."), 300);
346 signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
347
348 ContentSigPair pair;
349 pair.records = recordContents;
350 pair.signatures = signatureContents;
351 cspmap_t denialMap;
352 denialMap[std::make_pair(records.at(0).d_name, records.at(0).d_type)] = pair;
353 records.clear();
354
355 /* this NSEC3 is not valid to deny a.powerdns.com|A since it states that a CNAME exists */
356 dState denialState = getDenial(denialMap, DNSName("a.powerdns.com."), QType::A, false, true);
357 BOOST_CHECK_EQUAL(denialState, NODATA);
358 }
359
360 BOOST_AUTO_TEST_CASE(test_nsec_nxdomain_denial_missing_wildcard)
361 {
362 initSR();
363
364 testkeysset_t keys;
365 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
366
367 vector<DNSRecord> records;
368
369 sortedRecords_t recordContents;
370 vector<shared_ptr<RRSIGRecordContent>> signatureContents;
371
372 addNSECRecordToLW(DNSName("a.powerdns.com."), DNSName("d.powerdns.com"), {QType::A, QType::TXT, QType::RRSIG, QType::NSEC}, 600, records);
373 recordContents.insert(records.at(0).d_content);
374 addRRSIG(keys, records, DNSName("powerdns.com."), 300);
375 signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
376 records.clear();
377
378 ContentSigPair pair;
379 pair.records = recordContents;
380 pair.signatures = signatureContents;
381 cspmap_t denialMap;
382 denialMap[std::make_pair(DNSName("a.powerdns.com."), QType::NSEC)] = pair;
383
384 dState denialState = getDenial(denialMap, DNSName("b.powerdns.com."), QType::A, false, false);
385 BOOST_CHECK_EQUAL(denialState, NODATA);
386 }
387
388 BOOST_AUTO_TEST_CASE(test_nsec3_nxdomain_denial_missing_wildcard)
389 {
390 initSR();
391
392 testkeysset_t keys;
393 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
394
395 vector<DNSRecord> records;
396
397 sortedRecords_t recordContents;
398 vector<shared_ptr<RRSIGRecordContent>> signatureContents;
399
400 addNSEC3NarrowRecordToLW(DNSName("a.powerdns.com."), DNSName("powerdns.com."), {QType::A, QType::TXT, QType::RRSIG, QType::NSEC}, 600, records);
401 recordContents.insert(records.at(0).d_content);
402 addRRSIG(keys, records, DNSName("powerdns.com."), 300);
403 signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
404
405 ContentSigPair pair;
406 pair.records = recordContents;
407 pair.signatures = signatureContents;
408 cspmap_t denialMap;
409 denialMap[std::make_pair(records.at(0).d_name, records.at(0).d_type)] = pair;
410
411 /* Add NSEC3 for the closest encloser */
412 recordContents.clear();
413 signatureContents.clear();
414 records.clear();
415 addNSEC3UnhashedRecordToLW(DNSName("powerdns.com."), DNSName("powerdns.com."), "whatever", {QType::A, QType::TXT, QType::RRSIG, QType::NSEC}, 600, records);
416 recordContents.insert(records.at(0).d_content);
417 addRRSIG(keys, records, DNSName("powerdns.com."), 300);
418 signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
419
420 pair.records = recordContents;
421 pair.signatures = signatureContents;
422 denialMap[std::make_pair(records.at(0).d_name, records.at(0).d_type)] = pair;
423
424 dState denialState = getDenial(denialMap, DNSName("b.powerdns.com."), QType::A, false, false);
425 BOOST_CHECK_EQUAL(denialState, NODATA);
426 }
427
428 BOOST_AUTO_TEST_CASE(test_nsec_ent_denial)
429 {
430 initSR();
431
432 testkeysset_t keys;
433 generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
434
435 vector<DNSRecord> records;
436
437 sortedRecords_t recordContents;
438 vector<shared_ptr<RRSIGRecordContent>> signatureContents;
439
440 addNSECRecordToLW(DNSName("a.powerdns.com."), DNSName("a.c.powerdns.com."), {QType::A}, 600, records);
441 recordContents.insert(records.at(0).d_content);
442 addRRSIG(keys, records, DNSName("powerdns.com."), 300);
443 signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
444 records.clear();
445
446 ContentSigPair pair;
447 pair.records = recordContents;
448 pair.signatures = signatureContents;
449 cspmap_t denialMap;
450 denialMap[std::make_pair(DNSName("a.powerdns.com."), QType::NSEC)] = pair;
451
452 /* this NSEC is valid to prove a NXQTYPE at c.powerdns.com because it proves that
453 it is an ENT */
454 dState denialState = getDenial(denialMap, DNSName("c.powerdns.com."), QType::AAAA, true, true);
455 BOOST_CHECK_EQUAL(denialState, NXQTYPE);
456
457 /* this NSEC is not valid to prove a NXQTYPE at b.powerdns.com,
458 it could prove a NXDOMAIN if it had an additional wildcard denial */
459 denialState = getDenial(denialMap, DNSName("b.powerdns.com."), QType::AAAA, true, true);
460 BOOST_CHECK_EQUAL(denialState, NODATA);
461
462 /* this NSEC is not valid to prove a NXQTYPE for QType::A at a.c.powerdns.com either */
463 denialState = getDenial(denialMap, DNSName("a.c.powerdns.com."), QType::A, true, true);
464 BOOST_CHECK_EQUAL(denialState, NODATA);
465
466 /* if we add the wildcard denial proof, we should get a NXDOMAIN proof for b.powerdns.com */
467 recordContents.clear();
468 signatureContents.clear();
469 addNSECRecordToLW(DNSName(").powerdns.com."), DNSName("+.powerdns.com."), {}, 600, records);
470 recordContents.insert(records.at(0).d_content);
471 addRRSIG(keys, records, DNSName("powerdns.com."), 300);
472 signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
473 records.clear();
474 pair.records = recordContents;
475 pair.signatures = signatureContents;
476 denialMap[std::make_pair(DNSName(").powerdns.com."), QType::NSEC)] = pair;
477
478 denialState = getDenial(denialMap, DNSName("b.powerdns.com."), QType::A, true, false);
479 BOOST_CHECK_EQUAL(denialState, NXDOMAIN);
480
481 /* this NSEC is NOT valid to prove a NXDOMAIN at c.powerdns.com because it proves that
482 it exists and is an ENT */
483 denialState = getDenial(denialMap, DNSName("c.powerdns.com."), QType::AAAA, true, false);
484 BOOST_CHECK_EQUAL(denialState, NODATA);
485 }
486
487 BOOST_AUTO_TEST_CASE(test_nsec3_ancestor_nxqtype_denial)
488 {
489 initSR();
490
491 testkeysset_t keys;
492 generateKeyMaterial(DNSName("."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
493
494 vector<DNSRecord> records;
495
496 sortedRecords_t recordContents;
497 vector<shared_ptr<RRSIGRecordContent>> signatureContents;
498
499 /*
500 The RRSIG from "." denies the existence of any type except NS at a.
501 However since it's an ancestor delegation NSEC (NS bit set, SOA bit clear,
502 signer field that is shorter than the owner name of the NSEC RR) it can't
503 be used to deny anything except the whole name or a DS.
504 */
505 addNSEC3UnhashedRecordToLW(DNSName("a."), DNSName("."), "whatever", {QType::NS}, 600, records);
506 recordContents.insert(records.at(0).d_content);
507 addRRSIG(keys, records, DNSName("."), 300);
508 signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
509
510 ContentSigPair pair;
511 pair.records = recordContents;
512 pair.signatures = signatureContents;
513 cspmap_t denialMap;
514 denialMap[std::make_pair(records.at(0).d_name, records.at(0).d_type)] = pair;
515 records.clear();
516
517 /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
518 Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
519 nonexistence of any RRs below that zone cut, which include all RRs at
520 that (original) owner name other than DS RRs, and all RRs below that
521 owner name regardless of type.
522 */
523
524 dState denialState = getDenial(denialMap, DNSName("a."), QType::A, false, true);
525 /* no data means the qname/qtype is not denied, because an ancestor
526 delegation NSEC3 can only deny the DS */
527 BOOST_CHECK_EQUAL(denialState, NODATA);
528
529 denialState = getDenial(denialMap, DNSName("a."), QType::DS, true, true);
530 BOOST_CHECK_EQUAL(denialState, NXQTYPE);
531
532 /* it can not be used to deny any RRs below that owner name either */
533 /* Add NSEC3 for the next closer */
534 recordContents.clear();
535 signatureContents.clear();
536 records.clear();
537 addNSEC3NarrowRecordToLW(DNSName("sub.a."), DNSName("."), {QType::A, QType::TXT, QType::RRSIG, QType::NSEC3}, 600, records);
538 recordContents.insert(records.at(0).d_content);
539 addRRSIG(keys, records, DNSName("."), 300);
540 signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
541
542 pair.records = recordContents;
543 pair.signatures = signatureContents;
544 denialMap[std::make_pair(records.at(0).d_name, records.at(0).d_type)] = pair;
545
546 /* add wildcard denial */
547 recordContents.clear();
548 signatureContents.clear();
549 records.clear();
550 addNSEC3NarrowRecordToLW(DNSName("*.a."), DNSName("."), {QType::A, QType::TXT, QType::RRSIG, QType::NSEC3}, 600, records);
551 recordContents.insert(records.at(0).d_content);
552 addRRSIG(keys, records, DNSName("."), 300);
553 signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
554
555 pair.records = recordContents;
556 pair.signatures = signatureContents;
557 denialMap[std::make_pair(records.at(0).d_name, records.at(0).d_type)] = pair;
558
559 denialState = getDenial(denialMap, DNSName("sub.a."), QType::A, false, true);
560 BOOST_CHECK_EQUAL(denialState, NODATA);
561 }
562
563 BOOST_AUTO_TEST_CASE(test_nsec3_denial_too_many_iterations)
564 {
565 initSR();
566
567 testkeysset_t keys;
568 generateKeyMaterial(DNSName("."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
569
570 vector<DNSRecord> records;
571
572 sortedRecords_t recordContents;
573 vector<shared_ptr<RRSIGRecordContent>> signatureContents;
574
575 /* adding a NSEC3 with more iterations that we support */
576 addNSEC3UnhashedRecordToLW(DNSName("a."), DNSName("."), "whatever", {QType::AAAA}, 600, records, g_maxNSEC3Iterations + 100);
577 recordContents.insert(records.at(0).d_content);
578 addRRSIG(keys, records, DNSName("."), 300);
579 signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
580
581 ContentSigPair pair;
582 pair.records = recordContents;
583 pair.signatures = signatureContents;
584 cspmap_t denialMap;
585 denialMap[std::make_pair(records.at(0).d_name, records.at(0).d_type)] = pair;
586 records.clear();
587
588 dState denialState = getDenial(denialMap, DNSName("a."), QType::A, false, true);
589 /* since we refuse to compute more than g_maxNSEC3Iterations iterations, it should be Insecure */
590 BOOST_CHECK_EQUAL(denialState, INSECURE);
591 }
592
593 BOOST_AUTO_TEST_CASE(test_nsec3_insecure_delegation_denial)
594 {
595 initSR();
596
597 testkeysset_t keys;
598 generateKeyMaterial(DNSName("."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
599
600 vector<DNSRecord> records;
601
602 sortedRecords_t recordContents;
603 vector<shared_ptr<RRSIGRecordContent>> signatureContents;
604
605 /*
606 * RFC 5155 section 8.9:
607 * If there is an NSEC3 RR present in the response that matches the
608 * delegation name, then the validator MUST ensure that the NS bit is
609 * set and that the DS bit is not set in the Type Bit Maps field of the
610 * NSEC3 RR.
611 */
612 /*
613 The RRSIG from "." denies the existence of any type at a.
614 NS should be set if it was proving an insecure delegation, let's check that
615 we correctly detect that it's not.
616 */
617 addNSEC3UnhashedRecordToLW(DNSName("a."), DNSName("."), "whatever", {}, 600, records);
618 recordContents.insert(records.at(0).d_content);
619 addRRSIG(keys, records, DNSName("."), 300);
620 signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
621
622 ContentSigPair pair;
623 pair.records = recordContents;
624 pair.signatures = signatureContents;
625 cspmap_t denialMap;
626 denialMap[std::make_pair(records.at(0).d_name, records.at(0).d_type)] = pair;
627 records.clear();
628
629 /* Insecure because the NS is not set, so while it does
630 denies the DS, it can't prove an insecure delegation */
631 dState denialState = getDenial(denialMap, DNSName("a."), QType::DS, true, true);
632 BOOST_CHECK_EQUAL(denialState, NODATA);
633 }
634
635 BOOST_AUTO_TEST_CASE(test_dnssec_rrsig_negcache_validity)
636 {
637 std::unique_ptr<SyncRes> sr;
638 initSR(sr, true);
639
640 setDNSSECValidation(sr, DNSSECMode::ValidateAll);
641
642 primeHints();
643 const DNSName target("com.");
644 testkeysset_t keys;
645
646 auto luaconfsCopy = g_luaconfs.getCopy();
647 luaconfsCopy.dsAnchors.clear();
648 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
649 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
650 g_luaconfs.setState(luaconfsCopy);
651
652 size_t queriesCount = 0;
653 const time_t fixedNow = sr->getNow().tv_sec;
654
655 sr->setAsyncCallback([target, &queriesCount, keys, fixedNow](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) {
656 queriesCount++;
657
658 DNSName auth = domain;
659 auth.chopOff();
660
661 if (type == QType::DS || type == QType::DNSKEY) {
662 return genericDSAndDNSKEYHandler(res, domain, auth, type, keys);
663 }
664 else {
665 setLWResult(res, RCode::NoError, true, false, true);
666 addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
667 addRRSIG(keys, res->d_records, domain, 300);
668 addNSECRecordToLW(domain, DNSName("z."), {QType::NSEC, QType::RRSIG}, 600, res->d_records);
669 addRRSIG(keys, res->d_records, domain, 1, false, boost::none, boost::none, fixedNow);
670 return 1;
671 }
672
673 return 0;
674 });
675
676 vector<DNSRecord> ret;
677 int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
678 BOOST_CHECK_EQUAL(res, RCode::NoError);
679 BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
680 BOOST_REQUIRE_EQUAL(ret.size(), 4U);
681 BOOST_CHECK_EQUAL(queriesCount, 4U);
682
683 /* check that the entry has not been negatively cached for longer than the RRSIG validity */
684 NegCache::NegCacheEntry ne;
685 BOOST_CHECK_EQUAL(SyncRes::t_sstorage.negcache.size(), 1U);
686 BOOST_REQUIRE_EQUAL(SyncRes::t_sstorage.negcache.get(target, QType(QType::A), sr->getNow(), ne), true);
687 BOOST_CHECK_EQUAL(ne.d_ttd, fixedNow + 1);
688 BOOST_CHECK_EQUAL(ne.d_validationState, Secure);
689 BOOST_CHECK_EQUAL(ne.authoritySOA.records.size(), 1U);
690 BOOST_CHECK_EQUAL(ne.authoritySOA.signatures.size(), 1U);
691 BOOST_CHECK_EQUAL(ne.DNSSECRecords.records.size(), 1U);
692 BOOST_CHECK_EQUAL(ne.DNSSECRecords.signatures.size(), 1U);
693
694 /* again, to test the cache */
695 ret.clear();
696 res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
697 BOOST_CHECK_EQUAL(res, RCode::NoError);
698 BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
699 BOOST_REQUIRE_EQUAL(ret.size(), 4U);
700 BOOST_CHECK_EQUAL(queriesCount, 4U);
701 }
702
703 BOOST_AUTO_TEST_CASE(test_dnssec_rrsig_negcache_bogus_validity)
704 {
705 std::unique_ptr<SyncRes> sr;
706 initSR(sr, true);
707
708 setDNSSECValidation(sr, DNSSECMode::ValidateAll);
709
710 primeHints();
711 const DNSName target("com.");
712 testkeysset_t keys;
713
714 auto luaconfsCopy = g_luaconfs.getCopy();
715 luaconfsCopy.dsAnchors.clear();
716 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
717 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
718 g_luaconfs.setState(luaconfsCopy);
719
720 size_t queriesCount = 0;
721 const time_t fixedNow = sr->getNow().tv_sec;
722
723 sr->setAsyncCallback([&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) {
724 queriesCount++;
725
726 DNSName auth = domain;
727 auth.chopOff();
728
729 if (type == QType::DS || type == QType::DNSKEY) {
730 return genericDSAndDNSKEYHandler(res, domain, auth, type, keys);
731 }
732 else {
733 setLWResult(res, RCode::NoError, true, false, true);
734 addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 86400);
735 addRRSIG(keys, res->d_records, domain, 86400);
736 addNSECRecordToLW(domain, DNSName("z."), {QType::NSEC, QType::RRSIG}, 86400, res->d_records);
737 /* no RRSIG */
738 return 1;
739 }
740
741 return 0;
742 });
743
744 SyncRes::s_maxnegttl = 3600;
745 SyncRes::s_maxbogusttl = 360;
746
747 vector<DNSRecord> ret;
748 int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
749 BOOST_CHECK_EQUAL(res, RCode::NoError);
750 BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
751 BOOST_REQUIRE_EQUAL(ret.size(), 3U);
752 BOOST_CHECK_EQUAL(queriesCount, 4U);
753
754 /* check that the entry has been negatively cached but not longer than s_maxbogusttl */
755 NegCache::NegCacheEntry ne;
756 BOOST_CHECK_EQUAL(SyncRes::t_sstorage.negcache.size(), 1U);
757 BOOST_REQUIRE_EQUAL(SyncRes::t_sstorage.negcache.get(target, QType(QType::A), sr->getNow(), ne), true);
758 BOOST_CHECK_EQUAL(ne.d_ttd, fixedNow + SyncRes::s_maxbogusttl);
759 BOOST_CHECK_EQUAL(ne.d_validationState, Bogus);
760 BOOST_CHECK_EQUAL(ne.authoritySOA.records.size(), 1U);
761 BOOST_CHECK_EQUAL(ne.authoritySOA.signatures.size(), 1U);
762 BOOST_CHECK_EQUAL(ne.DNSSECRecords.records.size(), 1U);
763 BOOST_CHECK_EQUAL(ne.DNSSECRecords.signatures.size(), 0U);
764
765 /* again, to test the cache */
766 ret.clear();
767 res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
768 BOOST_CHECK_EQUAL(res, RCode::NoError);
769 BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
770 BOOST_REQUIRE_EQUAL(ret.size(), 3U);
771 BOOST_CHECK_EQUAL(queriesCount, 4U);
772 }
773
774 BOOST_AUTO_TEST_CASE(test_dnssec_rrsig_cache_validity)
775 {
776 std::unique_ptr<SyncRes> sr;
777 initSR(sr, true);
778
779 setDNSSECValidation(sr, DNSSECMode::ValidateAll);
780
781 primeHints();
782 const DNSName target("com.");
783 const ComboAddress targetAddr("192.0.2.42");
784 testkeysset_t keys;
785
786 auto luaconfsCopy = g_luaconfs.getCopy();
787 luaconfsCopy.dsAnchors.clear();
788 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
789 generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
790 g_luaconfs.setState(luaconfsCopy);
791
792 size_t queriesCount = 0;
793 const time_t tnow = sr->getNow().tv_sec;
794
795 sr->setAsyncCallback([target, targetAddr, &queriesCount, keys, tnow](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) {
796 queriesCount++;
797
798 DNSName auth = domain;
799 auth.chopOff();
800
801 if (type == QType::DS || type == QType::DNSKEY) {
802 return genericDSAndDNSKEYHandler(res, domain, auth, type, keys);
803 }
804 else {
805 setLWResult(res, RCode::NoError, true, false, true);
806 addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
807 addRRSIG(keys, res->d_records, domain, 1, false, boost::none, boost::none, tnow);
808 return 1;
809 }
810
811 return 0;
812 });
813
814 vector<DNSRecord> ret;
815 int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
816 BOOST_CHECK_EQUAL(res, RCode::NoError);
817 BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
818 BOOST_REQUIRE_EQUAL(ret.size(), 2U);
819 BOOST_CHECK_EQUAL(queriesCount, 4U);
820
821 /* check that the entry has not been cached for longer than the RRSIG validity */
822 const ComboAddress who;
823 vector<DNSRecord> cached;
824 vector<std::shared_ptr<RRSIGRecordContent>> signatures;
825 BOOST_REQUIRE_EQUAL(s_RC->get(tnow, target, QType(QType::A), true, &cached, who, boost::none, &signatures), 1);
826 BOOST_REQUIRE_EQUAL(cached.size(), 1U);
827 BOOST_REQUIRE_EQUAL(signatures.size(), 1U);
828 BOOST_CHECK_EQUAL((cached[0].d_ttl - tnow), 1);
829
830 /* again, to test the cache */
831 ret.clear();
832 res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
833 BOOST_CHECK_EQUAL(res, RCode::NoError);
834 BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
835 BOOST_REQUIRE_EQUAL(ret.size(), 2U);
836 BOOST_CHECK_EQUAL(queriesCount, 4U);
837 }
838
839 BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_cache_secure)
840 {
841 /*
842 Validation is optional, and the first query does not ask for it,
843 so the answer is cached as Indeterminate.
844 The second query asks for validation, answer should be marked as
845 Secure, after just-in-time validation.
846 */
847 std::unique_ptr<SyncRes> sr;
848 initSR(sr, true);
849
850 setDNSSECValidation(sr, DNSSECMode::Process);
851
852 primeHints();
853 const DNSName target("com.");
854 testkeysset_t keys;
855
856 auto luaconfsCopy = g_luaconfs.getCopy();
857 luaconfsCopy.dsAnchors.clear();
858 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
859 g_luaconfs.setState(luaconfsCopy);
860
861 size_t queriesCount = 0;
862
863 sr->setAsyncCallback([target, &queriesCount, keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
864 queriesCount++;
865
866 if (type == QType::DS || type == QType::DNSKEY) {
867 return genericDSAndDNSKEYHandler(res, domain, domain, type, keys, false);
868 }
869 else {
870 if (domain == target && type == QType::A) {
871 setLWResult(res, 0, true, false, true);
872 addRecordToLW(res, target, QType::A, "192.0.2.1");
873 addRRSIG(keys, res->d_records, DNSName("."), 300);
874 return 1;
875 }
876 }
877
878 return 0;
879 });
880
881 vector<DNSRecord> ret;
882 /* first query does not require validation */
883 sr->setDNSSECValidationRequested(false);
884 int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
885 BOOST_CHECK_EQUAL(res, RCode::NoError);
886 BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
887 BOOST_REQUIRE_EQUAL(ret.size(), 2U);
888 for (const auto& record : ret) {
889 BOOST_CHECK(record.d_type == QType::A || record.d_type == QType::RRSIG);
890 }
891 BOOST_CHECK_EQUAL(queriesCount, 1U);
892
893 ret.clear();
894 /* second one _does_ require validation */
895 sr->setDNSSECValidationRequested(true);
896 res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
897 BOOST_CHECK_EQUAL(res, RCode::NoError);
898 BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
899 BOOST_REQUIRE_EQUAL(ret.size(), 2U);
900 for (const auto& record : ret) {
901 BOOST_CHECK(record.d_type == QType::A || record.d_type == QType::RRSIG);
902 }
903 BOOST_CHECK_EQUAL(queriesCount, 3U);
904 }
905
906 BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_cache_insecure)
907 {
908 /*
909 Validation is optional, and the first query does not ask for it,
910 so the answer is cached as Indeterminate.
911 The second query asks for validation, answer should be marked as
912 Insecure.
913 */
914 std::unique_ptr<SyncRes> sr;
915 initSR(sr, true);
916
917 setDNSSECValidation(sr, DNSSECMode::Process);
918
919 primeHints();
920 const DNSName target("com.");
921 testkeysset_t keys;
922
923 auto luaconfsCopy = g_luaconfs.getCopy();
924 luaconfsCopy.dsAnchors.clear();
925 g_luaconfs.setState(luaconfsCopy);
926
927 size_t queriesCount = 0;
928
929 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) {
930 queriesCount++;
931
932 if (type == QType::DS || type == QType::DNSKEY) {
933 return genericDSAndDNSKEYHandler(res, domain, domain, type, keys, false);
934 }
935 else {
936 if (domain == target && type == QType::A) {
937 setLWResult(res, 0, true, false, true);
938 addRecordToLW(res, target, QType::A, "192.0.2.1");
939 return 1;
940 }
941 }
942
943 return 0;
944 });
945
946 vector<DNSRecord> ret;
947 /* first query does not require validation */
948 sr->setDNSSECValidationRequested(false);
949 int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
950 BOOST_CHECK_EQUAL(res, RCode::NoError);
951 BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
952 BOOST_REQUIRE_EQUAL(ret.size(), 1U);
953 for (const auto& record : ret) {
954 BOOST_CHECK(record.d_type == QType::A);
955 }
956 BOOST_CHECK_EQUAL(queriesCount, 1U);
957
958 ret.clear();
959 /* second one _does_ require validation */
960 sr->setDNSSECValidationRequested(true);
961 res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
962 BOOST_CHECK_EQUAL(res, RCode::NoError);
963 BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
964 BOOST_REQUIRE_EQUAL(ret.size(), 1U);
965 for (const auto& record : ret) {
966 BOOST_CHECK(record.d_type == QType::A);
967 }
968 BOOST_CHECK_EQUAL(queriesCount, 1U);
969 }
970
971 BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_cache_bogus)
972 {
973 /*
974 Validation is optional, and the first query does not ask for it,
975 so the answer is cached as Indeterminate.
976 The second query asks for validation, answer should be marked as
977 Bogus.
978 */
979 std::unique_ptr<SyncRes> sr;
980 initSR(sr, true);
981
982 setDNSSECValidation(sr, DNSSECMode::Process);
983
984 primeHints();
985 const DNSName target("com.");
986 testkeysset_t keys;
987
988 auto luaconfsCopy = g_luaconfs.getCopy();
989 luaconfsCopy.dsAnchors.clear();
990 generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
991 g_luaconfs.setState(luaconfsCopy);
992
993 size_t queriesCount = 0;
994
995 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) {
996 queriesCount++;
997
998 if (type == QType::DS || type == QType::DNSKEY) {
999 return genericDSAndDNSKEYHandler(res, domain, domain, type, keys, false);
1000 }
1001 else {
1002 if (domain == target && type == QType::A) {
1003 setLWResult(res, 0, true, false, true);
1004 addRecordToLW(res, target, QType::A, "192.0.2.1", DNSResourceRecord::ANSWER, 86400);
1005 /* no RRSIG */
1006 return 1;
1007 }
1008 }
1009
1010 return 0;
1011 });
1012
1013 SyncRes::s_maxbogusttl = 3600;
1014
1015 vector<DNSRecord> ret;
1016 /* first query does not require validation */
1017 sr->setDNSSECValidationRequested(false);
1018 int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
1019 BOOST_CHECK_EQUAL(res, RCode::NoError);
1020 BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
1021 BOOST_REQUIRE_EQUAL(ret.size(), 1U);
1022 for (const auto& record : ret) {
1023 BOOST_CHECK(record.d_type == QType::A);
1024 BOOST_CHECK_EQUAL(record.d_ttl, 86400U);
1025 }
1026 BOOST_CHECK_EQUAL(queriesCount, 1U);
1027
1028 ret.clear();
1029 /* second one _does_ require validation */
1030 sr->setDNSSECValidationRequested(true);
1031 res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
1032 BOOST_CHECK_EQUAL(res, RCode::NoError);
1033 BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
1034 /* check that we correctly capped the TTD for a Bogus record after
1035 just-in-time validation */
1036 BOOST_REQUIRE_EQUAL(ret.size(), 1U);
1037 for (const auto& record : ret) {
1038 BOOST_CHECK(record.d_type == QType::A);
1039 BOOST_CHECK_EQUAL(record.d_ttl, SyncRes::s_maxbogusttl);
1040 }
1041 BOOST_CHECK_EQUAL(queriesCount, 3U);
1042
1043 ret.clear();
1044 /* third time also _does_ require validation, so we
1045 can check that the cache has been updated */
1046 sr->setDNSSECValidationRequested(true);
1047 res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
1048 BOOST_CHECK_EQUAL(res, RCode::NoError);
1049 BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
1050 BOOST_REQUIRE_EQUAL(ret.size(), 1U);
1051 for (const auto& record : ret) {
1052 BOOST_CHECK(record.d_type == QType::A);
1053 BOOST_CHECK_EQUAL(record.d_ttl, SyncRes::s_maxbogusttl);
1054 }
1055 BOOST_CHECK_EQUAL(queriesCount, 3U);
1056 }
1057
1058 BOOST_AUTO_TEST_SUITE_END()