• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PowerDNS / pdns / 13012068652

28 Jan 2025 01:59PM UTC coverage: 64.71% (+0.01%) from 64.699%
13012068652

Pull #14724

github

web-flow
Merge b15562560 into db18c3a17
Pull Request #14724: dnsdist: Add meson support

38328 of 90334 branches covered (42.43%)

Branch coverage included in aggregate %.

361 of 513 new or added lines in 35 files covered. (70.37%)

42 existing lines in 13 files now uncovered.

128150 of 166934 relevant lines covered (76.77%)

4540890.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.24
/pdns/validate.cc
1
/*
2
 * This file is part of PowerDNS or dnsdist.
3
 * Copyright -- PowerDNS.COM B.V. and its contributors
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of version 2 of the GNU General Public License as
7
 * published by the Free Software Foundation.
8
 *
9
 * In addition, for the avoidance of any doubt, permission is granted to
10
 * link this program with OpenSSL and to (re)distribute the binaries
11
 * produced as the result of such linking.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
 */
22

23
#include "validate.hh"
24
#include "misc.hh"
25
#include "dnssecinfra.hh"
26
#include "dnsseckeeper.hh"
27
#include "rec-lua-conf.hh"
28
#include "base32.hh"
29
#include "logger.hh"
30

31
time_t g_signatureInceptionSkew{0};
32
uint16_t g_maxNSEC3Iterations{0};
33
uint16_t g_maxRRSIGsPerRecordToConsider{0};
34
uint16_t g_maxNSEC3sPerRecordToConsider{0};
35
uint16_t g_maxDNSKEYsToConsider{0};
36
uint16_t g_maxDSsToConsider{0};
37

38
static bool isAZoneKey(const DNSKEYRecordContent& key)
39
{
9,280✔
40
  /* rfc4034 Section 2.1.1:
41
     "Bit 7 of the Flags field is the Zone Key flag.  If bit 7 has value 1,
42
     then the DNSKEY record holds a DNS zone key, and the DNSKEY RR's
43
     owner name MUST be the name of a zone.  If bit 7 has value 0, then
44
     the DNSKEY record holds some other type of DNS public key and MUST
45
     NOT be used to verify RRSIGs that cover RRsets."
46

47
     Let's check that this is a ZONE key, even though there is no other
48
     types of DNSKEYs at the moment.
49
  */
50
  return (key.d_flags & 256) != 0;
9,280✔
51
}
9,280✔
52

53
static bool isRevokedKey(const DNSKEYRecordContent& key)
54
{
9,275✔
55
  /* rfc5011 Section 3 */
56
  return (key.d_flags & 128) != 0;
9,275✔
57
}
9,275✔
58

59
static vector<shared_ptr<const DNSKEYRecordContent > > getByTag(const skeyset_t& keys, uint16_t tag, uint8_t algorithm, const OptLog& log)
60
{
6,965✔
61
  vector<shared_ptr<const DNSKEYRecordContent>> ret;
6,965✔
62

63
  for (const auto& key : keys) {
9,231✔
64
    if (!isAZoneKey(*key)) {
9,229✔
65
      VLOG(log, "Key for tag "<<std::to_string(tag)<<" and algorithm "<<std::to_string(algorithm)<<" is not a zone key, skipping"<<endl;);
2!
66
      continue;
2✔
67
    }
2✔
68

69
    if (isRevokedKey(*key)) {
9,227✔
70
      VLOG(log, "Key for tag "<<std::to_string(tag)<<" and algorithm "<<std::to_string(algorithm)<<" has been revoked, skipping"<<endl;);
2!
71
      continue;
2✔
72
    }
2✔
73

74
    if (key->d_protocol == 3 && key->getTag() == tag && key->d_algorithm == algorithm) {
9,227✔
75
      ret.push_back(key);
6,138✔
76
    }
6,138✔
77
  }
9,225✔
78

79
  return ret;
6,965✔
80
}
6,965✔
81

82
bool isCoveredByNSEC3Hash(const std::string& hash, const std::string& beginHash, const std::string& nextHash)
83
{
8,917✔
84
  return ((beginHash < hash && hash < nextHash) ||          // no wrap          BEGINNING --- HASH -- END
8,917✔
85
          (nextHash > hash  && beginHash > nextHash) ||  // wrap             HASH --- END --- BEGINNING
8,917✔
86
          (nextHash < beginHash  && beginHash < hash) || // wrap other case  END --- BEGINNING --- HASH
8,917✔
87
          (beginHash == nextHash && hash != beginHash));   // "we have only 1 NSEC3 record, LOL!"
8,917!
88
}
8,917✔
89

90
bool isCoveredByNSEC3Hash(const DNSName& name, const DNSName& beginHash, const DNSName& nextHash)
91
{
49✔
92
  return ((beginHash.canonCompare(name) && name.canonCompare(nextHash)) ||          // no wrap          BEGINNING --- HASH -- END
49✔
93
          (name.canonCompare(nextHash) && nextHash.canonCompare(beginHash)) ||  // wrap             HASH --- END --- BEGINNING
49!
94
          (nextHash.canonCompare(beginHash) && beginHash.canonCompare(name)) || // wrap other case  END --- BEGINNING --- HASH
49!
95
          (beginHash == nextHash && name != beginHash));   // "we have only 1 NSEC3 record, LOL!"
49!
96
}
49✔
97

98
bool isCoveredByNSEC(const DNSName& name, const DNSName& begin, const DNSName& next)
99
{
540✔
100
  return ((begin.canonCompare(name) && name.canonCompare(next)) ||  // no wrap          BEGINNING --- NAME --- NEXT
540✔
101
          (name.canonCompare(next) && next.canonCompare(begin)) ||  // wrap             NAME --- NEXT --- BEGINNING
540✔
102
          (next.canonCompare(begin) && begin.canonCompare(name)) || // wrap other case  NEXT --- BEGINNING --- NAME
540✔
103
          (begin == next && name != begin));                        // "we have only 1 NSEC record, LOL!"
540!
104
}
540✔
105

106
static bool nsecProvesENT(const DNSName& name, const DNSName& begin, const DNSName& next)
107
{
332✔
108
  /* if name is an ENT:
109
     - begin < name
110
     - next is a child of name
111
  */
112
  return begin.canonCompare(name) && next != name && next.isPartOf(name);
332!
113
}
332✔
114

115
[[nodiscard]] std::string getHashFromNSEC3(const DNSName& qname, uint16_t iterations, const std::string& salt, pdns::validation::ValidationContext& context)
116
{
14,750✔
117
  std::string result;
14,750✔
118

119
  if (g_maxNSEC3Iterations != 0 && iterations > g_maxNSEC3Iterations) {
14,750!
120
    return result;
6✔
121
  }
6✔
122

123
  auto key = std::tuple(qname, salt, iterations);
14,744✔
124
  auto iter = context.d_nsec3Cache.find(key);
14,744✔
125
  if (iter != context.d_nsec3Cache.end()) {
14,744✔
126
    return iter->second;
11,106✔
127
  }
11,106✔
128

129
  if (context.d_nsec3IterationsRemainingQuota < iterations) {
3,638✔
130
    // we throw here because we cannot take the risk that the result
131
    // be cached, since a different query can try to validate the
132
    // same result with a bigger NSEC3 iterations quota
133
    throw pdns::validation::TooManySEC3IterationsException();
2✔
134
  }
2✔
135

136
  result = hashQNameWithSalt(salt, iterations, qname);
3,636✔
137
  context.d_nsec3IterationsRemainingQuota -= iterations;
3,636✔
138
  context.d_nsec3Cache[key] = result;
3,636✔
139
  return result;
3,636✔
140
}
3,638✔
141

142
[[nodiscard]] static std::string getHashFromNSEC3(const DNSName& qname, const NSEC3RecordContent& nsec3, pdns::validation::ValidationContext& context)
143
{
14,539✔
144
  return getHashFromNSEC3(qname, nsec3.d_iterations, nsec3.d_salt, context);
14,539✔
145
}
14,539✔
146

147
/* There is no delegation at this exact point if:
148
   - the name exists but the NS type is not set
149
   - the name does not exist
150
   One exception, if the name is covered by an opt-out NSEC3
151
   it doesn't prove that an insecure delegation doesn't exist.
152
*/
153
bool denialProvesNoDelegation(const DNSName& zone, const std::vector<DNSRecord>& dsrecords, pdns::validation::ValidationContext& context)
154
{
2,103✔
155
  uint16_t nsec3sConsidered = 0;
2,103✔
156

157
  for (const auto& record : dsrecords) {
4,166✔
158
    if (record.d_type == QType::NSEC) {
4,166✔
159
      const auto nsec = getRR<NSECRecordContent>(record);
143✔
160
      if (!nsec) {
143!
161
        continue;
×
162
      }
×
163

164
      if (record.d_name == zone) {
143✔
165
        return !nsec->isSet(QType::NS);
141✔
166
      }
141✔
167

168
      if (isCoveredByNSEC(zone, record.d_name, nsec->d_next)) {
2!
169
        return true;
2✔
170
      }
2✔
171
    }
2✔
172
    else if (record.d_type == QType::NSEC3) {
4,023✔
173
      const auto nsec3 = getRR<NSEC3RecordContent>(record);
3,884✔
174
      if (!nsec3) {
3,884!
175
        continue;
×
176
      }
×
177

178
      if (g_maxNSEC3sPerRecordToConsider > 0 && nsec3sConsidered >= g_maxNSEC3sPerRecordToConsider) {
3,885!
179
        context.d_limitHit = true;
×
180
        return false;
×
181
      }
×
182
      nsec3sConsidered++;
3,884✔
183

184
      const string hash = getHashFromNSEC3(zone, *nsec3, context);
3,884✔
185
      if (hash.empty()) {
3,884!
186
        return false;
×
187
      }
×
188

189
      const string beginHash = fromBase32Hex(record.d_name.getRawLabels()[0]);
3,884✔
190
      if (beginHash == hash) {
3,884✔
191
        return !nsec3->isSet(QType::NS);
15✔
192
      }
15✔
193

194
      if (isCoveredByNSEC3Hash(hash, beginHash, nsec3->d_nexthash)) {
3,869✔
195
        return !(nsec3->isOptOut());
1,935✔
196
      }
1,935✔
197
    }
3,869✔
198
  }
4,166✔
199

200
  return false;
10✔
201
}
2,103✔
202

203
/* RFC 4035 section-5.3.4:
204
   "If the number of labels in an RRset's owner name is greater than the
205
   Labels field of the covering RRSIG RR, then the RRset and its
206
   covering RRSIG RR were created as a result of wildcard expansion."
207
*/
208
bool isWildcardExpanded(unsigned int labelCount, const RRSIGRecordContent& sign)
209
{
4,736✔
210
  return sign.d_labels < labelCount;
4,736✔
211
}
4,736✔
212

213
static bool isWildcardExpanded(const DNSName& owner, const std::vector<std::shared_ptr<const RRSIGRecordContent> >& signatures)
214
{
187✔
215
  if (signatures.empty()) {
187!
216
    return false;
×
217
  }
×
218

219
  const auto& sign = signatures.at(0);
187✔
220
  unsigned int labelsCount = owner.countLabels();
187✔
221
  return isWildcardExpanded(labelsCount, *sign);
187✔
222
}
187✔
223

224
bool isWildcardExpandedOntoItself(const DNSName& owner, unsigned int labelCount, const RRSIGRecordContent& sign)
225
{
464✔
226
  /* this is a wildcard alright, but it has not been expanded */
227
  return owner.isWildcard() && (labelCount - 1) == sign.d_labels;
464✔
228
}
464✔
229

230
static bool isWildcardExpandedOntoItself(const DNSName& owner, const std::vector<std::shared_ptr<const RRSIGRecordContent> >& signatures)
231
{
2✔
232
  if (signatures.empty()) {
2!
233
    return false;
×
234
  }
×
235

236
  const auto& sign = signatures.at(0);
2✔
237
  unsigned int labelsCount = owner.countLabels();
2✔
238
  return isWildcardExpandedOntoItself(owner, labelsCount, *sign);
2✔
239
}
2✔
240

241
/* if this is a wildcard NSEC, the owner name has been modified
242
   to match the name. Make sure we use the original '*' form. */
243
DNSName getNSECOwnerName(const DNSName& initialOwner, const std::vector<std::shared_ptr<const RRSIGRecordContent> >& signatures)
244
{
1,038✔
245
  DNSName result = initialOwner;
1,038✔
246

247
  if (signatures.empty()) {
1,038!
248
    return result;
×
249
  }
×
250

251
  const auto& sign = signatures.at(0);
1,038✔
252
  unsigned int labelsCount = initialOwner.countLabels();
1,038✔
253
  if (sign && sign->d_labels < labelsCount) {
1,038!
254
    do {
291✔
255
      result.chopOff();
291✔
256
      labelsCount--;
291✔
257
    }
291✔
258
    while (sign->d_labels < labelsCount);
291!
259

260
    result = g_wildcarddnsname + result;
291✔
261
  }
291✔
262

263
  return result;
1,038✔
264
}
1,038✔
265

266
static bool isNSECAncestorDelegation(const DNSName& signer, const DNSName& owner, const NSECRecordContent& nsec)
267
{
337✔
268
  return nsec.isSet(QType::NS) &&
337✔
269
    !nsec.isSet(QType::SOA) &&
337✔
270
    signer.countLabels() < owner.countLabels();
337✔
271
}
337✔
272

273
bool isNSEC3AncestorDelegation(const DNSName& signer, const DNSName& owner, const NSEC3RecordContent& nsec3)
274
{
1,611✔
275
  return nsec3.isSet(QType::NS) &&
1,611✔
276
    !nsec3.isSet(QType::SOA) &&
1,611✔
277
    signer.countLabels() < owner.countLabels();
1,611!
278
}
1,611✔
279

280
static bool provesNoDataWildCard(const DNSName& qname, const uint16_t qtype, const DNSName& closestEncloser, const cspmap_t& validrrsets, const OptLog& log)
281
{
18✔
282
  const DNSName wildcard = g_wildcarddnsname + closestEncloser;
18✔
283
  VLOG(log, qname << ": Trying to prove that there is no data in wildcard for "<<qname<<"/"<<QType(qtype)<<endl);
18!
284
  for (const auto& validset : validrrsets) {
18✔
285
    VLOG(log, qname << ": Do have: "<<validset.first.first<<"/"<<DNSRecordContent::NumberToType(validset.first.second)<<endl);
18!
286
    if (validset.first.second == QType::NSEC) {
18!
287
      for (const auto& record : validset.second.records) {
18✔
288
        VLOG(log, ":\t"<<record->getZoneRepresentation()<<endl);
18!
289
        auto nsec = std::dynamic_pointer_cast<const NSECRecordContent>(record);
18✔
290
        if (!nsec) {
18!
291
          continue;
×
292
        }
×
293

294
        DNSName owner = getNSECOwnerName(validset.first.first, validset.second.signatures);
18✔
295
        if (owner != wildcard) {
18✔
296
          continue;
4✔
297
        }
4✔
298

299
        VLOG(log, qname << ":\tWildcard matches");
14!
300
        if (qtype == 0 || isTypeDenied(*nsec, QType(qtype))) {
14!
301
          VLOG_NO_PREFIX(log, " and proves that the type did not exist"<<endl);
8!
302
          return true;
8✔
303
        }
8✔
304
        VLOG_NO_PREFIX(log, " BUT the type did exist!"<<endl);
6!
305
        return false;
6✔
306
      }
14✔
307
    }
18✔
308
  }
18✔
309

310
  return false;
4✔
311
}
18✔
312

313
DNSName getClosestEncloserFromNSEC(const DNSName& name, const DNSName& owner, const DNSName& next)
314
{
85✔
315
  DNSName commonWithOwner(name.getCommonLabels(owner));
85✔
316
  DNSName commonWithNext(name.getCommonLabels(next));
85✔
317
  if (commonWithOwner.countLabels() >= commonWithNext.countLabels()) {
85!
318
    return commonWithOwner;
85✔
319
  }
85✔
320
  return commonWithNext;
×
321
}
85✔
322

323
/*
324
  This function checks whether the non-existence of a wildcard covering qname|qtype
325
  is proven by the NSEC records in validrrsets.
326
*/
327
static bool provesNoWildCard(const DNSName& qname, const uint16_t qtype, const DNSName& closestEncloser, const cspmap_t & validrrsets, const OptLog& log)
328
{
53✔
329
  VLOG(log, qname << ": Trying to prove that there is no wildcard for "<<qname<<"/"<<QType(qtype)<<endl);
53!
330
  const DNSName wildcard = g_wildcarddnsname + closestEncloser;
53✔
331
  for (const auto& validset : validrrsets) {
53✔
332
    VLOG(log, qname << ": Do have: "<<validset.first.first<<"/"<<DNSRecordContent::NumberToType(validset.first.second)<<endl);
53!
333
    if (validset.first.second == QType::NSEC) {
53!
334
      for (const auto& records : validset.second.records) {
53✔
335
        VLOG(log, qname << ":\t"<<records->getZoneRepresentation()<<endl);
53!
336
        auto nsec = std::dynamic_pointer_cast<const NSECRecordContent>(records);
53✔
337
        if (!nsec) {
53!
338
          continue;
×
339
        }
×
340

341
        const DNSName owner = getNSECOwnerName(validset.first.first, validset.second.signatures);
53✔
342
        VLOG(log, qname << ": Comparing owner: "<<owner<<" with target: "<<wildcard<<endl);
53!
343

344
        if (qname != owner && qname.isPartOf(owner) && nsec->isSet(QType::DNAME)) {
53!
345
          /* rfc6672 section 5.3.2: DNAME Bit in NSEC Type Map
346

347
             In any negative response, the NSEC or NSEC3 [RFC5155] record type
348
             bitmap SHOULD be checked to see that there was no DNAME that could
349
             have been applied.  If the DNAME bit in the type bitmap is set and
350
             the query name is a subdomain of the closest encloser that is
351
             asserted, then DNAME substitution should have been done, but the
352
             substitution has not been done as specified.
353
          */
354
          VLOG(log, qname << ":\tThe qname is a subdomain of the NSEC and the DNAME bit is set"<<endl);
×
355
          return false;
×
356
        }
×
357

358
        if (wildcard != owner && isCoveredByNSEC(wildcard, owner, nsec->d_next)) {
53!
359
          VLOG(log, qname << ":\tWildcard is covered"<<endl);
51!
360
          return true;
51✔
361
        }
51✔
362
      }
53✔
363
    }
53✔
364
  }
53✔
365

366
  return false;
2✔
367
}
53✔
368

369
/*
370
  This function checks whether the non-existence of a wildcard covering qname|qtype
371
  is proven by the NSEC3 records in validrrsets.
372
  If `wildcardExists` is not NULL, if will be set to true if a wildcard exists
373
  for this qname but doesn't have this qtype.
374
*/
375
static bool provesNSEC3NoWildCard(const DNSName& closestEncloser, uint16_t const qtype, const cspmap_t& validrrsets, bool* wildcardExists, const OptLog& log, pdns::validation::ValidationContext& context)
376
{
1,493✔
377
  auto wildcard = g_wildcarddnsname + closestEncloser;
1,493✔
378
  VLOG(log, closestEncloser << ": Trying to prove that there is no wildcard for "<<wildcard<<"/"<<QType(qtype)<<endl);
1,493!
379

380
  for (const auto& validset : validrrsets) {
2,969✔
381
    VLOG(log, closestEncloser << ": Do have: "<<validset.first.first<<"/"<<DNSRecordContent::NumberToType(validset.first.second)<<endl);
2,969!
382
    if (validset.first.second == QType::NSEC3) {
2,969!
383
      for (const auto& records : validset.second.records) {
2,969✔
384
        VLOG(log, closestEncloser << ":\t"<<records->getZoneRepresentation()<<endl);
2,969!
385
        auto nsec3 = std::dynamic_pointer_cast<const NSEC3RecordContent>(records);
2,969✔
386
        if (!nsec3) {
2,969!
387
          continue;
×
388
        }
×
389

390
        const DNSName signer = getSigner(validset.second.signatures);
2,969✔
391
        if (!validset.first.first.isPartOf(signer) || !closestEncloser.isPartOf(signer)) {
2,969!
392
          continue;
×
393
        }
×
394

395
        string hash = getHashFromNSEC3(wildcard, *nsec3, context);
2,969✔
396
        if (hash.empty()) {
2,969!
397
          VLOG(log, closestEncloser << ": Unsupported hash, ignoring"<<endl);
×
398
          return false;
×
399
        }
×
400
        VLOG(log, closestEncloser << ":\tWildcard hash: "<<toBase32Hex(hash)<<endl);
2,969!
401
        string beginHash=fromBase32Hex(validset.first.first.getRawLabels()[0]);
2,969✔
402
        VLOG(log, closestEncloser << ":\tNSEC3 hash: "<<toBase32Hex(beginHash)<<" -> "<<toBase32Hex(nsec3->d_nexthash)<<endl);
2,969!
403

404
        if (beginHash == hash) {
2,969✔
405
          VLOG(log, closestEncloser << ":\tWildcard hash matches");
17!
406
          if (wildcardExists != nullptr) {
17!
407
            *wildcardExists = true;
17✔
408
          }
17✔
409

410
          /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
411
             Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
412
             nonexistence of any RRs below that zone cut, which include all RRs at
413
             that (original) owner name other than DS RRs, and all RRs below that
414
             owner name regardless of type.
415
          */
416
          if (qtype != QType::DS && isNSEC3AncestorDelegation(signer, validset.first.first, *nsec3)) {
17!
417
            /* this is an "ancestor delegation" NSEC3 RR */
418
            VLOG_NO_PREFIX(log, " BUT an ancestor delegation NSEC3 RR can only deny the existence of a DS"<<endl);
×
419
            return false;
×
420
          }
×
421

422
          if (qtype == 0 || isTypeDenied(*nsec3, QType(qtype))) {
17!
423
            VLOG_NO_PREFIX(log, " and proves that the type did not exist"<<endl);
11!
424
            return true;
11✔
425
          }
11✔
426
          VLOG_NO_PREFIX(log, " BUT the type did exist!"<<endl);
6!
427
          return false;
6✔
428
        }
17✔
429

430
        if (isCoveredByNSEC3Hash(hash, beginHash, nsec3->d_nexthash)) {
2,952✔
431
          VLOG(log, closestEncloser << ":\tWildcard hash is covered"<<endl);
21!
432
          return true;
21✔
433
        }
21✔
434
      }
2,952✔
435
    }
2,969✔
436
  }
2,969✔
437

438
  return false;
1,455✔
439
}
1,493✔
440

441
dState matchesNSEC(const DNSName& name, uint16_t qtype, const DNSName& nsecOwner, const NSECRecordContent& nsec, const std::vector<std::shared_ptr<const RRSIGRecordContent>>& signatures, const OptLog& log)
442
{
163✔
443
  const DNSName signer = getSigner(signatures);
163✔
444
  if (!name.isPartOf(signer) || !nsecOwner.isPartOf(signer)) {
163!
445
    return dState::INCONCLUSIVE;
×
446
  }
×
447

448
  const DNSName owner = getNSECOwnerName(nsecOwner, signatures);
163✔
449
  /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
450
     Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
451
     nonexistence of any RRs below that zone cut, which include all RRs at
452
     that (original) owner name other than DS RRs, and all RRs below that
453
     owner name regardless of type.
454
  */
455
  if (name.isPartOf(owner) && isNSECAncestorDelegation(signer, owner, nsec)) {
163✔
456
    /* this is an "ancestor delegation" NSEC RR */
457
    if (qtype != QType::DS || name != owner) {
6✔
458
      VLOG_NO_PREFIX(log, "An ancestor delegation NSEC RR can only deny the existence of a DS"<<endl);
4!
459
      return dState::NODENIAL;
4✔
460
    }
4✔
461
  }
6✔
462

463
  /* check if the type is denied */
464
  if (name == owner) {
159✔
465
    if (!isTypeDenied(nsec, QType(qtype))) {
35✔
466
      VLOG_NO_PREFIX(log, "does _not_ deny existence of type "<<QType(qtype)<<endl);
15!
467
      return dState::NODENIAL;
15✔
468
    }
15✔
469

470
    if (qtype == QType::DS && signer == name) {
20✔
471
      VLOG_NO_PREFIX(log, "the NSEC comes from the child zone and cannot be used to deny a DS"<<endl);
2!
472
      return dState::NODENIAL;
2✔
473
    }
2✔
474

475
    VLOG_NO_PREFIX(log, "Denies existence of type "<<QType(qtype)<<endl);
18!
476
    return dState::NXQTYPE;
18✔
477
  }
20✔
478

479
  if (name.isPartOf(owner) && nsec.isSet(QType::DNAME)) {
124✔
480
    /* rfc6672 section 5.3.2: DNAME Bit in NSEC Type Map
481

482
       In any negative response, the NSEC or NSEC3 [RFC5155] record type
483
       bitmap SHOULD be checked to see that there was no DNAME that could
484
       have been applied.  If the DNAME bit in the type bitmap is set and
485
       the query name is a subdomain of the closest encloser that is
486
       asserted, then DNAME substitution should have been done, but the
487
       substitution has not been done as specified.
488
    */
489
    VLOG(log, "the DNAME bit is set and the query name is a subdomain of that NSEC");
9!
490
    return dState::NODENIAL;
9✔
491
  }
9✔
492

493
  if (isCoveredByNSEC(name, owner, nsec.d_next)) {
115✔
494
    VLOG_NO_PREFIX(log, name << ": is covered by ("<<owner<<" to "<<nsec.d_next<<")");
34!
495

496
    if (nsecProvesENT(name, owner, nsec.d_next)) {
34✔
497
      VLOG_NO_PREFIX(log, " denies existence of type "<<name<<"/"<<QType(qtype)<<" by proving that "<<name<<" is an ENT"<<endl);
13!
498
      return dState::NXQTYPE;
13✔
499
    }
13✔
500

501
    return dState::NXDOMAIN;
21✔
502
  }
34✔
503

504
  return dState::INCONCLUSIVE;
81✔
505
}
115✔
506

507
[[nodiscard]] uint64_t getNSEC3DenialProofWorstCaseIterationsCount(uint8_t maxLabels, uint16_t iterations, size_t saltLength)
508
{
80✔
509
  return static_cast<uint64_t>((iterations + 1U + (saltLength > 0 ? 1U : 0U))) * maxLabels;
80✔
510
}
80✔
511

512
/*
513
  This function checks whether the existence of qname|qtype is denied by the NSEC and NSEC3
514
  in validrrsets.
515
  - If `referralToUnsigned` is true and qtype is QType::DS, this functions returns NODENIAL
516
  if a NSEC or NSEC3 proves that the name exists but no NS type exists, as specified in RFC 5155 section 8.9.
517
  - If `wantsNoDataProof` is set but a NSEC proves that the whole name does not exist, the function will return
518
  NXQTYPE if the name is proven to be ENT and NXDOMAIN otherwise.
519
  - If `needWildcardProof` is false, the proof that a wildcard covering this qname|qtype is not checked. It is
520
  useful when we have a positive answer synthesized from a wildcard and we only need to prove that the exact
521
  name does not exist.
522
*/
523
dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16_t qtype, bool referralToUnsigned, bool wantsNoDataProof, pdns::validation::ValidationContext& context, const OptLog& log, bool needWildcardProof, unsigned int wildcardLabelsCount) // NOLINT(readability-function-cognitive-complexity): https://github.com/PowerDNS/pdns/issues/12791
524
{
2,095✔
525
  bool nsec3Seen = false;
2,095✔
526
  if (!needWildcardProof && wildcardLabelsCount == 0) {
2,095!
527
    throw PDNSException("Invalid wildcard labels count for the validation of a positive answer synthesized from a wildcard");
×
528
  }
×
529

530
  uint8_t numberOfLabelsOfParentZone{std::numeric_limits<uint8_t>::max()};
2,095✔
531
  uint16_t nsec3sConsidered = 0;
2,095✔
532
  for (const auto& validset : validrrsets) {
3,693✔
533
    VLOG(log, qname << ": Do have: "<<validset.first.first<<"/"<<DNSRecordContent::NumberToType(validset.first.second)<<endl);
3,693!
534

535
    if (validset.first.second==QType::NSEC) {
3,693✔
536
      for (const auto& record : validset.second.records) {
575✔
537
        VLOG(log, qname << ":\t"<<record->getZoneRepresentation()<<endl);
575!
538

539
        if (validset.second.signatures.empty()) {
575!
540
          continue;
×
541
        }
×
542

543
        auto nsec = std::dynamic_pointer_cast<const NSECRecordContent>(record);
575✔
544
        if (!nsec) {
575!
545
          continue;
×
546
        }
×
547

548
        const DNSName owner = getNSECOwnerName(validset.first.first, validset.second.signatures);
575✔
549
        const DNSName signer = getSigner(validset.second.signatures);
575✔
550
        if (!validset.first.first.isPartOf(signer) || !owner.isPartOf(signer) || !qname.isPartOf(signer)) {
575!
551
           continue;
4✔
552
        }
4✔
553

554
        /* The NSEC is either a delegation one, from the parent zone, and
555
         * must have the NS bit set but not the SOA one, or a regular NSEC
556
         * either at apex (signer == owner) or with the SOA or NS bits clear.
557
         */
558
        const bool notApex = signer.countLabels() < owner.countLabels();
571✔
559
        if (notApex && nsec->isSet(QType::NS) && nsec->isSet(QType::SOA)) {
571✔
560
          VLOG(log, qname << ": However, that NSEC is not at the apex and has both the NS and the SOA bits set!"<<endl);
2!
561
          continue;
2✔
562
        }
2✔
563

564
        /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
565
           Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
566
           nonexistence of any RRs below that zone cut, which include all RRs at
567
           that (original) owner name other than DS RRs, and all RRs below that
568
           owner name regardless of type.
569
        */
570
        if (qname.isPartOf(owner) && isNSECAncestorDelegation(signer, owner, *nsec)) {
569✔
571
          /* this is an "ancestor delegation" NSEC RR */
572
          if (qtype != QType::DS || qname != owner) {
113!
573
            VLOG(log, qname << ": An ancestor delegation NSEC RR can only deny the existence of a DS"<<endl);
4!
574
            return dState::NODENIAL;
4✔
575
          }
4✔
576
        }
113✔
577

578
        if (qtype == QType::DS && !qname.isRoot() && signer == qname) {
565✔
579
          VLOG(log, qname << ": A NSEC RR from the child zone cannot deny the existence of a DS"<<endl);
2!
580
          continue;
2✔
581
        }
2✔
582

583
        /* check if the type is denied */
584
        if (qname == owner) {
563✔
585
          if (!isTypeDenied(*nsec, QType(qtype))) {
193✔
586
            VLOG(log, qname << ": Does _not_ deny existence of type "<<QType(qtype)<<endl);
4!
587
            return dState::NODENIAL;
4✔
588
          }
4✔
589

590
          VLOG(log, qname << ": Denies existence of type "<<QType(qtype)<<endl);
189!
591

592
          /*
593
           * RFC 4035 Section 2.3:
594
           * The bitmap for the NSEC RR at a delegation point requires special
595
           * attention.  Bits corresponding to the delegation NS RRset and any
596
           * RRsets for which the parent zone has authoritative data MUST be set
597
           */
598
          if (referralToUnsigned && qtype == QType::DS) {
189!
599
            if (!nsec->isSet(QType::NS)) {
75✔
600
              VLOG(log, qname << ": However, no NS record exists at this level!"<<endl);
2!
601
              return dState::NODENIAL;
2✔
602
            }
2✔
603
          }
75✔
604

605
          /* we know that the name exists (but this qtype doesn't) so except
606
             if the answer was generated by a wildcard expansion, no wildcard
607
             could have matched (rfc4035 section 5.4 bullet 1) */
608
          if (needWildcardProof && (!isWildcardExpanded(owner, validset.second.signatures) || isWildcardExpandedOntoItself(owner, validset.second.signatures))) {
187!
609
            needWildcardProof = false;
187✔
610
          }
187✔
611

612
          if (!needWildcardProof) {
187!
613
            return dState::NXQTYPE;
187✔
614
          }
187✔
615

616
          DNSName closestEncloser = getClosestEncloserFromNSEC(qname, owner, nsec->d_next);
×
617
          if (provesNoWildCard(qname, qtype, closestEncloser, validrrsets, log)) {
×
618
            return dState::NXQTYPE;
×
619
          }
×
620

621
          VLOG(log, qname << ": But the existence of a wildcard is not denied for "<<qname<<"/"<<endl);
×
622
          return dState::NODENIAL;
×
623
        }
×
624

625
        if (qname.isPartOf(owner) && nsec->isSet(QType::DNAME)) {
370!
626
          /* rfc6672 section 5.3.2: DNAME Bit in NSEC Type Map
627

628
             In any negative response, the NSEC or NSEC3 [RFC5155] record type
629
             bitmap SHOULD be checked to see that there was no DNAME that could
630
             have been applied.  If the DNAME bit in the type bitmap is set and
631
             the query name is a subdomain of the closest encloser that is
632
             asserted, then DNAME substitution should have been done, but the
633
             substitution has not been done as specified.
634
          */
635
          VLOG(log, qname << ": The DNAME bit is set and the query name is a subdomain of that NSEC"<< endl);
×
636
          return dState::NODENIAL;
×
637
        }
×
638

639
        /* check if the whole NAME is denied existing */
640
        if (isCoveredByNSEC(qname, owner, nsec->d_next)) {
370✔
641
          VLOG(log, qname<< ": Is covered by ("<<owner<<" to "<<nsec->d_next<<") ");
298!
642

643
          if (nsecProvesENT(qname, owner, nsec->d_next)) {
298✔
644
            if (wantsNoDataProof) {
9✔
645
              /* if the name is an ENT and we received a NODATA answer,
646
                 we are fine with a NSEC proving that the name does not exist. */
647
              VLOG_NO_PREFIX(log, "Denies existence of type "<<qname<<"/"<<QType(qtype)<<" by proving that "<<qname<<" is an ENT"<<endl);
7!
648
              return dState::NXQTYPE;
7✔
649
            }
7✔
650
            /* but for a NXDOMAIN proof, this doesn't make sense! */
651
            VLOG_NO_PREFIX(log, "but it tries to deny the existence of "<<qname<<" by proving that "<<qname<<" is an ENT, this does not make sense!"<<endl);
2!
652
            return dState::NODENIAL;
2✔
653
          }
9✔
654

655
          if (!needWildcardProof) {
289✔
656
            VLOG_NO_PREFIX(log, "and we did not need a wildcard proof"<<endl);
218!
657
            return dState::NXDOMAIN;
218✔
658
          }
218✔
659

660
          VLOG_NO_PREFIX(log, "but we do need a wildcard proof so ");
71!
661
          DNSName closestEncloser = getClosestEncloserFromNSEC(qname, owner, nsec->d_next);
71✔
662
          if (wantsNoDataProof) {
71✔
663
            VLOG_NO_PREFIX(log, "looking for NODATA proof"<<endl);
18!
664
            if (provesNoDataWildCard(qname, qtype, closestEncloser, validrrsets, log)) {
18✔
665
              return dState::NXQTYPE;
8✔
666
            }
8✔
667
          }
18✔
668
          else {
53✔
669
            VLOG_NO_PREFIX(log, "looking for NO wildcard proof"<<endl);
53!
670
            if (provesNoWildCard(qname, qtype, closestEncloser, validrrsets, log)) {
53✔
671
              return dState::NXDOMAIN;
51✔
672
            }
51✔
673
          }
53✔
674

675
          VLOG(log, qname << ": But the existence of a wildcard is not denied for "<<qname<<"/"<<endl);
12!
676
          return dState::NODENIAL;
12✔
677
        }
71✔
678

679
        VLOG(log, qname << ": Did not deny existence of "<<QType(qtype)<<", "<<validset.first.first<<"?="<<qname<<", "<<nsec->isSet(qtype)<<", next: "<<nsec->d_next<<endl);
72!
680
      }
72✔
681
    } else if(validset.first.second==QType::NSEC3) {
3,435!
682
      for (const auto& record : validset.second.records) {
3,120✔
683
        VLOG(log, qname << ":\t"<<record->getZoneRepresentation()<<endl);
3,119!
684
        auto nsec3 = std::dynamic_pointer_cast<const NSEC3RecordContent>(record);
3,119✔
685
        if (!nsec3) {
3,119!
686
          continue;
×
687
        }
×
688

689
        if (validset.second.signatures.empty()) {
3,119!
690
          continue;
×
691
        }
×
692

693
        const DNSName& hashedOwner = validset.first.first;
3,119✔
694
        const DNSName signer = getSigner(validset.second.signatures);
3,119✔
695
        if (!hashedOwner.isPartOf(signer)) {
3,119✔
696
          VLOG(log, qname << ": Owner "<<hashedOwner<<" is not part of the signer "<<signer<<", ignoring"<<endl);
2!
697
          continue;
2✔
698
        }
2✔
699
        numberOfLabelsOfParentZone = std::min(numberOfLabelsOfParentZone, static_cast<uint8_t>(signer.countLabels()));
3,117✔
700

701
        if (!qname.isPartOf(signer)) {
3,117!
702
          continue;
×
703
        }
×
704

705
        if (qtype == QType::DS && !qname.isRoot() && signer == qname) {
3,118!
706
          VLOG(log, qname << ": A NSEC3 RR from the child zone cannot deny the existence of a DS"<<endl);
2!
707
          continue;
2✔
708
        }
2✔
709

710
        if (g_maxNSEC3sPerRecordToConsider > 0 && nsec3sConsidered >= g_maxNSEC3sPerRecordToConsider) {
3,116✔
711
          VLOG(log, qname << ": Too many NSEC3s for this record"<<endl);
4!
712
          context.d_limitHit = true;
4✔
713
          return dState::NODENIAL;
4✔
714
        }
4✔
715
        nsec3sConsidered++;
3,111✔
716

717
        string hash = getHashFromNSEC3(qname, *nsec3, context);
3,111✔
718
        if (hash.empty()) {
3,111✔
719
          VLOG(log, qname << ": Unsupported hash, ignoring"<<endl);
6!
720
          return dState::INSECURE;
6✔
721
        }
6✔
722

723
        nsec3Seen = true;
3,105✔
724

725
        VLOG(log, qname << ":\tquery hash: "<<toBase32Hex(hash)<<endl);
3,105!
726
        string beginHash = fromBase32Hex(hashedOwner.getRawLabels()[0]);
3,105✔
727

728
        // If the name exists, check if the qtype is denied
729
        if (beginHash == hash) {
3,105✔
730

731
          /* The NSEC3 is either a delegation one, from the parent zone, and
732
           * must have the NS bit set but not the SOA one, or a regular NSEC3
733
           * either at apex (signer == owner) or with the SOA or NS bits clear.
734
           */
735
          const bool notApex = signer.countLabels() < qname.countLabels();
50✔
736
          if (notApex && nsec3->isSet(QType::NS) && nsec3->isSet(QType::SOA)) {
50✔
737
            VLOG(log, qname << ": However, that NSEC3 is not at the apex and has both the NS and the SOA bits set!"<<endl);
2!
738
            continue;
2✔
739
          }
2✔
740

741
          /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
742
             Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
743
             nonexistence of any RRs below that zone cut, which include all RRs at
744
             that (original) owner name other than DS RRs, and all RRs below that
745
             owner name regardless of type.
746
          */
747
          if (qtype != QType::DS && isNSEC3AncestorDelegation(signer, qname, *nsec3)) {
48✔
748
            /* this is an "ancestor delegation" NSEC3 RR */
749
            VLOG(log, qname << ": An ancestor delegation NSEC3 RR can only deny the existence of a DS"<<endl);
2!
750
            return dState::NODENIAL;
2✔
751
          }
2✔
752

753
          if (!isTypeDenied(*nsec3, QType(qtype))) {
46✔
754
            VLOG(log, qname << ": Does _not_ deny existence of type "<<QType(qtype)<<" for name "<<qname<<" (not opt-out)."<<endl);
2!
755
            return dState::NODENIAL;
2✔
756
          }
2✔
757

758
          VLOG(log, qname << ": Denies existence of type "<<QType(qtype)<<" for name "<<qname<<" (not opt-out)."<<endl);
44!
759

760
          /*
761
           * RFC 5155 section 8.9:
762
           * If there is an NSEC3 RR present in the response that matches the
763
           * delegation name, then the validator MUST ensure that the NS bit is
764
           * set and that the DS bit is not set in the Type Bit Maps field of the
765
           * NSEC3 RR.
766
           */
767
          if (referralToUnsigned && qtype == QType::DS) {
44!
768
            if (!nsec3->isSet(QType::NS)) {
15✔
769
              VLOG(log, qname << ": However, no NS record exists at this level!"<<endl);
2!
770
              return dState::NODENIAL;
2✔
771
            }
2✔
772
          }
15✔
773

774
          return dState::NXQTYPE;
42✔
775
        }
44✔
776
      }
3,105✔
777
    }
3,120✔
778
  }
3,693✔
779

780
  /* if we have no NSEC3 records, we are done */
781
  if (!nsec3Seen) {
1,542✔
782
    return dState::NODENIAL;
32✔
783
  }
32✔
784

785
  DNSName closestEncloser(qname);
1,510✔
786
  bool found = false;
1,510✔
787
  if (needWildcardProof) {
1,510✔
788
    /* We now need to look for a NSEC3 covering the closest (provable) encloser
789
       RFC 5155 section-7.2.1
790
       RFC 7129 section-5.5
791
    */
792
    VLOG(log, qname << ": Now looking for the closest encloser for "<<qname<<endl);
1,501!
793

794
    while (!found && closestEncloser.chopOff() && closestEncloser.countLabels() >= numberOfLabelsOfParentZone) {
3,016!
795
      nsec3sConsidered = 0;
1,515✔
796

797
      for(const auto& validset : validrrsets) {
2,474✔
798
        if(validset.first.second==QType::NSEC3) {
2,474!
799
          for(const auto& record : validset.second.records) {
2,474✔
800
            VLOG(log, qname << ":\t"<<record->getZoneRepresentation()<<endl);
2,474!
801
            auto nsec3 = std::dynamic_pointer_cast<const NSEC3RecordContent>(record);
2,474✔
802
            if (!nsec3) {
2,474!
803
              continue;
×
804
            }
×
805

806
            const DNSName signer = getSigner(validset.second.signatures);
2,474✔
807
            if (!validset.first.first.isPartOf(signer)) {
2,474!
808
              VLOG(log, qname << ": Owner "<<validset.first.first<<" is not part of the signer "<<signer<<", ignoring"<<endl);
×
809
              continue;
×
810
            }
×
811

812
            if (!closestEncloser.isPartOf(signer)) {
2,474!
813
              continue;
×
814
            }
×
815

816
            if (g_maxNSEC3sPerRecordToConsider > 0 && nsec3sConsidered >= g_maxNSEC3sPerRecordToConsider) {
2,474!
817
              VLOG(log, qname << ": Too many NSEC3s for this record"<<endl);
×
818
              context.d_limitHit = true;
×
819
              return dState::NODENIAL;
×
820
            }
×
821
            nsec3sConsidered++;
2,474✔
822

823
            string hash = getHashFromNSEC3(closestEncloser, *nsec3, context);
2,474✔
824
            if (hash.empty()) {
2,474!
825
              VLOG(log, qname << ": Unsupported hash, ignoring"<<endl);
×
826
              return dState::INSECURE;
×
827
            }
×
828

829
            string beginHash=fromBase32Hex(validset.first.first.getRawLabels()[0]);
2,474✔
830

831
            VLOG(log, qname << ": Comparing "<<toBase32Hex(hash)<<" ("<<closestEncloser<<") against "<<toBase32Hex(beginHash)<<endl);
2,474!
832
            if (beginHash == hash) {
2,474✔
833
              /* If the closest encloser is a delegation NS we know nothing about the names in the child zone. */
834
              if (isNSEC3AncestorDelegation(signer, validset.first.first, *nsec3)) {
1,499✔
835
                VLOG(log, qname << ": An ancestor delegation NSEC3 RR can only deny the existence of a DS"<<endl);
4!
836
                continue;
4✔
837
              }
4✔
838

839
              VLOG(log, qname << ": Closest encloser for "<<qname<<" is "<<closestEncloser<<endl);
1,495!
840
              found = true;
1,495✔
841

842
              if (nsec3->isSet(QType::DNAME)) {
1,495!
843
                /* rfc6672 section 5.3.2: DNAME Bit in NSEC Type Map
844

845
                   In any negative response, the NSEC or NSEC3 [RFC5155] record type
846
                   bitmap SHOULD be checked to see that there was no DNAME that could
847
                   have been applied.  If the DNAME bit in the type bitmap is set and
848
                   the query name is a subdomain of the closest encloser that is
849
                   asserted, then DNAME substitution should have been done, but the
850
                   substitution has not been done as specified.
851
                */
852
                VLOG(log, qname << ":\tThe closest encloser NSEC3 has the DNAME bit is set"<<endl);
×
853
                return dState::NODENIAL;
×
854
              }
×
855

856
              break;
1,495✔
857
            }
1,495✔
858
          }
2,474✔
859
        }
2,474✔
860
        if (found) {
2,474✔
861
          break;
1,495✔
862
        }
1,495✔
863
      }
2,474✔
864
    }
1,515✔
865
  }
1,501✔
866
  else {
9✔
867
    /* RFC 5155 section-7.2.6:
868
       "It is not necessary to return an NSEC3 RR that matches the closest encloser,
869
       as the existence of this closest encloser is proven by the presence of the
870
       expanded wildcard in the response.
871
    */
872
    found = true;
9✔
873
    unsigned int closestEncloserLabelsCount = closestEncloser.countLabels();
9✔
874
    while (wildcardLabelsCount > 0 && closestEncloserLabelsCount > wildcardLabelsCount) {
20!
875
      closestEncloser.chopOff();
11✔
876
      closestEncloserLabelsCount--;
11✔
877
    }
11✔
878
  }
9✔
879

880
  bool nextCloserFound = false;
1,510✔
881
  bool isOptOut = false;
1,510✔
882

883
  if (found) {
1,510✔
884
    /* now that we have found the closest (provable) encloser,
885
       we can construct the next closer (RFC7129 section-5.5) name
886
       and look for a NSEC3 RR covering it */
887
    unsigned int labelIdx = qname.countLabels() - closestEncloser.countLabels();
1,504✔
888
    if (labelIdx >= 1) {
1,504!
889
      DNSName nextCloser(closestEncloser);
1,504✔
890
      nextCloser.prependRawLabel(qname.getRawLabel(labelIdx - 1));
1,504✔
891
      nsec3sConsidered = 0;
1,504✔
892
      VLOG(log, qname << ":Looking for a NSEC3 covering the next closer name "<<nextCloser<<endl);
1,504!
893

894
      for (const auto& validset : validrrsets) {
2,099✔
895
        if (validset.first.second == QType::NSEC3) {
2,099!
896
          for (const auto& record : validset.second.records) {
2,099✔
897
            VLOG(log, qname << ":\t"<<record->getZoneRepresentation()<<endl);
2,099!
898
            auto nsec3 = std::dynamic_pointer_cast<const NSEC3RecordContent>(record);
2,099✔
899
            if (!nsec3) {
2,099!
900
              continue;
×
901
            }
×
902

903
            if (g_maxNSEC3sPerRecordToConsider > 0 && nsec3sConsidered >= g_maxNSEC3sPerRecordToConsider) {
2,099!
904
              VLOG(log, qname << ": Too many NSEC3s for this record"<<endl);
×
905
              context.d_limitHit = true;
×
906
              return dState::NODENIAL;
×
907
            }
×
908
            nsec3sConsidered++;
2,099✔
909

910
            string hash = getHashFromNSEC3(nextCloser, *nsec3, context);
2,099✔
911
            if (hash.empty()) {
2,099!
912
              VLOG(log, qname << ": Unsupported hash, ignoring"<<endl);
×
913
              return dState::INSECURE;
×
914
            }
×
915

916
            const DNSName signer = getSigner(validset.second.signatures);
2,099✔
917
            if (!validset.first.first.isPartOf(signer)) {
2,099✔
918
              VLOG(log, qname << ": Owner "<<validset.first.first<<" is not part of the signer "<<signer<<", ignoring"<<endl);
2!
919
              continue;
2✔
920
            }
2✔
921

922
            if (!nextCloser.isPartOf(signer)) {
2,097!
923
              continue;
×
924
            }
×
925

926
            string beginHash=fromBase32Hex(validset.first.first.getRawLabels()[0]);
2,097✔
927

928
            VLOG(log, qname << ": Comparing "<<toBase32Hex(hash)<<" against "<<toBase32Hex(beginHash)<<" -> "<<toBase32Hex(nsec3->d_nexthash)<<endl);
2,097!
929
            if (isCoveredByNSEC3Hash(hash, beginHash, nsec3->d_nexthash)) {
2,097✔
930
              VLOG(log, qname << ": Denies existence of name "<<qname<<"/"<<QType(qtype));
1,502!
931
              nextCloserFound = true;
1,502✔
932

933
              if (nsec3->isOptOut()) {
1,502✔
934
                VLOG_NO_PREFIX(log, " but is opt-out!");
1,459!
935
                isOptOut = true;
1,459✔
936
              }
1,459✔
937

938
              VLOG_NO_PREFIX(log, endl);
1,502!
939
              break;
1,502✔
940
            }
1,502✔
941
            VLOG(log, qname << ": Did not cover us ("<<qname<<"), start="<<validset.first.first<<", us="<<toBase32Hex(hash)<<", end="<<toBase32Hex(nsec3->d_nexthash)<<endl);
595!
942
          }
595✔
943
        }
2,099✔
944
        if (nextCloserFound) {
2,099✔
945
          break;
1,502✔
946
        }
1,502✔
947
      }
2,099✔
948
    }
1,504✔
949
  }
1,504✔
950

951
  if (nextCloserFound) {
1,510✔
952
    bool wildcardExists = false;
1,502✔
953
    /* RFC 7129 section-5.6 */
954
    if (needWildcardProof && !provesNSEC3NoWildCard(closestEncloser, qtype, validrrsets, &wildcardExists, log, context)) {
1,502✔
955
      if (!isOptOut) {
1,459✔
956
        VLOG(log, qname << ": But the existence of a wildcard is not denied for "<<qname<<"/"<<QType(qtype)<<endl);
8!
957
        return dState::NODENIAL;
8✔
958
      }
8✔
959
    }
1,459✔
960

961
    if (isOptOut) {
1,494✔
962
      return dState::OPTOUT;
1,459✔
963
    }
1,459✔
964
    if (wildcardExists) {
35✔
965
      return dState::NXQTYPE;
11✔
966
    }
11✔
967
    return dState::NXDOMAIN;
24✔
968
  }
35✔
969

970
  // There were no valid NSEC(3) records
971
  return dState::NODENIAL;
8✔
972
}
1,510✔
973

974
bool isRRSIGNotExpired(const time_t now, const RRSIGRecordContent& sig)
975
{
17,897✔
976
  // Should use https://www.rfc-editor.org/rfc/rfc4034.txt section 3.1.5
977
  return sig.d_sigexpire >= now;
17,897✔
978
}
17,897✔
979

980
bool isRRSIGIncepted(const time_t now, const RRSIGRecordContent& sig)
981
{
4,455✔
982
  // Should use https://www.rfc-editor.org/rfc/rfc4034.txt section 3.1.5
983
  return sig.d_siginception - g_signatureInceptionSkew <= now;
4,455✔
984
}
4,455✔
985

986
namespace {
987
[[nodiscard]] bool checkSignatureInceptionAndExpiry(const DNSName& qname, time_t now, const RRSIGRecordContent& sig, vState& ede, const OptLog& log)
988
{
4,424✔
989
  /* rfc4035:
990
     - The validator's notion of the current time MUST be less than or equal to the time listed in the RRSIG RR's Expiration field.
991
     - The validator's notion of the current time MUST be greater than or equal to the time listed in the RRSIG RR's Inception field.
992
  */
993
  if (isRRSIGIncepted(now, sig) && isRRSIGNotExpired(now, sig)) {
4,424✔
994
    return true;
4,418✔
995
  }
4,418✔
996
  ede = ((sig.d_siginception - g_signatureInceptionSkew) > now) ? vState::BogusSignatureNotYetValid : vState::BogusSignatureExpired;
6✔
997
  VLOG(log, qname << ": Signature is "<<(ede == vState::BogusSignatureNotYetValid ? "not yet valid" : "expired")<<" (inception: "<<sig.d_siginception<<", inception skew: "<<g_signatureInceptionSkew<<", expiration: "<<sig.d_sigexpire<<", now: "<<now<<")"<<endl);
6!
998
  return false;
6✔
999
}
4,424✔
1000

1001
[[nodiscard]] bool checkSignatureWithKey(const DNSName& qname, const RRSIGRecordContent& sig, const DNSKEYRecordContent& key, const std::string& msg, vState& ede, const OptLog& log)
1002
{
4,362✔
1003
  bool result = false;
4,362✔
1004
  try {
4,362✔
1005
    auto dke = DNSCryptoKeyEngine::makeFromPublicKeyString(key.d_algorithm, key.d_key);
4,362✔
1006
    result = dke->verify(msg, sig.d_signature);
4,362✔
1007
    VLOG(log, qname << ": Signature by key with tag "<<sig.d_tag<<" and algorithm "<<DNSSECKeeper::algorithm2name(sig.d_algorithm)<<" was " << (result ? "" : "NOT ")<<"valid"<<endl);
4,362!
1008
    if (!result) {
4,362✔
1009
      ede = vState::BogusNoValidRRSIG;
27✔
1010
    }
27✔
1011
  }
4,362✔
1012
  catch (const std::exception& e) {
4,362✔
1013
    VLOG(log, qname << ": Could not make a validator for signature: "<<e.what()<<endl);
×
1014
    ede = vState::BogusUnsupportedDNSKEYAlgo;
×
1015
  }
×
1016
  return result;
4,362✔
1017
}
4,362✔
1018

1019
}
1020

1021
vState validateWithKeySet(time_t now, const DNSName& name, const sortedRecords_t& toSign, const vector<shared_ptr<const RRSIGRecordContent> >& signatures, const skeyset_t& keys, const OptLog& log, pdns::validation::ValidationContext& context, bool validateAllSigs)
1022
{
4,147✔
1023
  bool missingKey = false;
4,147✔
1024
  bool isValid = false;
4,147✔
1025
  bool allExpired = true;
4,147✔
1026
  bool noneIncepted = true;
4,147✔
1027
  uint16_t signaturesConsidered = 0;
4,147✔
1028

1029
  for (const auto& signature : signatures) {
4,164✔
1030
    unsigned int labelCount = name.countLabels();
4,164✔
1031
    if (signature->d_labels > labelCount) {
4,164!
1032
      VLOG(log, name<<": Discarding invalid RRSIG whose label count is "<<signature->d_labels<<" while the RRset owner name has only "<<labelCount<<endl);
×
1033
      continue;
×
1034
    }
×
1035

1036
    vState ede = vState::Indeterminate;
4,164✔
1037
    if (!DNSCryptoKeyEngine::isAlgorithmSupported(signature->d_algorithm)) {
4,164!
1038
        continue;
×
1039
    }
×
1040
    if (!checkSignatureInceptionAndExpiry(name, now, *signature, ede, log)) {
4,164✔
1041
      if (isRRSIGIncepted(now, *signature)) {
6✔
1042
        noneIncepted = false;
3✔
1043
      }
3✔
1044
      if (isRRSIGNotExpired(now, *signature)) {
6✔
1045
        allExpired = false;
3✔
1046
      }
3✔
1047
      continue;
6✔
1048
    }
6✔
1049

1050
    if (g_maxRRSIGsPerRecordToConsider > 0 && signaturesConsidered >= g_maxRRSIGsPerRecordToConsider) {
4,158✔
1051
      VLOG(log, name<<": We have already considered "<<std::to_string(signaturesConsidered)<<" RRSIG"<<addS(signaturesConsidered)<<" for this record, stopping now"<<endl;);
2!
1052
      // possibly going Bogus, the RRSIGs have not been validated so Insecure would be wrong
1053
      context.d_limitHit = true;
2✔
1054
      break;
2✔
1055
    }
2✔
1056
    signaturesConsidered++;
4,156✔
1057
    context.d_validationsCounter++;
4,156✔
1058

1059
    auto keysMatchingTag = getByTag(keys, signature->d_tag, signature->d_algorithm, log);
4,156✔
1060

1061
    if (keysMatchingTag.empty()) {
4,156✔
1062
      VLOG(log, name << ": No key provided for "<<signature->d_tag<<" and algorithm "<<std::to_string(signature->d_algorithm)<<endl;);
8!
1063
      missingKey = true;
8✔
1064
      continue;
8✔
1065
    }
8✔
1066

1067
    string msg = getMessageForRRSET(name, *signature, toSign, true);
4,148✔
1068
    uint16_t dnskeysConsidered = 0;
4,148✔
1069
    for (const auto& key : keysMatchingTag) {
4,150✔
1070
      if (g_maxDNSKEYsToConsider > 0 && dnskeysConsidered >= g_maxDNSKEYsToConsider) {
4,150✔
1071
        VLOG(log, name << ": We have already considered "<<std::to_string(dnskeysConsidered)<<" DNSKEY"<<addS(dnskeysConsidered)<<" for tag "<<std::to_string(signature->d_tag)<<" and algorithm "<<std::to_string(signature->d_algorithm)<<", not considering the remaining ones for this signature"<<endl;);
2!
1072
        if (!isValid) {
2!
1073
          context.d_limitHit = true;
2✔
1074
        }
2✔
1075
        return isValid ? vState::Secure : vState::BogusNoValidRRSIG;
2!
1076
      }
2✔
1077
      dnskeysConsidered++;
4,148✔
1078

1079
      bool signIsValid = checkSignatureWithKey(name, *signature, *key, msg, ede, log);
4,148✔
1080

1081
      if (signIsValid) {
4,148✔
1082
        isValid = true;
4,119✔
1083
        VLOG(log, name<< ": Validated "<<name<<"/"<<DNSRecordContent::NumberToType(signature->d_type)<<endl);
4,119!
1084
        //          cerr<<"valid"<<endl;
1085
        //          cerr<<"! validated "<<i->first.first<<"/"<<)<<endl;
1086
      }
4,119✔
1087
      else {
29✔
1088
        VLOG(log, name << ": signature invalid"<<endl);
29!
1089
        if (isRRSIGIncepted(now, *signature)) {
29✔
1090
          noneIncepted = false;
27✔
1091
        }
27✔
1092
        if (isRRSIGNotExpired(now, *signature)) {
29✔
1093
          allExpired = false;
27✔
1094
        }
27✔
1095
      }
29✔
1096

1097
      if (signIsValid && !validateAllSigs) {
4,148✔
1098
        return vState::Secure;
4,117✔
1099
      }
4,117✔
1100
    }
4,148✔
1101
  }
4,148✔
1102

1103
  if (isValid) {
2,147,483,675✔
1104
    return vState::Secure;
3✔
1105
  }
3✔
1106
  if (missingKey) {
2,147,483,672✔
1107
    return vState::BogusNoValidRRSIG;
8✔
1108
  }
8✔
1109
  if (noneIncepted) {
2,147,483,664✔
1110
    // ede should be vState::BogusSignatureNotYetValid
1111
    return vState::BogusSignatureNotYetValid;
3✔
1112
  }
3✔
1113
  if (allExpired) {
2,147,483,661✔
1114
    // ede should be vState::BogusSignatureExpired);
1115
    return vState::BogusSignatureExpired;
3✔
1116
  }
3✔
1117

1118
  return vState::BogusNoValidRRSIG;
2,147,483,658✔
1119
}
2,147,483,661✔
1120

1121
bool getTrustAnchor(const map<DNSName,dsset_t>& anchors, const DNSName& zone, dsset_t &res)
1122
{
53,600✔
1123
  const auto& iter = anchors.find(zone);
53,600✔
1124

1125
  if (iter == anchors.cend()) {
53,600✔
1126
    return false;
37,392✔
1127
  }
37,392✔
1128

1129
  res = iter->second;
16,208✔
1130
  return true;
16,208✔
1131
}
53,600✔
1132

1133
bool haveNegativeTrustAnchor(const map<DNSName,std::string>& negAnchors, const DNSName& zone, std::string& reason)
1134
{
53,617✔
1135
  const auto& iter = negAnchors.find(zone);
53,617✔
1136

1137
  if (iter == negAnchors.cend()) {
53,617✔
1138
    return false;
53,601✔
1139
  }
53,601✔
1140

1141
  reason = iter->second;
16✔
1142
  return true;
16✔
1143
}
53,617✔
1144

1145
vState validateDNSKeysAgainstDS(time_t now, const DNSName& zone, const dsset_t& dsset, const skeyset_t& tkeys, const sortedRecords_t& toSign, const vector<shared_ptr<const RRSIGRecordContent> >& sigs, skeyset_t& validkeys, const OptLog& log, pdns::validation::ValidationContext& context) // NOLINT(readability-function-cognitive-complexity)
1146
{
1,763✔
1147
  /*
1148
   * Check all DNSKEY records against all DS records and place all DNSKEY records
1149
   * that have DS records (that we support the algo for) in the tentative key storage
1150
   */
1151
  uint16_t dssConsidered = 0;
1,763✔
1152
  for (const auto& dsrc : dsset) {
2,552✔
1153
    if (g_maxDSsToConsider > 0 && dssConsidered > g_maxDSsToConsider) {
2,552✔
1154
      VLOG(log, zone << ": We have already considered "<<std::to_string(dssConsidered)<<" DS"<<addS(dssConsidered)<<", not considering the remaining ones"<<endl;);
2!
1155
      return vState::BogusNoValidDNSKEY;
2✔
1156
    }
2✔
1157
    ++dssConsidered;
2,550✔
1158

1159
    uint16_t dnskeysConsidered = 0;
2,550✔
1160
    auto record = getByTag(tkeys, dsrc.d_tag, dsrc.d_algorithm, log);
2,550✔
1161
    // cerr<<"looking at DS with tag "<<dsrc.d_tag<<", algo "<<DNSSECKeeper::algorithm2name(dsrc.d_algorithm)<<", digest "<<std::to_string(dsrc.d_digesttype)<<" for "<<zone<<", got "<<r.size()<<" DNSKEYs for tag"<<endl;
1162

1163
    for (const auto& drc : record) {
2,550✔
1164
      bool isValid = false;
1,774✔
1165
      bool dsCreated = false;
1,774✔
1166
      DSRecordContent dsrc2;
1,774✔
1167

1168
      if (g_maxDNSKEYsToConsider > 0 && dnskeysConsidered >= g_maxDNSKEYsToConsider) {
1,774✔
1169
        VLOG(log, zone << ": We have already considered "<<std::to_string(dnskeysConsidered)<<" DNSKEY"<<addS(dnskeysConsidered)<<" for tag "<<std::to_string(dsrc.d_tag)<<" and algorithm "<<std::to_string(dsrc.d_algorithm)<<", not considering the remaining ones for this DS"<<endl;);
4!
1170
        // we need to break because we can have a partially validated set
1171
        // where the KSK signs the ZSK(s), and even if we don't
1172
        // we are going to try to get the correct EDE status (revoked, expired, ...)
1173
        context.d_limitHit = true;
4✔
1174
        break;
4✔
1175
      }
4✔
1176
      dnskeysConsidered++;
1,770✔
1177

1178
      try {
1,770✔
1179
        dsrc2 = makeDSFromDNSKey(zone, *drc, dsrc.d_digesttype);
1,770✔
1180
        dsCreated = true;
1,770✔
1181
        isValid = dsrc == dsrc2;
1,770✔
1182
      }
1,770✔
1183
      catch (const std::exception &e) {
1,770✔
1184
        VLOG(log, zone << ": Unable to make DS from DNSKey: "<<e.what()<<endl);
×
1185
      }
×
1186

1187
      if (isValid) {
1,770✔
1188
        VLOG(log, zone << ": got valid DNSKEY (it matches the DS) with tag "<<dsrc.d_tag<<" and algorithm "<<std::to_string(dsrc.d_algorithm)<<" for "<<zone<<endl);
1,768!
1189

1190
        validkeys.insert(drc);
1,768✔
1191
      }
1,768✔
1192
      else {
2✔
1193
        if (dsCreated) {
2!
1194
          VLOG(log, zone << ": DNSKEY did not match the DS, parent DS: "<<dsrc.getZoneRepresentation() << " ! = "<<dsrc2.getZoneRepresentation()<<endl);
2!
1195
        }
2✔
1196
      }
2✔
1197
    }
1,770✔
1198
  }
2,550✔
1199

1200
  vState ede = vState::BogusNoValidDNSKEY;
1,761✔
1201

1202
  //    cerr<<"got "<<validkeys.size()<<"/"<<tkeys.size()<<" valid/tentative keys"<<endl;
1203
  // these counts could be off if we somehow ended up with
1204
  // duplicate keys. Should switch to a type that prevents that.
1205
  if (!tkeys.empty() && validkeys.size() < tkeys.size()) {
1,761!
1206
    // this should mean that we have one or more DS-validated DNSKEYs
1207
    // but not a fully validated DNSKEY set, yet
1208
    // one of these valid DNSKEYs should be able to validate the
1209
    // whole set
1210
    uint16_t signaturesConsidered = 0;
259✔
1211
    for (const auto& sig : sigs) {
260✔
1212
      if (!DNSCryptoKeyEngine::isAlgorithmSupported(sig->d_algorithm)) {
260!
1213
        continue;
×
1214
      }
×
1215
      if (!checkSignatureInceptionAndExpiry(zone, now, *sig, ede, log)) {
260!
1216
        continue;
×
1217
      }
×
1218

1219
      //        cerr<<"got sig for keytag "<<i->d_tag<<" matching "<<getByTag(tkeys, i->d_tag).size()<<" keys of which "<<getByTag(validkeys, i->d_tag).size()<<" valid"<<endl;
1220
      auto bytag = getByTag(validkeys, sig->d_tag, sig->d_algorithm, log);
260✔
1221

1222
      if (bytag.empty()) {
260✔
1223
        continue;
46✔
1224
      }
46✔
1225

1226
      if (g_maxRRSIGsPerRecordToConsider > 0 && signaturesConsidered >= g_maxRRSIGsPerRecordToConsider) {
214!
1227
        VLOG(log, zone << ": We have already considered "<<std::to_string(signaturesConsidered)<<" RRSIG"<<addS(signaturesConsidered)<<" for this record, stopping now"<<endl;);
×
1228
        // possibly going Bogus, the RRSIGs have not been validated so Insecure would be wrong
1229
        context.d_limitHit = true;
×
1230
        return vState::BogusNoValidDNSKEY;
×
1231
      }
×
1232

1233
      string msg = getMessageForRRSET(zone, *sig, toSign);
214✔
1234
      uint16_t dnskeysConsidered = 0;
214✔
1235
      for (const auto& key : bytag) {
214!
1236
        if (g_maxDNSKEYsToConsider > 0 && dnskeysConsidered >= g_maxDNSKEYsToConsider) {
214!
1237
          VLOG(log, zone << ": We have already considered "<<std::to_string(dnskeysConsidered)<<" DNSKEY"<<addS(dnskeysConsidered)<<" for tag "<<std::to_string(sig->d_tag)<<" and algorithm "<<std::to_string(sig->d_algorithm)<<", not considering the remaining ones for this signature"<<endl;);
×
1238
          context.d_limitHit = true;
×
1239
          return vState::BogusNoValidDNSKEY;
×
1240
        }
×
1241
        dnskeysConsidered++;
214✔
1242

1243
        if (g_maxRRSIGsPerRecordToConsider > 0 && signaturesConsidered >= g_maxRRSIGsPerRecordToConsider) {
214!
1244
          VLOG(log, zone << ": We have already considered "<<std::to_string(signaturesConsidered)<<" RRSIG"<<addS(signaturesConsidered)<<" for this record, stopping now"<<endl;);
×
1245
          // possibly going Bogus, the RRSIGs have not been validated so Insecure would be wrong
1246
          context.d_limitHit = true;
×
1247
          return vState::BogusNoValidDNSKEY;
×
1248
        }
×
1249
        //          cerr<<"validating : ";
1250
        bool signIsValid = checkSignatureWithKey(zone, *sig, *key, msg, ede, log);
214✔
1251
        signaturesConsidered++;
214✔
1252
        context.d_validationsCounter++;
214✔
1253

1254
        if (signIsValid) {
214!
1255
          VLOG(log, zone << ": Validation succeeded - whole DNSKEY set is valid"<<endl);
214!
1256
          validkeys = tkeys;
214✔
1257
          break;
214✔
1258
        }
214✔
1259
        VLOG(log, zone << ": Validation did not succeed!"<<endl);
×
1260
      }
×
1261

1262
      if (validkeys.size() == tkeys.size()) {
214!
1263
        // we validated the whole DNSKEY set already */
1264
        break;
214✔
1265
      }
214✔
1266
      //        if(validkeys.empty()) cerr<<"did not manage to validate DNSKEY set based on DS-validated KSK, only passing KSK on"<<endl;
1267
    }
214✔
1268
  }
259✔
1269

1270
  if (validkeys.size() < tkeys.size()) {
1,761✔
1271
    /* so we failed to validate the whole set, let's try to find out why exactly */
1272
    bool dnskeyAlgoSupported = false;
45✔
1273
    bool dsDigestSupported = false;
45✔
1274

1275
    for (const auto& dsrc : dsset)
45✔
1276
    {
50✔
1277
      if (DNSCryptoKeyEngine::isAlgorithmSupported(dsrc.d_algorithm)) {
50!
1278
        dnskeyAlgoSupported = true;
50✔
1279
        if (DNSCryptoKeyEngine::isDigestSupported(dsrc.d_digesttype)) {
50!
1280
          dsDigestSupported = true;
50✔
1281
        }
50✔
1282
      }
50✔
1283
    }
50✔
1284

1285
    if (!dnskeyAlgoSupported) {
45!
1286
      return vState::BogusUnsupportedDNSKEYAlgo;
×
1287
    }
×
1288
    if (!dsDigestSupported) {
45!
1289
      return vState::BogusUnsupportedDSDigestType;
×
1290
    }
×
1291

1292
    bool zoneKey = false;
45✔
1293
    bool notRevoked = false;
45✔
1294
    bool validProtocol = false;
45✔
1295

1296
    for (const auto& key : tkeys) {
50✔
1297
      if (!isAZoneKey(*key)) {
50✔
1298
        continue;
2✔
1299
      }
2✔
1300
      zoneKey = true;
48✔
1301

1302
      if (isRevokedKey(*key)) {
48✔
1303
        continue;
2✔
1304
      }
2✔
1305
      notRevoked = true;
46✔
1306

1307
      if (key->d_protocol != 3) {
46!
1308
        continue;
×
1309
      }
×
1310
      validProtocol = true;
46✔
1311
    }
46✔
1312

1313
    if (!zoneKey) {
45✔
1314
      return vState::BogusNoZoneKeyBitSet;
2✔
1315
    }
2✔
1316
    if (!notRevoked) {
43✔
1317
      return vState::BogusRevokedDNSKEY;
2✔
1318
    }
2✔
1319
    if (!validProtocol) {
41!
1320
      return vState::BogusInvalidDNSKEYProtocol;
×
1321
    }
×
1322

1323
    return ede;
41✔
1324
  }
41✔
1325

1326
  return vState::Secure;
1,716✔
1327
}
1,761✔
1328

1329
bool isSupportedDS(const DSRecordContent& dsrec, const OptLog& log)
1330
{
36,778✔
1331
  if (!DNSCryptoKeyEngine::isAlgorithmSupported(dsrec.d_algorithm)) {
36,778✔
1332
    VLOG(log, "Discarding DS "<<dsrec.d_tag<<" because we don't support algorithm number "<<std::to_string(dsrec.d_algorithm)<<endl);
4!
1333
    return false;
4✔
1334
  }
4✔
1335

1336
  if (!DNSCryptoKeyEngine::isDigestSupported(dsrec.d_digesttype)) {
36,774✔
1337
    VLOG(log, "Discarding DS "<<dsrec.d_tag<<" because we don't support digest number "<<std::to_string(dsrec.d_digesttype)<<endl);
4!
1338
    return false;
4✔
1339
  }
4✔
1340

1341
  return true;
36,770✔
1342
}
36,774✔
1343

1344
DNSName getSigner(const std::vector<std::shared_ptr<const RRSIGRecordContent> >& signatures)
1345
{
24,613✔
1346
  for (const auto& sig : signatures) {
24,613!
1347
    if (sig) {
24,613✔
1348
      return sig->d_signer;
24,613✔
1349
    }
24,613✔
1350
  }
24,612✔
1351

UNCOV
1352
  return {};
×
1353
}
24,613✔
1354

1355
const std::string& vStateToString(vState state)
1356
{
13,639✔
1357
  static const std::vector<std::string> vStates = {"Indeterminate", "Insecure", "Secure", "NTA", "TA", "Bogus - No valid DNSKEY", "Bogus - Invalid denial", "Bogus - Unable to get DSs", "Bogus - Unable to get DNSKEYs", "Bogus - Self Signed DS", "Bogus - No RRSIG", "Bogus - No valid RRSIG", "Bogus - Missing negative indication", "Bogus - Signature not yet valid", "Bogus - Signature expired", "Bogus - Unsupported DNSKEY algorithm", "Bogus - Unsupported DS digest type", "Bogus - No zone key bit set", "Bogus - Revoked DNSKEY", "Bogus - Invalid DNSKEY Protocol" };
13,639✔
1358
  return vStates.at(static_cast<size_t>(state));
13,639✔
1359
}
13,639✔
1360

1361
std::ostream& operator<<(std::ostream &ostr, const vState dstate)
1362
{
13,446✔
1363
  ostr<<vStateToString(dstate);
13,446✔
1364
  return ostr;
13,446✔
1365
}
13,446✔
1366

1367
std::ostream& operator<<(std::ostream &ostr, const dState dstate)
1368
{
×
1369
  static const std::vector<std::string> dStates = {"no denial", "inconclusive", "nxdomain", "nxqtype", "empty non-terminal", "insecure", "opt-out"};
×
1370
  ostr<<dStates.at(static_cast<size_t>(dstate));
×
1371
  return ostr;
×
1372
}
×
1373

1374
void updateDNSSECValidationState(vState& state, const vState stateUpdate)
1375
{
11,048✔
1376
  if (stateUpdate == vState::TA) {
11,048!
1377
    state = vState::Secure;
×
1378
  }
×
1379
  else if (stateUpdate == vState::NTA) {
11,048!
1380
    state = vState::Insecure;
×
1381
  }
×
1382
  else if (vStateIsBogus(stateUpdate) || state == vState::Indeterminate) {
11,048✔
1383
    state = stateUpdate;
5,419✔
1384
  }
5,419✔
1385
  else if (stateUpdate == vState::Insecure) {
5,629✔
1386
    if (!vStateIsBogus(state)) {
4,417✔
1387
      state = vState::Insecure;
4,413✔
1388
    }
4,413✔
1389
  }
4,417✔
1390
}
11,048✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc