ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
        int id, int rcode, struct dns_msg* msg, struct query_info* qinfo,
	struct key_entry_key** ke)
{
	struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
	char* reason = NULL;
	sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
	enum val_classification subtype;
	if(rcode != LDNS_RCODE_NOERROR) {
		char rc[16];
		rc[0]=0;
		(void)sldns_wire2str_rcode_buf(rcode, rc, sizeof(rc));
		/* errors here pretty much break validation */
		verbose(VERB_DETAIL, "DS response was error, thus bogus");
		errinf(qstate, rc);
		errinf_ede(qstate, "no DS", LDNS_EDE_NETWORK_ERROR);

		goto return_bogus;
	}

	subtype = val_classify_response(BIT_RD, qinfo, qinfo, msg->rep, 0);
	if(subtype == VAL_CLASS_POSITIVE) {
		struct ub_packed_rrset_key* ds;
		enum sec_status sec;
		ds = reply_find_answer_rrset(qinfo, msg->rep);
		/* If there was no DS rrset, then we have mis-classified 
		 * this message. */
		if(!ds) {
			log_warn("internal error: POSITIVE DS response was "
				"missing DS.");
			errinf_ede(qstate, "no DS record", LDNS_EDE_DNSSEC_BOGUS);
			goto return_bogus;
		}
		/* Verify only returns BOGUS or SECURE. If the rrset is 
		 * bogus, then we are done. */
		sec = val_verify_rrset_entry(qstate->env, ve, ds,
			vq->key_entry, &reason, &reason_bogus, LDNS_SECTION_ANSWER, qstate);
		if(sec != sec_status_secure) {
			verbose(VERB_DETAIL, "DS rrset in DS response did "
				"not verify");
			errinf_ede(qstate, reason, reason_bogus);
			goto return_bogus;
		}

		/* If the DS rrset validates, we still have to make sure 
		 * that they are usable. */
		if(!val_dsset_isusable(ds)) {
			/* If they aren't usable, then we treat it like 
			 * there was no DS. */

			/* TODO add EDE Unsupported DS Digest Type; this needs
			 * EDE to be added on non SERVFAIL answers. */

			*ke = key_entry_create_null(qstate->region, 
				qinfo->qname, qinfo->qname_len, qinfo->qclass, 
				ub_packed_rrset_ttl(ds), *qstate->env->now);
			return (*ke) != NULL;
		}

		/* Otherwise, we return the positive response. */
		log_query_info(VERB_DETAIL, "validated DS", qinfo);
		*ke = key_entry_create_rrset(qstate->region,
			qinfo->qname, qinfo->qname_len, qinfo->qclass, ds,
			NULL, *qstate->env->now);
		return (*ke) != NULL;
	} else if(subtype == VAL_CLASS_NODATA || 
		subtype == VAL_CLASS_NAMEERROR) {
		/* NODATA means that the qname exists, but that there was 
		 * no DS.  This is a pretty normal case. */
		time_t proof_ttl = 0;
		enum sec_status sec;

		/* make sure there are NSECs or NSEC3s with signatures */
		if(!val_has_signed_nsecs(msg->rep, &reason)) {
			verbose(VERB_ALGO, "no NSECs: %s", reason);
			errinf_ede(qstate, reason, LDNS_EDE_NSEC_MISSING);
			goto return_bogus;
		}

		/* For subtype Name Error.
		 * attempt ANS 2.8.1.0 compatibility where it sets rcode
		 * to nxdomain, but really this is an Nodata/Noerror response.
		 * Find and prove the empty nonterminal in that case */

		/* Try to prove absence of the DS with NSEC */
		sec = val_nsec_prove_nodata_dsreply(
			qstate->env, ve, qinfo, msg->rep, vq->key_entry, 
			&proof_ttl, &reason, qstate);
		switch(sec) {
			case sec_status_secure:
				verbose(VERB_DETAIL, "NSEC RRset for the "
					"referral proved no DS.");
				*ke = key_entry_create_null(qstate->region, 
					qinfo->qname, qinfo->qname_len, 
					qinfo->qclass, proof_ttl,
					*qstate->env->now);
				return (*ke) != NULL;
			case sec_status_insecure:
				verbose(VERB_DETAIL, "NSEC RRset for the "
				  "referral proved not a delegation point");
				*ke = NULL;
				return 1;
			case sec_status_bogus:
				verbose(VERB_DETAIL, "NSEC RRset for the "
					"referral did not prove no DS.");
				errinf(qstate, reason);
				goto return_bogus;
			case sec_status_unchecked:
			default:
				/* NSEC proof did not work, try next */
				break;
		}

		sec = nsec3_prove_nods(qstate->env, ve, 
			msg->rep->rrsets + msg->rep->an_numrrsets,
			msg->rep->ns_numrrsets, qinfo, vq->key_entry, &reason,
			&reason_bogus, qstate);
		switch(sec) {
			case sec_status_insecure:
				/* case insecure also continues to unsigned
				 * space.  If nsec3-iter-count too high or
				 * optout, then treat below as unsigned */
			case sec_status_secure:
				verbose(VERB_DETAIL, "NSEC3s for the "
					"referral proved no DS.");
				*ke = key_entry_create_null(qstate->region, 
					qinfo->qname, qinfo->qname_len, 
					qinfo->qclass, proof_ttl,
					*qstate->env->now);
				return (*ke) != NULL;
			case sec_status_indeterminate:
				verbose(VERB_DETAIL, "NSEC3s for the "
				  "referral proved no delegation");
				*ke = NULL;
				return 1;
			case sec_status_bogus:
				verbose(VERB_DETAIL, "NSEC3s for the "
					"referral did not prove no DS.");
				errinf_ede(qstate, reason, reason_bogus);
				goto return_bogus;
			case sec_status_unchecked:
			default:
				/* NSEC3 proof did not work */
				break;
		}

		/* Apparently, no available NSEC/NSEC3 proved NODATA, so 
		 * this is BOGUS. */
		verbose(VERB_DETAIL, "DS %s ran out of options, so return "
			"bogus", val_classification_to_string(subtype));
		errinf(qstate, "no DS but also no proof of that");
		goto return_bogus;
	} else if(subtype == VAL_CLASS_CNAME || 
		subtype == VAL_CLASS_CNAMENOANSWER) {
		/* if the CNAME matches the exact name we want and is signed
		 * properly, then also, we are sure that no DS exists there,
		 * much like a NODATA proof */
		enum sec_status sec;
		struct ub_packed_rrset_key* cname;
		cname = reply_find_rrset_section_an(msg->rep, qinfo->qname,
			qinfo->qname_len, LDNS_RR_TYPE_CNAME, qinfo->qclass);
		if(!cname) {
			errinf(qstate, "validator classified CNAME but no "
				"CNAME of the queried name for DS");
			goto return_bogus;
		}
		if(((struct packed_rrset_data*)cname->entry.data)->rrsig_count
			== 0) {
		        if(msg->rep->an_numrrsets != 0 && ntohs(msg->rep->
				rrsets[0]->rk.type)==LDNS_RR_TYPE_DNAME) {
				errinf(qstate, "DS got DNAME answer");
			} else {
				errinf(qstate, "DS got unsigned CNAME answer");
			}
			goto return_bogus;
		}
		sec = val_verify_rrset_entry(qstate->env, ve, cname, 
			vq->key_entry, &reason, NULL, LDNS_SECTION_ANSWER, qstate);
		if(sec == sec_status_secure) {
			verbose(VERB_ALGO, "CNAME validated, "
				"proof that DS does not exist");
			/* and that it is not a referral point */
			*ke = NULL;
			return 1;
		}
		errinf(qstate, "CNAME in DS response was not secure.");
		errinf(qstate, reason);
		goto return_bogus;
	} else {
		verbose(VERB_QUERY, "Encountered an unhandled type of "
			"DS response, thus bogus.");
		errinf(qstate, "no DS and");
		if(FLAGS_GET_RCODE(msg->rep->flags) != LDNS_RCODE_NOERROR) {
			char rc[16];
			rc[0]=0;
			(void)sldns_wire2str_rcode_buf((int)FLAGS_GET_RCODE(
				msg->rep->flags), rc, sizeof(rc));
			errinf(qstate, rc);
		} else	errinf(qstate, val_classification_to_string(subtype));
		errinf(qstate, "message fails to prove that");
		goto return_bogus;
	}
return_bogus:
	*ke = key_entry_create_bad(qstate->region, qinfo->qname,
		qinfo->qname_len, qinfo->qclass, 
		BOGUS_KEY_TTL, *qstate->env->now);
	return (*ke) != NULL;
}