<commit_before>/*
 * validator/validator.c - secure validator DNS query response module
 *
 * Copyright (c) 2007, NLnet Labs. All rights reserved.
 *
 * This software is open source.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * 
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * 
 * Neither the name of the NLNET LABS nor the names of its contributors may
 * be used to endorse or promote products derived from this software without
 * specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * \file
 *
 * This file contains a module that performs validation of DNS queries.
 * According to RFC 4034.
 */
#include "config.h"
#include "validator/validator.h"
#include "validator/val_anchor.h"
#include "validator/val_kcache.h"
#include "validator/val_kentry.h"
#include "validator/val_utils.h"
#include "validator/val_nsec.h"
#include "validator/val_nsec3.h"
#include "validator/val_neg.h"
#include "services/cache/dns.h"
#include "util/data/dname.h"
#include "util/module.h"
#include "util/log.h"
#include "util/net_help.h"
#include "util/regional.h"
#include "util/config_file.h"
#include "util/fptr_wlist.h"

/** fill up nsec3 key iterations config entry */
static int
fill_nsec3_iter(struct val_env* ve, char* s, int c)
{
	char* e;
	int i;
	free(ve->nsec3_keysize);
	free(ve->nsec3_maxiter);
	ve->nsec3_keysize = (size_t*)calloc(sizeof(size_t), (size_t)c);
	ve->nsec3_maxiter = (size_t*)calloc(sizeof(size_t), (size_t)c);
	if(!ve->nsec3_keysize || !ve->nsec3_maxiter) {
		log_err("out of memory");
		return 0;
	}
	for(i=0; i<c; i++) {
		ve->nsec3_keysize[i] = (size_t)strtol(s, &e, 10);
		if(s == e) {
			log_err("cannot parse: %s", s);
			return 0;
		}
		s = e;
		ve->nsec3_maxiter[i] = (size_t)strtol(s, &e, 10);
		if(s == e) {
			log_err("cannot parse: %s", s);
			return 0;
		}
		s = e;
		if(i>0 && ve->nsec3_keysize[i-1] >= ve->nsec3_keysize[i]) {
			log_err("nsec3 key iterations not ascending: %d %d",
				(int)ve->nsec3_keysize[i-1], 
				(int)ve->nsec3_keysize[i]);
			return 0;
		}
		verbose(VERB_ALGO, "validator nsec3cfg keysz %d mxiter %d",
			(int)ve->nsec3_keysize[i], (int)ve->nsec3_maxiter[i]);
	}
	return 1;
}

/** apply config settings to validator */
static int
val_apply_cfg(struct module_env* env, struct val_env* val_env, 
	struct config_file* cfg)
{
	int c;
	val_env->bogus_ttl = (uint32_t)cfg->bogus_ttl;
	val_env->clean_additional = cfg->val_clean_additional;
	val_env->permissive_mode = cfg->val_permissive_mode;
	if(!env->anchors)
		env->anchors = anchors_create();
	if(!env->anchors) {
		log_err("out of memory");
		return 0;
	}
	if(!val_env->kcache)
		val_env->kcache = key_cache_create(cfg);
	if(!val_env->kcache) {
		log_err("out of memory");
		return 0;
	}
	if(!anchors_apply_cfg(env->anchors, cfg)) {
		log_err("validator: error in trustanchors config");
		return 0;
	}
	val_env->date_override = cfg->val_date_override;
	val_env->skew_min = cfg->val_sig_skew_min;
	val_env->skew_max = cfg->val_sig_skew_max;
	c = cfg_count_numbers(cfg->val_nsec3_key_iterations);
	if(c < 1 || (c&1)) {
		log_err("validator: unparseable or odd nsec3 key "
			"iterations: %s", cfg->val_nsec3_key_iterations);
		return 0;
	}
	val_env->nsec3_keyiter_count = c/2;
	if(!fill_nsec3_iter(val_env, cfg->val_nsec3_key_iterations, c/2)) {
		log_err("validator: cannot apply nsec3 key iterations");
		return 0;
	}
	if(!val_env->neg_cache)
		val_env->neg_cache = val_neg_create(cfg,
			val_env->nsec3_maxiter[val_env->nsec3_keyiter_count-1]);
	if(!val_env->neg_cache) {
		log_err("out of memory");
		return 0;
	}
	env->neg_cache = val_env->neg_cache;
	return 1;
}

int
val_init(struct module_env* env, int id)
{
	struct val_env* val_env = (struct val_env*)calloc(1,
		sizeof(struct val_env));
	if(!val_env) {
		log_err("malloc failure");
		return 0;
	}
	env->modinfo[id] = (void*)val_env;
	env->need_to_validate = 1;
	val_env->permissive_mode = 0;
	lock_basic_init(&val_env->bogus_lock);
	lock_protect(&val_env->bogus_lock, &val_env->num_rrset_bogus,
		sizeof(val_env->num_rrset_bogus));
	if(!val_apply_cfg(env, val_env, env->cfg)) {
		log_err("validator: could not apply configuration settings.");
		return 0;
	}
	return 1;
}

void
val_deinit(struct module_env* env, int id)
{
	struct val_env* val_env;
	if(!env || !env->modinfo[id])
		return;
	val_env = (struct val_env*)env->modinfo[id];
	lock_basic_destroy(&val_env->bogus_lock);
	anchors_delete(env->anchors);
	env->anchors = NULL;
	key_cache_delete(val_env->kcache);
	neg_cache_delete(val_env->neg_cache);
	free(val_env->nsec3_keysize);
	free(val_env->nsec3_maxiter);
	free(val_env);
	env->modinfo[id] = NULL;
}

/** allocate new validator query state */
static struct val_qstate*
val_new(struct module_qstate* qstate, int id)
{
	struct val_qstate* vq = (struct val_qstate*)regional_alloc(
		qstate->region, sizeof(*vq));
	log_assert(!qstate->minfo[id]);
	if(!vq)
		return NULL;
	memset(vq, 0, sizeof(*vq));
	qstate->minfo[id] = vq;
	vq->state = VAL_INIT_STATE;
	if(!qstate->return_msg || qstate->return_rcode != LDNS_RCODE_NOERROR) {
		/* create a message to verify */
		verbose(VERB_ALGO, "constructing reply for validation");
		vq->orig_msg = (struct dns_msg*)regional_alloc(qstate->region,
			sizeof(struct dns_msg));
		if(!vq->orig_msg)
			return NULL;
		vq->orig_msg->qinfo = qstate->qinfo;
		vq->orig_msg->rep = (struct reply_info*)regional_alloc(
			qstate->region, sizeof(struct reply_info));
		if(!vq->orig_msg->rep)
			return NULL;
		memset(vq->orig_msg->rep, 0, sizeof(struct reply_info));
		vq->orig_msg->rep->flags = (uint16_t)(qstate->return_rcode&0xf)
			|BIT_QR|BIT_RA|(qstate->query_flags|(BIT_CD|BIT_RD));
		vq->orig_msg->rep->qdcount = 1;
	} else {
		vq->orig_msg = qstate->return_msg;
	}
	vq->qchase = qstate->qinfo;
	/* chase reply will be an edited (sub)set of the orig msg rrset ptrs */
	vq->chase_reply = regional_alloc_init(qstate->region, 
		vq->orig_msg->rep, 
		sizeof(struct reply_info) - sizeof(struct rrset_ref));
	if(!vq->chase_reply)
		return NULL;
	vq->chase_reply->rrsets = regional_alloc_init(qstate->region,
		vq->orig_msg->rep->rrsets, sizeof(struct ub_packed_rrset_key*)
			* vq->orig_msg->rep->rrset_count);
	if(!vq->chase_reply->rrsets)
		return NULL;
	vq->rrset_skip = 0;
	return vq;
}

/**
 * Exit validation with an error status
 * 
 * @param qstate: query state
 * @param id: validator id.
 * @return false, for use by caller to return to stop processing.
 */
static int
val_error(struct module_qstate* qstate, int id)
{
	qstate->ext_state[id] = module_error;
	qstate->return_rcode = LDNS_RCODE_SERVFAIL;
	return 0;
}

/** 
 * Check to see if a given response needs to go through the validation
 * process. Typical reasons for this routine to return false are: CD bit was
 * on in the original request, or the response is a kind of message that 
 * is unvalidatable (i.e., SERVFAIL, REFUSED, etc.)
 *
 * @param qstate: query state.
 * @param ret_rc: rcode for this message (if noerror - examine ret_msg).
 * @param ret_msg: return msg, can be NULL; look at rcode instead.
 * @return true if the response could use validation (although this does not
 *         mean we can actually validate this response).
 */
static int
needs_validation(struct module_qstate* qstate, int ret_rc, 
	struct dns_msg* ret_msg)
{
	int rcode;

	/* If the CD bit is on in the original request, then we don't bother to
	 * validate anything.*/
	if(qstate->query_flags & BIT_CD) {
		verbose(VERB_ALGO, "not validating response due to CD bit");
		return 0;
	}

	if(ret_rc != LDNS_RCODE_NOERROR || !ret_msg)
		rcode = ret_rc;
	else 	rcode = (int)FLAGS_GET_RCODE(ret_msg->rep->flags);

	if(rcode != LDNS_RCODE_NOERROR && rcode != LDNS_RCODE_NXDOMAIN) {
		verbose(VERB_ALGO, "cannot validate non-answer, rcode %s",
			ldns_lookup_by_id(ldns_rcodes, rcode)?
			ldns_lookup_by_id(ldns_rcodes, rcode)->name:"??");
		return 0;
	}

	/* cannot validate positive RRSIG response. (negatives can) */
	if(qstate->qinfo.qtype == LDNS_RR_TYPE_RRSIG &&
		rcode == LDNS_RCODE_NOERROR && ret_msg &&
		ret_msg->rep->an_numrrsets > 0) {
		verbose(VERB_ALGO, "cannot validate RRSIG, no sigs on sigs.");
		return 0;
	}
	return 1;
}

/**
 * Check to see if the response has already been validated.
 * @param ret_msg: return msg, can be NULL
 * @return true if the response has already been validated
 */
static int
already_validated(struct dns_msg* ret_msg)
{
	/* validate unchecked, and re-validate bogus messages */
	if (ret_msg && ret_msg->rep->security > sec_status_bogus)
	{
		verbose(VERB_ALGO, "response has already been validated: %s",
			sec_status_to_string(ret_msg->rep->security));
		return 1;
	}
	return 0;
}

/**
 * Generate a request for DNS data.
 *
 * @param qstate: query state that is the parent.
 * @param id: module id.
 * @param name: what name to query for.
 * @param namelen: length of name.
 * @param qtype: query type.
 * @param qclass: query class.
 * @param flags: additional flags, such as the CD bit (BIT_CD), or 0.
 * @return false on alloc failure.
 */
static int
generate_request(struct module_qstate* qstate, int id, uint8_t* name, 
	size_t namelen, uint16_t qtype, uint16_t qclass, uint16_t flags)
{
	struct module_qstate* newq;
	struct query_info ask;
	ask.qname = name;
	ask.qname_len = namelen;
	ask.qtype = qtype;
	ask.qclass = qclass;
	log_query_info(VERB_ALGO, "generate request", &ask);
	fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub));
	if(!(*qstate->env->attach_sub)(qstate, &ask, 
		(uint16_t)(BIT_RD|flags), 0, &newq)){
		log_err("Could not generate request: out of memory");
		return 0;
	}
	/* ignore newq; validator does not need state created for that
	 * query, and its a 'normal' for iterator as well */
	qstate->ext_state[id] = module_wait_subquery;
	return 1;
}

/**
 * Prime trust anchor for use.
 * Generate and dispatch a priming query for the given trust anchor.
 * The trust anchor can be DNSKEY or DS and does not have to be signed.
 *
 * @param qstate: query state.
 * @param vq: validator query state.
 * @param id: module id.
 * @param toprime: what to prime.
 * @return false on a processing error.
 */
static int
prime_trust_anchor(struct module_qstate* qstate, struct val_qstate* vq,
	int id, struct trust_anchor* toprime)
{
	int ret = generate_request(qstate, id, toprime->name, toprime->namelen,
		LDNS_RR_TYPE_DNSKEY, toprime->dclass, BIT_CD);
	if(!ret) {
		log_err("Could not prime trust anchor: out of memory");
		return 0;
	}
	/* ignore newq; validator does not need state created for that
	 * query, and its a 'normal' for iterator as well */
	vq->wait_prime_ta = 1; /* to elicit PRIME_RESP_STATE processing 
		from the validator inform_super() routine */
	return 1;
}

/**
 * Validate if the ANSWER and AUTHORITY sections contain valid rrsets.
 * They must be validly signed with the given key.
 * Tries to validate ADDITIONAL rrsets as well, but only to check them.
 * Allows unsigned CNAME after a DNAME that expands the DNAME.
 * 
 * Note that by the time this method is called, the process of finding the
 * trusted DNSKEY rrset that signs this response must already have been
 * completed.
 * 
 * @param env: module env for verify.
 * @param ve: validator env for verify.
 * @param qchase: query that was made.
 * @param chase_reply: answer to validate.
 * @param key_entry: the key entry, which is trusted, and which matches
 * 	the signer of the answer. The key entry isgood().
 * @return false if any of the rrsets in the an or ns sections of the message 
 * 	fail to verify. The message is then set to bogus.
 */
static int
validate_msg_signatures(struct module_env* env, struct val_env* ve,
	struct query_info* qchase, struct reply_info* chase_reply, 
	struct key_entry_key* key_entry)
{
	uint8_t* sname;
	size_t i, slen;
	struct ub_packed_rrset_key* s;
	enum sec_status sec;
	int dname_seen = 0;

	/* validate the ANSWER section */
	for(i=0; i<chase_reply->an_numrrsets; i++) {
		s = chase_reply->rrsets[i];
		/* Skip the CNAME following a (validated) DNAME.
		 * Because of the normalization routines in the iterator, 
		 * there will always be an unsigned CNAME following a DNAME 
		 * (unless qtype=DNAME). */
		if(dname_seen && ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME) {
			dname_seen = 0;
			/* CNAME was synthesized by our own iterator */
			/* since the DNAME verified, mark the CNAME as secure */
			((struct packed_rrset_data*)s->entry.data)->security =
				sec_status_secure;
			((struct packed_rrset_data*)s->entry.data)->trust =
				rrset_trust_validated;
			continue;
		}

		/* Verify the answer rrset */
		sec = val_verify_rrset_entry(env, ve, s, key_entry);
		/* If the (answer) rrset failed to validate, then this 
		 * message is BAD. */
		if(sec != sec_status_secure) {
			log_nametypeclass(VERB_QUERY, "validator: response "
				"has failed ANSWER rrset:", s->rk.dname,
				ntohs(s->rk.type), ntohs(s->rk.rrset_class));
			chase_reply->security = sec_status_bogus;
			return 0;
		}

		/* Notice a DNAME that should be followed by an unsigned 
		 * CNAME. */
		if(qchase->qtype != LDNS_RR_TYPE_DNAME && 
			ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME) {
			dname_seen = 1;
		}
	}

	/* validate the AUTHORITY section */
	for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
		chase_reply->ns_numrrsets; i++) {
		s = chase_reply->rrsets[i];
		sec = val_verify_rrset_entry(env, ve, s, key_entry);
		/* If anything in the authority section fails to be secure, 
		 * we have a bad message. */
		if(sec != sec_status_secure) {
			log_nametypeclass(VERB_QUERY, "validator: response "
				"has failed AUTHORITY rrset:", s->rk.dname,
				ntohs(s->rk.type), ntohs(s->rk.rrset_class));
			chase_reply->security = sec_status_bogus;
			return 0;
		}
	}

	/* attempt to validate the ADDITIONAL section rrsets */
	if(!ve->clean_additional)
		return 1;
	for(i=chase_reply->an_numrrsets+chase_reply->ns_numrrsets; 
		i<chase_reply->rrset_count; i++) {
		s = chase_reply->rrsets[i];
		/* only validate rrs that have signatures with the key */
		/* leave others unchecked, those get removed later on too */
		val_find_rrset_signer(s, &sname, &slen);
		if(sname && query_dname_compare(sname, key_entry->name)==0)
			(void)val_verify_rrset_entry(env, ve, s, key_entry);
		/* the additional section can fail to be secure, 
		 * it is optional, check signature in case we need
		 * to clean the additional section later. */
	}

	return 1;
}

/**
 * Detect wrong truncated response, by a bad recursor out there.
 * The positive response has a mangled authority section.
 * Remove that authority section.
 * @param rep: reply
 * @return true if a wrongly truncated response.
 */
static int
detect_wrongly_truncated(struct reply_info* rep)
{
	size_t i;
	/* no additional, only NS in authority, and it is bogus */
	if(rep->ar_numrrsets != 0 || rep->ns_numrrsets != 1 ||
		rep->an_numrrsets == 0)
		return 0;
	if(ntohs(rep->rrsets[ rep->an_numrrsets ]->rk.type) != LDNS_RR_TYPE_NS)
		return 0;
	if(((struct packed_rrset_data*)rep->rrsets[ rep->an_numrrsets ]
		->entry.data)->security != sec_status_bogus)
		return 0;
	/* answer section is present and secure */
	for(i=0; i<rep->an_numrrsets; i++) {
		if(((struct packed_rrset_data*)rep->rrsets[ i ]
			->entry.data)->security != sec_status_secure)
			return 0;
	}
	return 1;
}
<commit_msg>documentation<commit_after>/*
 * validator/validator.c - secure validator DNS query response module
 *
 * Copyright (c) 2007, NLnet Labs. All rights reserved.
 *
 * This software is open source.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * 
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * 
 * Neither the name of the NLNET LABS nor the names of its contributors may
 * be used to endorse or promote products derived from this software without
 * specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * \file
 *
 * This file contains a module that performs validation of DNS queries.
 * According to RFC 4034.
 */
#include "config.h"
#include "validator/validator.h"
#include "validator/val_anchor.h"
#include "validator/val_kcache.h"
#include "validator/val_kentry.h"
#include "validator/val_utils.h"
#include "validator/val_nsec.h"
#include "validator/val_nsec3.h"
#include "validator/val_neg.h"
#include "services/cache/dns.h"
#include "util/data/dname.h"
#include "util/module.h"
#include "util/log.h"
#include "util/net_help.h"
#include "util/regional.h"
#include "util/config_file.h"
#include "util/fptr_wlist.h"

/** fill up nsec3 key iterations config entry */
static int
fill_nsec3_iter(struct val_env* ve, char* s, int c)
{
	char* e;
	int i;
	free(ve->nsec3_keysize);
	free(ve->nsec3_maxiter);
	ve->nsec3_keysize = (size_t*)calloc(sizeof(size_t), (size_t)c);
	ve->nsec3_maxiter = (size_t*)calloc(sizeof(size_t), (size_t)c);
	if(!ve->nsec3_keysize || !ve->nsec3_maxiter) {
		log_err("out of memory");
		return 0;
	}
	for(i=0; i<c; i++) {
		ve->nsec3_keysize[i] = (size_t)strtol(s, &e, 10);
		if(s == e) {
			log_err("cannot parse: %s", s);
			return 0;
		}
		s = e;
		ve->nsec3_maxiter[i] = (size_t)strtol(s, &e, 10);
		if(s == e) {
			log_err("cannot parse: %s", s);
			return 0;
		}
		s = e;
		if(i>0 && ve->nsec3_keysize[i-1] >= ve->nsec3_keysize[i]) {
			log_err("nsec3 key iterations not ascending: %d %d",
				(int)ve->nsec3_keysize[i-1], 
				(int)ve->nsec3_keysize[i]);
			return 0;
		}
		verbose(VERB_ALGO, "validator nsec3cfg keysz %d mxiter %d",
			(int)ve->nsec3_keysize[i], (int)ve->nsec3_maxiter[i]);
	}
	return 1;
}

/** apply config settings to validator */
static int
val_apply_cfg(struct module_env* env, struct val_env* val_env, 
	struct config_file* cfg)
{
	int c;
	val_env->bogus_ttl = (uint32_t)cfg->bogus_ttl;
	val_env->clean_additional = cfg->val_clean_additional;
	val_env->permissive_mode = cfg->val_permissive_mode;
	if(!env->anchors)
		env->anchors = anchors_create();
	if(!env->anchors) {
		log_err("out of memory");
		return 0;
	}
	if(!val_env->kcache)
		val_env->kcache = key_cache_create(cfg);
	if(!val_env->kcache) {
		log_err("out of memory");
		return 0;
	}
	if(!anchors_apply_cfg(env->anchors, cfg)) {
		log_err("validator: error in trustanchors config");
		return 0;
	}
	val_env->date_override = cfg->val_date_override;
	val_env->skew_min = cfg->val_sig_skew_min;
	val_env->skew_max = cfg->val_sig_skew_max;
	c = cfg_count_numbers(cfg->val_nsec3_key_iterations);
	if(c < 1 || (c&1)) {
		log_err("validator: unparseable or odd nsec3 key "
			"iterations: %s", cfg->val_nsec3_key_iterations);
		return 0;
	}
	val_env->nsec3_keyiter_count = c/2;
	if(!fill_nsec3_iter(val_env, cfg->val_nsec3_key_iterations, c/2)) {
		log_err("validator: cannot apply nsec3 key iterations");
		return 0;
	}
	if(!val_env->neg_cache)
		val_env->neg_cache = val_neg_create(cfg,
			val_env->nsec3_maxiter[val_env->nsec3_keyiter_count-1]);
	if(!val_env->neg_cache) {
		log_err("out of memory");
		return 0;
	}
	env->neg_cache = val_env->neg_cache;
	return 1;
}

int
val_init(struct module_env* env, int id)
{
	struct val_env* val_env = (struct val_env*)calloc(1,
		sizeof(struct val_env));
	if(!val_env) {
		log_err("malloc failure");
		return 0;
	}
	env->modinfo[id] = (void*)val_env;
	env->need_to_validate = 1;
	val_env->permissive_mode = 0;
	lock_basic_init(&val_env->bogus_lock);
	lock_protect(&val_env->bogus_lock, &val_env->num_rrset_bogus,
		sizeof(val_env->num_rrset_bogus));
	if(!val_apply_cfg(env, val_env, env->cfg)) {
		log_err("validator: could not apply configuration settings.");
		return 0;
	}
	return 1;
}

void
val_deinit(struct module_env* env, int id)
{
	struct val_env* val_env;
	if(!env || !env->modinfo[id])
		return;
	val_env = (struct val_env*)env->modinfo[id];
	lock_basic_destroy(&val_env->bogus_lock);
	anchors_delete(env->anchors);
	env->anchors = NULL;
	key_cache_delete(val_env->kcache);
	neg_cache_delete(val_env->neg_cache);
	free(val_env->nsec3_keysize);
	free(val_env->nsec3_maxiter);
	free(val_env);
	env->modinfo[id] = NULL;
}

/** allocate new validator query state */
static struct val_qstate*
val_new(struct module_qstate* qstate, int id)
{
	struct val_qstate* vq = (struct val_qstate*)regional_alloc(
		qstate->region, sizeof(*vq));
	log_assert(!qstate->minfo[id]);
	if(!vq)
		return NULL;
	memset(vq, 0, sizeof(*vq));
	qstate->minfo[id] = vq;
	vq->state = VAL_INIT_STATE;
	if(!qstate->return_msg || qstate->return_rcode != LDNS_RCODE_NOERROR) {
		/* create a message to verify */
		verbose(VERB_ALGO, "constructing reply for validation");
		vq->orig_msg = (struct dns_msg*)regional_alloc(qstate->region,
			sizeof(struct dns_msg));
		if(!vq->orig_msg)
			return NULL;
		vq->orig_msg->qinfo = qstate->qinfo;
		vq->orig_msg->rep = (struct reply_info*)regional_alloc(
			qstate->region, sizeof(struct reply_info));
		if(!vq->orig_msg->rep)
			return NULL;
		memset(vq->orig_msg->rep, 0, sizeof(struct reply_info));
		vq->orig_msg->rep->flags = (uint16_t)(qstate->return_rcode&0xf)
			|BIT_QR|BIT_RA|(qstate->query_flags|(BIT_CD|BIT_RD));
		vq->orig_msg->rep->qdcount = 1;
	} else {
		vq->orig_msg = qstate->return_msg;
	}
	vq->qchase = qstate->qinfo;
	/* chase reply will be an edited (sub)set of the orig msg rrset ptrs */
	vq->chase_reply = regional_alloc_init(qstate->region, 
		vq->orig_msg->rep, 
		sizeof(struct reply_info) - sizeof(struct rrset_ref));
	if(!vq->chase_reply)
		return NULL;
	vq->chase_reply->rrsets = regional_alloc_init(qstate->region,
		vq->orig_msg->rep->rrsets, sizeof(struct ub_packed_rrset_key*)
			* vq->orig_msg->rep->rrset_count);
	if(!vq->chase_reply->rrsets)
		return NULL;
	vq->rrset_skip = 0;
	return vq;
}

/**
 * Exit validation with an error status
 * 
 * @param qstate: query state
 * @param id: validator id.
 * @return false, for use by caller to return to stop processing.
 */
static int
val_error(struct module_qstate* qstate, int id)
{
	qstate->ext_state[id] = module_error;
	qstate->return_rcode = LDNS_RCODE_SERVFAIL;
	return 0;
}

/** 
 * Check to see if a given response needs to go through the validation
 * process. Typical reasons for this routine to return false are: CD bit was
 * on in the original request, or the response is a kind of message that 
 * is unvalidatable (i.e., SERVFAIL, REFUSED, etc.)
 *
 * @param qstate: query state.
 * @param ret_rc: rcode for this message (if noerror - examine ret_msg).
 * @param ret_msg: return msg, can be NULL; look at rcode instead.
 * @return true if the response could use validation (although this does not
 *         mean we can actually validate this response).
 */
static int
needs_validation(struct module_qstate* qstate, int ret_rc, 
	struct dns_msg* ret_msg)
{
	int rcode;

	/* If the CD bit is on in the original request, then we don't bother to
	 * validate anything.*/
	if(qstate->query_flags & BIT_CD) {
		verbose(VERB_ALGO, "not validating response due to CD bit");
		return 0;
	}

	if(ret_rc != LDNS_RCODE_NOERROR || !ret_msg)
		rcode = ret_rc;
	else 	rcode = (int)FLAGS_GET_RCODE(ret_msg->rep->flags);

	if(rcode != LDNS_RCODE_NOERROR && rcode != LDNS_RCODE_NXDOMAIN) {
		verbose(VERB_ALGO, "cannot validate non-answer, rcode %s",
			ldns_lookup_by_id(ldns_rcodes, rcode)?
			ldns_lookup_by_id(ldns_rcodes, rcode)->name:"??");
		return 0;
	}

	/* cannot validate positive RRSIG response. (negatives can) */
	if(qstate->qinfo.qtype == LDNS_RR_TYPE_RRSIG &&
		rcode == LDNS_RCODE_NOERROR && ret_msg &&
		ret_msg->rep->an_numrrsets > 0) {
		verbose(VERB_ALGO, "cannot validate RRSIG, no sigs on sigs.");
		return 0;
	}
	return 1;
}

/**
 * Check to see if the response has already been validated.
 * @param ret_msg: return msg, can be NULL
 * @return true if the response has already been validated
 */
static int
already_validated(struct dns_msg* ret_msg)
{
	/* validate unchecked, and re-validate bogus messages */
	if (ret_msg && ret_msg->rep->security > sec_status_bogus)
	{
		verbose(VERB_ALGO, "response has already been validated: %s",
			sec_status_to_string(ret_msg->rep->security));
		return 1;
	}
	return 0;
}

/**
 * Generate a request for DNS data.
 *
 * @param qstate: query state that is the parent.
 * @param id: module id.
 * @param name: what name to query for.
 * @param namelen: length of name.
 * @param qtype: query type.
 * @param qclass: query class.
 * @param flags: additional flags, such as the CD bit (BIT_CD), or 0.
 * @return false on alloc failure.
 */
static int
generate_request(struct module_qstate* qstate, int id, uint8_t* name, 
	size_t namelen, uint16_t qtype, uint16_t qclass, uint16_t flags)
{
	struct module_qstate* newq;
	struct query_info ask;
	ask.qname = name;
	ask.qname_len = namelen;
	ask.qtype = qtype;
	ask.qclass = qclass;
	log_query_info(VERB_ALGO, "generate request", &ask);
	fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub));
	if(!(*qstate->env->attach_sub)(qstate, &ask, 
		(uint16_t)(BIT_RD|flags), 0, &newq)){
		log_err("Could not generate request: out of memory");
		return 0;
	}
	/* ignore newq; validator does not need state created for that
	 * query, and its a 'normal' for iterator as well */
	qstate->ext_state[id] = module_wait_subquery;
	return 1;
}

/**
 * Prime trust anchor for use.
 * Generate and dispatch a priming query for the given trust anchor.
 * The trust anchor can be DNSKEY or DS and does not have to be signed.
 *
 * @param qstate: query state.
 * @param vq: validator query state.
 * @param id: module id.
 * @param toprime: what to prime.
 * @return false on a processing error.
 */
static int
prime_trust_anchor(struct module_qstate* qstate, struct val_qstate* vq,
	int id, struct trust_anchor* toprime)
{
	int ret = generate_request(qstate, id, toprime->name, toprime->namelen,
		LDNS_RR_TYPE_DNSKEY, toprime->dclass, BIT_CD);
	if(!ret) {
		log_err("Could not prime trust anchor: out of memory");
		return 0;
	}
	/* ignore newq; validator does not need state created for that
	 * query, and its a 'normal' for iterator as well */
	vq->wait_prime_ta = 1; /* to elicit PRIME_RESP_STATE processing 
		from the validator inform_super() routine */
	return 1;
}

/**
 * Validate if the ANSWER and AUTHORITY sections contain valid rrsets.
 * They must be validly signed with the given key.
 * Tries to validate ADDITIONAL rrsets as well, but only to check them.
 * Allows unsigned CNAME after a DNAME that expands the DNAME.
 * 
 * Note that by the time this method is called, the process of finding the
 * trusted DNSKEY rrset that signs this response must already have been
 * completed.
 * 
 * @param env: module env for verify.
 * @param ve: validator env for verify.
 * @param qchase: query that was made.
 * @param chase_reply: answer to validate.
 * @param key_entry: the key entry, which is trusted, and which matches
 * 	the signer of the answer. The key entry isgood().
 * @return false if any of the rrsets in the an or ns sections of the message 
 * 	fail to verify. The message is then set to bogus.
 */
static int
validate_msg_signatures(struct module_env* env, struct val_env* ve,
	struct query_info* qchase, struct reply_info* chase_reply, 
	struct key_entry_key* key_entry)
{
	uint8_t* sname;
	size_t i, slen;
	struct ub_packed_rrset_key* s;
	enum sec_status sec;
	int dname_seen = 0;

	/* validate the ANSWER section */
	for(i=0; i<chase_reply->an_numrrsets; i++) {
		s = chase_reply->rrsets[i];
		/* Skip the CNAME following a (validated) DNAME.
		 * Because of the normalization routines in the iterator, 
		 * there will always be an unsigned CNAME following a DNAME 
		 * (unless qtype=DNAME). */
		if(dname_seen && ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME) {
			dname_seen = 0;
			/* CNAME was synthesized by our own iterator */
			/* since the DNAME verified, mark the CNAME as secure */
			((struct packed_rrset_data*)s->entry.data)->security =
				sec_status_secure;
			((struct packed_rrset_data*)s->entry.data)->trust =
				rrset_trust_validated;
			continue;
		}

		/* Verify the answer rrset */
		sec = val_verify_rrset_entry(env, ve, s, key_entry);
		/* If the (answer) rrset failed to validate, then this 
		 * message is BAD. */
		if(sec != sec_status_secure) {
			log_nametypeclass(VERB_QUERY, "validator: response "
				"has failed ANSWER rrset:", s->rk.dname,
				ntohs(s->rk.type), ntohs(s->rk.rrset_class));
			chase_reply->security = sec_status_bogus;
			return 0;
		}

		/* Notice a DNAME that should be followed by an unsigned 
		 * CNAME. */
		if(qchase->qtype != LDNS_RR_TYPE_DNAME && 
			ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME) {
			dname_seen = 1;
		}
	}

	/* validate the AUTHORITY section */
	for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
		chase_reply->ns_numrrsets; i++) {
		s = chase_reply->rrsets[i];
		sec = val_verify_rrset_entry(env, ve, s, key_entry);
		/* If anything in the authority section fails to be secure, 
		 * we have a bad message. */
		if(sec != sec_status_secure) {
			log_nametypeclass(VERB_QUERY, "validator: response "
				"has failed AUTHORITY rrset:", s->rk.dname,
				ntohs(s->rk.type), ntohs(s->rk.rrset_class));
			chase_reply->security = sec_status_bogus;
			return 0;
		}
	}

	/* attempt to validate the ADDITIONAL section rrsets */
	if(!ve->clean_additional)
		return 1;
	for(i=chase_reply->an_numrrsets+chase_reply->ns_numrrsets; 
		i<chase_reply->rrset_count; i++) {
		s = chase_reply->rrsets[i];
		/* only validate rrs that have signatures with the key */
		/* leave others unchecked, those get removed later on too */
		val_find_rrset_signer(s, &sname, &slen);
		if(sname && query_dname_compare(sname, key_entry->name)==0)
			(void)val_verify_rrset_entry(env, ve, s, key_entry);
		/* the additional section can fail to be secure, 
		 * it is optional, check signature in case we need
		 * to clean the additional section later. */
	}

	return 1;
}

/**
 * Detect wrong truncated response (from BIND 9.6.1 with minimal-responses).
 * The positive response has a mangled authority section.
 * Remove that authority section.
 * @param rep: reply
 * @return true if a wrongly truncated response.
 */
static int
detect_wrongly_truncated(struct reply_info* rep)
{
	size_t i;
	/* no additional, only NS in authority, and it is bogus */
	if(rep->ar_numrrsets != 0 || rep->ns_numrrsets != 1 ||
		rep->an_numrrsets == 0)
		return 0;
	if(ntohs(rep->rrsets[ rep->an_numrrsets ]->rk.type) != LDNS_RR_TYPE_NS)
		return 0;
	if(((struct packed_rrset_data*)rep->rrsets[ rep->an_numrrsets ]
		->entry.data)->security != sec_status_bogus)
		return 0;
	/* answer section is present and secure */
	for(i=0; i<rep->an_numrrsets; i++) {
		if(((struct packed_rrset_data*)rep->rrsets[ i ]
			->entry.data)->security != sec_status_secure)
			return 0;
	}
	return 1;
}

{'fraction_non_alphanumeric': 0.12323084431429966, 'fraction_numerical': 0.007473157637872133, 'mean_word_length': 4.307592682531974, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 357, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5357576', 'n_tokens_mistral': 12659, 'n_tokens_neox': 11818, 'n_words': 3329}