Пример #1
0
/**
 * Find best signer name in this set of rrsigs.
 * @param rrset: which rrsigs to look through.
 * @param qinf: the query name that needs validation.
 * @param signer_name: the best signer_name. Updated if a better one is found.
 * @param signer_len: length of signer name.
 * @param matchcount: count of current best name (starts at 0 for no match).
 * 	Updated if match is improved.
 */
static void
val_find_best_signer(struct ub_packed_rrset_key* rrset, 
	struct query_info* qinf, uint8_t** signer_name, size_t* signer_len, 
	int* matchcount)
{
	struct packed_rrset_data* d = (struct packed_rrset_data*)
		rrset->entry.data;
	uint8_t* sign;
	size_t i;
	int m;
	for(i=d->count; i<d->count+d->rrsig_count; i++) {
		sign = d->rr_data[i]+2+18;
		/* look at signatures that are valid (long enough),
		 * and have a signer name that is a superdomain of qname,
		 * and then check the number of labels in the shared topdomain
		 * improve the match if possible */
		if(d->rr_len[i] > 2+19 && /* rdata, sig + root label*/
			dname_subdomain_c(qinf->qname, sign)) {
			(void)dname_lab_cmp(qinf->qname, 
				dname_count_labels(qinf->qname), 
				sign, dname_count_labels(sign), &m);
			if(m > *matchcount) {
				*matchcount = m;
				*signer_name = sign;
				(void)dname_count_size_labels(*signer_name,
					signer_len);
			}
		}
	}
}
Пример #2
0
int
dname_canonical_compare(uint8_t* d1, uint8_t* d2)
{
    int labs1, labs2, m;
    labs1 = dname_count_labels(d1);
    labs2 = dname_count_labels(d2);
    return dname_canon_lab_cmp(d1, labs1, d2, labs2, &m);
}
Пример #3
0
/**
 * Given a qname and its proven closest encloser, calculate the "next
 * closest" name. Basically, this is the name that is one label longer than
 * the closest encloser that is still a subdomain of qname.
 *
 * @param qname: query name.
 * @param qnamelen: length of qname.
 * @param ce: closest encloser
 * @param nm: result name.
 * @param nmlen: length of nm.
 */
static void
next_closer(uint8_t* qname, size_t qnamelen, uint8_t* ce, 
	uint8_t** nm, size_t* nmlen)
{
	int strip = dname_count_labels(qname) - dname_count_labels(ce) -1;
	*nm = qname;
	*nmlen = qnamelen;
	if(strip>0)
		dname_remove_labels(nm, nmlen, strip);
}
Пример #4
0
uint8_t* dname_get_shared_topdomain(uint8_t* d1, uint8_t* d2)
{
    int labs1, labs2, m;
    size_t len = LDNS_MAX_DOMAINLEN;
    labs1 = dname_count_labels(d1);
    labs2 = dname_count_labels(d2);
    (void)dname_lab_cmp(d1, labs1, d2, labs2, &m);
    dname_remove_labels(&d1, &len, labs1-m);
    return d1;
}
Пример #5
0
/** test dname_count_labels */
static void
dname_test_count_labels(void)
{
	unit_show_func("util/data/dname.c", "dname_count_labels");
	unit_assert(dname_count_labels((uint8_t*)"") == 1);
	unit_assert(dname_count_labels((uint8_t*)"\003com") == 2);
	unit_assert(dname_count_labels((uint8_t*)"\003org") == 2);
	unit_assert(dname_count_labels((uint8_t*)"\007example\003com") == 3);
	unit_assert(dname_count_labels((uint8_t*)"\003bla\007example\003com") 
		== 4);
}
Пример #6
0
/** enter tagstring into zone */
static int
lz_enter_zone_tag(struct local_zones* zones, char* zname, uint8_t* list,
	size_t len, uint16_t rr_class)
{
	uint8_t dname[LDNS_MAX_DOMAINLEN+1];
	size_t dname_len = sizeof(dname);
	int dname_labs, r = 0;
	struct local_zone* z;

	if(sldns_str2wire_dname_buf(zname, dname, &dname_len) != 0) {
		log_err("cannot parse zone name in local-zone-tag: %s", zname);
		return 0;
	}
	dname_labs = dname_count_labels(dname);
	
	lock_rw_rdlock(&zones->lock);
	z = local_zones_find(zones, dname, dname_len, dname_labs, rr_class);
	if(!z) {
		lock_rw_unlock(&zones->lock);
		log_err("no local-zone for tag %s", zname);
		return 0;
	}
	lock_rw_wrlock(&z->lock);
	lock_rw_unlock(&zones->lock);
	free(z->taglist);
	z->taglist = memdup(list, len);
	z->taglen = len;
	if(z->taglist)
		r = 1;
	lock_rw_unlock(&z->lock);
	return r;
}
Пример #7
0
/** apply config caps whitelist items to name tree */
static int
caps_white_apply_cfg(rbtree_t* ntree, struct config_file* cfg)
{
	struct config_strlist* p;
	for(p=cfg->caps_whitelist; p; p=p->next) {
		struct name_tree_node* n;
		size_t len;
		uint8_t* nm = sldns_str2wire_dname(p->str, &len);
		if(!nm) {
			log_err("could not parse %s", p->str);
			return 0;
		}
		n = (struct name_tree_node*)calloc(1, sizeof(*n));
		if(!n) {
			log_err("out of memory");
			free(nm);
			return 0;
		}
		n->node.key = n;
		n->name = nm;
		n->len = len;
		n->labs = dname_count_labels(nm);
		n->dclass = LDNS_RR_CLASS_IN;
		if(!name_tree_insert(ntree, n, nm, len, n->labs, n->dclass)) {
			/* duplicate element ignored, idempotent */
			free(n->name);
			free(n);
		}
	}
	name_tree_init_parents(ntree);
	return 1;
}
Пример #8
0
struct val_neg_zone* neg_create_zone(struct val_neg_cache* neg,
	uint8_t* nm, size_t nm_len, uint16_t dclass)
{
	struct val_neg_zone* zone;
	struct val_neg_zone* parent;
	struct val_neg_zone* p, *np;
	int labs = dname_count_labels(nm);

	/* find closest enclosing parent zone that (still) exists */
	parent = neg_closest_zone_parent(neg, nm, nm_len, labs, dclass);
	if(parent && query_dname_compare(parent->name, nm) == 0)
		return parent; /* already exists, weird */
	/* if parent exists, it is in use */
	log_assert(!parent || parent->count > 0);
	zone = neg_zone_chain(nm, nm_len, labs, dclass, parent);
	if(!zone) {
		return NULL;
	}

	/* insert the list of zones into the tree */
	p = zone;
	while(p) {
		np = p->parent;
		/* mem use */
		neg->use += sizeof(struct val_neg_zone) + p->len;
		/* insert in tree */
		(void)rbtree_insert(&neg->tree, &p->node);
		/* last one needs proper parent pointer */
		if(np == NULL)
			p->parent = parent;
		p = np;
	}
	return zone;
}
Пример #9
0
int
dname_subdomain_c(uint8_t* d1, uint8_t* d2)
{
    int m;
    /* check subdomain: d1: www.example.com. and d2: example.com. */
    /*  	or 	    d1: example.com. and d2: example.com. */
    int labs1 = dname_count_labels(d1);
    int labs2 = dname_count_labels(d2);
    if(labs2 > labs1)
        return 0;
    if(dname_lab_cmp(d1, labs1, d2, labs2, &m) < 0) {
        /* must have been example.com , www.example.com - wrong */
        /* or otherwise different dnames */
        return 0;
    }
    return (m == labs2);
}
Пример #10
0
int iter_dp_cangodown(struct query_info* qinfo, struct delegpt* dp)
{
	/* no delegation point, do not see how we can go down,
	 * robust check, it should really exist */
	if(!dp) return 0;

	/* see if dp equals the qname, then we cannot go down further */
	if(query_dname_compare(qinfo->qname, dp->name) == 0)
		return 0;
	/* if dp is one label above the name we also cannot go down further */
	if(dname_count_labels(qinfo->qname) == dp->namelabs+1)
		return 0;
	return 1;
}
Пример #11
0
struct val_neg_zone* neg_find_zone(struct val_neg_cache* neg, 
	uint8_t* nm, size_t len, uint16_t dclass)
{
	struct val_neg_zone lookfor;
	struct val_neg_zone* result;
	lookfor.node.key = &lookfor;
	lookfor.name = nm;
	lookfor.len = len;
	lookfor.labs = dname_count_labels(lookfor.name);
	lookfor.dclass = dclass;

	result = (struct val_neg_zone*)
		rbtree_search(&neg->tree, lookfor.node.key);
	return result;
}
Пример #12
0
int iter_msg_from_zone(struct dns_msg* msg, struct delegpt* dp,
        enum response_type type, uint16_t dclass)
{
	if(!msg || !dp || !msg->rep || !dp->name)
		return 0;
	/* SOA RRset - always from reply zone */
	if(reply_find_rrset_section_an(msg->rep, dp->name, dp->namelen,
		LDNS_RR_TYPE_SOA, dclass) ||
	   reply_find_rrset_section_ns(msg->rep, dp->name, dp->namelen,
		LDNS_RR_TYPE_SOA, dclass))
		return 1;
	if(type == RESPONSE_TYPE_REFERRAL) {
		size_t i;
		/* if it adds a single label, i.e. we expect .com,
		 * and referral to example.com. NS ... , then origin zone
		 * is .com. For a referral to sub.example.com. NS ... then
		 * we do not know, since example.com. may be in between. */
		for(i=0; i<msg->rep->an_numrrsets+msg->rep->ns_numrrsets; 
			i++) {
			struct ub_packed_rrset_key* s = msg->rep->rrsets[i];
			if(ntohs(s->rk.type) == LDNS_RR_TYPE_NS &&
				ntohs(s->rk.rrset_class) == dclass) {
				int l = dname_count_labels(s->rk.dname);
				if(l == dp->namelabs + 1 &&
					dname_strict_subdomain(s->rk.dname,
					l, dp->name, dp->namelabs))
					return 1;
			}
		}
		return 0;
	}
	log_assert(type==RESPONSE_TYPE_ANSWER || type==RESPONSE_TYPE_CNAME);
	/* not a referral, and not lame delegation (upwards), so, 
	 * any NS rrset must be from the zone itself */
	if(reply_find_rrset_section_an(msg->rep, dp->name, dp->namelen,
		LDNS_RR_TYPE_NS, dclass) ||
	   reply_find_rrset_section_ns(msg->rep, dp->name, dp->namelen,
		LDNS_RR_TYPE_NS, dclass))
		return 1;
	/* a DNSKEY set is expected at the zone apex as well */
	/* this is for 'minimal responses' for DNSKEYs */
	if(reply_find_rrset_section_an(msg->rep, dp->name, dp->namelen,
		LDNS_RR_TYPE_DNSKEY, dclass))
		return 1;
	return 0;
}
Пример #13
0
/** find or create element in domainlimit tree */
static struct domain_limit_data* domain_limit_findcreate(
	struct infra_cache* infra, char* name)
{
	uint8_t* nm;
	int labs;
	size_t nmlen;
	struct domain_limit_data* d;

	/* parse name */
	nm = sldns_str2wire_dname(name, &nmlen);
	if(!nm) {
		log_err("could not parse %s", name);
		return NULL;
	}
	labs = dname_count_labels(nm);

	/* can we find it? */
	d = (struct domain_limit_data*)name_tree_find(&infra->domain_limits,
		nm, nmlen, labs, LDNS_RR_CLASS_IN);
	if(d) {
		free(nm);
		return d;
	}
	
	/* create it */
	d = (struct domain_limit_data*)calloc(1, sizeof(*d));
	if(!d) {
		free(nm);
		return NULL;
	}
	d->node.node.key = &d->node;
	d->node.name = nm;
	d->node.len = nmlen;
	d->node.labs = labs;
	d->node.dclass = LDNS_RR_CLASS_IN;
	d->lim = -1;
	d->below = -1;
	if(!name_tree_insert(&infra->domain_limits, &d->node, nm, nmlen,
		labs, LDNS_RR_CLASS_IN)) {
		log_err("duplicate element in domainlimit tree");
		free(nm);
		free(d);
		return NULL;
	}
	return d;
}
Пример #14
0
int 
local_zones_answer(struct local_zones* zones, struct query_info* qinfo,
	struct edns_data* edns, sldns_buffer* buf, struct regional* temp,
	struct comm_reply* repinfo, uint8_t* taglist, size_t taglen,
	uint8_t* tagactions, size_t tagactionssize,
	struct config_strlist** tag_datas, size_t tag_datas_size,
	char** tagname, int num_tags)
{
	/* see if query is covered by a zone,
	 * 	if so:	- try to match (exact) local data 
	 * 		- look at zone type for negative response. */
	int labs = dname_count_labels(qinfo->qname);
	struct local_data* ld = NULL;
	struct local_zone* z;
	enum localzone_type lzt;
	int r, tag = -1;
	lock_rw_rdlock(&zones->lock);
	z = local_zones_tags_lookup(zones, qinfo->qname,
		qinfo->qname_len, labs, qinfo->qclass, taglist, taglen, 0);
	if(!z) {
		lock_rw_unlock(&zones->lock);
		return 0;
	}
	lock_rw_rdlock(&z->lock);
	lock_rw_unlock(&zones->lock);

	lzt = lz_type(taglist, taglen, z->taglist, z->taglen, tagactions,
		tagactionssize, z->type, repinfo, z->override_tree, &tag,
		tagname, num_tags);

	if((lzt == local_zone_inform || lzt == local_zone_inform_deny)
		&& repinfo)
		lz_inform_print(z, qinfo, repinfo);

	if(lzt != local_zone_always_refuse && lzt != local_zone_always_transparent
		&& lzt != local_zone_always_nxdomain
		&& local_data_answer(z, qinfo, edns, buf, temp, labs, &ld, lzt,
		tag, tag_datas, tag_datas_size, tagname, num_tags)) {
		lock_rw_unlock(&z->lock);
		return 1;
	}
	r = lz_zone_answer(z, qinfo, edns, buf, temp, ld, lzt);
	lock_rw_unlock(&z->lock);
	return r;
}
Пример #15
0
/** store query section in wireformat buffer, return RETVAL */
static int
insert_query(struct query_info* qinfo, struct compress_tree_node** tree, 
	ldns_buffer* buffer, struct regional* region)
{
	if(ldns_buffer_remaining(buffer) < 
		qinfo->qname_len+sizeof(uint16_t)*2)
		return RETVAL_TRUNC; /* buffer too small */
	/* the query is the first name inserted into the tree */
	if(!compress_tree_store(qinfo->qname, 
		dname_count_labels(qinfo->qname), 
		ldns_buffer_position(buffer), region, NULL, tree))
		return RETVAL_OUTMEM;
	if(ldns_buffer_current(buffer) == qinfo->qname)
		ldns_buffer_skip(buffer, (ssize_t)qinfo->qname_len);
	else	ldns_buffer_write(buffer, qinfo->qname, qinfo->qname_len);
	ldns_buffer_write_u16(buffer, qinfo->qtype);
	ldns_buffer_write_u16(buffer, qinfo->qclass);
	return RETVAL_OK;
}
Пример #16
0
struct trust_anchor* 
anchors_lookup(struct val_anchors* anchors,
        uint8_t* qname, size_t qname_len, uint16_t qclass)
{
	struct trust_anchor key;
	struct trust_anchor* result;
	rbnode_t* res = NULL;
	key.node.key = &key;
	key.name = qname;
	key.namelabs = dname_count_labels(qname);
	key.namelen = qname_len;
	key.dclass = qclass;
	lock_basic_lock(&anchors->lock);
	if(rbtree_find_less_equal(anchors->tree, &key, &res)) {
		/* exact */
		result = (struct trust_anchor*)res;
	} else {
		/* smaller element (or no element) */
		int m;
		result = (struct trust_anchor*)res;
		if(!result || result->dclass != qclass) {
			lock_basic_unlock(&anchors->lock);
			return NULL;
		}
		/* count number of labels matched */
		(void)dname_lab_cmp(result->name, result->namelabs, key.name,
			key.namelabs, &m);
		while(result) { /* go up until qname is subdomain of stub */
			if(result->namelabs <= m)
				break;
			result = result->parent;
		}
	}
	if(result) {
		lock_basic_lock(&result->lock);
	}
	lock_basic_unlock(&anchors->lock);
	return result;
}
Пример #17
0
int infra_find_ratelimit(struct infra_cache* infra, uint8_t* name,
	size_t namelen)
{
	int labs = dname_count_labels(name);
	struct domain_limit_data* d = (struct domain_limit_data*)
		name_tree_lookup(&infra->domain_limits, name, namelen, labs,
		LDNS_RR_CLASS_IN);
	if(!d) return infra_dp_ratelimit;

	if(d->node.labs == labs && d->lim != -1)
		return d->lim; /* exact match */

	/* find 'below match' */
	if(d->node.labs == labs)
		d = (struct domain_limit_data*)d->node.parent;
	while(d) {
		if(d->below != -1)
			return d->below;
		d = (struct domain_limit_data*)d->node.parent;
	}
	return infra_dp_ratelimit;
}
Пример #18
0
int 
val_rrset_wildcard(struct ub_packed_rrset_key* rrset, uint8_t** wc)
{
	struct packed_rrset_data* d = (struct packed_rrset_data*)rrset->
		entry.data;
	uint8_t labcount;
	int labdiff;
	uint8_t* wn;
	size_t i, wl;
	if(d->rrsig_count == 0) {
		return 1;
	}
	labcount = rrsig_get_labcount(d, d->count + 0);
	/* check rest of signatures identical */
	for(i=1; i<d->rrsig_count; i++) {
		if(labcount != rrsig_get_labcount(d, d->count + i)) {
			return 0;
		}
	}
	/* OK the rrsigs check out */
	/* if the RRSIG label count is shorter than the number of actual 
	 * labels, then this rrset was synthesized from a wildcard.
	 * Note that the RRSIG label count doesn't count the root label. */
	wn = rrset->rk.dname;
	wl = rrset->rk.dname_len;
	/* skip a leading wildcard label in the dname (RFC4035 2.2) */
	if(dname_is_wild(wn)) {
		wn += 2;
		wl -= 2;
	}
	labdiff = (dname_count_labels(wn) - 1) - (int)labcount;
	if(labdiff > 0) {
		*wc = wn;
		dname_remove_labels(wc, &wl, labdiff);
		return 1;
	}
	return 1;
}
Пример #19
0
/** enter override into zone */
static int
lz_enter_override(struct local_zones* zones, char* zname, char* netblock,
	char* type, uint16_t rr_class)
{
	uint8_t dname[LDNS_MAX_DOMAINLEN+1];
	size_t dname_len = sizeof(dname);
	int dname_labs;
	struct sockaddr_storage addr;
	int net;
	socklen_t addrlen;
	struct local_zone* z;
	enum localzone_type t;

	/* parse zone name */
	if(sldns_str2wire_dname_buf(zname, dname, &dname_len) != 0) {
		log_err("cannot parse zone name in local-zone-override: %s %s",
			zname, netblock);
		return 0;
	}
	dname_labs = dname_count_labels(dname);

	/* parse netblock */
	if(!netblockstrtoaddr(netblock, UNBOUND_DNS_PORT, &addr, &addrlen,
		&net)) {
		log_err("cannot parse netblock in local-zone-override: %s %s",
			zname, netblock);
		return 0;
	}

	/* parse zone type */
	if(!local_zone_str2type(type, &t)) {
		log_err("cannot parse type in local-zone-override: %s %s %s",
			zname, netblock, type);
		return 0;
	}

	/* find localzone entry */
	lock_rw_rdlock(&zones->lock);
	z = local_zones_find(zones, dname, dname_len, dname_labs, rr_class);
	if(!z) {
		lock_rw_unlock(&zones->lock);
		log_err("no local-zone for local-zone-override %s", zname);
		return 0;
	}
	lock_rw_wrlock(&z->lock);
	lock_rw_unlock(&zones->lock);

	/* create netblock addr_tree if not present yet */
	if(!z->override_tree) {
		z->override_tree = (struct rbtree_t*)regional_alloc_zero(
			z->region, sizeof(*z->override_tree));
		if(!z->override_tree) {
			lock_rw_unlock(&z->lock);
			log_err("out of memory");
			return 0;
		}
		addr_tree_init(z->override_tree);
	}
	/* add new elem to tree */
	if(z->override_tree) {
		struct local_zone_override* n;
		n = (struct local_zone_override*)regional_alloc_zero(
			z->region, sizeof(*n));
		if(!n) {
			lock_rw_unlock(&z->lock);
			log_err("out of memory");
			return 0;
		}
		n->type = t;
		if(!addr_tree_insert(z->override_tree,
			(struct addr_tree_node*)n, &addr, addrlen, net)) {
			lock_rw_unlock(&z->lock);
			log_err("duplicate local-zone-override %s %s",
				zname, netblock);
			return 1;
		}
	}

	lock_rw_unlock(&z->lock);
	return 1;
}
Пример #20
0
/** store rrset in buffer in wireformat, return RETVAL_* */
static int
packed_rrset_encode(struct ub_packed_rrset_key* key, sldns_buffer* pkt, 
	uint16_t* num_rrs, time_t timenow, struct regional* region,
	int do_data, int do_sig, struct compress_tree_node** tree,
	sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset)
{
	size_t i, j, owner_pos;
	int r, owner_labs;
	uint16_t owner_ptr = 0;
	struct packed_rrset_data* data = (struct packed_rrset_data*)
		key->entry.data;
	
	/* does this RR type belong in the answer? */
	if(!rrset_belongs_in_reply(s, ntohs(key->rk.type), qtype, dnssec))
		return RETVAL_OK;

	owner_labs = dname_count_labels(key->rk.dname);
	owner_pos = sldns_buffer_position(pkt);

	if(do_data) {
		const sldns_rr_descriptor* c = type_rdata_compressable(key);
		for(i=0; i<data->count; i++) {
			/* rrset roundrobin */
			j = (i + rr_offset) % data->count;
			if((r=compress_owner(key, pkt, region, tree, 
				owner_pos, &owner_ptr, owner_labs))
				!= RETVAL_OK)
				return r;
			sldns_buffer_write(pkt, &key->rk.type, 2);
			sldns_buffer_write(pkt, &key->rk.rrset_class, 2);
			if(data->rr_ttl[j] < timenow)
				sldns_buffer_write_u32(pkt, 0);
			else 	sldns_buffer_write_u32(pkt, 
					data->rr_ttl[j]-timenow);
			if(c) {
				if((r=compress_rdata(pkt, data->rr_data[j],
					data->rr_len[j], region, tree, c))
					!= RETVAL_OK)
					return r;
			} else {
				if(sldns_buffer_remaining(pkt) < data->rr_len[j])
					return RETVAL_TRUNC;
				sldns_buffer_write(pkt, data->rr_data[j],
					data->rr_len[j]);
			}
		}
	}
	/* insert rrsigs */
	if(do_sig && dnssec) {
		size_t total = data->count+data->rrsig_count;
		for(i=data->count; i<total; i++) {
			if(owner_ptr && owner_labs != 1) {
				if(sldns_buffer_remaining(pkt) <
					2+4+4+data->rr_len[i]) 
					return RETVAL_TRUNC;
				sldns_buffer_write(pkt, &owner_ptr, 2);
			} else {
				if((r=compress_any_dname(key->rk.dname, 
					pkt, owner_labs, region, tree))
					!= RETVAL_OK)
					return r;
				if(sldns_buffer_remaining(pkt) < 
					4+4+data->rr_len[i])
					return RETVAL_TRUNC;
			}
			sldns_buffer_write_u16(pkt, LDNS_RR_TYPE_RRSIG);
			sldns_buffer_write(pkt, &key->rk.rrset_class, 2);
			if(data->rr_ttl[i] < timenow)
				sldns_buffer_write_u32(pkt, 0);
			else 	sldns_buffer_write_u32(pkt, 
					data->rr_ttl[i]-timenow);
			/* rrsig rdata cannot be compressed, perform 100+ byte
			 * memcopy. */
			sldns_buffer_write(pkt, data->rr_data[i],
				data->rr_len[i]);
		}
	}
	/* change rrnum only after we are sure it fits */
	if(do_data)
		*num_rrs += data->count;
	if(do_sig && dnssec)
		*num_rrs += data->rrsig_count;

	return RETVAL_OK;
}
Пример #21
0
int
dname_strict_subdomain_c(uint8_t* d1, uint8_t* d2)
{
    return dname_strict_subdomain(d1, dname_count_labels(d1), d2,
                                  dname_count_labels(d2));
}
Пример #22
0
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;
}
Пример #23
0
/**
 * Remove NSEC records between start and end points.
 * By walking the tree, the tree is sorted canonically.
 * @param neg: negative cache.
 * @param zone: the zone
 * @param el: element to start walking at.
 * @param nsec: the nsec record with the end point
 */
static void wipeout(struct val_neg_cache* neg, struct val_neg_zone* zone, 
	struct val_neg_data* el, struct ub_packed_rrset_key* nsec)
{
	struct packed_rrset_data* d = (struct packed_rrset_data*)nsec->
		entry.data;
	uint8_t* end;
	size_t end_len;
	int end_labs, m;
	rbnode_t* walk, *next;
	struct val_neg_data* cur;
	uint8_t buf[257];
	/* get endpoint */
	if(!d || d->count == 0 || d->rr_len[0] < 2+1)
		return;
	if(ntohs(nsec->rk.type) == LDNS_RR_TYPE_NSEC) {
		end = d->rr_data[0]+2;
		end_len = dname_valid(end, d->rr_len[0]-2);
		end_labs = dname_count_labels(end);
	} else {
		/* NSEC3 */
		if(!nsec3_get_nextowner_b32(nsec, 0, buf, sizeof(buf)))
			return;
		end = buf;
		end_labs = dname_count_size_labels(end, &end_len);
	}

	/* sanity check, both owner and end must be below the zone apex */
	if(!dname_subdomain_c(el->name, zone->name) || 
		!dname_subdomain_c(end, zone->name))
		return;

	/* detect end of zone NSEC ; wipe until the end of zone */
	if(query_dname_compare(end, zone->name) == 0) {
		end = NULL;
	}

	walk = rbtree_next(&el->node);
	while(walk && walk != RBTREE_NULL) {
		cur = (struct val_neg_data*)walk;
		/* sanity check: must be larger than start */
		if(dname_canon_lab_cmp(cur->name, cur->labs, 
			el->name, el->labs, &m) <= 0) {
			/* r == 0 skip original record. */
			/* r < 0  too small! */
			walk = rbtree_next(walk);
			continue;
		}
		/* stop at endpoint, also data at empty nonterminals must be
		 * removed (no NSECs there) so everything between 
		 * start and end */
		if(end && dname_canon_lab_cmp(cur->name, cur->labs,
			end, end_labs, &m) >= 0) {
			break;
		}
		/* this element has to be deleted, but we cannot do it
		 * now, because we are walking the tree still ... */
		/* get the next element: */
		next = rbtree_next(walk);
		/* now delete the original element, this may trigger
		 * rbtree rebalances, but really, the next element is
		 * the one we need.
		 * But it may trigger delete of other data and the
		 * entire zone. However, if that happens, this is done
		 * by deleting the *parents* of the element for deletion,
		 * and maybe also the entire zone if it is empty. 
		 * But parents are smaller in canonical compare, thus,
		 * if a larger element exists, then it is not a parent,
		 * it cannot get deleted, the zone cannot get empty.
		 * If the next==NULL, then zone can be empty. */
		if(cur->in_use)
			neg_delete_data(neg, cur);
		walk = next;
	}
}
Пример #24
0
int val_neg_dlvlookup(struct val_neg_cache* neg, uint8_t* qname, size_t len,
        uint16_t qclass, struct rrset_cache* rrset_cache, uint32_t now)
{
	/* lookup closest zone */
	struct val_neg_zone* zone;
	struct val_neg_data* data;
	int labs;
	struct ub_packed_rrset_key* nsec;
	struct packed_rrset_data* d;
	uint32_t flags;
	uint8_t* wc;
	struct query_info qinfo;
	if(!neg) return 0;

	log_nametypeclass(VERB_ALGO, "negcache dlvlookup", qname, 
		LDNS_RR_TYPE_DLV, qclass);
	
	labs = dname_count_labels(qname);
	lock_basic_lock(&neg->lock);
	zone = neg_closest_zone_parent(neg, qname, len, labs, qclass);
	while(zone && !zone->in_use)
		zone = zone->parent;
	if(!zone) {
		lock_basic_unlock(&neg->lock);
		return 0;
	}
	log_nametypeclass(VERB_ALGO, "negcache zone", zone->name, 0, 
		zone->dclass);

	/* DLV is defined to use NSEC only */
	if(zone->nsec3_hash) {
		lock_basic_unlock(&neg->lock);
		return 0;
	}

	/* lookup closest data record */
	(void)neg_closest_data(zone, qname, len, labs, &data);
	while(data && !data->in_use)
		data = data->parent;
	if(!data) {
		lock_basic_unlock(&neg->lock);
		return 0;
	}
	log_nametypeclass(VERB_ALGO, "negcache rr", data->name, 
		LDNS_RR_TYPE_NSEC, zone->dclass);

	/* lookup rrset in rrset cache */
	flags = 0;
	if(query_dname_compare(data->name, zone->name) == 0)
		flags = PACKED_RRSET_NSEC_AT_APEX;
	nsec = rrset_cache_lookup(rrset_cache, data->name, data->len,
		LDNS_RR_TYPE_NSEC, zone->dclass, flags, now, 0);

	/* check if secure and TTL ok */
	if(!nsec) {
		lock_basic_unlock(&neg->lock);
		return 0;
	}
	d = (struct packed_rrset_data*)nsec->entry.data;
	if(!d || now > d->ttl) {
		lock_rw_unlock(&nsec->entry.lock);
		/* delete data record if expired */
		neg_delete_data(neg, data);
		lock_basic_unlock(&neg->lock);
		return 0;
	}
	if(d->security != sec_status_secure) {
		lock_rw_unlock(&nsec->entry.lock);
		neg_delete_data(neg, data);
		lock_basic_unlock(&neg->lock);
		return 0;
	}
	verbose(VERB_ALGO, "negcache got secure rrset");

	/* check NSEC security */
	/* check if NSEC proves no DLV type exists */
	/* check if NSEC proves NXDOMAIN for qname */
	qinfo.qname = qname;
	qinfo.qtype = LDNS_RR_TYPE_DLV;
	qinfo.qclass = qclass;
	if(!nsec_proves_nodata(nsec, &qinfo, &wc) &&
		!val_nsec_proves_name_error(nsec, qname)) {
		/* the NSEC is not a denial for the DLV */
		lock_rw_unlock(&nsec->entry.lock);
		lock_basic_unlock(&neg->lock);
		verbose(VERB_ALGO, "negcache not proven");
		return 0;
	}
	/* so the NSEC was a NODATA proof, or NXDOMAIN proof. */

	/* no need to check for wildcard NSEC; no wildcards in DLV repos */
	/* no need to lookup SOA record for client; no response message */

	lock_rw_unlock(&nsec->entry.lock);
	/* if OK touch the LRU for neg_data element */
	neg_lru_touch(neg, data);
	lock_basic_unlock(&neg->lock);
	verbose(VERB_ALGO, "negcache DLV denial proven");
	return 1;
}
Пример #25
0
void neg_insert_data(struct val_neg_cache* neg, 
	struct val_neg_zone* zone, struct ub_packed_rrset_key* nsec)
{
	struct packed_rrset_data* d;
	struct val_neg_data* parent;
	struct val_neg_data* el;
	uint8_t* nm = nsec->rk.dname;
	size_t nm_len = nsec->rk.dname_len;
	int labs = dname_count_labels(nsec->rk.dname);

	d = (struct packed_rrset_data*)nsec->entry.data;
	if( !(d->security == sec_status_secure ||
		(d->security == sec_status_unchecked && d->rrsig_count > 0)))
		return;
	log_nametypeclass(VERB_ALGO, "negcache rr", 
		nsec->rk.dname, ntohs(nsec->rk.type), 
		ntohs(nsec->rk.rrset_class));

	/* find closest enclosing parent data that (still) exists */
	parent = neg_closest_data_parent(zone, nm, nm_len, labs);
	if(parent && query_dname_compare(parent->name, nm) == 0) {
		/* perfect match already exists */
		log_assert(parent->count > 0);
		el = parent;
	} else { 
		struct val_neg_data* p, *np;

		/* create subtree for perfect match */
		/* if parent exists, it is in use */
		log_assert(!parent || parent->count > 0);

		el = neg_data_chain(nm, nm_len, labs, parent);
		if(!el) {
			log_err("out of memory inserting NSEC negative cache");
			return;
		}
		el->in_use = 0; /* set on below */

		/* insert the list of zones into the tree */
		p = el;
		while(p) {
			np = p->parent;
			/* mem use */
			neg->use += sizeof(struct val_neg_data) + p->len;
			/* insert in tree */
			p->zone = zone;
			(void)rbtree_insert(&zone->tree, &p->node);
			/* last one needs proper parent pointer */
			if(np == NULL)
				p->parent = parent;
			p = np;
		}
	}

	if(!el->in_use) {
		struct val_neg_data* p;

		el->in_use = 1;
		/* increase usage count of all parents */
		for(p=el; p; p = p->parent) {
			p->count++;
		}

		neg_lru_front(neg, el);
	} else {
		/* in use, bring to front, lru */
		neg_lru_touch(neg, el);
	}

	/* if nsec3 store last used parameters */
	if(ntohs(nsec->rk.type) == LDNS_RR_TYPE_NSEC3) {
		int h;
		uint8_t* s;
		size_t slen, it;
		if(nsec3_get_params(nsec, 0, &h, &it, &s, &slen) &&
			it <= neg->nsec3_max_iter &&
			(h != zone->nsec3_hash || it != zone->nsec3_iter ||
			slen != zone->nsec3_saltlen || 
			memcmp(zone->nsec3_salt, s, slen) != 0)) {
			uint8_t* sa = memdup(s, slen);
			if(sa) {
				free(zone->nsec3_salt);
				zone->nsec3_salt = sa;
				zone->nsec3_saltlen = slen;
				zone->nsec3_hash = h;
				zone->nsec3_iter = it;
			}
		}
	}

	/* wipe out the cache items between NSEC start and end */
	wipeout(neg, zone, el, nsec);
}