1/* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2011 Apple Computer, Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18// ***************************************************************************
19// nsec.c: This file contains support functions to validate NSEC records for
20// NODATA and NXDOMAIN error.
21// ***************************************************************************
22
23#include "mDNSEmbeddedAPI.h"
24#include "DNSCommon.h"
25#include "nsec.h"
26#include "nsec3.h"
27
28// Define DNSSEC_DISABLED to remove all the DNSSEC functionality
29// and use the stub functions implemented later in this file.
30
31#ifndef DNSSEC_DISABLED
32
33// Implementation Notes
34//
35// NSEC records in DNSSEC are used for authenticated denial of existence i.e., if the response to a query
36// results in NXDOMAIN or NODATA error, the response also contains NSEC records in the additional section
37// to prove the non-existence of the original name. In most of the cases, NSEC records don't have any
38// relationship to the original name queried i.e, if they are cached based on the name like other records,
39// it can't be located to prove the non-existence of the original name. Hence, we create a negative cache
40// record like we do for the NXDOMAIN/NODATA error and then cache the NSEC records as part of that. Sometimes,
41// NSEC records are also used for wildcard expanded answer in which case they are cached with the cache record
42// that is created for the original name. NSEC records are freed when the parent cache (the record that they
43// are attached to is expired).
44//
45// NSEC records also can be queried like any other record and hence can exist independent of the negative
46// cache record. It exists as part of negative cache record only when we get a NXDOMAIN/NODATA error with
47// NSEC records. When a query results in NXDOMAIN/NODATA error and needs to be validated, the NSEC
48// records (and its RRSIGS) are cached as part of the negative cache record. The NSEC records that
49// exist separately from the negative cache record should not be used to answer ValidationRequired/
50// ValidatingResponse questions as it may not be sufficient to prove the non-existence of the name.
51// The exception is when the NSEC record is looked up explicitly. See DNSSECRecordAnswersQuestion
52// for more details.
53//
54
55mDNSlocal CacheRecord *NSECParentForQuestion(mDNS *const m, DNSQuestion *q)
56{
57    CacheGroup *cg;
58    CacheRecord *cr;
59    mDNSu32 slot;
60    mDNSu32 namehash;
61
62    slot = HashSlot(&q->qname);
63    namehash = DomainNameHashValue(&q->qname);
64    cg = CacheGroupForName(m, slot, namehash, &q->qname);
65    if (!cg)
66    {
67        LogDNSSEC("NSECParentForQuestion: Cannot find cg for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
68        return mDNSNULL;
69    }
70    for (cr = cg->members; cr; cr = cr->next)
71        if (SameNameRecordAnswersQuestion(&cr->resrec, q))
72            return cr;
73    return mDNSNULL;
74}
75
76mDNSlocal void UpdateParent(DNSSECVerifier *dv)
77{
78    AuthChainLink(dv->parent, dv->ac);
79    ResetAuthChain(dv);
80    dv->parent->NumPackets += dv->NumPackets;
81}
82
83// Note: This should just call the parent callback which will free the DNSSECVerifier.
84mDNSlocal void VerifyNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status)
85{
86    if (!dv->parent)
87    {
88        LogMsg("VerifyNSECCCallback: ERROR!! no parent DV\n");
89        FreeDNSSECVerifier(m, dv);
90        return;
91    }
92    if (dv->ac)
93    {
94        // Before we free the "dv", we need to update the
95        // parent with our AuthChain information
96        UpdateParent(dv);
97    }
98    // "status" indicates whether we are able to successfully verify
99    // the NSEC/NSEC3 signatures. For NSEC3, the OptOut flag may be set
100    // for which we need to deliver insecure result.
101    if ((dv->parent->flags & NSEC3_OPT_OUT) && (status == DNSSEC_Secure))
102    {
103        dv->parent->DVCallback(m, dv->parent, DNSSEC_Insecure);
104    }
105    else
106    {
107        dv->parent->DVCallback(m, dv->parent, status);
108    }
109    // The callback we called in the previous line should recursively
110    // free all the DNSSECVerifiers starting from dv->parent and above.
111    // So, set that to NULL and free the "dv" itself here.
112    dv->parent = mDNSNULL;
113    FreeDNSSECVerifier(m, dv);
114}
115
116// If the caller provides a callback, it takes the responsibility of calling the original callback
117// in "pdv" when it is done.
118//
119// INPUT:
120//
121// rr: The NSEC record that should be verified
122// rv: The NSEC record can also be provided like this
123// pdv: Parent DNSSECVerifier which will be called when the verification is done.
124// callback:  As part of the proof, we need multiple NSEC verifications before we call the "pdv" callback in
125// which case a intermediate "callback" is provided which can be used to do multiple verifications.
126// ncr: The cache record where the RRSIGS are cached
127//
128// NSEC records and signatures are cached along with the cache record so that we can expire them all together. We can't cache
129// them based on the name hash like other records as in most cases the returned NSECs has a different name than we asked for
130// (except for NODATA error where the name exists but type does not exist).
131//
132mDNSexport void VerifyNSEC(mDNS *const m, ResourceRecord *rr, RRVerifier *rv, DNSSECVerifier *pdv, CacheRecord *ncr, DNSSECVerifierCallback callback)
133{
134    DNSSECVerifier *dv = mDNSNULL;
135    CacheRecord **rp;
136    const domainname *name;
137    mDNSu16 rrtype;
138
139    if (!rv && !rr)
140    {
141        LogDNSSEC("VerifyNSEC: Both rr and rv are NULL");
142        goto error;
143    }
144    if (!pdv)
145    {
146        LogDNSSEC("VerifyNSEC: ERROR!! pdv is NULL");
147        return;
148    }
149    // Remember the name and type for which we are verifying, so that when we are done processing all
150    // the verifications, we can trace it back.
151    //
152    // Note: Currently it is not used because when the verification completes as we just
153    // call the "pdv" callback which has its origName and origType.
154    if (rr)
155    {
156        name = rr->name;
157        rrtype = rr->rrtype;
158    }
159    else
160    {
161        name = &rv->name;
162        rrtype = rv->rrtype;
163    }
164
165    dv = AllocateDNSSECVerifier(m, name, rrtype, pdv->q.InterfaceID, DNSSEC_VALIDATION_SECURE,
166        (callback ? callback : VerifyNSECCallback), mDNSNULL);
167    if (!dv)
168    {
169        LogMsg("VerifyNSEC: mDNSPlatformMemAlloc failed");
170        return;
171    }
172
173    dv->parent = pdv;
174
175    if (AddRRSetToVerifier(dv, rr, rv, RRVS_rr) != mStatus_NoError)
176    {
177        LogMsg("VerifyNSEC: ERROR!! AddRRSetToVerifier failed to add NSEC");
178        goto error;
179    }
180
181    // Add the signatures after validating them
182    rp = &(ncr->nsec);
183    while (*rp)
184    {
185        if ((*rp)->resrec.rrtype == kDNSType_RRSIG)
186        {
187            ValidateRRSIG(dv, RRVS_rrsig, &(*rp)->resrec);
188        }
189        rp=&(*rp)->next;
190    }
191
192    if (!dv->rrset)
193    {
194        LogMsg("VerifyNSEC: ERROR!! AddRRSetToVerifier missing rrset");
195        goto error;
196    }
197    // Expired signatures.
198    if (!dv->rrsig)
199        goto error;
200
201    // Next step is to fetch the keys
202    dv->next = RRVS_key;
203
204    StartDNSSECVerification(m, dv);
205    return;
206error:
207    pdv->DVCallback(m, pdv, DNSSEC_Bogus);
208    if (dv)
209    {
210        dv->parent = mDNSNULL;
211        FreeDNSSECVerifier(m, dv);
212    }
213    return;
214}
215
216mDNSlocal void DeleteCachedNSECS(mDNS *const m, CacheRecord *cr)
217{
218    CacheRecord *rp, *next;
219
220    if (cr->nsec) LogDNSSEC("DeleteCachedNSECS: Deleting NSEC Records\n");
221    for (rp = cr->nsec; rp; rp = next)
222    {
223        next  = rp->next;
224        ReleaseCacheRecord(m, rp);
225    }
226    cr->nsec = mDNSNULL;
227}
228
229// Returns success if it adds the nsecs and the rrsigs to the cache record. Otherwise, it returns
230// failure (mDNSfalse)
231mDNSexport mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, CacheRecord *negcr, mDNSu8 rcode)
232{
233    CacheRecord *cr;
234    mDNSBool nsecs_seen = mDNSfalse;
235    mDNSBool nsec3s_seen = mDNSfalse;
236
237    if (rcode != kDNSFlag1_RC_NoErr && rcode != kDNSFlag1_RC_NXDomain)
238    {
239        LogMsg("AddNSECSForCacheRecord: Addings nsecs for rcode %d", rcode);
240        return mDNSfalse;
241    }
242
243    // Sanity check the list to see if we have anything else other than
244    // NSECs and its RRSIGs
245    for (cr = crlist; cr; cr = cr->next)
246    {
247        if (cr->resrec.rrtype != kDNSType_NSEC && cr->resrec.rrtype != kDNSType_NSEC3 &&
248            cr->resrec.rrtype != kDNSType_SOA && cr->resrec.rrtype != kDNSType_RRSIG)
249        {
250            LogMsg("AddNSECSForCacheRecord: ERROR!! Adding Wrong record %s", CRDisplayString(m, cr));
251            return mDNSfalse;
252        }
253        if (cr->resrec.rrtype == kDNSType_RRSIG)
254        {
255            RDataBody2 *const rdb = (RDataBody2 *)cr->smallrdatastorage.data;
256            rdataRRSig *rrsig = &rdb->rrsig;
257            mDNSu16 tc = swap16(rrsig->typeCovered);
258            if (tc != kDNSType_NSEC && tc != kDNSType_NSEC3 && tc != kDNSType_SOA)
259            {
260                LogMsg("AddNSECSForCacheRecord:ERROR!! Adding RRSIG with Wrong type %s", CRDisplayString(m, cr));
261                return mDNSfalse;
262            }
263        }
264        else if (cr->resrec.rrtype == kDNSType_NSEC)
265        {
266            nsecs_seen = mDNStrue;
267        }
268        else if (cr->resrec.rrtype == kDNSType_NSEC3)
269        {
270            nsec3s_seen = mDNStrue;
271        }
272        LogDNSSEC("AddNSECSForCacheRecord: Found a valid record %s", CRDisplayString(m, cr));
273    }
274    if ((nsecs_seen && nsec3s_seen) || (!nsecs_seen && !nsec3s_seen))
275    {
276        LogDNSSEC("AddNSECSForCacheRecord:ERROR  nsecs_seen %d, nsec3s_seen %d", nsecs_seen, nsec3s_seen);
277        return mDNSfalse;
278    }
279    DeleteCachedNSECS(m, negcr);
280    LogDNSSEC("AddNSECSForCacheRecord: Adding NSEC Records for %s", CRDisplayString(m, negcr));
281    negcr->nsec = crlist;
282    return mDNStrue;
283}
284
285// Return the number of labels that matches starting from the right (excluding the
286// root label)
287mDNSexport int CountLabelsMatch(const domainname *const d1, const domainname *const d2)
288{
289    int count, c1, c2;
290    int match, i, skip1, skip2;
291
292    c1 = CountLabels(d1);
293    skip1 = c1 - 1;
294    c2 = CountLabels(d2);
295    skip2 = c2 - 1;
296
297    // Root label always matches. And we don't include it here to
298    // match CountLabels
299    match = 0;
300
301    // Compare as many labels as possible starting from the rightmost
302    count = c1 < c2 ? c1 : c2;
303    for (i = count; i > 0; i--)
304    {
305        const domainname *da, *db;
306
307        da = SkipLeadingLabels(d1, skip1);
308        db = SkipLeadingLabels(d2, skip2);
309        if (!SameDomainName(da, db)) return match;
310        skip1--;
311        skip2--;
312        match++;
313    }
314    return match;
315}
316
317// Empty Non-Terminal (ENT): if the qname is bigger than nsec owner's name and a
318// subdomain of the nsec's nxt field, then the qname is a empty non-terminal. For
319// example, if you are looking for (in RFC 4035 example zone) "y.w.example  A"
320// record, if it is a ENT, then it would return
321//
322// x.w.example. 3600 NSEC x.y.w.example. MX RRSIG NSEC
323//
324// This function is normally called before checking for wildcard matches. If you
325// find this NSEC, there is no need to look for a wildcard record
326// that could possibly answer the question.
327mDNSlocal mDNSBool NSECAnswersENT(const ResourceRecord *const rr, domainname *qname)
328{
329    const domainname *oname = rr->name;
330    const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
331    const domainname *nxt = (const domainname *)&rdb->data;
332    int ret;
333    int subdomain;
334
335    // Is the owner name smaller than qname?
336    ret = DNSSECCanonicalOrder(oname, qname, mDNSNULL);
337    if (ret < 0)
338    {
339        // Is the next domain field a subdomain of qname ?
340        ret = DNSSECCanonicalOrder(nxt, qname, &subdomain);
341        if (subdomain)
342        {
343            if (ret <= 0)
344            {
345                LogMsg("NSECAnswersENT: ERROR!! DNSSECCanonicalOrder subdomain set "
346                       " qname %##s, NSEC %##s", qname->c, rr->name->c);
347            }
348            return mDNStrue;
349        }
350    }
351    return mDNSfalse;
352}
353
354mDNSlocal const domainname *NSECClosestEncloser(ResourceRecord *rr, domainname *qname)
355{
356    const domainname *oname = rr->name;
357    const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
358    const domainname *nxt = (const domainname *)&rdb->data;
359    int match1, match2;
360
361    match1 = CountLabelsMatch(oname, qname);
362    match2 = CountLabelsMatch(nxt, qname);
363    // Return the closest i.e the one that matches more labels
364    if (match1 > match2)
365        return SkipLeadingLabels(oname, CountLabels(oname) - match1);
366    else
367        return SkipLeadingLabels(nxt, CountLabels(nxt) - match2);
368}
369
370// Assumption: NSEC has been validated outside of this function
371//
372// Does the name exist given the name and NSEC rr ?
373//
374// Returns -1 if it is an inappropriate nsec
375// Returns 1 if the name exists
376// Returns 0 if the name does not exist
377//
378mDNSlocal int NSECNameExists(mDNS *const m, ResourceRecord *rr, domainname *name, mDNSu16 qtype)
379{
380    const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
381    const domainname *nxt = (const domainname *)&rdb->data;
382    const domainname *oname = rr->name; // owner name
383    int ret1, subdomain1;
384    int ret2, subdomain2;
385    int ret3, subdomain3;
386
387    ret1 = DNSSECCanonicalOrder(oname, name, &subdomain1);
388    if (ret1 > 0)
389    {
390        LogDNSSEC("NSECNameExists: owner name %##s is bigger than name %##s", oname->c, name->c);
391        return -1;
392    }
393
394    // Section 4.1 of draft-ietf-dnsext-dnssec-bis-updates-14:
395    //
396    //   Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume non-
397    //   existence of any RRs below that zone cut, which include all RRs at
398    //   that (original) owner name other than DS RRs, and all RRs below that
399    //   owner name regardless of type.
400    //
401    // This also implies that we can't use the child side NSEC for DS question.
402
403    if (!ret1)
404    {
405        mDNSBool soa = RRAssertsExistence(rr, kDNSType_SOA);
406        mDNSBool ns = RRAssertsExistence(rr, kDNSType_NS);
407
408        // We are here because the owner name is the same as "name". Make sure the
409        // NSEC has the right NS and SOA bits set.
410        if (qtype != kDNSType_DS && ns && !soa)
411        {
412            LogDNSSEC("NSECNameExists: Parent side NSEC %s can't be used for question %##s (%s)",
413                      RRDisplayString(m, rr), name->c, DNSTypeName(qtype));
414            return -1;
415        }
416        else if (qtype == kDNSType_DS && soa)
417        {
418            LogDNSSEC("NSECNameExists: Child side NSEC %s can't be used for question %##s (%s)",
419                      RRDisplayString(m, rr), name->c, DNSTypeName(qtype));
420            return -1;
421        }
422        LogDNSSEC("NSECNameExists: owner name %##s is same as name %##s", oname->c, name->c);
423        return 1;
424    }
425
426    // If the name is a.b.com and NSEC's owner name is b.com i.e., a subdomain
427    // and nsec comes from the parent (NS is set and SOA is not set), then this
428    // NSEC can't be used for names below the owner name.
429    //
430    // Similarly if DNAME is set, we can't use it here. See RFC2672-bis-dname
431    // appendix.
432    if (subdomain1 && (RRAssertsExistence(rr, kDNSType_DNAME) ||
433                       (RRAssertsNonexistence(rr, kDNSType_SOA) && RRAssertsExistence(rr, kDNSType_NS))))
434    {
435        LogDNSSEC("NSECNameExists: NSEC %s comes from the parent, can't use it here",
436                  RRDisplayString(m, rr));
437        return -1;
438    }
439
440    // At this stage, we know that name is greater than the owner name and
441    // the nsec is not from the parent side.
442    //
443    // Compare with the next field in the nsec.
444    //
445    ret2 = DNSSECCanonicalOrder(name, nxt, &subdomain2);
446
447    // Exact match with the nsec next name
448    if (!ret2)
449    {
450        LogDNSSEC("NSECNameExists: name %##s is same as nxt name %##s", name->c, nxt->c);
451        return 1;
452    }
453
454    ret3 = DNSSECCanonicalOrder(oname, nxt, &subdomain3);
455
456    if (!ret3)
457    {
458        // Pathological case of a single name in the domain. This means only the
459        // apex of the zone itself exists. Nothing below it. "subdomain2" indicates
460        // that name is a subdmain of "next" and hence below the zone.
461        if (subdomain2)
462        {
463            LogDNSSEC("NSECNameExists: owner name %##s subdomain of nxt name %##s", oname->c, nxt->c);
464            return 0;
465        }
466        else
467        {
468            LogDNSSEC("NSECNameExists: Single name in zone, owner name %##s is same as nxt name %##s", oname->c, nxt->c);
469            return -1;
470        }
471    }
472
473    if (ret3 < 0)
474    {
475        // Regular NSEC in the zone. Make sure that the "name" lies within
476        // oname and next. oname < name and name < next
477        if (ret1 < 0 && ret2 < 0)
478        {
479            LogDNSSEC("NSECNameExists: Normal NSEC name %##s lies within owner %##s and nxt name %##s",
480                      name->c, oname->c, nxt->c);
481            return 0;
482        }
483        else
484        {
485            LogDNSSEC("NSECNameExists: Normal NSEC name %##s does not lie within owner %##s and nxt name %##s",
486                      name->c, oname->c, nxt->c);
487            return -1;
488        }
489    }
490    else
491    {
492        // Last NSEC in the zone. The "next" is pointing to the apex. All names
493        // should be a subdomain of that and the name should be bigger than
494        // oname
495        if (ret1 < 0 && subdomain2)
496        {
497            LogDNSSEC("NSECNameExists: Last NSEC name %##s lies within owner %##s and nxt name %##s",
498                      name->c, oname->c, nxt->c);
499            return 0;
500        }
501        else
502        {
503            LogDNSSEC("NSECNameExists: Last NSEC name %##s does not lie within owner %##s and nxt name %##s",
504                      name->c, oname->c, nxt->c);
505            return -1;
506        }
507    }
508
509    LogDNSSEC("NSECNameExists: NSEC %s did not match any case", RRDisplayString(m, rr));
510    return -1;
511}
512
513// If the answer was result of a wildcard match, then this function proves
514// that a proper wildcard was used to answer the question and that the
515// original name does not exist
516mDNSexport void WildcardAnswerProof(mDNS *const m, DNSSECVerifier *dv)
517{
518    CacheRecord *ncr;
519    CacheRecord **rp;
520    const domainname *ce;
521    DNSQuestion q;
522    CacheRecord **nsec3 = mDNSNULL;
523
524    LogDNSSEC("WildcardAnswerProof: Question %##s (%s)", dv->origName.c, DNSTypeName(dv->origType));
525    //
526    // RFC 4035: Section 3.1.3.3
527    //
528    // 1) We used a wildcard because the qname does not exist, so verify
529    //    that the qname does not exist
530    //
531    // 2) Is the wildcard the right one ?
532    //
533    // Unfortunately, this is not well explained in that section. Refer to
534    // RFC 5155 section 7.2.6.
535
536    // Walk the list of nsecs we received and see if they prove that
537    // the name does not exist
538
539    mDNSPlatformMemZero(&q, sizeof(DNSQuestion));
540    q.ThisQInterval = -1;
541    InitializeQuestion(m, &q, dv->InterfaceID, &dv->origName, dv->origType, mDNSNULL, mDNSNULL);
542
543    ncr = NSECParentForQuestion(m, &q);
544    if (!ncr)
545    {
546        LogMsg("WildcardAnswerProof: Can't find NSEC Parent for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
547        goto error;
548    }
549    else
550    {
551        LogDNSSEC("WildcardAnswerProof: found %s", CRDisplayString(m, ncr));
552    }
553    rp = &(ncr->nsec);
554    while (*rp)
555    {
556        if ((*rp)->resrec.rrtype == kDNSType_NSEC)
557        {
558            CacheRecord *cr = *rp;
559            if (!NSECNameExists(m, &cr->resrec, &dv->origName, dv->origType))
560                break;
561        }
562        else if ((*rp)->resrec.rrtype == kDNSType_NSEC3)
563        {
564            nsec3 = rp;
565        }
566        rp=&(*rp)->next;
567    }
568    if (!(*rp))
569    {
570        mDNSBool ret = mDNSfalse;
571        if (nsec3)
572        {
573            ret = NSEC3WildcardAnswerProof(m, ncr, dv);
574        }
575        if (!ret)
576        {
577            LogDNSSEC("WildcardAnswerProof: NSEC3 wildcard proof failed for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
578            goto error;
579        }
580        rp = nsec3;
581    }
582    else
583    {
584        ce = NSECClosestEncloser(&((*rp)->resrec), &dv->origName);
585        if (!ce)
586        {
587            LogMsg("WildcardAnswerProof: ERROR!! Closest Encloser NULL for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
588            goto error;
589        }
590        if (!SameDomainName(ce, dv->wildcardName))
591        {
592            LogMsg("WildcardAnswerProof: ERROR!! Closest Encloser %##s does not match wildcard name %##s", q.qname.c, dv->wildcardName->c);
593            goto error;
594        }
595    }
596
597    VerifyNSEC(m, &((*rp)->resrec), mDNSNULL, dv, ncr, mDNSNULL);
598    return;
599error:
600    dv->DVCallback(m, dv, DNSSEC_Bogus);
601}
602
603// We have a NSEC. Need to see if it proves that NODATA exists for the given name. Note that this
604// function does not prove anything as proof may require more than one NSEC and this function
605// processes only one NSEC at a time.
606//
607// Returns mDNSfalse if the NSEC does not prove the NODATA error
608// Returns mDNStrue if the NSEC proves the NODATA error
609//
610mDNSlocal mDNSBool NSECNoDataError(mDNS *const m, ResourceRecord *rr, domainname *name, mDNSu16 qtype, domainname **wildcard)
611{
612    const domainname *oname = rr->name; // owner name
613
614    if (wildcard) *wildcard = mDNSNULL;
615    // RFC 4035
616    //
617    // section 3.1.3.1 : Name matches. Prove that the type does not exist and also CNAME is
618    // not set as in that case CNAME should have been returned ( CNAME part is mentioned in
619    // section 4.3 of dnssec-bis-updates.) Without the CNAME check, a positive response can
620    // be converted to a NODATA/NOERROR response.
621    //
622    // section 3.1.3.4 : No exact match for the name but there is a wildcard that could match
623    // the name but not the type. There are two NSECs in this case. One of them is a wildcard
624    // NSEC and another NSEC proving that the qname does not exist. We are called with one
625    // NSEC at a time. We return what we matched and the caller should decide whether all
626    // conditions are met for the proof.
627    if (SameDomainName(oname, name))
628    {
629        mDNSBool soa = RRAssertsExistence(rr, kDNSType_SOA);
630        mDNSBool ns = RRAssertsExistence(rr, kDNSType_NS);
631        if (qtype != kDNSType_DS)
632        {
633            // For non-DS type questions, we don't want to use the parent side records to
634            // answer it
635            if (ns && !soa)
636            {
637                LogDNSSEC("NSECNoDataError: Parent side NSEC %s, can't use for child qname %##s (%s)",
638                          RRDisplayString(m, rr), name->c, DNSTypeName(qtype));
639                return mDNSfalse;
640            }
641        }
642        else
643        {
644            if (soa)
645            {
646                LogDNSSEC("NSECNoDataError: Child side NSEC %s, can't use for parent qname %##s (%s)",
647                          RRDisplayString(m, rr), name->c, DNSTypeName(qtype));
648                return mDNSfalse;
649            }
650        }
651        if (RRAssertsExistence(rr, qtype) || RRAssertsExistence(rr, kDNSType_CNAME))
652        {
653            LogMsg("NSECNoDataError: ERROR!! qtype %s exists in %s", DNSTypeName(qtype), RRDisplayString(m, rr));
654            return mDNSfalse;
655        }
656        LogDNSSEC("NSECNoDataError: qype %s does not exist in %s", DNSTypeName(qtype), RRDisplayString(m, rr));
657        return mDNStrue;
658    }
659    else
660    {
661        // Name does not exist. Before we check for a wildcard match, make sure that
662        // this is not an ENT.
663        if (NSECAnswersENT(rr, name))
664        {
665            LogDNSSEC("NSECNoDataError: name %##s exists %s", name->c, RRDisplayString(m, rr));
666            return mDNSfalse;
667        }
668
669        // Wildcard check. If this is a wildcard NSEC, then check to see if we could
670        // have answered the question using this wildcard and it should not have the
671        // "qtype" passed in with its bitmap.
672        //
673        // See RFC 4592, on how wildcards are used to synthesize answers. Find the
674        // closest encloser and the qname should be a subdomain i.e if the wildcard
675        // is *.x.example, x.example is the closest encloser and the qname should be
676        // a subdomain e.g., y.x.example or z.y.x.example and so on.
677        if (oname->c[0] == 1 && oname->c[1] == '*')
678        {
679            int r, s;
680            const domainname *ce = SkipLeadingLabels(oname, 1);
681
682            r = DNSSECCanonicalOrder(name, ce, &s);
683            if (s)
684            {
685                if (RRAssertsExistence(rr, qtype) || RRAssertsExistence(rr, kDNSType_CNAME))
686                {
687                    LogMsg("NSECNoDataError: ERROR!! qtype %s exists in wildcard %s", DNSTypeName(qtype), RRDisplayString(m, rr));
688                    return mDNSfalse;
689                }
690                if (qtype == kDNSType_DS && RRAssertsExistence(rr, kDNSType_SOA))
691                {
692                    LogDNSSEC("NSECNoDataError: Child side wildcard NSEC %s, can't use for parent qname %##s (%s)",
693                              RRDisplayString(m, rr), name->c, DNSTypeName(qtype));
694                    return mDNSfalse;
695                }
696                else if (qtype != kDNSType_DS && RRAssertsNonexistence(rr, kDNSType_SOA) &&
697                    RRAssertsExistence(rr, kDNSType_NS))
698                {
699                    // Don't use the parent side record for this
700                    LogDNSSEC("NSECNoDataError: Parent side wildcard NSEC %s, can't use for child qname %##s (%s)",
701                              RRDisplayString(m, rr), name->c, DNSTypeName(qtype));
702                    return mDNSfalse;
703                }
704                *wildcard = (domainname *)ce;
705                LogDNSSEC("NSECNoDataError: qtype %s does not exist in wildcard %s", DNSTypeName(qtype), RRDisplayString(m, rr));
706                return mDNStrue;
707            }
708        }
709        return mDNSfalse;
710    }
711}
712
713mDNSexport void NoDataNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status)
714{
715    RRVerifier *rv;
716    DNSSECVerifier *pdv;
717    CacheRecord *ncr;
718
719    LogDNSSEC("NoDataNSECCallback: called");
720    if (!dv->parent)
721    {
722        LogMsg("NoDataNSECCCallback: no parent DV");
723        FreeDNSSECVerifier(m, dv);
724        return;
725    }
726
727    if (dv->ac)
728    {
729        // Before we free the "dv", we need to update the
730        // parent with our AuthChain information
731        UpdateParent(dv);
732    }
733
734    pdv = dv->parent;
735
736    // We don't care about the "dv" that was allocated in VerifyNSEC
737    // as it just verifies one of the nsecs. Get the original verifier and
738    // verify the other NSEC like we did the first time.
739    dv->parent = mDNSNULL;
740    FreeDNSSECVerifier(m, dv);
741
742    if (status != DNSSEC_Secure)
743    {
744        goto error;
745    }
746
747    ncr = NSECParentForQuestion(m, &pdv->q);
748    if (!ncr)
749    {
750        LogMsg("NoDataNSECCallback: Can't find NSEC Parent for %##s (%s)", pdv->q.qname.c, DNSTypeName(pdv->q.qtype));
751        goto error;
752    }
753    rv = pdv->pendingNSEC;
754    pdv->pendingNSEC = rv->next;
755    // We might have more than one pendingNSEC in the case of NSEC3. If this is the last one,
756    // we don't need to come back here; let the regular NSECCallback call the original callback.
757    rv->next = mDNSNULL;
758    LogDNSSEC("NoDataNSECCallback: Verifying %##s (%s)", rv->name.c, DNSTypeName(rv->rrtype));
759    if (!pdv->pendingNSEC)
760        VerifyNSEC(m, mDNSNULL, rv, pdv, ncr, mDNSNULL);
761    else
762        VerifyNSEC(m, mDNSNULL, rv, pdv, ncr, NoDataNSECCallback);
763    return;
764
765error:
766    pdv->DVCallback(m, pdv, status);
767}
768
769mDNSexport void NameErrorNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status)
770{
771    RRVerifier *rv;
772    DNSSECVerifier *pdv;
773    CacheRecord *ncr;
774
775    LogDNSSEC("NameErrorNSECCallback: called");
776    if (!dv->parent)
777    {
778        LogMsg("NameErrorNSECCCallback: no parent DV");
779        FreeDNSSECVerifier(m, dv);
780        return;
781    }
782
783    if (dv->ac)
784    {
785        // Before we free the "dv", we need to update the
786        // parent with our AuthChain information
787        UpdateParent(dv);
788    }
789
790    pdv = dv->parent;
791    // We don't care about the "dv" that was allocated in VerifyNSEC
792    // as it just verifies one of the nsecs. Get the original verifier and
793    // verify the other NSEC like we did the first time.
794    dv->parent = mDNSNULL;
795    FreeDNSSECVerifier(m, dv);
796
797    if (status != DNSSEC_Secure)
798    {
799        goto error;
800    }
801
802    ncr = NSECParentForQuestion(m, &pdv->q);
803    if (!ncr)
804    {
805        LogMsg("NameErrorNSECCallback: Can't find NSEC Parent for %##s (%s)", pdv->q.qname.c, DNSTypeName(pdv->q.qtype));
806        goto error;
807    }
808    rv = pdv->pendingNSEC;
809    pdv->pendingNSEC = rv->next;
810    // We might have more than one pendingNSEC in the case of NSEC3. If this is the last one,
811    // we don't need to come back here; let the regular NSECCallback call the original callback.
812    rv->next = mDNSNULL;
813    LogDNSSEC("NameErrorNSECCallback: Verifying %##s (%s)", rv->name.c, DNSTypeName(rv->rrtype));
814    if (!pdv->pendingNSEC)
815        VerifyNSEC(m, mDNSNULL, rv, pdv, ncr, mDNSNULL);
816    else
817        VerifyNSEC(m, mDNSNULL, rv, pdv, ncr, NameErrorNSECCallback);
818
819    return;
820
821error:
822    pdv->DVCallback(m, pdv, status);
823}
824
825// We get a NODATA error with no records in answer section. This proves
826// that qname does not exist.
827mDNSlocal void NoDataProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr)
828{
829    CacheRecord **rp;
830    domainname *wildcard = mDNSNULL;
831    const domainname *ce = mDNSNULL;
832    ResourceRecord *nsec_wild = mDNSNULL;
833    ResourceRecord *nsec_noname = mDNSNULL;
834
835    // NODATA Error could mean two things. The name exists with no type or there is a
836    // wildcard that matches the name but no type. This is done by NSECNoDataError.
837    //
838    // If it is the case of wildcard, there are two NSECs. One is the wildcard NSEC and
839    // the other NSEC to prove that there is no other closer match.
840
841    wildcard = mDNSNULL;
842    rp = &(ncr->nsec);
843    while (*rp)
844    {
845        if ((*rp)->resrec.rrtype == kDNSType_NSEC)
846        {
847            CacheRecord *cr = *rp;
848            if (NSECNoDataError(m, &cr->resrec, &dv->q.qname, dv->q.qtype, &wildcard))
849            {
850                if (wildcard)
851                {
852                    dv->flags |= WILDCARD_PROVES_NONAME_EXISTS;
853                    LogDNSSEC("NoDataProof: NSEC %s proves NODATA error for %##s (%s)",
854                              RRDisplayString(m, &(*rp)->resrec), dv->q.qname.c, DNSTypeName(dv->q.qtype));
855                }
856                else
857                {
858                    dv->flags |= NSEC_PROVES_NOTYPE_EXISTS;
859                    LogDNSSEC("NoDataProof: NSEC %s proves NOTYPE error for %##s (%s)",
860                              RRDisplayString(m, &(*rp)->resrec), dv->q.qname.c, DNSTypeName(dv->q.qtype));
861                }
862                nsec_wild = &cr->resrec;
863            }
864            if (!NSECNameExists(m, &cr->resrec, &dv->q.qname, dv->q.qtype))
865            {
866                LogDNSSEC("NoDataProof: NSEC %s proves that  name %##s (%s) does not exist",
867                          RRDisplayString(m, &(*rp)->resrec), dv->q.qname.c, DNSTypeName(dv->q.qtype));
868                // If we have a wildcard, then we should check to see if the closest
869                // encloser is the same as the wildcard.
870                ce = NSECClosestEncloser(&cr->resrec, &dv->q.qname);
871                dv->flags |= NSEC_PROVES_NONAME_EXISTS;
872                nsec_noname = &cr->resrec;
873            }
874        }
875        rp=&(*rp)->next;
876    }
877    if (!nsec_noname && !nsec_wild)
878    {
879        LogDNSSEC("NoDataProof: No valid NSECs for %##s (%s)", dv->q.qname.c, DNSTypeName(dv->q.qtype));
880        goto error;
881    }
882    // If the type exists, then we have to verify just that NSEC
883    if (!(dv->flags & NSEC_PROVES_NOTYPE_EXISTS))
884    {
885        // If we have a wildcard, then we should have a "ce" which matches the wildcard
886        // If we don't have a wildcard, then we should have proven that the name does not
887        // exist which means we would have set the "ce".
888        if (wildcard && !ce)
889        {
890            LogMsg("NoDataProof: Cannot prove that the name %##s (%s) does not exist", dv->q.qname.c, DNSTypeName(dv->q.qtype));
891            goto error;
892        }
893        if (wildcard && !SameDomainName(wildcard, ce))
894        {
895            LogMsg("NoDataProof: wildcard %##s does not match closest encloser %##s", wildcard->c, ce->c);
896            goto error;
897        }
898        // If a single NSEC can prove both, then we just have validate that one NSEC.
899        if (nsec_wild == nsec_noname)
900        {
901            nsec_noname = mDNSNULL;
902            dv->flags &= ~NSEC_PROVES_NONAME_EXISTS;
903        }
904    }
905
906    if ((dv->flags & (WILDCARD_PROVES_NONAME_EXISTS|NSEC_PROVES_NONAME_EXISTS)) ==
907        (WILDCARD_PROVES_NONAME_EXISTS|NSEC_PROVES_NONAME_EXISTS))
908    {
909        mStatus status;
910        RRVerifier *r = AllocateRRVerifier(nsec_noname, &status);
911        if (!r) goto error;
912        // First verify wildcard NSEC and then when we are done, we
913        // will verify the noname nsec
914        dv->pendingNSEC = r;
915        LogDNSSEC("NoDataProof: Verifying wild and noname %s", RRDisplayString(m, nsec_wild));
916        VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, NoDataNSECCallback);
917    }
918    else if ((dv->flags & WILDCARD_PROVES_NONAME_EXISTS) ||
919             (dv->flags & NSEC_PROVES_NOTYPE_EXISTS))
920    {
921        LogDNSSEC("NoDataProof: Verifying wild %s", RRDisplayString(m, nsec_wild));
922        VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, mDNSNULL);
923    }
924    else if (dv->flags & NSEC_PROVES_NONAME_EXISTS)
925    {
926        LogDNSSEC("NoDataProof: Verifying noname %s", RRDisplayString(m, nsec_noname));
927        VerifyNSEC(m, nsec_noname, mDNSNULL, dv, ncr, mDNSNULL);
928    }
929    return;
930error:
931    LogDNSSEC("NoDataProof: Error return");
932    dv->DVCallback(m, dv, DNSSEC_Bogus);
933}
934
935mDNSlocal mDNSBool NSECNoWildcard(mDNS *const m, ResourceRecord *rr, domainname *qname, mDNSu16 qtype)
936{
937    const domainname *ce;
938    domainname wild;
939
940    // If the query name is c.x.w.example and if the name does not exist, we should get
941    // get a nsec back that looks something like this:
942    //
943    //      w.example NSEC a.w.example
944    //
945    // First, we need to get the closest encloser which in this case is w.example. Wild
946    // card synthesis works by finding the closest encloser first and then look for
947    // a "*" label (assuming * label does not appear in the question). If it does not
948    // exists, it would return the NSEC at that name. And the wildcard name at the
949    // closest encloser "*.w.example" would be covered by such an NSEC. (Appending "*"
950    // makes it bigger than w.example and "* is smaller than "a" for the above NSEC)
951    //
952    ce = NSECClosestEncloser(rr, qname);
953    if (!ce) { LogMsg("NSECNoWildcard: No closest encloser for rr %s, qname %##s (%s)", qname->c, DNSTypeName(qtype)); return mDNSfalse; }
954
955    wild.c[0] = 1;
956    wild.c[1] = '*';
957    wild.c[2] = 0;
958    if (!AppendDomainName(&wild, ce))
959    {
960        LogMsg("NSECNoWildcard: ERROR!! Can't append domainname closest encloser name %##s, qname %##s (%s)", ce->c, qname->c, DNSTypeName(qtype));
961        return mDNSfalse;
962    }
963    if (NSECNameExists(m, rr, &wild, qtype) != 0)
964    {
965        LogDNSSEC("NSECNoWildcard: Wildcard name %##s exists or not valid qname %##s (%s)", wild.c, qname->c, DNSTypeName(qtype));
966        return mDNSfalse;
967    }
968    LogDNSSEC("NSECNoWildcard: Wildcard name %##s does not exist for record %s, qname %##s (%s)", wild.c,
969              RRDisplayString(m, rr), qname->c, DNSTypeName(qtype));
970    return mDNStrue;
971}
972
973// We get a NXDOMAIN error with no records in answer section. This proves
974// that qname does not exist.
975mDNSlocal void NameErrorProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr)
976{
977    CacheRecord **rp;
978    ResourceRecord *nsec_wild = mDNSNULL;
979    ResourceRecord *nsec_noname = mDNSNULL;
980    mStatus status;
981
982    // NXDOMAIN Error. We need to prove that the qname does not exist and there
983    // is no wildcard that can be used to answer the question.
984
985    rp = &(ncr->nsec);
986    while (*rp)
987    {
988        if ((*rp)->resrec.rrtype == kDNSType_NSEC)
989        {
990            CacheRecord *cr = *rp;
991            if (!NSECNameExists(m, &cr->resrec, &dv->q.qname, dv->q.qtype))
992            {
993                LogDNSSEC("NameErrorProof: NSEC %s proves name does not exist for %##s (%s)",
994                          RRDisplayString(m, &(*rp)->resrec), dv->q.qname.c, DNSTypeName(dv->q.qtype));
995                // If we have a wildcard, then we should check to see if the closest
996                // encloser is the same as the wildcard.
997                dv->flags |= NSEC_PROVES_NONAME_EXISTS;
998                nsec_noname = &cr->resrec;
999            }
1000            if (NSECNoWildcard(m, &cr->resrec, &dv->q.qname, dv->q.qtype))
1001            {
1002                dv->flags |= WILDCARD_PROVES_NONAME_EXISTS;
1003                nsec_wild = &cr->resrec;
1004                LogDNSSEC("NameErrorProof: NSEC %s proves wildcard cannot answer question for %##s (%s)",
1005                          RRDisplayString(m, &(*rp)->resrec), dv->q.qname.c, DNSTypeName(dv->q.qtype));
1006            }
1007        }
1008        rp=&(*rp)->next;
1009    }
1010    if (!nsec_noname || !nsec_wild)
1011    {
1012        LogMsg("NameErrorProof: Proof failed for %##s (%s) noname %p, wild %p", dv->q.qname.c, DNSTypeName(dv->q.qtype), nsec_noname, nsec_wild);
1013        goto error;
1014    }
1015
1016    // First verify wildcard NSEC and then when we are done, we will verify the noname nsec.
1017    // Sometimes a single NSEC can prove both that the "qname" does not exist and a wildcard
1018    // could not have produced qname. These are a few examples where this can happen.
1019    //
1020    // 1. If the zone is example.com and you look up *.example.com and if there are no wildcards,
1021    //    you will get a NSEC back "example.com NSEC a.example.com". This proves that both the
1022    //    name does not exist and *.example.com also does not exist
1023    //
1024    // 2. If the zone is example.com and it has a record like this:
1025    //
1026    //					example.com NSEC d.example.com
1027    //
1028    // any name you lookup in between like a.example.com,b.example.com etc. you will get a single
1029    // NSEC back. In that case we just have to verify only once.
1030    //
1031    if (nsec_wild != nsec_noname)
1032    {
1033        RRVerifier *r = AllocateRRVerifier(nsec_noname, &status);
1034        if (!r) goto error;
1035        dv->pendingNSEC = r;
1036        LogDNSSEC("NoDataProof: Verifying wild %s", RRDisplayString(m, nsec_wild));
1037        VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, NameErrorNSECCallback);
1038    }
1039    else
1040    {
1041        LogDNSSEC("NoDataProof: Verifying only one %s", RRDisplayString(m, nsec_wild));
1042        VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, mDNSNULL);
1043    }
1044    return;
1045error:
1046    dv->DVCallback(m, dv, DNSSEC_Bogus);
1047}
1048
1049mDNSexport CacheRecord *NSECRecordIsDelegation(mDNS *const m, domainname *name, mDNSu16 qtype)
1050{
1051    CacheGroup *cg;
1052    CacheRecord *cr;
1053    mDNSu32 slot, namehash;
1054
1055    slot = HashSlot(name);
1056    namehash = DomainNameHashValue(name);
1057
1058    cg = CacheGroupForName(m, (const mDNSu32)slot, namehash, name);
1059    if (!cg)
1060    {
1061        LogDNSSEC("NSECRecordForName: cg NULL for %##s", name);
1062        return mDNSNULL;
1063    }
1064    for (cr = cg->members; cr; cr = cr->next)
1065    {
1066        if (cr->resrec.RecordType == kDNSRecordTypePacketNegative && cr->resrec.rrtype == qtype)
1067        {
1068            CacheRecord *ncr;
1069            for (ncr = cr->nsec; ncr; ncr = ncr->next)
1070            {
1071                if (ncr->resrec.rrtype == kDNSType_NSEC &&
1072                    SameDomainName(ncr->resrec.name, name))
1073                {
1074                    // See the Insecure Delegation Proof section in dnssec-bis: DS bit and SOA bit
1075                    // should be absent
1076                    if (RRAssertsExistence(&ncr->resrec, kDNSType_SOA) ||
1077                        RRAssertsExistence(&ncr->resrec, kDNSType_DS))
1078                    {
1079                        LogDNSSEC("NSECRecordForName: found record %s for %##s (%s), but DS or SOA bit set", CRDisplayString(m, ncr), name,
1080                            DNSTypeName(qtype));
1081                        return mDNSNULL;
1082                    }
1083                    // Section 2.3 of RFC 4035 states that:
1084                    //
1085                    // Each owner name in the zone that has authoritative data or a delegation point NS RRset MUST
1086                    // have an NSEC resource record.
1087                    //
1088                    // So, if we have an NSEC record matching the question name with the NS bit set,
1089                    // then this is a delegation.
1090                    //
1091                    if (RRAssertsExistence(&ncr->resrec, kDNSType_NS))
1092                    {
1093                        LogDNSSEC("NSECRecordForName: found record %s for %##s (%s)", CRDisplayString(m, ncr), name, DNSTypeName(qtype));
1094                        return ncr;
1095                    }
1096                    else
1097                    {
1098                        LogDNSSEC("NSECRecordForName: found record %s for %##s (%s), but NS bit is not set", CRDisplayString(m, ncr), name,
1099                            DNSTypeName(qtype));
1100                        return mDNSNULL;
1101                    }
1102                }
1103            }
1104        }
1105    }
1106    return mDNSNULL;
1107}
1108
1109mDNSlocal void StartInsecureProof(mDNS * const m, DNSSECVerifier *dv)
1110{
1111    domainname trigger;
1112    DNSSECVerifier *prevdv = mDNSNULL;
1113
1114    // Remember the name that triggered the insecure proof
1115    AssignDomainName(&trigger, &dv->q.qname);
1116    while (dv->parent)
1117    {
1118        prevdv = dv;
1119        dv = dv->parent;
1120    }
1121    if (prevdv)
1122    {
1123        prevdv->parent = mDNSNULL;
1124        FreeDNSSECVerifier(m, prevdv);
1125    }
1126    // For Optional DNSSEC, we are opportunistically verifying dnssec. We don't care
1127    // if something results in bogus as we still want to deliver results to the
1128    // application e.g., CNAME processing results in bogus because the path is broken,
1129    // but we still want to follow CNAMEs so that we can deliver the final results to
1130    // the application.
1131    if (dv->ValidationRequired == DNSSEC_VALIDATION_SECURE_OPTIONAL)
1132    {
1133        LogDNSSEC("StartInsecureProof: Aborting insecure proof for %##s (%s)", dv->q.qname.c, DNSTypeName(dv->q.qtype));
1134        dv->DVCallback(m, dv, DNSSEC_Bogus);
1135        return;
1136    }
1137
1138    LogDNSSEC("StartInsecureProof for %##s (%s)", dv->q.qname.c, DNSTypeName(dv->q.qtype));
1139    // Don't start the insecure proof again after we finish the one that we start here by
1140    // setting InsecureProofDone.
1141    dv->InsecureProofDone = 1;
1142    ProveInsecure(m, dv, mDNSNULL, &trigger);
1143    return;
1144}
1145
1146mDNSexport void ValidateWithNSECS(mDNS *const m, DNSSECVerifier *dv, CacheRecord *cr)
1147{
1148    LogDNSSEC("ValidateWithNSECS: called for %s", CRDisplayString(m, cr));
1149
1150    // If we are encountering a break in the chain of trust i.e., NSEC/NSEC3s for
1151    // DS query, then do the insecure proof. This is important because if we
1152    // validate these NSECs normally and prove that they are "secure", we will
1153    // end up delivering the secure result to the original question where as
1154    // these NSEC/NSEC3s actually prove that DS does not exist and hence insecure.
1155    //
1156    // This break in the chain can happen after we have partially validated the
1157    // path (dv->ac is non-NULL) or the first time (dv->ac is NULL) after we
1158    // fetched the DNSKEY (dv->key is non-NULL). We don't want to do this
1159    // if we have just started the non-existence proof (dv->key is NULL) as
1160    // it does not indicate a break in the chain of trust.
1161    //
1162    // If we are already doing a insecurity proof, don't start another one. In
1163    // the case of NSECs, it is possible that insecurity proof starts and it
1164    // gets NSECs and as part of validating that we receive more NSECS in which
1165    // case we don't want to start another insecurity proof.
1166    if (dv->ValidationRequired != DNSSEC_VALIDATION_INSECURE &&
1167        (!dv->parent || dv->parent->ValidationRequired != DNSSEC_VALIDATION_INSECURE))
1168    {
1169         if ((dv->ac && dv->q.qtype == kDNSType_DS) ||
1170             (!dv->ac && dv->key && dv->q.qtype == kDNSType_DS))
1171        {
1172            LogDNSSEC("ValidateWithNSECS: Starting insecure proof: name %##s ac %p, key %p, parent %p", dv->q.qname.c,
1173                dv->ac, dv->key, dv->parent);
1174            StartInsecureProof(m, dv);
1175            return;
1176        }
1177    }
1178    // "parent" is set when we are validating a NSEC and we should not be here in
1179    // the normal case when parent is set. For example, we are looking up the A
1180    // record for www.example.com and following can happen.
1181    //
1182    // a) Record does not exist and we get a NSEC
1183    // b) While validating (a), we get an NSEC for the first DS record that we look up
1184    // c) Record exists but we get NSECs for the first DS record
1185    // d) We are able to partially validate (a) or (b), but we get NSECs somewhere in
1186    //    the chain
1187    //
1188    // For (a), parent is not set as we are not validating the NSEC yet. Hence we would
1189    // start the validation now.
1190    //
1191    // For (b), the parent is set, but should be caught by the above "if" block because we
1192    // should have gotten the DNSKEY at least. In the case of nested insecurity proof,
1193    // we would end up here and fail with bogus.
1194    //
1195    // For (c), the parent is not set and should be caught by the above "if" block because we
1196    // should have gotten the DNSKEY at least.
1197    //
1198    // For (d), the above "if" block would catch it as "dv->ac" is non-NULL.
1199    //
1200    // Hence, we should not come here in the normal case. Possible pathological cases are:
1201    // Insecure proof getting NSECs while validating NSECs, getting NSECs for DNSKEY for (c)
1202    // above etc.
1203    if (dv->parent)
1204    {
1205        LogDNSSEC("ValidateWithNSECS: dv parent set for %##s (%s)", dv->q.qname.c, DNSTypeName(dv->q.qtype));
1206        dv->DVCallback(m, dv, DNSSEC_Bogus);
1207        return;
1208    }
1209    if (cr->resrec.RecordType == kDNSRecordTypePacketNegative)
1210    {
1211        mDNSu8 rcode;
1212        CacheRecord *neg = cr->nsec;
1213        mDNSBool nsecs_seen = mDNSfalse;
1214
1215        while (neg)
1216        {
1217            // The list can only have NSEC or NSEC3s. This was checked when we added the
1218            // NSECs to the cache record.
1219            if (neg->resrec.rrtype == kDNSType_NSEC)
1220                nsecs_seen = mDNStrue;
1221            LogDNSSEC("ValidateWithNSECS: NSECCached Record %s", CRDisplayString(m, neg));
1222            neg = neg->next;
1223        }
1224
1225        rcode = (mDNSu8)(cr->responseFlags.b[1] & kDNSFlag1_RC_Mask);
1226        if (rcode == kDNSFlag1_RC_NoErr)
1227        {
1228            if (nsecs_seen)
1229                NoDataProof(m, dv, cr);
1230            else
1231                NSEC3NoDataProof(m, dv, cr);
1232        }
1233        else if (rcode == kDNSFlag1_RC_NXDomain)
1234        {
1235            if (nsecs_seen)
1236                NameErrorProof(m, dv, cr);
1237            else
1238                NSEC3NameErrorProof(m, dv, cr);
1239        }
1240        else
1241        {
1242            LogDNSSEC("ValidateWithNSECS: Rcode %d invalid", rcode);
1243            dv->DVCallback(m, dv, DNSSEC_Bogus);
1244        }
1245    }
1246    else
1247    {
1248        LogMsg("ValidateWithNSECS: Not a valid cache record %s for NSEC proofs", CRDisplayString(m, cr));
1249        dv->DVCallback(m, dv, DNSSEC_Bogus);
1250        return;
1251    }
1252}
1253
1254#else // !DNSSEC_DISABLED
1255
1256mDNSexport mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, CacheRecord *negcr, mDNSu8 rcode)
1257{
1258    (void)m;
1259    (void)crlist;
1260    (void)negcr;
1261    (void)rcode;
1262
1263    return mDNSfalse;
1264}
1265
1266#endif // !DNSSEC_DISABLED
1267