processInit(struct module_qstate* qstate, struct val_qstate* vq, 
	struct val_env* ve, int id)
{
	uint8_t* lookup_name;
	size_t lookup_len;
	struct trust_anchor* anchor;
	enum val_classification subtype = val_classify_response(
		qstate->query_flags, &qstate->qinfo, &vq->qchase, 
		vq->orig_msg->rep, vq->rrset_skip);
	if(vq->restart_count > ve->max_restart) {
		verbose(VERB_ALGO, "restart count exceeded");
		return val_error(qstate, id);
	}

	/* correctly initialize reason_bogus */
	update_reason_bogus(vq->chase_reply, LDNS_EDE_DNSSEC_BOGUS);

	verbose(VERB_ALGO, "validator classification %s", 
		val_classification_to_string(subtype));
	if(subtype == VAL_CLASS_REFERRAL && 
		vq->rrset_skip < vq->orig_msg->rep->rrset_count) {
		/* referral uses the rrset name as qchase, to find keys for
		 * that rrset */
		vq->qchase.qname = vq->orig_msg->rep->
			rrsets[vq->rrset_skip]->rk.dname;
		vq->qchase.qname_len = vq->orig_msg->rep->
			rrsets[vq->rrset_skip]->rk.dname_len;
		vq->qchase.qtype = ntohs(vq->orig_msg->rep->
			rrsets[vq->rrset_skip]->rk.type);
		vq->qchase.qclass = ntohs(vq->orig_msg->rep->
			rrsets[vq->rrset_skip]->rk.rrset_class);
	}
	lookup_name = vq->qchase.qname;
	lookup_len = vq->qchase.qname_len;
	/* for type DS look at the parent side for keys/trustanchor */
	/* also for NSEC not at apex */
	if(vq->qchase.qtype == LDNS_RR_TYPE_DS ||
		(vq->qchase.qtype == LDNS_RR_TYPE_NSEC && 
		 vq->orig_msg->rep->rrset_count > vq->rrset_skip &&
		 ntohs(vq->orig_msg->rep->rrsets[vq->rrset_skip]->rk.type) ==
		 LDNS_RR_TYPE_NSEC &&
		 !(vq->orig_msg->rep->rrsets[vq->rrset_skip]->
		 rk.flags&PACKED_RRSET_NSEC_AT_APEX))) {
		dname_remove_label(&lookup_name, &lookup_len);
	}

	val_mark_indeterminate(vq->chase_reply, qstate->env->anchors, 
		qstate->env->rrset_cache, qstate->env);
	vq->key_entry = NULL;
	vq->empty_DS_name = NULL;
	vq->ds_rrset = 0;
	anchor = anchors_lookup(qstate->env->anchors, 
		lookup_name, lookup_len, vq->qchase.qclass);

	/* Determine the signer/lookup name */
	val_find_signer(subtype, &vq->qchase, vq->orig_msg->rep, 
		vq->rrset_skip, &vq->signer_name, &vq->signer_len);
	if(vq->signer_name != NULL &&
		!dname_subdomain_c(lookup_name, vq->signer_name)) {
		log_nametypeclass(VERB_ALGO, "this signer name is not a parent "
			"of lookupname, omitted", vq->signer_name, 0, 0);
		vq->signer_name = NULL;
	}
	if(vq->signer_name == NULL) {
		log_nametypeclass(VERB_ALGO, "no signer, using", lookup_name,
			0, 0);
	} else {
		lookup_name = vq->signer_name;
		lookup_len = vq->signer_len;
		log_nametypeclass(VERB_ALGO, "signer is", lookup_name, 0, 0);
	}

	/* for NXDOMAIN it could be signed by a parent of the trust anchor */
	if(subtype == VAL_CLASS_NAMEERROR && vq->signer_name &&
		anchor && dname_strict_subdomain_c(anchor->name, lookup_name)){
		lock_basic_unlock(&anchor->lock);
		anchor = anchors_lookup(qstate->env->anchors, 
			lookup_name, lookup_len, vq->qchase.qclass);
		if(!anchor) { /* unsigned parent denies anchor*/
			verbose(VERB_QUERY, "unsigned parent zone denies"
				" trust anchor, indeterminate");
			vq->chase_reply->security = sec_status_indeterminate;
			update_reason_bogus(vq->chase_reply, LDNS_EDE_DNSSEC_INDETERMINATE);
			vq->state = VAL_FINISHED_STATE;
			return 1;
		}
		verbose(VERB_ALGO, "trust anchor NXDOMAIN by signed parent");
	} else if(subtype == VAL_CLASS_POSITIVE &&
		qstate->qinfo.qtype == LDNS_RR_TYPE_DNSKEY &&
		query_dname_compare(lookup_name, qstate->qinfo.qname) == 0) {
		/* is a DNSKEY so lookup a bit higher since we want to
		 * get it from a parent or from trustanchor */
		dname_remove_label(&lookup_name, &lookup_len);
	}

	if(vq->rrset_skip > 0 || subtype == VAL_CLASS_CNAME ||
		subtype == VAL_CLASS_REFERRAL) {
		/* extract this part of orig_msg into chase_reply for
		 * the eventual VALIDATE stage */
		val_fill_reply(vq->chase_reply, vq->orig_msg->rep, 
			vq->rrset_skip, lookup_name, lookup_len, 
			vq->signer_name);
		if(verbosity >= VERB_ALGO)
			log_dns_msg("chased extract", &vq->qchase, 
				vq->chase_reply);
	}

	vq->key_entry = key_cache_obtain(ve->kcache, lookup_name, lookup_len,
		vq->qchase.qclass, qstate->region, *qstate->env->now);

	/* there is no key and no trust anchor */
	if(vq->key_entry == NULL && anchor == NULL) {
		/*response isn't under a trust anchor, so we cannot validate.*/
		vq->chase_reply->security = sec_status_indeterminate;
		update_reason_bogus(vq->chase_reply, LDNS_EDE_DNSSEC_INDETERMINATE);
		/* go to finished state to cache this result */
		vq->state = VAL_FINISHED_STATE;
		return 1;
	}
	/* if not key, or if keyentry is *above* the trustanchor, i.e.
	 * the keyentry is based on another (higher) trustanchor */
	else if(vq->key_entry == NULL || (anchor &&
		dname_strict_subdomain_c(anchor->name, vq->key_entry->name))) {
		/* trust anchor is an 'unsigned' trust anchor */
		if(anchor && anchor->numDS == 0 && anchor->numDNSKEY == 0) {
			vq->chase_reply->security = sec_status_insecure;
			val_mark_insecure(vq->chase_reply, anchor->name, 
				qstate->env->rrset_cache, qstate->env);
			lock_basic_unlock(&anchor->lock);
			/* go to finished state to cache this result */
			vq->state = VAL_FINISHED_STATE;
			return 1;
		}
		/* fire off a trust anchor priming query. */
		verbose(VERB_DETAIL, "prime trust anchor");
		if(!prime_trust_anchor(qstate, vq, id, anchor)) {
			lock_basic_unlock(&anchor->lock);
			return val_error(qstate, id);
		}
		lock_basic_unlock(&anchor->lock);
		/* and otherwise, don't continue processing this event.
		 * (it will be reactivated when the priming query returns). */
		vq->state = VAL_FINDKEY_STATE;
		return 0;
	}
	if(anchor) {
		lock_basic_unlock(&anchor->lock);
	}

	if(key_entry_isnull(vq->key_entry)) {
		/* response is under a null key, so we cannot validate
		 * However, we do set the status to INSECURE, since it is 
		 * essentially proven insecure. */
		vq->chase_reply->security = sec_status_insecure;
		val_mark_insecure(vq->chase_reply, vq->key_entry->name, 
			qstate->env->rrset_cache, qstate->env);
		/* go to finished state to cache this result */
		vq->state = VAL_FINISHED_STATE;
		return 1;
	} else if(key_entry_isbad(vq->key_entry)) {
		sldns_ede_code ede = LDNS_EDE_DNSSEC_BOGUS;

		/* the key could have a more spefic EDE than just bogus */
		if(key_entry_get_reason_bogus(vq->key_entry) != LDNS_EDE_NONE) {
			ede = key_entry_get_reason_bogus(vq->key_entry);
		}

		/* key is bad, chain is bad, reply is bogus */
		errinf_dname(qstate, "key for validation", vq->key_entry->name);
		errinf_ede(qstate, "is marked as invalid", ede);
		if(key_entry_get_reason(vq->key_entry)) {
			errinf(qstate, "because of a previous");
			errinf(qstate, key_entry_get_reason(vq->key_entry));
		}

		/* no retries, stop bothering the authority until timeout */
		vq->restart_count = ve->max_restart;
		vq->chase_reply->security = sec_status_bogus;
		update_reason_bogus(vq->chase_reply, ede);
		vq->state = VAL_FINISHED_STATE;
		return 1;
	}

	/* otherwise, we have our "closest" cached key -- continue 
	 * processing in the next state. */
	vq->state = VAL_FINDKEY_STATE;
	return 1;
}