validate_msg_signatures(struct module_qstate* qstate, 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;
	char* reason = NULL;
	sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS;

	/* 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, &reason,
			&reason_bogus, LDNS_SECTION_ANSWER, qstate);
		/* 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));
			errinf_ede(qstate, reason, reason_bogus);
			if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME)
				errinf(qstate, "for CNAME");
			else if(ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME)
				errinf(qstate, "for DNAME");
			errinf_origin(qstate, qstate->reply_origin);
			chase_reply->security = sec_status_bogus;
			update_reason_bogus(chase_reply, reason_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, &reason,
			&reason_bogus, LDNS_SECTION_AUTHORITY, qstate);
		/* 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));
			errinf_ede(qstate, reason, reason_bogus);
			errinf_origin(qstate, qstate->reply_origin);
			errinf_rrset(qstate, s);
			chase_reply->security = sec_status_bogus;
			update_reason_bogus(chase_reply, reason_bogus);
			return 0;
		}
	}

	/* If set, the validator should clean the additional section of
	 * secure messages. */
	if(!env->cfg->val_clean_additional)
		return 1;
	/* attempt to validate the ADDITIONAL section rrsets */
	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,
				&reason, NULL, LDNS_SECTION_ADDITIONAL, qstate);
		/* 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;
}