struct dns_msg* val_find_DS(struct module_env* env, uint8_t* nm, size_t nmlen, uint16_t c, struct regional* region) { struct dns_msg* msg; struct query_info qinfo; struct ub_packed_rrset_key *rrset = rrset_cache_lookup( env->rrset_cache, nm, nmlen, LDNS_RR_TYPE_DS, c, 0, *env->now, 0); if(rrset) { /* DS rrset exists. Return it to the validator immediately*/ struct ub_packed_rrset_key* copy = packed_rrset_copy_region( rrset, region, *env->now); lock_rw_unlock(&rrset->entry.lock); if(!copy) return NULL; msg = dns_msg_create(nm, nmlen, LDNS_RR_TYPE_DS, c, region, 1); if(!msg) return NULL; msg->rep->rrsets[0] = copy; msg->rep->rrset_count++; msg->rep->an_numrrsets++; return msg; } /* lookup in rrset and negative cache for NSEC/NSEC3 */ qinfo.qname = nm; qinfo.qname_len = nmlen; qinfo.qtype = LDNS_RR_TYPE_DS; qinfo.qclass = c; /* do not add SOA to reply message, it is going to be used internal */ msg = val_neg_getmsg(env->neg_cache, &qinfo, region, env->rrset_cache, env->scratch_buffer, *env->now, 0); return msg; }
/** Fill TYPE_ANY response with some data from cache */ static struct dns_msg* fill_any(struct module_env* env, uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, struct regional* region) { time_t now = *env->now; struct dns_msg* msg = NULL; uint16_t lookup[] = {LDNS_RR_TYPE_A, LDNS_RR_TYPE_AAAA, LDNS_RR_TYPE_MX, LDNS_RR_TYPE_SOA, LDNS_RR_TYPE_NS, LDNS_RR_TYPE_DNAME, 0}; int i, num=6; /* number of RR types to look up */ log_assert(lookup[num] == 0); for(i=0; i<num; i++) { /* look up this RR for inclusion in type ANY response */ struct ub_packed_rrset_key* rrset = rrset_cache_lookup( env->rrset_cache, qname, qnamelen, lookup[i], qclass, 0, now, 0); struct packed_rrset_data *d; if(!rrset) continue; /* only if rrset from answer section */ d = (struct packed_rrset_data*)rrset->entry.data; if(d->trust == rrset_trust_add_noAA || d->trust == rrset_trust_auth_noAA || d->trust == rrset_trust_add_AA || d->trust == rrset_trust_auth_AA) { lock_rw_unlock(&rrset->entry.lock); continue; } /* create msg if none */ if(!msg) { msg = dns_msg_create(qname, qnamelen, qtype, qclass, region, (size_t)(num-i)); if(!msg) { lock_rw_unlock(&rrset->entry.lock); return NULL; } } /* add RRset to response */ if(!dns_msg_ansadd(msg, region, rrset, now)) { lock_rw_unlock(&rrset->entry.lock); return NULL; } lock_rw_unlock(&rrset->entry.lock); } return msg; }
struct delegpt* dns_cache_find_delegation(struct module_env* env, uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, struct regional* region, struct dns_msg** msg, time_t now) { /* try to find closest NS rrset */ struct ub_packed_rrset_key* nskey; struct packed_rrset_data* nsdata; struct delegpt* dp; nskey = find_closest_of_type(env, qname, qnamelen, qclass, now, LDNS_RR_TYPE_NS, 0); if(!nskey) /* hope the caller has hints to prime or something */ return NULL; nsdata = (struct packed_rrset_data*)nskey->entry.data; /* got the NS key, create delegation point */ dp = delegpt_create(region); if(!dp || !delegpt_set_name(dp, region, nskey->rk.dname)) { lock_rw_unlock(&nskey->entry.lock); log_err("find_delegation: out of memory"); return NULL; } /* create referral message */ if(msg) { /* allocate the array to as much as we could need: * NS rrset + DS/NSEC rrset + * A rrset for every NS RR * AAAA rrset for every NS RR */ *msg = dns_msg_create(qname, qnamelen, qtype, qclass, region, 2 + nsdata->count*2); if(!*msg || !dns_msg_authadd(*msg, region, nskey, now)) { lock_rw_unlock(&nskey->entry.lock); log_err("find_delegation: out of memory"); return NULL; } } if(!delegpt_rrset_add_ns(dp, region, nskey, 0)) log_err("find_delegation: addns out of memory"); lock_rw_unlock(&nskey->entry.lock); /* first unlock before next lookup*/ /* find and add DS/NSEC (if any) */ if(msg) find_add_ds(env, region, *msg, dp, now); /* find and add A entries */ if(!find_add_addrs(env, qclass, region, dp, now, msg)) log_err("find_delegation: addrs out of memory"); return dp; }
struct dns_msg* val_neg_getmsg(struct val_neg_cache* neg, struct query_info* qinfo, struct regional* region, struct rrset_cache* rrset_cache, ldns_buffer* buf, uint32_t now, int addsoa, uint8_t* topname) { struct dns_msg* msg; struct ub_packed_rrset_key* rrset; uint8_t* zname; size_t zname_len; int zname_labs; struct val_neg_zone* zone; /* only for DS queries */ if(qinfo->qtype != LDNS_RR_TYPE_DS) return NULL; log_assert(!topname || dname_subdomain_c(qinfo->qname, topname)); /* see if info from neg cache is available * For NSECs, because there is no optout; a DS next to a delegation * always has exactly an NSEC for it itself; check its DS bit. * flags=0 (not the zone apex). */ rrset = grab_nsec(rrset_cache, qinfo->qname, qinfo->qname_len, LDNS_RR_TYPE_NSEC, qinfo->qclass, 0, region, 1, qinfo->qtype, now); if(rrset) { /* return msg with that rrset */ if(!(msg = dns_msg_create(qinfo->qname, qinfo->qname_len, qinfo->qtype, qinfo->qclass, region, 2))) return NULL; /* TTL already subtracted in grab_nsec */ if(!dns_msg_authadd(msg, region, rrset, 0)) return NULL; if(addsoa && !add_soa(rrset_cache, now, region, msg, NULL)) return NULL; return msg; } /* check NSEC3 neg cache for type DS */ /* need to look one zone higher for DS type */ zname = qinfo->qname; zname_len = qinfo->qname_len; dname_remove_label(&zname, &zname_len); zname_labs = dname_count_labels(zname); /* lookup closest zone */ lock_basic_lock(&neg->lock); zone = neg_closest_zone_parent(neg, zname, zname_len, zname_labs, qinfo->qclass); while(zone && !zone->in_use) zone = zone->parent; /* check that the zone is not too high up so that we do not pick data * out of a zone that is above the last-seen key (or trust-anchor). */ if(zone && topname) { if(!dname_subdomain_c(zone->name, topname)) zone = NULL; } if(!zone) { lock_basic_unlock(&neg->lock); return NULL; } msg = neg_nsec3_proof_ds(zone, qinfo->qname, qinfo->qname_len, zname_labs+1, buf, rrset_cache, region, now, topname); if(msg && addsoa && !add_soa(rrset_cache, now, region, msg, zone)) { lock_basic_unlock(&neg->lock); return NULL; } lock_basic_unlock(&neg->lock); return msg; }
/** neg cache nsec3 proof procedure*/ static struct dns_msg* neg_nsec3_proof_ds(struct val_neg_zone* zone, uint8_t* qname, size_t qname_len, int qlabs, ldns_buffer* buf, struct rrset_cache* rrset_cache, struct regional* region, uint32_t now, uint8_t* topname) { struct dns_msg* msg; struct val_neg_data* data; uint8_t hashnc[SHA_DIGEST_LENGTH]; size_t nclen; struct ub_packed_rrset_key* ce_rrset, *nc_rrset; struct nsec3_cached_hash c; uint8_t nc_b32[257]; /* for NSEC3 ; determine the closest encloser for which we * can find an exact match. Remember the hashed lower name, * since that is the one we need a closest match for. * If we find a match straight away, then it becomes NODATA. * Otherwise, NXDOMAIN or if OPTOUT, an insecure delegation. * Also check that parameters are the same on closest encloser * and on closest match. */ if(!zone->nsec3_hash) return NULL; /* not nsec3 zone */ if(!(data=neg_find_nsec3_ce(zone, qname, qname_len, qlabs, buf, hashnc, &nclen))) { return NULL; } /* grab the ce rrset */ ce_rrset = grab_nsec(rrset_cache, data->name, data->len, LDNS_RR_TYPE_NSEC3, zone->dclass, 0, region, 1, LDNS_RR_TYPE_DS, now); if(!ce_rrset) return NULL; if(!neg_params_ok(zone, ce_rrset)) return NULL; if(nclen == 0) { /* exact match, just check the type bits */ /* need: -SOA, -DS, +NS */ if(nsec3_has_type(ce_rrset, 0, LDNS_RR_TYPE_SOA) || nsec3_has_type(ce_rrset, 0, LDNS_RR_TYPE_DS) || !nsec3_has_type(ce_rrset, 0, LDNS_RR_TYPE_NS)) return NULL; if(!(msg = dns_msg_create(qname, qname_len, LDNS_RR_TYPE_DS, zone->dclass, region, 1))) return NULL; /* TTL reduced in grab_nsec */ if(!dns_msg_authadd(msg, region, ce_rrset, 0)) return NULL; return msg; } /* optout is not allowed without knowing the trust-anchor in use, * otherwise the optout could spoof away that anchor */ if(!topname) return NULL; /* if there is no exact match, it must be in an optout span * (an existing DS implies an NSEC3 must exist) */ nc_rrset = neg_nsec3_getnc(zone, hashnc, nclen, rrset_cache, region, now, nc_b32, sizeof(nc_b32)); if(!nc_rrset) return NULL; if(!neg_params_ok(zone, nc_rrset)) return NULL; if(!nsec3_has_optout(nc_rrset, 0)) return NULL; c.hash = hashnc; c.hash_len = nclen; c.b32 = nc_b32+1; c.b32_len = (size_t)nc_b32[0]; if(nsec3_covers(zone->name, &c, nc_rrset, 0, buf)) { /* nc_rrset covers the next closer name. * ce_rrset equals a closer encloser. * nc_rrset is optout. * No need to check wildcard for type DS */ /* capacity=3: ce + nc + soa(if needed) */ if(!(msg = dns_msg_create(qname, qname_len, LDNS_RR_TYPE_DS, zone->dclass, region, 3))) return NULL; /* now=0 because TTL was reduced in grab_nsec */ if(!dns_msg_authadd(msg, region, ce_rrset, 0)) return NULL; if(!dns_msg_authadd(msg, region, nc_rrset, 0)) return NULL; return msg; } return NULL; }