#include "mDNSEmbeddedAPI.h"
#include "DNSCommon.h"
#include "nsec.h"
mDNSlocal CacheRecord *NSECParentForQuestion(mDNS *const m, DNSQuestion *q)
{
CacheGroup *cg;
CacheRecord *cr;
mDNSu32 slot;
mDNSu32 namehash;
slot = HashSlot(&q->qname);
namehash = DomainNameHashValue(&q->qname);
cg = CacheGroupForName(m, slot, namehash, &q->qname);
if (!cg)
{
LogDNSSEC("NSECParentForQuestion: Cannot find cg for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
return mDNSNULL;
}
for (cr = cg->members; cr; cr = cr->next)
if (SameNameRecordAnswersQuestion(&cr->resrec, q))
return cr;
return mDNSNULL;
}
mDNSlocal void VerifyNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status)
{
if (!dv->parent)
{
LogMsg("VerifyNSECCCallback: ERROR!! no parent DV\n");
FreeDNSSECVerifier(m, dv);
return;
}
if (dv->ac)
{
AuthChainLink(dv->parent, dv->ac);
dv->ac = mDNSNULL;
dv->actail = &dv->ac;
}
dv->parent->DVCallback(m, dv->parent, status);
dv->parent = mDNSNULL;
FreeDNSSECVerifier(m, dv);
}
mDNSlocal void VerifyNSEC(mDNS *const m, ResourceRecord *rr, RRVerifier *rv, DNSSECVerifier *pdv, CacheRecord *ncr,
DNSSECVerifierCallback callback)
{
DNSSECVerifier *dv = mDNSNULL;
CacheRecord **rp;
const domainname *name;
mDNSu16 rrtype;
if (!rv && !rr)
{
LogDNSSEC("VerifyNSEC: Both rr and rv are NULL");
goto error;
}
if (!pdv)
{
LogDNSSEC("VerifyNSEC: ERROR!! pdv is NULL");
return;
}
if (rr)
{
name = rr->name;
rrtype = rr->rrtype;
}
else
{
name = &rv->name;
rrtype = rv->rrtype;
}
dv = AllocateDNSSECVerifier(m, name, rrtype, pdv->q.InterfaceID, (callback ? callback : VerifyNSECCallback), mDNSNULL);
if (!dv) { LogMsg("VerifyNSEC: mDNSPlatformMemAlloc failed"); return; }
dv->parent = pdv;
if (AddRRSetToVerifier(dv, rr, rv, RRVS_rr) != mStatus_NoError)
{
LogMsg("VerifyNSEC: ERROR!! AddRRSetToVerifier failed to add NSEC");
goto error;
}
rp = &(ncr->nsec);
while (*rp)
{
if ((*rp)->resrec.rrtype == kDNSType_RRSIG)
{
ValidateRRSIG(dv, RRVS_rrsig, &(*rp)->resrec);
}
rp=&(*rp)->next;
}
if (!dv->rrset || !dv->rrsig)
{
LogMsg("VerifyNSEC: ERROR!! AddRRSetToVerifier missing rrset %p, rrsig %p", dv->rrset, dv->rrsig);
goto error;
}
dv->next = RRVS_key;
StartDNSSECVerification(m, dv);
return;
error:
pdv->DVCallback(m, pdv, DNSSEC_Indeterminate);
if (dv)
{
dv->parent = mDNSNULL;
FreeDNSSECVerifier(m, dv);
}
return;
}
mDNSlocal void DeleteCachedNSECS(mDNS *const m, CacheRecord *cr)
{
CacheRecord *rp, *next;
if (cr->nsec) LogDNSSEC("DeleteCachedNSECS: Deleting NSEC Records\n");
for (rp = cr->nsec; rp; rp = next)
{
next = rp->next;
ReleaseCacheRecord(m, rp);
}
cr->nsec = mDNSNULL;
}
mDNSexport mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, CacheRecord *negcr, mDNSu8 rcode)
{
CacheRecord *cr, *next;
if (rcode != kDNSFlag1_RC_NoErr && rcode != kDNSFlag1_RC_NXDomain)
{
LogMsg("AddNSECSForCacheRecord: Addings nsecs for rcode %d", rcode);
return mDNSfalse;
}
for (cr = crlist; cr; cr = cr->next)
{
next = cr->next;
if (cr->resrec.rrtype != kDNSType_NSEC && cr->resrec.rrtype != kDNSType_RRSIG)
{
LogMsg("AddNSECSForCacheRecord: ERROR!! Adding Wrong record %s", CRDisplayString(m, cr));
return mDNSfalse;
}
if (cr->resrec.rrtype == kDNSType_RRSIG)
{
RDataBody2 *const rdb = (RDataBody2 *)cr->smallrdatastorage.data;
rdataRRSig *rrsig = &rdb->rrsig;
if (swap16(rrsig->typeCovered) != kDNSType_NSEC)
{
LogMsg("AddNSECSForCacheRecord:ERROR!! Adding RRSIG with Wrong type %s", CRDisplayString(m, cr));
return mDNSfalse;
}
}
LogDNSSEC("AddNSECSForCacheRecord: Found a valid record %s", CRDisplayString(m, cr));
}
DeleteCachedNSECS(m, negcr);
LogDNSSEC("AddNSECSForCacheRecord: Adding NSEC Records for %s", CRDisplayString(m, negcr));
negcr->nsec = crlist;
negcr->rcode = rcode;
return mDNStrue;
}
mDNSlocal int CountLabelsMatch(const domainname *const d1, const domainname *const d2)
{
int count, c1, c2;
int match, i, skip1, skip2;
c1 = CountLabels(d1);
skip1 = c1 - 1;
c2 = CountLabels(d2);
skip2 = c2 - 1;
match = 0;
count = c1 < c2 ? c1 : c2;
for (i = count; i > 0; i--)
{
const domainname *da, *db;
da = SkipLeadingLabels(d1, skip1);
db = SkipLeadingLabels(d2, skip2);
if (!SameDomainName(da, db)) return match;
skip1--;
skip2--;
match++;
}
return match;
}
mDNSlocal int DNSSECCanonicalOrder(const domainname *const d1, const domainname *const d2, int *subdomain)
{
int count, c1, c2;
int i, skip1, skip2;
c1 = CountLabels(d1);
skip1 = c1 - 1;
c2 = CountLabels(d2);
skip2 = c2 - 1;
if (subdomain) *subdomain = 0;
count = c1 < c2 ? c1 : c2;
for (i = count; i > 0; i--)
{
mDNSu8 *a, *b;
int j, len, lena, lenb;
a = (mDNSu8 *)SkipLeadingLabels(d1, skip1);
b = (mDNSu8 *)SkipLeadingLabels(d2, skip2);
lena = *a;
lenb = *b;
len = lena < lenb ? lena : lenb;
a++; b++;
for (j = 0; j < len; j++)
{
mDNSu8 ac = *a++;
mDNSu8 bc = *b++;
if (mDNSIsUpperCase(ac)) ac += 'a' - 'A';
if (mDNSIsUpperCase(bc)) bc += 'a' - 'A';
if (ac != bc)
{
verbosedebugf("DNSSECCanonicalOrder: returning ac %c, bc %c", ac, bc);
return ((ac < bc) ? -1 : 1);
}
}
if ((lena - lenb) != 0)
{
verbosedebugf("DNSSECCanonicalOrder: returning lena %d lenb %d", lena, lenb);
return ((lena < lenb) ? -1 : 1);
}
skip1--;
skip2--;
}
if ((c1 > c2) && subdomain)
*subdomain = 1;
verbosedebugf("DNSSECCanonicalOrder: returning c1 %d c2 %d\n", c1, c2);
if (c1 != c2)
return ((c1 < c2) ? -1 : 1);
else
return 0;
}
mDNSexport mDNSBool NSECAnswersENT(const ResourceRecord *const rr, domainname *qname)
{
const domainname *oname = rr->name;
const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
const domainname *nxt = (const domainname *)&rdb->data;
int ret;
int subdomain;
ret = DNSSECCanonicalOrder(oname, qname, mDNSNULL);
if (ret < 0)
{
ret = DNSSECCanonicalOrder(nxt, qname, &subdomain);
if (subdomain)
{
if (ret <= 0)
{
LogMsg("NSECAnswersENT: ERROR!! DNSSECCanonicalOrder subdomain set "
" qname %##s, NSEC %##s", qname->c, rr->name->c);
}
return mDNStrue;
}
}
return mDNSfalse;
}
mDNSlocal const domainname *NSECClosestEncloser(ResourceRecord *rr, domainname *qname)
{
const domainname *oname = rr->name;
const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
const domainname *nxt = (const domainname *)&rdb->data;
int match1, match2;
match1 = CountLabelsMatch(oname, qname);
match2 = CountLabelsMatch(nxt, qname);
if (match1 > match2)
return SkipLeadingLabels(oname, CountLabels(oname) - match1);
else
return SkipLeadingLabels(nxt, CountLabels(nxt) - match2);
}
mDNSlocal int NSECNameExists(mDNS *const m, ResourceRecord *rr, domainname *name, mDNSu16 qtype)
{
const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
const domainname *nxt = (const domainname *)&rdb->data;
const domainname *oname = rr->name; int ret1, subdomain1;
int ret2, subdomain2;
int ret3, subdomain3;
ret1 = DNSSECCanonicalOrder(oname, name, &subdomain1);
if (ret1 > 0)
{
LogDNSSEC("NSECNameExists: owner name %##s is bigger than name %##s", oname->c, name->c);
return -1;
}
if (!ret1)
{
mDNSBool soa = RRAssertsExistence(rr, kDNSType_SOA);
mDNSBool ns = RRAssertsExistence(rr, kDNSType_NS);
if (ns && !soa && qtype != kDNSType_DS)
{
LogDNSSEC("NSECNameExists: Parent side NSEC %s can't be used for question %##s (%s)",
RRDisplayString(m, rr), name->c, DNSTypeName(qtype));
return -1;
}
else if (ns && soa && qtype == kDNSType_DS)
{
LogDNSSEC("NSECNameExists: Child side NSEC %s can't be used for question %##s (%s)",
RRDisplayString(m, rr), name->c, DNSTypeName(qtype));
return -1;
}
LogDNSSEC("NSECNameExists: owner name %##s is same as name %##s", oname->c, name->c);
return 1;
}
if (subdomain1 && (RRAssertsExistence(rr, kDNSType_DNAME) ||
(RRAssertsNonexistence(rr, kDNSType_SOA) && RRAssertsExistence(rr, kDNSType_NS))))
{
LogDNSSEC("NSECNameExists: NSEC %s comes from the parent, can't use it here",
RRDisplayString(m, rr));
return -1;
}
ret2 = DNSSECCanonicalOrder(name, nxt, &subdomain2);
if (!ret2)
{
LogDNSSEC("NSECNameExists: name %##s is same as nxt name %##s", name->c, nxt->c);
return 1;
}
ret3 = DNSSECCanonicalOrder(oname, nxt, &subdomain3);
if (!ret3)
{
if (subdomain2)
{
LogDNSSEC("NSECNameExists: owner name %##s subdomain of nxt name %##s", oname->c, nxt->c);
return 0;
}
else
{
LogDNSSEC("NSECNameExists: Single name in zone, owner name %##s is same as nxt name %##s", oname->c, nxt->c);
return -1;
}
}
if (ret3 < 0)
{
if (ret1 < 0 && ret2 < 0)
{
LogDNSSEC("NSECNameExists: Normal NSEC name %##s lies within owner %##s and nxt name %##s",
name->c, oname->c, nxt->c);
return 0;
}
else
{
LogDNSSEC("NSECNameExists: Normal NSEC name %##s does not lie within owner %##s and nxt name %##s",
name->c, oname->c, nxt->c);
return -1;
}
}
else
{
if (ret1 < 0 && subdomain2)
{
LogDNSSEC("NSECNameExists: Last NSEC name %##s lies within owner %##s and nxt name %##s",
name->c, oname->c, nxt->c);
return 0;
}
else
{
LogDNSSEC("NSECNameExists: Last NSEC name %##s does not lie within owner %##s and nxt name %##s",
name->c, oname->c, nxt->c);
return -1;
}
}
LogDNSSEC("NSECNameExists: NSEC %s did not match any case", RRDisplayString(m, rr));
return -1;
}
mDNSexport void WildcardAnswerProof(mDNS *const m, DNSSECVerifier *dv)
{
CacheRecord *ncr;
CacheRecord **rp;
const domainname *ce;
DNSQuestion q;
LogDNSSEC("WildcardAnswerProof: Question %##s (%s)", dv->origName.c, DNSTypeName(dv->origType));
mDNSPlatformMemZero(&q, sizeof(DNSQuestion));
q.ThisQInterval = -1;
InitializeQuestion(m, &q, dv->InterfaceID, &dv->origName, dv->origType, mDNSNULL, mDNSNULL);
ncr = NSECParentForQuestion(m, &q);
if (!ncr)
{
LogMsg("NSECWildCardProof: Can't find NSEC Parent for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
goto error;
}
rp = &(ncr->nsec);
while (*rp)
{
if ((*rp)->resrec.rrtype == kDNSType_NSEC)
{
CacheRecord *cr = *rp;
if (!NSECNameExists(m, &cr->resrec, &dv->origName, dv->origType))
break;
}
rp=&(*rp)->next;
}
if (!(*rp))
{
LogMsg("NSECWildCardProof: ERROR!! No NSECs found for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
goto error;
}
ce = NSECClosestEncloser(&((*rp)->resrec), &dv->origName);
if (!ce)
{
LogMsg("NSECWildCardProof: ERROR!! Closest Encloser NULL for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
goto error;
}
if (!SameDomainName(ce, dv->wildcardName))
{
LogMsg("NSECWildCardProof: ERROR!! Closest Encloser %##s does not match wildcard name %##s", q.qname.c, dv->wildcardName->c);
goto error;
}
VerifyNSEC(m, &((*rp)->resrec), mDNSNULL, dv, ncr, mDNSNULL);
return;
error:
dv->DVCallback(m, dv, DNSSEC_Insecure);
}
mDNSlocal mDNSBool NSECNoDataError(mDNS *const m, ResourceRecord *rr, domainname *name, mDNSu16 qtype, domainname **wildcard)
{
const domainname *oname = rr->name;
if (wildcard) *wildcard = mDNSNULL;
if (SameDomainName(oname, name))
{
mDNSBool soa = RRAssertsExistence(rr, kDNSType_SOA);
mDNSBool ns = RRAssertsExistence(rr, kDNSType_NS);
if (qtype != kDNSType_DS)
{
if (ns && !soa)
{
LogDNSSEC("NSECNoDataError: Parent side NSEC %s, can't use for child qname %##s (%s)",
RRDisplayString(m, rr), name->c, DNSTypeName(qtype));
return mDNSfalse;
}
}
else
{
if (ns && soa)
{
LogDNSSEC("NSECNoDataError: Child side NSEC %s, can't use for parent qname %##s (%s)",
RRDisplayString(m, rr), name->c, DNSTypeName(qtype));
return mDNSfalse;
}
}
if (RRAssertsExistence(rr, qtype) || RRAssertsExistence(rr, kDNSType_CNAME))
{
LogMsg("NSECNoDataError: ERROR!! qtype %s exists in %s", DNSTypeName(qtype), RRDisplayString(m, rr));
return mDNSfalse;
}
LogDNSSEC("NSECNoDataError: qype %s does not exist in %s", DNSTypeName(qtype), RRDisplayString(m, rr));
return mDNStrue;
}
else
{
if (NSECAnswersENT(rr, name))
{
LogDNSSEC("NSECNoDataError: ERROR!! name %##s exists %s", name->c, RRDisplayString(m, rr));
return mDNSfalse;
}
if (oname->c[0] == 1 && oname->c[1] == '*')
{
int r, s;
const domainname *ce = SkipLeadingLabels(oname, 1);
r = DNSSECCanonicalOrder(name, ce, &s);
if (s)
{
if (RRAssertsExistence(rr, qtype) || RRAssertsExistence(rr, kDNSType_CNAME))
{
LogMsg("NSECNoDataError: ERROR!! qtype %s exists in wildcard %s", DNSTypeName(qtype), RRDisplayString(m, rr));
return mDNSfalse;
}
if (qtype == kDNSType_DS)
{
LogMsg("NSECNoDataError: ERROR!! DS qtype exists in wildcard %s", RRDisplayString(m, rr));
return mDNSfalse;
}
if (RRAssertsNonexistence(rr, kDNSType_SOA) &&
RRAssertsExistence(rr, kDNSType_NS))
{
LogDNSSEC("NSECNoDataError: Parent side wildcard NSEC %s, can't use for child qname %##s (%s)",
RRDisplayString(m, rr), name->c, DNSTypeName(qtype));
return mDNSfalse;
}
*wildcard = (domainname *)ce;
LogDNSSEC("NSECNoDataError: qtype %s does not exist in wildcard %s", DNSTypeName(qtype), RRDisplayString(m, rr));
return mDNStrue;
}
}
return mDNSfalse;
}
}
mDNSlocal void NoDataNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status)
{
RRVerifier *rv;
DNSSECVerifier *pdv;
CacheRecord *ncr;
LogDNSSEC("NoDataNSECCallback: called");
if (!dv->parent)
{
LogMsg("NoDataNSECCCallback: no parent DV");
FreeDNSSECVerifier(m, dv);
return;
}
if (dv->ac)
{
AuthChainLink(dv->parent, dv->ac);
dv->ac = mDNSNULL;
dv->actail = &dv->ac;
}
pdv = dv->parent;
if (status != DNSSEC_Secure)
{
goto error;
}
if (!(pdv->flags & NSEC_PROVES_NONAME_EXISTS))
{
LogMsg("NoDataNSECCCallback: ERROR!! NSEC_PROVES_NONAME_EXISTS not set");
goto error;
}
if (!(pdv->flags & WILDCARD_PROVES_NONAME_EXISTS))
{
LogMsg("NoDataNSECCCallback: ERROR!! WILDCARD_PROVES_NONAME_EXISTS not set");
goto error;
}
dv->parent = mDNSNULL;
FreeDNSSECVerifier(m, dv);
ncr = NSECParentForQuestion(m, &pdv->q);
if (!ncr)
{
LogMsg("NoDataNSECCallback: Can't find NSEC Parent for %##s (%s)", pdv->q.qname.c, DNSTypeName(pdv->q.qtype));
goto error;
}
rv = pdv->pendingNSEC;
pdv->pendingNSEC = mDNSNULL;
VerifyNSEC(m, mDNSNULL, rv, pdv, ncr, mDNSNULL);
return;
error:
dv->parent->DVCallback(m, dv->parent, status);
dv->parent = mDNSNULL;
FreeDNSSECVerifier(m, dv);
}
mDNSlocal void NameErrorNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status)
{
RRVerifier *rv;
DNSSECVerifier *pdv;
CacheRecord *ncr;
LogDNSSEC("NameErrorNSECCallback: called");
if (!dv->parent)
{
LogMsg("NameErrorNSECCCallback: no parent DV");
FreeDNSSECVerifier(m, dv);
return;
}
if (dv->ac)
{
AuthChainLink(dv->parent, dv->ac);
dv->ac = mDNSNULL;
dv->actail = &dv->ac;
}
pdv = dv->parent;
if (status != DNSSEC_Secure)
{
goto error;
}
dv->parent = mDNSNULL;
FreeDNSSECVerifier(m, dv);
ncr = NSECParentForQuestion(m, &pdv->q);
if (!ncr)
{
LogMsg("NameErrorNSECCallback: Can't find NSEC Parent for %##s (%s)", pdv->q.qname.c, DNSTypeName(pdv->q.qtype));
goto error;
}
rv = pdv->pendingNSEC;
pdv->pendingNSEC = mDNSNULL;
VerifyNSEC(m, mDNSNULL, rv, pdv, ncr, mDNSNULL);
return;
error:
dv->parent->DVCallback(m, dv->parent, status);
dv->parent = mDNSNULL;
FreeDNSSECVerifier(m, dv);
}
mDNSlocal void NoDataProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr)
{
CacheRecord **rp;
domainname *wildcard = mDNSNULL;
const domainname *ce = mDNSNULL;
ResourceRecord *nsec_wild = mDNSNULL;
ResourceRecord *nsec_noname = mDNSNULL;
wildcard = mDNSNULL;
rp = &(ncr->nsec);
while (*rp)
{
if ((*rp)->resrec.rrtype == kDNSType_NSEC)
{
CacheRecord *cr = *rp;
if (NSECNoDataError(m, &cr->resrec, &dv->q.qname, dv->q.qtype, &wildcard))
{
if (wildcard)
{
dv->flags |= WILDCARD_PROVES_NONAME_EXISTS;
LogDNSSEC("NoDataProof: NSEC %s proves NODATA error for %##s (%s)",
RRDisplayString(m, &(*rp)->resrec), dv->q.qname.c, DNSTypeName(dv->q.qtype));
}
else
{
dv->flags |= NSEC_PROVES_NOTYPE_EXISTS;
LogDNSSEC("NoDataProof: NSEC %s proves NOTYPE error for %##s (%s)",
RRDisplayString(m, &(*rp)->resrec), dv->q.qname.c, DNSTypeName(dv->q.qtype));
}
nsec_wild = &cr->resrec;
}
if (!NSECNameExists(m, &cr->resrec, &dv->q.qname, dv->q.qtype))
{
LogDNSSEC("NoDataProof: NSEC %s proves that name %##s (%s) does not exist",
RRDisplayString(m, &(*rp)->resrec), dv->q.qname.c, DNSTypeName(dv->q.qtype));
ce = NSECClosestEncloser(&cr->resrec, &dv->q.qname);
dv->flags |= NSEC_PROVES_NONAME_EXISTS;
nsec_noname = &cr->resrec;
}
}
rp=&(*rp)->next;
}
if (!(dv->flags & NSEC_PROVES_NOTYPE_EXISTS))
{
if (wildcard && !ce)
{
LogMsg("NoDataProof: Cannot prove that the name %##s (%s) does not exist", dv->q.qname.c, DNSTypeName(dv->q.qtype));
goto error;
}
if (wildcard && !SameDomainName(wildcard, ce))
{
LogMsg("NoDataProof: wildcard %##s does not match closest encloser %##s", wildcard->c, ce->c);
goto error;
}
}
if ((dv->flags & (WILDCARD_PROVES_NONAME_EXISTS|NSEC_PROVES_NONAME_EXISTS)) ==
(WILDCARD_PROVES_NONAME_EXISTS|NSEC_PROVES_NONAME_EXISTS))
{
mStatus status;
RRVerifier *r = AllocateRRVerifier(nsec_noname, &status);
if (!r) goto error;
dv->pendingNSEC = r;
VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, NoDataNSECCallback);
}
else if ((dv->flags & WILDCARD_PROVES_NONAME_EXISTS) ||
(dv->flags & NSEC_PROVES_NOTYPE_EXISTS))
{
VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, mDNSNULL);
}
else if (dv->flags & NSEC_PROVES_NONAME_EXISTS)
{
VerifyNSEC(m, nsec_noname, mDNSNULL, dv, ncr, mDNSNULL);
}
return;
error:
LogDNSSEC("NoDataProof: Error return");
dv->DVCallback(m, dv, DNSSEC_Insecure);
}
mDNSlocal mDNSBool NSECNoWildcard(mDNS *const m, ResourceRecord *rr, domainname *qname, mDNSu16 qtype)
{
const domainname *ce;
domainname wild;
ce = NSECClosestEncloser(rr, qname);
if (!ce) { LogMsg("NSECNoWildcard: No closest encloser for rr %s, qname %##s (%s)", qname->c, DNSTypeName(qtype)); return mDNSfalse; }
wild.c[0] = 1;
wild.c[1] = '*';
wild.c[2] = 0;
if (!AppendDomainName(&wild, ce))
{
LogMsg("NSECNoWildcard: ERROR!! Can't append domainname closest encloser name %##s, qname %##s (%s)", ce->c, qname->c, DNSTypeName(qtype));
return mDNSfalse;
}
if (NSECNameExists(m, rr, &wild, qtype) != 0)
{
LogDNSSEC("NSECNoWildcard: Wildcard name %##s exists or not valid qname %##s (%s)", wild.c, qname->c, DNSTypeName(qtype));
return mDNSfalse;
}
LogDNSSEC("NSECNoWildcard: Wildcard name %##s does not exist for record %s, qname %##s (%s)", wild.c,
RRDisplayString(m, rr), qname->c, DNSTypeName(qtype));
return mDNStrue;
}
mDNSlocal void NameErrorProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr)
{
CacheRecord **rp;
ResourceRecord *nsec_wild = mDNSNULL;
ResourceRecord *nsec_noname = mDNSNULL;
mStatus status;
rp = &(ncr->nsec);
while (*rp)
{
if ((*rp)->resrec.rrtype == kDNSType_NSEC)
{
CacheRecord *cr = *rp;
if (!NSECNameExists(m, &cr->resrec, &dv->q.qname, dv->q.qtype))
{
LogDNSSEC("NameErrorProof: NSEC %s proves name does not exist for %##s (%s)",
RRDisplayString(m, &(*rp)->resrec), dv->q.qname.c, DNSTypeName(dv->q.qtype));
dv->flags |= NSEC_PROVES_NONAME_EXISTS;
nsec_noname = &cr->resrec;
}
if (NSECNoWildcard(m, &cr->resrec, &dv->q.qname, dv->q.qtype))
{
dv->flags |= WILDCARD_PROVES_NONAME_EXISTS;
nsec_wild = &cr->resrec;
LogDNSSEC("NameErrorProof: NSEC %s proves wildcard cannot answer question for %##s (%s)",
RRDisplayString(m, &(*rp)->resrec), dv->q.qname.c, DNSTypeName(dv->q.qtype));
}
}
rp=&(*rp)->next;
}
if (!nsec_noname || !nsec_wild)
{
LogMsg("NameErrorProof: Proof failed for %##s (%s) noname %p, wild %p", dv->q.qname.c, DNSTypeName(dv->q.qtype), nsec_noname, nsec_wild);
goto error;
}
if (nsec_wild != nsec_noname)
{
RRVerifier *r = AllocateRRVerifier(nsec_noname, &status);
if (!r) goto error;
dv->pendingNSEC = r;
VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, NameErrorNSECCallback);
}
else
{
VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, mDNSNULL);
}
return;
error:
dv->DVCallback(m, dv, DNSSEC_Insecure);
}
mDNSexport void ValidateWithNSECS(mDNS *const m, DNSSECVerifier *dv, CacheRecord *cr)
{
LogDNSSEC("ValidateWithNSECS: called for %s", CRDisplayString(m, cr));
if (dv->parent)
{
if (dv->parent->parent)
{
LogMsg("ValidateWithNSECS: ERROR!! dv parent is set already");
dv->DVCallback(m, dv, DNSSEC_Indeterminate);
return;
}
else
{
DNSSECVerifier *pdv = dv;
dv = AllocateDNSSECVerifier(m, &pdv->q.qname, pdv->q.qtype, pdv->q.InterfaceID, VerifyNSECCallback, mDNSNULL);
if (!dv)
{
LogMsg("VerifyNSEC: mDNSPlatformMemAlloc failed");
pdv->DVCallback(m, pdv, DNSSEC_Indeterminate);
return;
}
LogDNSSEC("ValidateWithNSECS: Parent set, Verifying dv %p %##s (%s)", dv, pdv->q.qname.c, DNSTypeName(pdv->q.qtype));
dv->parent = pdv;
}
}
if (cr->resrec.RecordType == kDNSRecordTypePacketNegative)
{
CacheRecord *neg = cr->nsec;
while (neg)
{
LogDNSSEC("ValidateWithNSECS: NSECCached Record %s", CRDisplayString(m, neg));
neg = neg->next;
}
if (cr->rcode == kDNSFlag1_RC_NoErr)
{
NoDataProof(m, dv, cr);
}
else if (cr->rcode == kDNSFlag1_RC_NXDomain)
{
NameErrorProof(m, dv, cr);
}
else
{
LogDNSSEC("ValidateWithNSECS: Rcode %d invalid", cr->rcode);
dv->DVCallback(m, dv, DNSSEC_Insecure);
}
}
else
{
LogMsg("ValidateWithNSECS: Not a valid cache record %s for NSEC proofs", CRDisplayString(m, cr));
dv->DVCallback(m, dv, DNSSEC_Insecure);
return;
}
}