#include "util/module.h"
enum val_classification
-val_classify_response(struct query_info* qinf, struct reply_info* rep,
- size_t skip)
+val_classify_response(uint16_t query_flags, struct query_info* qinf,
+ struct reply_info* rep, size_t skip)
{
int rcode = (int)FLAGS_GET_RCODE(rep->flags);
size_t i;
if(rcode == LDNS_RCODE_NXDOMAIN && rep->an_numrrsets == 0)
return VAL_CLASS_NAMEERROR;
+ /* check for referral: nonRD query */
+ if(!(query_flags&BIT_RD))
+ return VAL_CLASS_REFERRAL;
+
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)
void
val_find_signer(enum val_classification subtype, struct query_info* qinf,
- struct reply_info* rep, size_t cname_skip, uint8_t** signer_name,
+ struct reply_info* rep, size_t skip, uint8_t** signer_name,
size_t* signer_len)
{
size_t i;
if(subtype == VAL_CLASS_POSITIVE || subtype == VAL_CLASS_CNAME
|| subtype == VAL_CLASS_ANY) {
/* check for the answer rrset */
- for(i=cname_skip; i<rep->an_numrrsets; i++) {
+ for(i=skip; i<rep->an_numrrsets; i++) {
if(query_dname_compare(qinf->qname,
rep->rrsets[i]->rk.dname) == 0) {
val_find_rrset_signer(rep->rrsets[i],
signer_name, signer_len, &matchcount);
}
}
+ } else if(subtype == VAL_CLASS_REFERRAL) {
+ /* find keys for the item at skip */
+ if(skip < rep->rrset_count) {
+ val_find_rrset_signer(rep->rrsets[skip],
+ signer_name, signer_len);
+ return;
+ }
+ *signer_name = NULL;
+ *signer_len = 0;
} else {
verbose(VERB_ALGO, "find_signer: could not find signer name"
" for unknown type response");
void
val_fill_reply(struct reply_info* chase, struct reply_info* orig,
- size_t cname_skip, uint8_t* name, size_t len)
+ size_t skip, uint8_t* name, size_t len)
{
/* unsigned RRsets are never copied, but should not happen in
* secure answers anyway. Except for the synthesized CNAME after
chase->ns_numrrsets = 0;
chase->ar_numrrsets = 0;
/* ANSWER section */
- for(i=cname_skip; i<orig->an_numrrsets; i++) {
+ for(i=skip; i<orig->an_numrrsets; i++) {
if(seen_dname && ntohs(orig->rrsets[i]->rk.type) ==
LDNS_RR_TYPE_CNAME) {
chase->rrsets[chase->an_numrrsets++] = orig->rrsets[i];
}
}
/* AUTHORITY section */
- for(i=orig->an_numrrsets; i<orig->an_numrrsets+orig->ns_numrrsets;
+ for(i = (skip > orig->an_numrrsets)?skip:orig->an_numrrsets;
+ i<orig->an_numrrsets+orig->ns_numrrsets;
i++) {
if(rrset_has_signer(orig->rrsets[i], name, len)) {
chase->rrsets[chase->an_numrrsets+
}
}
/* ADDITIONAL section */
- for(i=orig->an_numrrsets+orig->ns_numrrsets; i<orig->rrset_count;
- i++) {
+ for(i= (skip>orig->an_numrrsets+orig->ns_numrrsets)?
+ skip:orig->an_numrrsets+orig->ns_numrrsets;
+ i<orig->rrset_count; i++) {
if(rrset_has_signer(orig->rrsets[i], name, len)) {
chase->rrsets[chase->an_numrrsets+orig->ns_numrrsets+
chase->ar_numrrsets++] = orig->rrsets[i];
}
}
}
+
+size_t
+val_next_unchecked(struct reply_info* rep, size_t skip)
+{
+ size_t i;
+ struct packed_rrset_data* d;
+ for(i=skip+1; i<rep->rrset_count; i++) {
+ d = (struct packed_rrset_data*)rep->rrsets[i]->entry.data;
+ if(d->security == sec_status_unchecked) {
+ return i;
+ }
+ }
+ return rep->rrset_count;
+}
/** A CNAME/DNAME chain, and the offset is at the end of it,
* but there is no answer here, it can be NAMERROR or NODATA. */
VAL_CLASS_CNAMENOANSWER,
+ /** A referral, from cache with a nonRD query. */
+ VAL_CLASS_REFERRAL,
/** A response to a qtype=ANY query. */
VAL_CLASS_ANY
};
/**
* Given a response, classify ANSWER responses into a subtype.
+ * @param query_flags: query flags for the original query.
* @param qinf: query info. The chased query name.
* @param rep: response. The original response.
* @param skip: offset into the original response answer section.
* Once CNAME type is returned you can increase skip.
* Then, another CNAME type, CNAME_NOANSWER or POSITIVE are possible.
*/
-enum val_classification val_classify_response(struct query_info* qinf,
- struct reply_info* rep, size_t skip);
+enum val_classification val_classify_response(uint16_t query_flags,
+ struct query_info* qinf, struct reply_info* rep, size_t skip);
/**
* Given a response, determine the name of the "signer". This is primarily
void val_mark_insecure(struct reply_info* rep, struct key_entry_key* kkey,
struct rrset_cache* r);
+/**
+ * Find next unchecked rrset position, return it for skip.
+ * @param rep: the original reply to look into.
+ * @param skip: the skip now.
+ * @return new skip, which may be at the rep->rrset_count position to signal
+ * there are no unchecked items.
+ */
+size_t val_next_unchecked(struct reply_info* rep, size_t skip);
+
#endif /* VALIDATOR_VAL_UTILS_H */
* vq->orig_msg->rep->rrset_count);
if(!vq->chase_reply->rrsets)
return NULL;
- vq->cname_skip = 0;
+ vq->rrset_skip = 0;
return vq;
}
chase_reply->security = sec_status_secure;
}
+/**
+ * Given a referral response, validate rrsets and take least trusted rrset
+ * as the current validation status.
+ *
+ * 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 chase_reply: answer to validate.
+ */
+static void
+validate_referral_response(struct reply_info* chase_reply)
+{
+ size_t i;
+ enum sec_status s;
+ /* message security equals lowest rrset security */
+ chase_reply->security = sec_status_secure;
+ for(i=0; i<chase_reply->rrset_count; i++) {
+ s = ((struct packed_rrset_data*)chase_reply->rrsets[i]
+ ->entry.data)->security;
+ if(s < chase_reply->security)
+ chase_reply->security = s;
+ }
+ verbose(VERB_ALGO, "validated part of referral response as %s",
+ sec_status_to_string(chase_reply->security));
+}
+
/**
* Given an "ANY" response -- a response that contains an answer to a
* qtype==ANY question, with answers. This does no checking that all
{
uint8_t* lookup_name;
size_t lookup_len;
- enum val_classification subtype = val_classify_response(&vq->qchase,
- vq->orig_msg->rep, vq->cname_skip);
+ enum val_classification subtype = val_classify_response(
+ qstate->query_flags, &vq->qchase, vq->orig_msg->rep,
+ vq->rrset_skip);
+ 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);
+ /* 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->rrsets[vq->rrset_skip]->
+ rk.flags&PACKED_RRSET_NSEC_AT_APEX))) {
+ dname_remove_label(&vq->qchase.qname,
+ &vq->qchase.qname_len);
+ }
+ }
val_mark_indeterminate(vq->chase_reply, ve->anchors,
qstate->env->rrset_cache);
/* Determine the signer/lookup name */
val_find_signer(subtype, &vq->qchase, vq->orig_msg->rep,
- vq->cname_skip, &vq->signer_name, &vq->signer_len);
+ vq->rrset_skip, &vq->signer_name, &vq->signer_len);
if(vq->signer_name == NULL) {
lookup_name = vq->qchase.qname;
lookup_len = vq->qchase.qname_len;
lookup_len = vq->signer_len;
}
- if(vq->cname_skip > 0 || subtype == VAL_CLASS_CNAME) {
+ 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->cname_skip, lookup_name, lookup_len);
+ vq->rrset_skip, lookup_name, lookup_len);
log_dns_msg("chased extract", &vq->qchase, vq->chase_reply);
}
return 1;
}
- subtype = val_classify_response(&vq->qchase, vq->orig_msg->rep,
- vq->cname_skip);
+ subtype = val_classify_response(qstate->query_flags, &vq->qchase,
+ vq->orig_msg->rep, vq->rrset_skip);
switch(subtype) {
case VAL_CLASS_POSITIVE:
verbose(VERB_ALGO, "Validating a positive response");
vq->chase_reply);
break;
+ case VAL_CLASS_REFERRAL:
+ verbose(VERB_ALGO, "Validating a referral response");
+ validate_referral_response(vq->chase_reply);
+ break;
+
case VAL_CLASS_ANY:
verbose(VERB_ALGO, "Validating a positive ANY "
"response");
processFinished(struct module_qstate* qstate, struct val_qstate* vq,
struct val_env* ve, int id)
{
- enum val_classification subtype = val_classify_response(&vq->qchase,
- vq->orig_msg->rep, vq->cname_skip);
+ enum val_classification subtype = val_classify_response(
+ qstate->query_flags, &vq->qchase, vq->orig_msg->rep,
+ vq->rrset_skip);
/* store overall validation result in orig_msg */
- if(vq->cname_skip == 0)
+ if(vq->rrset_skip == 0)
vq->orig_msg->rep->security = vq->chase_reply->security;
else {
/* use the lowest security status as end result. */
vq->chase_reply->security;
}
+ if(subtype == VAL_CLASS_REFERRAL) {
+ /* for a referral, move to next unchecked rrset and check it*/
+ vq->rrset_skip = val_next_unchecked(vq->orig_msg->rep,
+ vq->rrset_skip);
+ if(vq->rrset_skip < vq->orig_msg->rep->rrset_count) {
+ /* and restart for this rrset */
+ verbose(VERB_ALGO, "validator: go to next rrset");
+ vq->chase_reply->security = sec_status_unchecked;
+ vq->state = VAL_INIT_STATE;
+ return 1;
+ }
+ /* referral chase is done */
+ }
if(vq->chase_reply->security != sec_status_bogus &&
subtype == VAL_CLASS_CNAME) {
/* chase the CNAME; process next part of the message */
if(!val_chase_cname(&vq->qchase, vq->orig_msg->rep,
- &vq->cname_skip)) {
+ &vq->rrset_skip)) {
verbose(VERB_ALGO, "validator: failed to chase CNAME");
vq->orig_msg->rep->security = sec_status_bogus;
} else {
- /* restart process for new qchase at cname_skip */
+ /* restart process for new qchase at rrset_skip */
log_query_info(VERB_DETAIL, "validator: chased to",
&vq->qchase);
vq->chase_reply->security = sec_status_unchecked;
vq->orig_msg->rep, 0)) {
log_err("out of memory caching validator results");
}
+ } else {
+ /* for a referral, store the verified RRsets */
+ if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo,
+ vq->orig_msg->rep, 1)) {
+ log_err("out of memory caching validator results");
+ }
}
qstate->return_rcode = LDNS_RCODE_NOERROR;
qstate->return_msg = vq->orig_msg;
goto return_bogus;
}
- subtype = val_classify_response(qinfo, msg->rep, 0);
+ subtype = val_classify_response(BIT_RD, qinfo, msg->rep, 0);
if(subtype == VAL_CLASS_POSITIVE) {
struct ub_packed_rrset_key* ds;
enum sec_status sec;