Exemple #1
0
/** test dname_is_root */
static void
dname_test_isroot(void)
{
	unit_show_func("util/data/dname.c", "dname_isroot");
	unit_assert(dname_is_root((uint8_t*)"\000"));
	unit_assert(!dname_is_root((uint8_t*)"\001a\000"));
	unit_assert(!dname_is_root((uint8_t*)"\005abvcd\003com\000"));
	/* malformed dname in this test, but should work */
	unit_assert(!dname_is_root((uint8_t*)"\077a\000"));
	unit_assert(dname_is_root((uint8_t*)"\000"));
}
Exemple #2
0
int
forwards_next_root(struct iter_forwards* fwd, uint16_t* dclass)
{
	struct iter_forward_zone key;
	rbnode_t* n;
	struct iter_forward_zone* p;
	if(*dclass == 0) {
		/* first root item is first item in tree */
		n = rbtree_first(fwd->tree);
		if(n == RBTREE_NULL)
			return 0;
		p = (struct iter_forward_zone*)n;
		if(dname_is_root(p->name)) {
			*dclass = p->dclass;
			return 1;
		}
		/* root not first item? search for higher items */
		*dclass = p->dclass + 1;
		return forwards_next_root(fwd, dclass);
	}
	/* find class n in tree, we may get a direct hit, or if we don't
	 * this is the last item of the previous class so rbtree_next() takes
	 * us to the next root (if any) */
	key.node.key = &key;
	key.name = (uint8_t*)"\000";
	key.namelen = 1;
	key.namelabs = 0;
	key.dclass = *dclass;
	n = NULL;
	if(rbtree_find_less_equal(fwd->tree, &key, &n)) {
		/* exact */
		return 1;
	} else {
		/* smaller element */
		if(!n || n == RBTREE_NULL)
			return 0; /* nothing found */
		n = rbtree_next(n);
		if(n == RBTREE_NULL)
			return 0; /* no higher */
		p = (struct iter_forward_zone*)n;
		if(dname_is_root(p->name)) {
			*dclass = p->dclass;
			return 1;
		}
		/* not a root node, return next higher item */
		*dclass = p->dclass+1;
		return forwards_next_root(fwd, dclass);
	}
}
Exemple #3
0
struct key_entry_key* 
key_cache_obtain(struct key_cache* kcache, uint8_t* name, size_t namelen, 
	uint16_t key_class, struct regional* region, uint32_t now)
{
	/* keep looking until we find a nonexpired entry */
	while(1) {
		struct key_entry_key* k = key_cache_search(kcache, name, 
			namelen, key_class, 0);
		if(k) {
			/* see if TTL is OK */
			struct key_entry_data* d = (struct key_entry_data*)
				k->entry.data;
			if(now <= d->ttl) {
				/* copy and return it */
				struct key_entry_key* retkey =
					key_entry_copy_toregion(k, region);
				lock_rw_unlock(&k->entry.lock);
				return retkey;
			}
			lock_rw_unlock(&k->entry.lock);
		}
		/* snip off first label to continue */
		if(dname_is_root(name))
			break;
		dname_remove_label(&name, &namelen);
	}
	return NULL;
}
Exemple #4
0
/**
 * Calculate space needed for zone and all its parents
 * @param d: name of zone
 * @param len: length of name
 * @return size.
 */
static size_t calc_zone_need(uint8_t* d, size_t len)
{
	size_t res = sizeof(struct val_neg_zone) + len;
	while(!dname_is_root(d)) {
		log_assert(len > 1); /* not root label */
		dname_remove_label(&d, &len);
		res += sizeof(struct val_neg_zone) + len;
	}
	return res;
}
Exemple #5
0
/** delete empty terminals from tree when final data is deleted */
static void 
del_empty_term(struct local_zone* z, struct local_data* d, 
	uint8_t* name, size_t len, int labs)
{
	while(d && d->rrsets == NULL && is_terminal(d)) {
		/* is this empty nonterminal? delete */
		/* note, no memory recycling in zone region */
		(void)rbtree_delete(&z->data, d);

		/* go up and to the next label */
		if(dname_is_root(name))
			return;
		dname_remove_label(&name, &len);
		labs--;
		d = lz_find_node(z, name, len, labs);
	}
}
Exemple #6
0
/**
 * Calculate space needed for the data and all its parents
 * @param rep: NSEC entries.
 * @return size.
 */
static size_t calc_data_need(struct reply_info* rep)
{
	uint8_t* d;
	size_t i, len, res = 0;

	for(i=rep->an_numrrsets; i<rep->an_numrrsets+rep->ns_numrrsets; i++) {
		if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC) {
			d = rep->rrsets[i]->rk.dname;
			len = rep->rrsets[i]->rk.dname_len;
			res = sizeof(struct val_neg_data) + len;
			while(!dname_is_root(d)) {
				log_assert(len > 1); /* not root label */
				dname_remove_label(&d, &len);
				res += sizeof(struct val_neg_data) + len;
			}
		}
	}
	return res;
}
Exemple #7
0
/** check if negative cache is still valid */
static void check_zone_invariants(struct val_neg_cache* neg, 
	struct val_neg_zone* zone)
{
	unit_assert(zone->nsec3_hash == 0);
	unit_assert(zone->tree.cmp == &val_neg_data_compare);
	unit_assert(zone->count != 0);

	if(zone->tree.count == 0)
		unit_assert(!zone->in_use);
	else {
		if(!zone->in_use) {
			/* details on error */
			log_nametypeclass(0, "zone", zone->name, 0, 0);
			log_err("inuse %d count=%d tree.count=%d",
				zone->in_use, zone->count, 
				(int)zone->tree.count);
			if(negverbose)
				print_neg_cache(neg);
		}
		unit_assert(zone->in_use);
	}
	
	if(zone->parent) {
		unit_assert(zone->parent->count >= zone->count);
		if(zone->parent->in_use) {
			unit_assert(zone->parent->count > zone->count);
		}
		unit_assert(zone->parent->labs == zone->labs-1);
		/* and parent must be one label shorter */
		unit_assert(zone->name[0] == (zone->len-zone->parent->len-1));
		unit_assert(query_dname_compare(zone->name + zone->name[0]+1,
			zone->parent->name) == 0);
	} else {
		/* must be apex */
		unit_assert(dname_is_root(zone->name));
	}
	/* tree property: */
	unit_assert(zone->count == sum_zone_subtree_inuse(neg, zone));

	/* check structure of zone data tree */
	checkzonetree(zone);
}
Exemple #8
0
/** check point in data tree */
static void check_data(struct val_neg_zone* zone, struct val_neg_data* data)
{
	unit_assert(data->count > 0);
	if(data->parent) {
		unit_assert(data->parent->count >= data->count);
		if(data->parent->in_use) {
			unit_assert(data->parent->count > data->count);
		}
		unit_assert(data->parent->labs == data->labs-1);
		/* and parent must be one label shorter */
		unit_assert(data->name[0] == (data->len-data->parent->len-1));
		unit_assert(query_dname_compare(data->name + data->name[0]+1,
			data->parent->name) == 0);
	} else {
		/* must be apex */
		unit_assert(dname_is_root(data->name));
	}
	/* tree property: */
	unit_assert(data->count == sum_subtree_inuse(zone, data));
}
Exemple #9
0
/** 
 * Initialize the filter structure.
 * Finds the zone by looking at available NSEC3 records and best match.
 * 	(skips the unknown flag and unknown algo NSEC3s).
 *
 * @param filter: nsec3 filter structure.
 * @param list: list of rrsets, an array of them.
 * @param num: number of rrsets in list.
 * @param qinfo: 
 *	query name to match a zone for.
 *	query type (if DS a higher zone must be chosen)
 *	qclass, to filter NSEC3s with.
 */
static void
filter_init(struct nsec3_filter* filter, struct ub_packed_rrset_key** list,
	size_t num, struct query_info* qinfo)
{
	size_t i;
	uint8_t* nm;
	size_t nmlen;
	filter->zone = NULL;
	filter->zone_len = 0;
	filter->list = list;
	filter->num = num;
	filter->fclass = qinfo->qclass;
	for(i=0; i<num; i++) {
		/* ignore other stuff in the list */
		if(ntohs(list[i]->rk.type) != LDNS_RR_TYPE_NSEC3 ||
			ntohs(list[i]->rk.rrset_class) != qinfo->qclass) 
			continue;
		/* skip unknown flags, algo */
		if(!nsec3_rrset_has_known(list[i]))
			continue;

		/* since NSEC3s are base32.zonename, we can find the zone
		 * name by stripping off the first label of the record */
		nm = list[i]->rk.dname;
		nmlen = list[i]->rk.dname_len;
		dname_remove_label(&nm, &nmlen);
		/* if we find a domain that can prove about the qname,
		 * and if this domain is closer to the qname */
		if(dname_subdomain_c(qinfo->qname, nm) && (!filter->zone ||
			dname_subdomain_c(nm, filter->zone))) {
			/* for a type DS do not accept a zone equal to qname*/
			if(qinfo->qtype == LDNS_RR_TYPE_DS && 
				query_dname_compare(qinfo->qname, nm) == 0 &&
				!dname_is_root(qinfo->qname))
				continue;
			filter->zone = nm;
			filter->zone_len = nmlen;
		}
	}
}
Exemple #10
0
int print_deleg_lookup(SSL* ssl, struct worker* worker, uint8_t* nm,
	size_t nmlen, int ATTR_UNUSED(nmlabs))
{
	/* deep links into the iterator module */
	struct delegpt* dp;
	struct dns_msg* msg;
	struct regional* region = worker->scratchpad;
	char b[260];
	struct query_info qinfo;
	struct iter_hints_stub* stub;
	regional_free_all(region);
	qinfo.qname = nm;
	qinfo.qname_len = nmlen;
	qinfo.qtype = LDNS_RR_TYPE_A;
	qinfo.qclass = LDNS_RR_CLASS_IN;
	qinfo.local_alias = NULL;

	dname_str(nm, b);
	if(!ssl_printf(ssl, "The following name servers are used for lookup "
		"of %s\n", b)) 
		return 0;
	
	dp = forwards_lookup(worker->env.fwds, nm, qinfo.qclass);
	if(dp) {
		if(!ssl_printf(ssl, "forwarding request:\n"))
			return 0;
		print_dp_main(ssl, dp, NULL);
		print_dp_details(ssl, worker, dp);
		return 1;
	}
	
	while(1) {
		dp = dns_cache_find_delegation(&worker->env, nm, nmlen, 
			qinfo.qtype, qinfo.qclass, region, &msg, 
			*worker->env.now);
		if(!dp) {
			return ssl_printf(ssl, "no delegation from "
				"cache; goes to configured roots\n");
		}
		/* go up? */
		if(iter_dp_is_useless(&qinfo, BIT_RD, dp)) {
			print_dp_main(ssl, dp, msg);
			print_dp_details(ssl, worker, dp);
			if(!ssl_printf(ssl, "cache delegation was "
				"useless (no IP addresses)\n"))
				return 0;
			if(dname_is_root(nm)) {
				/* goes to root config */
				return ssl_printf(ssl, "no delegation from "
					"cache; goes to configured roots\n");
			} else {
				/* useless, goes up */
				nm = dp->name;
				nmlen = dp->namelen;
				dname_remove_label(&nm, &nmlen);
				dname_str(nm, b);
				if(!ssl_printf(ssl, "going up, lookup %s\n", b))
					return 0;
				continue;
			}
		} 
		stub = hints_lookup_stub(worker->env.hints, nm, qinfo.qclass,
			dp);
		if(stub) {
			if(stub->noprime) {
				if(!ssl_printf(ssl, "The noprime stub servers "
					"are used:\n"))
					return 0;
			} else {
				if(!ssl_printf(ssl, "The stub is primed "
						"with servers:\n"))
					return 0;
			}
			print_dp_main(ssl, stub->dp, NULL);
			print_dp_details(ssl, worker, stub->dp);
		} else {
			print_dp_main(ssl, dp, msg);
			print_dp_details(ssl, worker, dp);
		}
		break;
	}

	return 1;
}
Exemple #11
0
/** Do the nodata proof */
static enum sec_status
nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, 
	rbtree_t* ct, struct query_info* qinfo)
{
	struct ce_response ce;
	uint8_t* wc;
	size_t wclen;
	struct ub_packed_rrset_key* rrset;
	int rr;
	enum sec_status sec;

	if(find_matching_nsec3(env, flt, ct, qinfo->qname, qinfo->qname_len, 
		&rrset, &rr)) {
		/* cases 1 and 2 */
		if(nsec3_has_type(rrset, rr, qinfo->qtype)) {
			verbose(VERB_ALGO, "proveNodata: Matching NSEC3 "
				"proved that type existed, bogus");
			return sec_status_bogus;
		} else if(nsec3_has_type(rrset, rr, LDNS_RR_TYPE_CNAME)) {
			verbose(VERB_ALGO, "proveNodata: Matching NSEC3 "
				"proved that a CNAME existed, bogus");
			return sec_status_bogus;
		}

		/* 
		 * If type DS: filter_init zone find already found a parent
		 *   zone, so this nsec3 is from a parent zone. 
		 *   o can be not a delegation (unusual query for normal name,
		 *   	no DS anyway, but we can verify that).
		 *   o can be a delegation (which is the usual DS check).
		 *   o may not have the SOA bit set (only the top of the
		 *   	zone, which must have been above the name, has that).
		 *   	Except for the root; which is checked by itself.
		 *
		 * If not type DS: matching nsec3 must not be a delegation.
		 */
		if(qinfo->qtype == LDNS_RR_TYPE_DS && qinfo->qname_len != 1 
			&& nsec3_has_type(rrset, rr, LDNS_RR_TYPE_SOA) &&
			!dname_is_root(qinfo->qname)) {
			verbose(VERB_ALGO, "proveNodata: apex NSEC3 "
				"abused for no DS proof, bogus");
			return sec_status_bogus;
		} else if(qinfo->qtype != LDNS_RR_TYPE_DS && 
			nsec3_has_type(rrset, rr, LDNS_RR_TYPE_NS) &&
			!nsec3_has_type(rrset, rr, LDNS_RR_TYPE_SOA)) {
			if(!nsec3_has_type(rrset, rr, LDNS_RR_TYPE_DS)) {
				verbose(VERB_ALGO, "proveNodata: matching "
					"NSEC3 is insecure delegation");
				return sec_status_insecure;
			}
			verbose(VERB_ALGO, "proveNodata: matching "
				"NSEC3 is a delegation, bogus");
			return sec_status_bogus;
		}
		return sec_status_secure;
	}

	/* For cases 3 - 5, we need the proven closest encloser, and it 
	 * can't match qname. Although, at this point, we know that it 
	 * won't since we just checked that. */
	sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce);
	if(sec == sec_status_bogus) {
		verbose(VERB_ALGO, "proveNodata: did not match qname, "
		          "nor found a proven closest encloser.");
		return sec_status_bogus;
	} else if(sec==sec_status_insecure && qinfo->qtype!=LDNS_RR_TYPE_DS){
		verbose(VERB_ALGO, "proveNodata: closest nsec3 is insecure "
		          "delegation.");
		return sec_status_insecure;
	}

	/* Case 3: removed */

	/* Case 4: */
	log_assert(ce.ce);
	wc = nsec3_ce_wildcard(env->scratch, ce.ce, ce.ce_len, &wclen);
	if(wc && find_matching_nsec3(env, flt, ct, wc, wclen, &rrset, &rr)) {
		/* found wildcard */
		if(nsec3_has_type(rrset, rr, qinfo->qtype)) {
			verbose(VERB_ALGO, "nsec3 nodata proof: matching "
				"wildcard had qtype, bogus");
			return sec_status_bogus;
		} else if(nsec3_has_type(rrset, rr, LDNS_RR_TYPE_CNAME)) {
			verbose(VERB_ALGO, "nsec3 nodata proof: matching "
				"wildcard had a CNAME, bogus");
			return sec_status_bogus;
		}
		if(qinfo->qtype == LDNS_RR_TYPE_DS && qinfo->qname_len != 1 
			&& nsec3_has_type(rrset, rr, LDNS_RR_TYPE_SOA)) {
			verbose(VERB_ALGO, "nsec3 nodata proof: matching "
				"wildcard for no DS proof has a SOA, bogus");
			return sec_status_bogus;
		} else if(qinfo->qtype != LDNS_RR_TYPE_DS && 
			nsec3_has_type(rrset, rr, LDNS_RR_TYPE_NS) &&
			!nsec3_has_type(rrset, rr, LDNS_RR_TYPE_SOA)) {
			verbose(VERB_ALGO, "nsec3 nodata proof: matching "
				"wilcard is a delegation, bogus");
			return sec_status_bogus;
		}
		/* everything is peachy keen, except for optout spans */
		if(ce.nc_rrset && nsec3_has_optout(ce.nc_rrset, ce.nc_rr)) {
			verbose(VERB_ALGO, "nsec3 nodata proof: matching "
				"wildcard is in optout range, insecure");
			return sec_status_insecure;
		}
		return sec_status_secure;
	}

	/* Case 5: */
	/* Due to forwarders, cnames, and other collating effects, we
	 * can see the ordinary unsigned data from a zone beneath an
	 * insecure delegation under an optout here */
	if(!ce.nc_rrset) {
		verbose(VERB_ALGO, "nsec3 nodata proof: no next closer nsec3");
		return sec_status_bogus;
	}

	/* We need to make sure that the covering NSEC3 is opt-out. */
	log_assert(ce.nc_rrset);
	if(!nsec3_has_optout(ce.nc_rrset, ce.nc_rr)) {
		if(qinfo->qtype == LDNS_RR_TYPE_DS)
		  verbose(VERB_ALGO, "proveNodata: covering NSEC3 was not "
			"opt-out in an opt-out DS NOERROR/NODATA case.");
		else verbose(VERB_ALGO, "proveNodata: could not find matching "
			"NSEC3, nor matching wildcard, nor optout NSEC3 "
			"-- no more options, bogus.");
		return sec_status_bogus;
	}
	/* RFC5155 section 9.2: if nc has optout then no AD flag set */
	return sec_status_insecure;
}
Exemple #12
0
struct dns_msg* 
dns_cache_lookup(struct module_env* env,
	uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
	uint16_t flags, struct regional* region, struct regional* scratch,
	int no_partial)
{
	struct lruhash_entry* e;
	struct query_info k;
	hashvalue_type h;
	time_t now = *env->now;
	struct ub_packed_rrset_key* rrset;

	/* lookup first, this has both NXdomains and ANSWER responses */
	k.qname = qname;
	k.qname_len = qnamelen;
	k.qtype = qtype;
	k.qclass = qclass;
	k.local_alias = NULL;
	h = query_info_hash(&k, flags);
	e = slabhash_lookup(env->msg_cache, h, &k, 0);
	if(e) {
		struct msgreply_entry* key = (struct msgreply_entry*)e->key;
		struct reply_info* data = (struct reply_info*)e->data;
		struct dns_msg* msg = tomsg(env, &key->key, data, region, now, 
			scratch);
		if(msg) {
			lock_rw_unlock(&e->lock);
			return msg;
		}
		/* could be msg==NULL; due to TTL or not all rrsets available */
		lock_rw_unlock(&e->lock);
	}

	/* see if a DNAME exists. Checked for first, to enforce that DNAMEs
	 * are more important, the CNAME is resynthesized and thus 
	 * consistent with the DNAME */
	if(!no_partial &&
		(rrset=find_closest_of_type(env, qname, qnamelen, qclass, now,
		LDNS_RR_TYPE_DNAME, 1))) {
		/* synthesize a DNAME+CNAME message based on this */
		enum sec_status sec_status = sec_status_unchecked;
		struct dns_msg* msg = synth_dname_msg(rrset, region, now, &k,
			&sec_status);
		if(msg) {
			struct ub_packed_rrset_key* cname_rrset;
			lock_rw_unlock(&rrset->entry.lock);
			/* now, after unlocking the DNAME rrset lock,
			 * check the sec_status, and see if we need to look
			 * up the CNAME record associated before it can
			 * be used */
			/* normally, only secure DNAMEs allowed from cache*/
			if(sec_status == sec_status_secure)
				return msg;
			/* but if we have a CNAME cached with this name, then we
			 * have previously already allowed this name to pass.
			 * the next cache lookup is going to fetch that CNAME itself,
			 * but it is better to have the (unsigned)DNAME + CNAME in
			 * that case */
			cname_rrset = rrset_cache_lookup(
				env->rrset_cache, qname, qnamelen,
				LDNS_RR_TYPE_CNAME, qclass, 0, now, 0);
			if(cname_rrset) {
				/* CNAME already synthesized by
				 * synth_dname_msg routine, so we can
				 * straight up return the msg */
				lock_rw_unlock(&cname_rrset->entry.lock);
				return msg;
			}
		} else {
			lock_rw_unlock(&rrset->entry.lock);
		}
	}

	/* see if we have CNAME for this domain,
	 * but not for DS records (which are part of the parent) */
	if(!no_partial && qtype != LDNS_RR_TYPE_DS &&
	   (rrset=rrset_cache_lookup(env->rrset_cache, qname, qnamelen, 
		LDNS_RR_TYPE_CNAME, qclass, 0, now, 0))) {
		uint8_t* wc = NULL;
		size_t wl;
		/* if the rrset is not a wildcard expansion, with wcname */
		/* because, if we return that CNAME rrset on its own, it is
		 * missing the NSEC or NSEC3 proof */
		if(!(val_rrset_wildcard(rrset, &wc, &wl) && wc != NULL)) {
			struct dns_msg* msg = rrset_msg(rrset, region, now, &k);
			if(msg) {
				lock_rw_unlock(&rrset->entry.lock);
				return msg;
			}
		}
		lock_rw_unlock(&rrset->entry.lock);
	}

	/* construct DS, DNSKEY, DLV messages from rrset cache. */
	if((qtype == LDNS_RR_TYPE_DS || qtype == LDNS_RR_TYPE_DNSKEY ||
		qtype == LDNS_RR_TYPE_DLV) &&
		(rrset=rrset_cache_lookup(env->rrset_cache, qname, qnamelen, 
		qtype, qclass, 0, now, 0))) {
		/* if the rrset is from the additional section, and the
		 * signatures have fallen off, then do not synthesize a msg
		 * instead, allow a full query for signed results to happen.
		 * Forego all rrset data from additional section, because
		 * some signatures may not be present and cause validation
		 * failure.
		 */
		struct packed_rrset_data *d = (struct packed_rrset_data*)
			rrset->entry.data;
		if(d->trust != rrset_trust_add_noAA && 
			d->trust != rrset_trust_add_AA && 
			(qtype == LDNS_RR_TYPE_DS || 
				(d->trust != rrset_trust_auth_noAA 
				&& d->trust != rrset_trust_auth_AA) )) {
			struct dns_msg* msg = rrset_msg(rrset, region, now, &k);
			if(msg) {
				lock_rw_unlock(&rrset->entry.lock);
				return msg;
			}
		}
		lock_rw_unlock(&rrset->entry.lock);
	}

	/* stop downwards cache search on NXDOMAIN.
	 * Empty nonterminals are NOERROR, so an NXDOMAIN for foo
	 * means bla.foo also does not exist.  The DNSSEC proofs are
	 * the same.  We search upwards for NXDOMAINs. */
	if(env->cfg->harden_below_nxdomain)
	    while(!dname_is_root(k.qname)) {
		dname_remove_label(&k.qname, &k.qname_len);
		h = query_info_hash(&k, flags);
		e = slabhash_lookup(env->msg_cache, h, &k, 0);
		if(!e && k.qtype != LDNS_RR_TYPE_A &&
			env->cfg->qname_minimisation) {
			k.qtype = LDNS_RR_TYPE_A;
			h = query_info_hash(&k, flags);
			e = slabhash_lookup(env->msg_cache, h, &k, 0);
		}
		if(e) {
			struct reply_info* data = (struct reply_info*)e->data;
			struct dns_msg* msg;
			if(FLAGS_GET_RCODE(data->flags) == LDNS_RCODE_NXDOMAIN
			  && data->security == sec_status_secure
			  && (msg=tomsg(env, &k, data, region, now, scratch))){
				lock_rw_unlock(&e->lock);
				msg->qinfo.qname=qname;
				msg->qinfo.qname_len=qnamelen;
				/* check that DNSSEC really works out */
				msg->rep->security = sec_status_unchecked;
				return msg;
			}
			lock_rw_unlock(&e->lock);
		}
		k.qtype = qtype;
	    }

	/* fill common RR types for ANY response to avoid requery */
	if(qtype == LDNS_RR_TYPE_ANY) {
		return fill_any(env, qname, qnamelen, qtype, qclass, region);
	}

	return NULL;
}
Exemple #13
0
struct dns_msg*
dns_cache_lookup(struct module_env* env,
                 uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
                 uint16_t flags, struct regional* region, struct regional* scratch)
{
    struct lruhash_entry* e;
    struct query_info k;
    hashvalue_t h;
    time_t now = *env->now;
    struct ub_packed_rrset_key* rrset;

    /* lookup first, this has both NXdomains and ANSWER responses */
    k.qname = qname;
    k.qname_len = qnamelen;
    k.qtype = qtype;
    k.qclass = qclass;
    h = query_info_hash(&k, flags);
    e = slabhash_lookup(env->msg_cache, h, &k, 0);
    if(e) {
        struct msgreply_entry* key = (struct msgreply_entry*)e->key;
        struct reply_info* data = (struct reply_info*)e->data;
        struct dns_msg* msg = tomsg(env, &key->key, data, region, now,
                                    scratch);
        if(msg) {
            lock_rw_unlock(&e->lock);
            return msg;
        }
        /* could be msg==NULL; due to TTL or not all rrsets available */
        lock_rw_unlock(&e->lock);
    }

    /* see if a DNAME exists. Checked for first, to enforce that DNAMEs
     * are more important, the CNAME is resynthesized and thus
     * consistent with the DNAME */
    if( (rrset=find_closest_of_type(env, qname, qnamelen, qclass, now,
                                    LDNS_RR_TYPE_DNAME, 1))) {
        /* synthesize a DNAME+CNAME message based on this */
        struct dns_msg* msg = synth_dname_msg(rrset, region, now, &k);
        if(msg) {
            lock_rw_unlock(&rrset->entry.lock);
            return msg;
        }
        lock_rw_unlock(&rrset->entry.lock);
    }

    /* see if we have CNAME for this domain,
     * but not for DS records (which are part of the parent) */
    if( qtype != LDNS_RR_TYPE_DS &&
            (rrset=rrset_cache_lookup(env->rrset_cache, qname, qnamelen,
                                      LDNS_RR_TYPE_CNAME, qclass, 0, now, 0))) {
        struct dns_msg* msg = rrset_msg(rrset, region, now, &k);
        if(msg) {
            lock_rw_unlock(&rrset->entry.lock);
            return msg;
        }
        lock_rw_unlock(&rrset->entry.lock);
    }

    /* construct DS, DNSKEY, DLV messages from rrset cache. */
    if((qtype == LDNS_RR_TYPE_DS || qtype == LDNS_RR_TYPE_DNSKEY ||
            qtype == LDNS_RR_TYPE_DLV) &&
            (rrset=rrset_cache_lookup(env->rrset_cache, qname, qnamelen,
                                      qtype, qclass, 0, now, 0))) {
        /* if the rrset is from the additional section, and the
         * signatures have fallen off, then do not synthesize a msg
         * instead, allow a full query for signed results to happen.
         * Forego all rrset data from additional section, because
         * some signatures may not be present and cause validation
         * failure.
         */
        struct packed_rrset_data *d = (struct packed_rrset_data*)
                                      rrset->entry.data;
        if(d->trust != rrset_trust_add_noAA &&
                d->trust != rrset_trust_add_AA &&
                (qtype == LDNS_RR_TYPE_DS ||
                 (d->trust != rrset_trust_auth_noAA
                  && d->trust != rrset_trust_auth_AA) )) {
            struct dns_msg* msg = rrset_msg(rrset, region, now, &k);
            if(msg) {
                lock_rw_unlock(&rrset->entry.lock);
                return msg;
            }
        }
        lock_rw_unlock(&rrset->entry.lock);
    }

    /* stop downwards cache search on NXDOMAIN.
     * Empty nonterminals are NOERROR, so an NXDOMAIN for foo
     * means bla.foo also does not exist.  The DNSSEC proofs are
     * the same.  We search upwards for NXDOMAINs. */
    if(env->cfg->harden_below_nxdomain)
        while(!dname_is_root(k.qname)) {
            dname_remove_label(&k.qname, &k.qname_len);
            h = query_info_hash(&k, flags);
            e = slabhash_lookup(env->msg_cache, h, &k, 0);
            if(e) {
                struct reply_info* data = (struct reply_info*)e->data;
                struct dns_msg* msg;
                if(FLAGS_GET_RCODE(data->flags) == LDNS_RCODE_NXDOMAIN
                        && data->security == sec_status_secure
                        && (msg=tomsg(env, &k, data, region, now, scratch))) {
                    lock_rw_unlock(&e->lock);
                    msg->qinfo.qname=qname;
                    msg->qinfo.qname_len=qnamelen;
                    /* check that DNSSEC really works out */
                    msg->rep->security = sec_status_unchecked;
                    return msg;
                }
                lock_rw_unlock(&e->lock);
            }
        }

    return NULL;
}