コード例 #1
0
ファイル: msgencode.c プロジェクト: jedisct1/unbound
int 
reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep, 
	uint16_t id, uint16_t qflags, sldns_buffer* pkt, time_t timenow,
	int cached, struct regional* region, uint16_t udpsize, 
	struct edns_data* edns, int dnssec, int secure)
{
	uint16_t flags;
	unsigned int attach_edns = 0;

	if(!cached || rep->authoritative) {
		/* original flags, copy RD and CD bits from query. */
		flags = rep->flags | (qflags & (BIT_RD|BIT_CD)); 
	} else {
		/* remove AA bit, copy RD and CD bits from query. */
		flags = (rep->flags & ~BIT_AA) | (qflags & (BIT_RD|BIT_CD)); 
	}
	if(secure && (dnssec || (qflags&BIT_AD)))
		flags |= BIT_AD;
	/* restore AA bit if we have a local alias and the response can be
	 * authoritative.  Also clear AD bit if set as the local data is the
	 * primary answer. */
	if(qinf->local_alias &&
		(FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NOERROR ||
		FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NXDOMAIN)) {
		flags |= BIT_AA;
		flags &= ~BIT_AD;
	}
	log_assert(flags & BIT_QR); /* QR bit must be on in our replies */
	if(udpsize < LDNS_HEADER_SIZE)
		return 0;
	if(sldns_buffer_capacity(pkt) < udpsize)
		udpsize = sldns_buffer_capacity(pkt);
	if(udpsize < LDNS_HEADER_SIZE + calc_edns_field_size(edns)) {
		/* packet too small to contain edns, omit it. */
		attach_edns = 0;
	} else {
		/* reserve space for edns record */
		attach_edns = (unsigned int)calc_edns_field_size(edns);
		udpsize -= attach_edns;
	}

	if(!reply_info_encode(qinf, rep, id, flags, pkt, timenow, region,
		udpsize, dnssec)) {
		log_err("reply encode: out of memory");
		return 0;
	}
	if(attach_edns && sldns_buffer_capacity(pkt) >=
		sldns_buffer_limit(pkt)+attach_edns)
		attach_edns_record(pkt, edns);
	return 1;
}
コード例 #2
0
ファイル: libworker.c プロジェクト: derekmarcotte/freebsd
/** fill result from parsed message, on error fills servfail */
void
libworker_enter_result(struct ub_result* res, sldns_buffer* buf,
	struct regional* temp, enum sec_status msg_security)
{
	struct query_info rq;
	struct reply_info* rep;
	res->rcode = LDNS_RCODE_SERVFAIL;
	rep = parse_reply_in_temp_region(buf, temp, &rq);
	if(!rep) {
		log_err("cannot parse buf");
		return; /* error parsing buf, or out of memory */
	}
	if(!fill_res(res, reply_find_answer_rrset(&rq, rep), 
		reply_find_final_cname_target(&rq, rep), &rq, rep))
		return; /* out of memory */
	/* rcode, havedata, nxdomain, secure, bogus */
	res->rcode = (int)FLAGS_GET_RCODE(rep->flags);
	if(res->data && res->data[0])
		res->havedata = 1;
	if(res->rcode == LDNS_RCODE_NXDOMAIN)
		res->nxdomain = 1;
	if(msg_security == sec_status_secure)
		res->secure = 1;
	if(msg_security == sec_status_bogus ||
		msg_security == sec_status_secure_sentinel_fail)
		res->bogus = 1;
}
コード例 #3
0
ファイル: respip.c プロジェクト: Bluecoreg/monero
int
respip_merge_cname(struct reply_info* base_rep,
	const struct query_info* qinfo, const struct reply_info* tgt_rep,
	const struct respip_client_info* cinfo, int must_validate,
	struct reply_info** new_repp, struct regional* region)
{
	struct reply_info* new_rep;
	struct reply_info* tmp_rep = NULL; /* just a placeholder */
	struct ub_packed_rrset_key* alias_rrset = NULL; /* ditto */
	uint16_t tgt_rcode;
	size_t i, j;
	struct respip_action_info actinfo = {respip_none, NULL};

	/* If the query for the CNAME target would result in an unusual rcode,
	 * we generally translate it as a failure for the base query
	 * (which would then be translated into SERVFAIL).  The only exception
	 * is NXDOMAIN and YXDOMAIN, which are passed to the end client(s).
	 * The YXDOMAIN case would be rare but still possible (when
	 * DNSSEC-validated DNAME has been cached but synthesizing CNAME
	 * can't be generated due to length limitation) */
	tgt_rcode = FLAGS_GET_RCODE(tgt_rep->flags);
	if((tgt_rcode != LDNS_RCODE_NOERROR &&
		tgt_rcode != LDNS_RCODE_NXDOMAIN &&
		tgt_rcode != LDNS_RCODE_YXDOMAIN) ||
		(must_validate && tgt_rep->security <= sec_status_bogus)) {
		return 0;
	}

	/* see if the target reply would be subject to a response-ip action. */
	if(!respip_rewrite_reply(qinfo, cinfo, tgt_rep, &tmp_rep, &actinfo,
		&alias_rrset, 1, region))
		return 0;
	if(actinfo.action != respip_none) {
		log_info("CNAME target of redirect response-ip action would "
			"be subject to response-ip action, too; stripped");
		*new_repp = base_rep;
		return 1;
	}

	/* Append target reply to the base.  Since we cannot assume
	 * tgt_rep->rrsets is valid throughout the lifetime of new_rep
	 * or it can be safely shared by multiple threads, we need to make a
	 * deep copy. */
	new_rep = make_new_reply_info(base_rep, region,
		base_rep->an_numrrsets + tgt_rep->an_numrrsets,
		base_rep->an_numrrsets);
	if(!new_rep)
		return 0;
	for(i=0,j=base_rep->an_numrrsets; i<tgt_rep->an_numrrsets; i++,j++) {
		new_rep->rrsets[j] = copy_rrset(tgt_rep->rrsets[i], region);
		if(!new_rep->rrsets[j])
			return 0;
	}

	FLAGS_SET_RCODE(new_rep->flags, tgt_rcode);
	*new_repp = new_rep;
	return 1;
}
コード例 #4
0
int
scrub_message(ldns_buffer* pkt, struct msg_parse* msg,
              struct query_info* qinfo, uint8_t* zonename, struct regional* region,
              struct module_env* env, struct iter_env* ie)
{
    /* basic sanity checks */
    log_nametypeclass(VERB_ALGO, "scrub for", zonename, LDNS_RR_TYPE_NS,
                      qinfo->qclass);
    if(msg->qdcount > 1)
        return 0;
    if( !(msg->flags&BIT_QR) )
        return 0;
    msg->flags &= ~(BIT_AD|BIT_Z); /* force off bit AD and Z */

    /* make sure that a query is echoed back when NOERROR or NXDOMAIN */
    /* this is not required for basic operation but is a forgery
     * resistance (security) feature */
    if((FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NOERROR ||
            FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NXDOMAIN) &&
            msg->qdcount == 0)
        return 0;

    /* if a query is echoed back, make sure it is correct. Otherwise,
     * this may be not a reply to our query. */
    if(msg->qdcount == 1) {
        if(dname_pkt_compare(pkt, msg->qname, qinfo->qname) != 0)
            return 0;
        if(msg->qtype != qinfo->qtype || msg->qclass != qinfo->qclass)
            return 0;
    }

    /* normalize the response, this cleans up the additional.  */
    if(!scrub_normalize(pkt, msg, qinfo, region))
        return 0;
    /* delete all out-of-zone information */
    if(!scrub_sanitize(pkt, msg, qinfo, zonename, env, ie))
        return 0;
    return 1;
}
コード例 #5
0
ファイル: iter_delegpt.c プロジェクト: RS-liuyang/rsdns
void delegpt_add_neg_msg(struct delegpt* dp, struct msgreply_entry* msg)
{
	struct reply_info* rep = (struct reply_info*)msg->entry.data;
	if(!rep) return;

	/* if error or no answers */
	if(FLAGS_GET_RCODE(rep->flags) != 0 || rep->an_numrrsets == 0) {
		struct delegpt_ns* ns = delegpt_find_ns(dp, msg->key.qname, 
			msg->key.qname_len);
		if(ns) {
			if(msg->key.qtype == LDNS_RR_TYPE_A)
				ns->got4 = 1;
			else if(msg->key.qtype == LDNS_RR_TYPE_AAAA)
				ns->got6 = 1;
			if(ns->got4 && ns->got6)
				ns->resolved = 1;
		}
	}
}
コード例 #6
0
ファイル: dns.c プロジェクト: derekmarcotte/freebsd
/** remove servfail msg cache entry */
static void
msg_del_servfail(struct module_env* env, struct query_info* qinfo,
	uint32_t flags)
{
	struct msgreply_entry* e;
	/* see if the entry is servfail, and then remove it, so that
	 * lookups move from the cacheresponse stage to the recursionresponse
	 * stage */
	e = msg_cache_lookup(env, qinfo->qname, qinfo->qname_len,
		qinfo->qtype, qinfo->qclass, flags, 0, 0);
	if(!e) return;
	/* we don't check for the ttl here, also expired servfail entries
	 * are removed.  If the user uses serve-expired, they would still be
	 * used to answer from cache */
	if(FLAGS_GET_RCODE(((struct reply_info*)e->entry.data)->flags)
		!= LDNS_RCODE_SERVFAIL) {
		lock_rw_unlock(&e->entry.lock);
		return;
	}
	lock_rw_unlock(&e->entry.lock);
	msg_cache_remove(env, qinfo->qname, qinfo->qname_len, qinfo->qtype,
		qinfo->qclass, flags);
}
コード例 #7
0
ファイル: msgencode.c プロジェクト: thozza/unbound
static int
positive_answer(struct reply_info* rep, uint16_t qtype) {
	size_t i;
	if (FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NOERROR)
		return 0;

	for(i=0;i<rep->an_numrrsets; i++) {
		if(ntohs(rep->rrsets[i]->rk.type) == qtype) {
			/* in case it is a wildcard with DNSSEC, there will
			 * be NSEC/NSEC3 records in the authority section
			 * that we cannot remove */
			for(i=rep->an_numrrsets; i<rep->an_numrrsets+
				rep->ns_numrrsets; i++) {
				if(ntohs(rep->rrsets[i]->rk.type) ==
					LDNS_RR_TYPE_NSEC ||
				   ntohs(rep->rrsets[i]->rk.type) ==
				   	LDNS_RR_TYPE_NSEC3)
					return 0;
			}
			return 1;
		}
	}
	return 0;
}
コード例 #8
0
ファイル: mesh.c プロジェクト: stasic/debian-unbound
/**
 * Send reply to mesh reply entry
 * @param m: mesh state to send it for.
 * @param rcode: if not 0, error code.
 * @param rep: reply to send (or NULL if rcode is set).
 * @param r: reply entry
 * @param prev: previous reply, already has its answer encoded in buffer.
 */
static void
mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
	struct mesh_reply* r, struct mesh_reply* prev)
{
	struct timeval end_time;
	struct timeval duration;
	int secure;
	/* examine security status */
	if(m->s.env->need_to_validate && (!(r->qflags&BIT_CD) ||
		m->s.env->cfg->ignore_cd) && rep && 
		rep->security <= sec_status_bogus) {
		rcode = LDNS_RCODE_SERVFAIL;
		if(m->s.env->cfg->stat_extended) 
			m->s.env->mesh->ans_bogus++;
	}
	if(rep && rep->security == sec_status_secure)
		secure = 1;
	else	secure = 0;
	if(!rep && rcode == LDNS_RCODE_NOERROR)
		rcode = LDNS_RCODE_SERVFAIL;
	/* send the reply */
	if(prev && prev->qflags == r->qflags && 
		prev->edns.edns_present == r->edns.edns_present && 
		prev->edns.bits == r->edns.bits && 
		prev->edns.udp_size == r->edns.udp_size) {
		/* if the previous reply is identical to this one, fix ID */
		if(prev->query_reply.c->buffer != r->query_reply.c->buffer)
			ldns_buffer_copy(r->query_reply.c->buffer, 
				prev->query_reply.c->buffer);
		ldns_buffer_write_at(r->query_reply.c->buffer, 0, 
			&r->qid, sizeof(uint16_t));
		ldns_buffer_write_at(r->query_reply.c->buffer, 12, 
			r->qname, m->s.qinfo.qname_len);
		comm_point_send_reply(&r->query_reply);
	} else if(rcode) {
		m->s.qinfo.qname = r->qname;
		error_encode(r->query_reply.c->buffer, rcode, &m->s.qinfo,
			r->qid, r->qflags, &r->edns);
		comm_point_send_reply(&r->query_reply);
	} else {
		size_t udp_size = r->edns.udp_size;
		r->edns.edns_version = EDNS_ADVERTISED_VERSION;
		r->edns.udp_size = EDNS_ADVERTISED_SIZE;
		r->edns.ext_rcode = 0;
		r->edns.bits &= EDNS_DO;
		m->s.qinfo.qname = r->qname;
		if(!reply_info_answer_encode(&m->s.qinfo, rep, r->qid, 
			r->qflags, r->query_reply.c->buffer, 0, 1, 
			m->s.env->scratch, udp_size, &r->edns, 
			(int)(r->edns.bits & EDNS_DO), secure)) 
		{
			error_encode(r->query_reply.c->buffer, 
				LDNS_RCODE_SERVFAIL, &m->s.qinfo, r->qid, 
				r->qflags, &r->edns);
		}
		comm_point_send_reply(&r->query_reply);
	}
	/* account */
	m->s.env->mesh->num_reply_addrs--;
	end_time = *m->s.env->now_tv;
	timeval_subtract(&duration, &end_time, &r->start_time);
	verbose(VERB_ALGO, "query took %d.%6.6d sec",
		(int)duration.tv_sec, (int)duration.tv_usec);
	m->s.env->mesh->replies_sent++;
	timeval_add(&m->s.env->mesh->replies_sum_wait, &duration);
	timehist_insert(m->s.env->mesh->histogram, &duration);
	if(m->s.env->cfg->stat_extended) {
		uint16_t rc = FLAGS_GET_RCODE(ldns_buffer_read_u16_at(r->
			query_reply.c->buffer, 2));
		if(secure) m->s.env->mesh->ans_secure++;
		m->s.env->mesh->ans_rcode[ rc ] ++;
		if(rc == 0 && LDNS_ANCOUNT(ldns_buffer_begin(r->
			query_reply.c->buffer)) == 0)
			m->s.env->mesh->ans_nodata++;
	}
}
コード例 #9
0
ファイル: val_utils.c プロジェクト: RS-liuyang/rsdns
enum val_classification 
val_classify_response(uint16_t query_flags, struct query_info* origqinf,
	struct query_info* qinf, struct reply_info* rep, size_t skip)
{
	int rcode = (int)FLAGS_GET_RCODE(rep->flags);
	size_t i;

	/* Normal Name Error's are easy to detect -- but don't mistake a CNAME
	 * chain ending in NXDOMAIN. */
	if(rcode == LDNS_RCODE_NXDOMAIN && rep->an_numrrsets == 0)
		return VAL_CLASS_NAMEERROR;

	/* check for referral: nonRD query and it looks like a nodata */
	if(!(query_flags&BIT_RD) && rep->an_numrrsets == 0 &&
		rcode == LDNS_RCODE_NOERROR) {
		/* SOA record in auth indicates it is NODATA instead.
		 * All validation requiring NODATA messages have SOA in 
		 * authority section. */
		/* uses fact that answer section is empty */
		int saw_ns = 0;
		for(i=0; i<rep->ns_numrrsets; i++) {
			if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_SOA)
				return VAL_CLASS_NODATA;
			if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_DS)
				return VAL_CLASS_REFERRAL;
			if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS)
				saw_ns = 1;
		}
		return saw_ns?VAL_CLASS_REFERRAL:VAL_CLASS_NODATA;
	}
	/* root referral where NS set is in the answer section */
	if(!(query_flags&BIT_RD) && rep->ns_numrrsets == 0 &&
		rep->an_numrrsets == 1 && rcode == LDNS_RCODE_NOERROR &&
		ntohs(rep->rrsets[0]->rk.type) == LDNS_RR_TYPE_NS &&
		query_dname_compare(rep->rrsets[0]->rk.dname, 
			origqinf->qname) != 0)
		return VAL_CLASS_REFERRAL;

	/* dump bad messages */
	if(rcode != LDNS_RCODE_NOERROR)
		return VAL_CLASS_UNKNOWN;
	log_assert(rcode == LDNS_RCODE_NOERROR);
	/* next check if the skip into the answer section shows no answer */
	if(skip>0 && rep->an_numrrsets <= skip)
		return VAL_CLASS_CNAMENOANSWER;

	/* Next is NODATA */
	if(rep->an_numrrsets == 0)
		return VAL_CLASS_NODATA;
	
	/* We distinguish between CNAME response and other positive/negative
	 * responses because CNAME answers require extra processing. */

	/* We distinguish between ANY and CNAME or POSITIVE because 
	 * ANY responses are validated differently. */
	if(qinf->qtype == LDNS_RR_TYPE_ANY)
		return VAL_CLASS_ANY;
	
	/* Note that DNAMEs will be ignored here, unless qtype=DNAME. Unless
	 * qtype=CNAME, this will yield a CNAME response. */
	for(i=skip; i<rep->an_numrrsets; i++) {
		if(ntohs(rep->rrsets[i]->rk.type) == qinf->qtype)
			return VAL_CLASS_POSITIVE;
		if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_CNAME)
			return VAL_CLASS_CNAME;
	}
	log_dns_msg("validator: error. failed to classify response message: ",
		qinf, rep);
	return VAL_CLASS_UNKNOWN;
}
コード例 #10
0
ファイル: iter_utils.c プロジェクト: edmonds/unbound
int caps_failed_rcode(struct reply_info* rep)
{
	return !(FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NOERROR ||
		FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NXDOMAIN);
}
コード例 #11
0
ファイル: dns.c プロジェクト: derekmarcotte/freebsd
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;
}
コード例 #12
0
/**
 * Given a response event, remove suspect RRsets from the response.
 * "Suspect" rrsets are potentially poison. Note that this routine expects
 * the response to be in a "normalized" state -- that is, all "irrelevant"
 * RRsets have already been removed, CNAMEs are in order, etc.
 *
 * @param pkt: packet.
 * @param msg: msg to normalize.
 * @param qinfo: the question originally asked.
 * @param zonename: name of server zone.
 * @param env: module environment with config and cache.
 * @param ie: iterator environment with private address data.
 * @return 0 on error.
 */
static int
scrub_sanitize(ldns_buffer* pkt, struct msg_parse* msg,
               struct query_info* qinfo, uint8_t* zonename, struct module_env* env,
               struct iter_env* ie)
{
    int del_addi = 0; /* if additional-holding rrsets are deleted, we
		do not trust the normalized additional-A-AAAA any more */
    struct rrset_parse* rrset, *prev;
    prev = NULL;
    rrset = msg->rrset_first;

    /* the first DNAME is allowed to stay. It needs checking before
     * it can be used from the cache. After normalization, an initial
     * DNAME will have a correctly synthesized CNAME after it. */
    if(rrset && rrset->type == LDNS_RR_TYPE_DNAME &&
            rrset->section == LDNS_SECTION_ANSWER &&
            pkt_strict_sub(pkt, qinfo->qname, rrset->dname) &&
            pkt_sub(pkt, rrset->dname, zonename)) {
        prev = rrset; /* DNAME allowed to stay in answer section */
        rrset = rrset->rrset_all_next;
    }

    /* remove all records from the answer section that are
     * not the same domain name as the query domain name.
     * The answer section should contain rrsets with the same name
     * as the question. For DNAMEs a CNAME has been synthesized.
     * Wildcards have the query name in answer section.
     * ANY queries get query name in answer section.
     * Remainders of CNAME chains are cut off and resolved by iterator. */
    while(rrset && rrset->section == LDNS_SECTION_ANSWER) {
        if(dname_pkt_compare(pkt, qinfo->qname, rrset->dname) != 0) {
            if(has_additional(rrset->type)) del_addi = 1;
            remove_rrset("sanitize: removing extraneous answer "
                         "RRset:", pkt, msg, prev, &rrset);
            continue;
        }
        prev = rrset;
        rrset = rrset->rrset_all_next;
    }

    /* At this point, we brutally remove ALL rrsets that aren't
     * children of the originating zone. The idea here is that,
     * as far as we know, the server that we contacted is ONLY
     * authoritative for the originating zone. It, of course, MAY
     * be authoriative for any other zones, and of course, MAY
     * NOT be authoritative for some subdomains of the originating
     * zone. */
    prev = NULL;
    rrset = msg->rrset_first;
    while(rrset) {

        /* remove private addresses */
        if( (rrset->type == LDNS_RR_TYPE_A ||
                rrset->type == LDNS_RR_TYPE_AAAA) &&
                priv_rrset_bad(ie->priv, pkt, rrset)) {
            /* set servfail, so the classification becomes
             * THROWAWAY, instead of LAME or other unwanted */
            FLAGS_SET_RCODE(msg->flags, LDNS_RCODE_SERVFAIL);
            remove_rrset("sanitize: removing public name with "
                         "private address", pkt, msg, prev, &rrset);
            continue;
        }

        /* skip DNAME records -- they will always be followed by a
         * synthesized CNAME, which will be relevant.
         * FIXME: should this do something differently with DNAME
         * rrsets NOT in Section.ANSWER? */
        /* But since DNAME records are also subdomains of the zone,
         * same check can be used */

        if(!pkt_sub(pkt, rrset->dname, zonename)) {
            if(msg->an_rrsets == 0 &&
                    rrset->type == LDNS_RR_TYPE_NS &&
                    rrset->section == LDNS_SECTION_AUTHORITY &&
                    FLAGS_GET_RCODE(msg->flags) ==
                    LDNS_RCODE_NOERROR && !soa_in_auth(msg) &&
                    sub_of_pkt(pkt, zonename, rrset->dname)) {
                /* noerror, nodata and this NS rrset is above
                 * the zone. This is LAME!
                 * Leave in the NS for lame classification. */
                /* remove everything from the additional
                 * (we dont want its glue that was approved
                 * during the normalize action) */
                del_addi = 1;
            } else if(!env->cfg->harden_glue) {
                /* store in cache! Since it is relevant
                 * (from normalize) it will be picked up
                 * from the cache to be used later */
                store_rrset(pkt, msg, env, rrset);
                remove_rrset("sanitize: storing potential "
                             "poison RRset:", pkt, msg, prev, &rrset);
                continue;
            } else {
                if(has_additional(rrset->type)) del_addi = 1;
                remove_rrset("sanitize: removing potential "
                             "poison RRset:", pkt, msg, prev, &rrset);
                continue;
            }
        }
        if(del_addi && rrset->section == LDNS_SECTION_ADDITIONAL) {
            remove_rrset("sanitize: removing potential "
                         "poison reference RRset:", pkt, msg, prev, &rrset);
            continue;
        }
        /* check if right hand side of NSEC is within zone */
        if(rrset->type == LDNS_RR_TYPE_NSEC &&
                sanitize_nsec_is_overreach(rrset, zonename)) {
            remove_rrset("sanitize: removing overreaching NSEC "
                         "RRset:", pkt, msg, prev, &rrset);
            continue;
        }
        prev = rrset;
        rrset = rrset->rrset_all_next;
    }
    return 1;
}
コード例 #13
0
/**
 * This routine normalizes a response. This includes removing "irrelevant"
 * records from the answer and additional sections and (re)synthesizing
 * CNAMEs from DNAMEs, if present.
 *
 * @param pkt: packet.
 * @param msg: msg to normalize.
 * @param qinfo: original query.
 * @param region: where to allocate synthesized CNAMEs.
 * @return 0 on error.
 */
static int
scrub_normalize(ldns_buffer* pkt, struct msg_parse* msg,
                struct query_info* qinfo, struct regional* region)
{
    uint8_t* sname = qinfo->qname;
    size_t snamelen = qinfo->qname_len;
    struct rrset_parse* rrset, *prev, *nsset=NULL;

    if(FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NOERROR &&
            FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NXDOMAIN)
        return 1;

    /* For the ANSWER section, remove all "irrelevant" records and add
     * synthesized CNAMEs from DNAMEs
     * This will strip out-of-order CNAMEs as well. */

    /* walk through the parse packet rrset list, keep track of previous
     * for insert and delete ease, and examine every RRset */
    prev = NULL;
    rrset = msg->rrset_first;
    while(rrset && rrset->section == LDNS_SECTION_ANSWER) {
        if(rrset->type == LDNS_RR_TYPE_DNAME &&
                pkt_strict_sub(pkt, sname, rrset->dname)) {
            /* check if next rrset is correct CNAME. else,
             * synthesize a CNAME */
            struct rrset_parse* nx = rrset->rrset_all_next;
            uint8_t alias[LDNS_MAX_DOMAINLEN+1];
            size_t aliaslen = 0;
            if(rrset->rr_count != 1) {
                verbose(VERB_ALGO, "Found DNAME rrset with "
                        "size > 1: %u",
                        (unsigned)rrset->rr_count);
                return 0;
            }
            if(!synth_cname(sname, snamelen, rrset, alias,
                            &aliaslen, pkt)) {
                verbose(VERB_ALGO, "synthesized CNAME "
                        "too long");
                return 0;
            }
            /* internally we have CNAME'd/DNAME'd chains ending
             * in nxdomain with NOERROR rcode, change rcode
             * to reflect this (if needed) */
            FLAGS_SET_RCODE(msg->flags, LDNS_RCODE_NOERROR);
            if(nx && nx->type == LDNS_RR_TYPE_CNAME &&
                    dname_pkt_compare(pkt, sname, nx->dname) == 0) {
                /* check next cname */
                uint8_t* t = NULL;
                size_t tlen = 0;
                if(!parse_get_cname_target(rrset, &t, &tlen))
                    return 0;
                if(dname_pkt_compare(pkt, alias, t) == 0) {
                    /* it's OK and better capitalized */
                    prev = rrset;
                    rrset = nx;
                    continue;
                }
                /* synth ourselves */
            }
            /* synth a CNAME rrset */
            prev = synth_cname_rrset(&sname, &snamelen, alias,
                                     aliaslen, region, msg, rrset, rrset, nx, pkt);
            if(!prev) {
                log_err("out of memory synthesizing CNAME");
                return 0;
            }
            /* FIXME: resolve the conflict between synthesized
             * CNAME ttls and the cache. */
            rrset = nx;
            continue;

        }

        /* The only records in the ANSWER section not allowed to */
        if(dname_pkt_compare(pkt, sname, rrset->dname) != 0) {
            remove_rrset("normalize: removing irrelevant RRset:",
                         pkt, msg, prev, &rrset);
            continue;
        }

        /* Follow the CNAME chain. */
        if(rrset->type == LDNS_RR_TYPE_CNAME) {
            if(!parse_get_cname_target(rrset, &sname, &snamelen))
                return 0;
            prev = rrset;
            rrset = rrset->rrset_all_next;
            /* internally we have CNAME'd/DNAME'd chains ending
             * in nxdomain with NOERROR rcode, change rcode
             * to reflect this (if needed) */
            FLAGS_SET_RCODE(msg->flags, LDNS_RCODE_NOERROR);
            continue;
        }

        /* Otherwise, make sure that the RRset matches the qtype. */
        if(qinfo->qtype != LDNS_RR_TYPE_ANY &&
                qinfo->qtype != rrset->type) {
            remove_rrset("normalize: removing irrelevant RRset:",
                         pkt, msg, prev, &rrset);
            continue;
        }

        /* Mark the additional names from relevant rrset as OK. */
        /* only for RRsets that match the query name, other ones
         * will be removed by sanitize, so no additional for them */
        if(dname_pkt_compare(pkt, qinfo->qname, rrset->dname) == 0)
            mark_additional_rrset(pkt, msg, rrset);

        prev = rrset;
        rrset = rrset->rrset_all_next;
    }

    /* Mark additional names from AUTHORITY */
    while(rrset && rrset->section == LDNS_SECTION_AUTHORITY) {
        if(rrset->type==LDNS_RR_TYPE_DNAME ||
                rrset->type==LDNS_RR_TYPE_CNAME ||
                rrset->type==LDNS_RR_TYPE_A ||
                rrset->type==LDNS_RR_TYPE_AAAA) {
            remove_rrset("normalize: removing irrelevant "
                         "RRset:", pkt, msg, prev, &rrset);
            continue;
        }
        /* only one NS set allowed in authority section */
        if(rrset->type==LDNS_RR_TYPE_NS) {
            /* NS set must be pertinent to the query */
            if(!sub_of_pkt(pkt, qinfo->qname, rrset->dname)) {
                remove_rrset("normalize: removing irrelevant "
                             "RRset:", pkt, msg, prev, &rrset);
                continue;
            }
            if(nsset == NULL) {
                nsset = rrset;
            } else {
                remove_rrset("normalize: removing irrelevant "
                             "RRset:", pkt, msg, prev, &rrset);
                continue;
            }
        }
        mark_additional_rrset(pkt, msg, rrset);
        prev = rrset;
        rrset = rrset->rrset_all_next;
    }

    /* For each record in the additional section, remove it if it is an
     * address record and not in the collection of additional names
     * found in ANSWER and AUTHORITY. */
    /* These records have not been marked OK previously */
    while(rrset && rrset->section == LDNS_SECTION_ADDITIONAL) {
        /* FIXME: what about other types? */
        if(rrset->type==LDNS_RR_TYPE_A ||
                rrset->type==LDNS_RR_TYPE_AAAA)
        {
            if((rrset->flags & RRSET_SCRUB_OK)) {
                /* remove flag to clean up flags variable */
                rrset->flags &= ~RRSET_SCRUB_OK;
            } else {
                remove_rrset("normalize: removing irrelevant "
                             "RRset:", pkt, msg, prev, &rrset);
                continue;
            }
        }
        if(rrset->type==LDNS_RR_TYPE_DNAME ||
                rrset->type==LDNS_RR_TYPE_CNAME ||
                rrset->type==LDNS_RR_TYPE_NS) {
            remove_rrset("normalize: removing irrelevant "
                         "RRset:", pkt, msg, prev, &rrset);
            continue;
        }
        prev = rrset;
        rrset = rrset->rrset_all_next;
    }

    return 1;
}
コード例 #14
0
ファイル: dns.c プロジェクト: coyizumi/cs111
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;
}