Returns and caches validated replies.

git-svn-id: file:///svn/unbound/trunk@536 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2007-08-21 13:12:10 +00:00
parent 834a8fc30f
commit 272096d611
15 changed files with 215 additions and 66 deletions

View File

@ -271,6 +271,7 @@ static void daemon_setup_modules(struct daemon* daemon)
daemon->env->worker = NULL;
daemon->env->send_packet = &worker_send_packet;
daemon->env->send_query = &worker_send_query;
daemon->env->need_to_validate = 0; /* set by module init below */
for(i=0; i<daemon->num_modules; i++) {
log_info("init module %d: %s", i, daemon->modfunc[i]->name);
if(!(*daemon->modfunc[i]->init)(daemon->env, i)) {

View File

@ -327,6 +327,18 @@ check_cache_chain(struct reply_info* rep) {
return 1;
}
/** check security status in cache reply */
static int
all_rrsets_secure(struct reply_info* rep) {
size_t i;
for(i=0; i<rep->rrset_count; i++) {
if( ((struct packed_rrset_data*)rep->rrsets[i]->entry.data)
->security != sec_status_secure )
return 0;
}
return 1;
}
/** answer query from the cache */
static int
answer_from_cache(struct worker* worker, struct lruhash_entry* e, uint16_t id,
@ -336,6 +348,8 @@ answer_from_cache(struct worker* worker, struct lruhash_entry* e, uint16_t id,
struct reply_info* rep = (struct reply_info*)e->data;
uint32_t timenow = time(0);
uint16_t udpsize = edns->udp_size;
int secure;
int must_validate = !(flags&BIT_CD) && worker->env.need_to_validate;
/* see if it is possible */
if(rep->ttl <= timenow) {
/* the rrsets may have been updated in the meantime.
@ -356,15 +370,44 @@ answer_from_cache(struct worker* worker, struct lruhash_entry* e, uint16_t id,
htons(LDNS_RR_TYPE_CNAME) || rep->rrsets[0]->rk.type ==
htons(LDNS_RR_TYPE_DNAME))) {
if(!check_cache_chain(rep)) {
/* cname chain invalid, redo iterator steps */
verbose(VERB_ALGO, "Cache reply: cname chain broken");
bail_out:
rrset_array_unlock_touch(worker->env.rrset_cache,
worker->scratchpad, rep->ref, rep->rrset_count);
region_free_all(worker->scratchpad);
return 0;
}
}
/* check security status of the cached answer */
if( rep->security == sec_status_bogus && must_validate) {
/* BAD cached */
error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
&mrentry->key, id, flags, edns);
rrset_array_unlock_touch(worker->env.rrset_cache,
worker->scratchpad, rep->ref, rep->rrset_count);
region_free_all(worker->scratchpad);
return 1;
} else if( rep->security == sec_status_unchecked && must_validate) {
verbose(VERB_ALGO, "Cache reply: unchecked entry needs "
"validation");
goto bail_out; /* need to validate cache entry first */
} else if(rep->security == sec_status_secure) {
if(all_rrsets_secure(rep))
secure = 1;
else {
if(must_validate) {
verbose(VERB_ALGO, "Cache reply: secure entry"
" changed status");
goto bail_out; /* rrset changed, re-verify */
}
secure = 0;
}
} else secure = 0;
if(!reply_info_answer_encode(&mrentry->key, rep, id, flags,
repinfo->c->buffer, timenow, 1, worker->scratchpad,
udpsize, edns, (int)(edns->bits & EDNS_DO) )) {
udpsize, edns, (int)(edns->bits & EDNS_DO), secure)) {
error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
&mrentry->key, id, flags, edns);
}

View File

@ -1,5 +1,9 @@
21 August 2007: Wouter
- ANY response validation.
- store security status in cache.
- check cache security status and either send the query to be
validated, return the query to client, or send servfail to client.
Sets AD bit on validated replies.
20 August 2007: Wouter
- validate and positive validation, positive wildcard NSEC validation.

View File

@ -296,51 +296,7 @@ int
iter_dns_store(struct module_env* env, struct query_info* msgqinf,
struct reply_info* msgrep, int is_referral)
{
struct reply_info* rep = NULL;
/* alloc, malloc properly (not in region, like msg is) */
rep = reply_info_copy(msgrep, env->alloc, NULL);
if(!rep)
return 0;
if(is_referral) {
/* store rrsets */
struct rrset_ref ref;
uint32_t now = time(NULL);
size_t i;
for(i=0; i<rep->rrset_count; i++) {
packed_rrset_ttl_add((struct packed_rrset_data*)
rep->rrsets[i]->entry.data, now);
ref.key = rep->rrsets[i];
ref.id = rep->rrsets[i]->id;
/*ignore ret: it was in the cache, ref updated */
(void)rrset_cache_update(env->rrset_cache, &ref,
env->alloc, now);
}
free(rep);
return 1;
} else {
/* store msg, and rrsets */
struct query_info qinf;
hashvalue_t h;
qinf = *msgqinf;
qinf.qname = memdup(msgqinf->qname, msgqinf->qname_len);
if(!qinf.qname) {
reply_info_parsedelete(rep, env->alloc);
return 0;
}
/* fixup flags to be sensible for a reply based on the cache */
/* this module means that RA is available. It is an answer QR.
* Not AA from cache. Not CD in cache (depends on client bit). */
rep->flags |= (BIT_RA | BIT_QR);
rep->flags &= ~(BIT_AA | BIT_CD);
h = query_info_hash(&qinf);
dns_cache_store_msg(env, &qinf, h, rep);
/* qname is used inside query_info_entrysetup, and set to
* NULL. If it has not been used, free it. free(0) is safe. */
free(qinf.qname);
}
return 1;
return dns_cache_store(env, msgqinf, msgrep, is_referral);
}
int

52
services/cache/dns.c vendored
View File

@ -399,6 +399,7 @@ tomsg(struct module_env* env, struct msgreply_entry* e, struct reply_info* r,
msg->rep->flags = r->flags;
msg->rep->qdcount = r->qdcount;
msg->rep->ttl = r->ttl;
msg->rep->security = r->security;
msg->rep->an_numrrsets = r->an_numrrsets;
msg->rep->ns_numrrsets = r->ns_numrrsets;
msg->rep->ar_numrrsets = r->ar_numrrsets;
@ -585,3 +586,54 @@ dns_cache_lookup(struct module_env* env,
return NULL;
}
int
dns_cache_store(struct module_env* env, struct query_info* msgqinf,
struct reply_info* msgrep, int is_referral)
{
struct reply_info* rep = NULL;
/* alloc, malloc properly (not in region, like msg is) */
rep = reply_info_copy(msgrep, env->alloc, NULL);
if(!rep)
return 0;
if(is_referral) {
/* store rrsets */
struct rrset_ref ref;
uint32_t now = time(NULL);
size_t i;
for(i=0; i<rep->rrset_count; i++) {
packed_rrset_ttl_add((struct packed_rrset_data*)
rep->rrsets[i]->entry.data, now);
ref.key = rep->rrsets[i];
ref.id = rep->rrsets[i]->id;
/*ignore ret: it was in the cache, ref updated */
(void)rrset_cache_update(env->rrset_cache, &ref,
env->alloc, now);
}
free(rep);
return 1;
} else {
/* store msg, and rrsets */
struct query_info qinf;
hashvalue_t h;
qinf = *msgqinf;
qinf.qname = memdup(msgqinf->qname, msgqinf->qname_len);
if(!qinf.qname) {
reply_info_parsedelete(rep, env->alloc);
return 0;
}
/* fixup flags to be sensible for a reply based on the cache */
/* this module means that RA is available. It is an answer QR.
* Not AA from cache. Not CD in cache (depends on client bit). */
rep->flags |= (BIT_RA | BIT_QR);
rep->flags &= ~(BIT_AA | BIT_CD);
h = query_info_hash(&qinf);
dns_cache_store_msg(env, &qinf, h, rep);
/* qname is used inside query_info_entrysetup, and set to
* NULL. If it has not been used, free it. free(0) is safe. */
free(qinf.qname);
}
return 1;
}

18
services/cache/dns.h vendored
View File

@ -59,6 +59,24 @@ struct dns_msg {
struct reply_info *rep;
};
/**
* Allocate a dns_msg with malloc/alloc structure and store in dns cache.
*
* @param env: environment, with alloc structure and dns cache.
* @param qinf: query info, the query for which answer is stored.
* this is allocated in a region, and will be copied to malloc area
* before insertion.
* @param rep: reply in dns_msg from dns_alloc_msg for example.
* this is allocated in a region, and will be copied to malloc area
* before insertion.
* @param is_referral: If true, then the given message to be stored is a
* referral. The cache implementation may use this as a hint.
* It will store only the RRsets, not the message.
* @return 0 on alloc error (out of memory).
*/
int dns_cache_store(struct module_env* env, struct query_info* qinf,
struct reply_info* rep, int is_referral);
/**
* Store message in the cache. Stores in message cache and rrset cache.
* Both qinfo and rep should be malloced and are put in the cache.

View File

@ -425,6 +425,15 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
struct mesh_reply* r)
{
struct timeval end_time;
int secure;
/* examine security status */
if(m->s.env->need_to_validate && !(r->qflags&BIT_CD) &&
rep->security <= sec_status_bogus) {
rcode = LDNS_RCODE_SERVFAIL;
}
if(rep->security == sec_status_secure)
secure = 1;
else secure = 0;
/* send the reply */
if(rcode) {
error_encode(r->query_reply.c->buffer, rcode, &m->s.qinfo,
@ -439,7 +448,7 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
if(!reply_info_answer_encode(&m->s.qinfo, rep, r->qid,
r->qflags, r->query_reply.c->buffer, 0, 1,
m->s.env->scratch, udp_size, &r->edns,
(int)(r->edns.bits & EDNS_DO)))
(int)(r->edns.bits & EDNS_DO), secure))
{
error_encode(r->query_reply.c->buffer,
LDNS_RCODE_SERVFAIL, &m->s.qinfo, r->qid,

View File

@ -706,7 +706,7 @@ int
reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep,
uint16_t id, uint16_t qflags, ldns_buffer* pkt, uint32_t timenow,
int cached, struct region* region, uint16_t udpsize,
struct edns_data* edns, int dnssec)
struct edns_data* edns, int dnssec, int secure)
{
uint16_t flags;
int attach_edns = 1;
@ -718,6 +718,8 @@ reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep,
/* remove AA bit, copy RD and CD bits from query. */
flags = (rep->flags & ~BIT_AA) | (qflags & (BIT_RD|BIT_CD));
}
if(secure)
flags |= BIT_AD;
log_assert(flags & BIT_QR); /* QR bit must be on in our replies */
if(udpsize < LDNS_HEADER_SIZE)
return 0;

View File

@ -62,12 +62,13 @@ struct edns_data;
* @param edns: EDNS data included in the answer, NULL for none.
* or if edns_present = 0, it is not included.
* @param dnssec: if 0 DNSSEC records are omitted from the answer.
* @param secure: if 1, the AD bit is set in the reply.
* @return: 0 on error (server failure).
*/
int reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep,
uint16_t id, uint16_t qflags, ldns_buffer* dest, uint32_t timenow,
int cached, struct region* region, uint16_t udpsize,
struct edns_data* edns, int dnssec);
struct edns_data* edns, int dnssec, int secure);
/**
* Regenerate the wireformat from the stored msg reply.

View File

@ -151,6 +151,7 @@ enum rrset_trust {
/**
* Security status from validation for data.
* The order is significant; more secure, more proven later.
*/
enum sec_status {
/** UNCHECKED means that object has yet to be validated. */

View File

@ -191,6 +191,9 @@ struct module_env {
struct alloc_cache* alloc;
/** random table to generate random numbers */
struct ub_randstate* rnd;
/** is validation required for messages, controls client-facing
* validation status (AD bits) and servfails */
int need_to_validate;
/** module specific data. indexed by module id. */
void* modinfo[MAX_MODULE];
};

View File

@ -43,15 +43,25 @@
#define NET_HELP_H
#include "util/log.h"
/** DNS constants for uint16_t style flag manipulation. host byteorder. */
/** AA flag */
#define BIT_AA 0x0400
/** RD flag */
#define BIT_RD 0x0100
/** RA flag */
#define BIT_RA 0x0080
/** DNS constants for uint16_t style flag manipulation. host byteorder.
* 1 1 1 1 1 1
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* |QR| Opcode |AA|TC|RD|RA| Z|AD|CD| RCODE |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
*/
/** CD flag */
#define BIT_CD 0x0010
/** AD flag */
#define BIT_AD 0x0020
/** RA flag */
#define BIT_RA 0x0080
/** RD flag */
#define BIT_RD 0x0100
/** TC flag */
#define BIT_TC 0x0200
/** AA flag */
#define BIT_AA 0x0400
/** QR flag */
#define BIT_QR 0x8000
/** get RCODE bits from uint16 flags */

View File

@ -200,12 +200,27 @@ val_verify_rrset(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys)
{
enum sec_status sec;
struct packed_rrset_data* d = (struct packed_rrset_data*)rrset->
entry.data;
if(d->security == sec_status_secure) {
/* re-verify all other statuses, because keyset may change*/
log_nametypeclass(VERB_ALGO, "verify rrset cached",
rrset->rk.dname, ntohs(rrset->rk.type),
ntohs(rrset->rk.rrset_class));
return d->security;
}
log_nametypeclass(VERB_ALGO, "verify rrset", rrset->rk.dname,
ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class));
sec = dnskeyset_verify_rrset(env, ve, rrset, keys);
verbose(VERB_ALGO, "verify result: %s", sec_status_to_string(sec));
/* TODO: update rrset security status */
/* update rrset security status
* only improves security status */
if(sec > d->security) {
d->security = sec;
if(sec == sec_status_secure)
d->trust = rrset_trust_validated;
}
return sec;
}
@ -307,8 +322,6 @@ val_verify_new_DNSKEYs(struct region* region, struct module_env* env,
ds_rrset, i);
if(sec == sec_status_secure) {
verbose(VERB_ALGO, "DS matched DNSKEY.");
/* TODO -- cannot, wrong region for prime */
/* update dnskey RRset status as secure */
return key_entry_create_rrset(region,
ds_rrset->rk.dname, ds_rrset->rk.dname_len,
ntohs(ds_rrset->rk.rrset_class), dnskey_rrset);

View File

@ -89,6 +89,7 @@ val_init(struct module_env* env, int id)
return 0;
}
env->modinfo[id] = (void*)val_env;
env->need_to_validate = 1;
if(!val_apply_cfg(val_env, env->cfg)) {
log_err("validator: could not apply configuration settings.");
return 0;
@ -300,6 +301,12 @@ validate_positive_response(struct module_env* env, struct val_env* ve,
* (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;
}
@ -665,14 +672,18 @@ processInit(struct module_qstate* qstate, struct val_qstate* vq,
uint8_t* lookup_name;
size_t lookup_len;
if(!needs_validation(qstate, vq)) {
vq->state = vq->final_state;
return 1;
qstate->return_rcode = LDNS_RCODE_NOERROR;
qstate->return_msg = vq->orig_msg;
qstate->ext_state[id] = module_finished;
return 0;
}
vq->trust_anchor = anchors_lookup(ve->anchors, vq->qchase.qname,
vq->qchase.qname_len, vq->qchase.qclass);
if(vq->trust_anchor == NULL) {
/*response isn't under a trust anchor, so we cannot validate.*/
vq->state = vq->final_state;
vq->chase_reply->security = sec_status_indeterminate;
/* go to finished state to cache this result */
vq->state = VAL_FINISHED_STATE;
return 1;
}
@ -707,7 +718,8 @@ processInit(struct module_qstate* qstate, struct val_qstate* vq,
* However, we do set the status to INSECURE, since it is
* essentially proven insecure. */
vq->chase_reply->security = sec_status_insecure;
vq->state = vq->final_state;
/* go to finished state to cache this result */
vq->state = VAL_FINISHED_STATE;
return 1;
}
@ -911,6 +923,31 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq,
return 1;
}
/**
* The Finished state. The validation status (good or bad) has been determined.
*
* @param qstate: query state.
* @param vq: validator query state.
* @param id: module id.
* @return true if the event should be processed further on return, false if
* not.
*/
static int
processFinished(struct module_qstate* qstate, struct val_qstate* vq, int id)
{
/* TODO CNAME query restarts */
/* store results in cache */
if(!dns_cache_store(qstate->env, &vq->qchase, vq->chase_reply, 0)) {
log_err("out of memory caching validator results");
}
qstate->return_rcode = LDNS_RCODE_NOERROR;
qstate->return_msg = vq->orig_msg;
qstate->return_msg->rep = vq->chase_reply;
qstate->ext_state[id] = module_finished;
return 0;
}
/**
* Handle validator state.
* If a method returns true, the next state is started. If false, then
@ -939,6 +976,8 @@ val_handle(struct module_qstate* qstate, struct val_qstate* vq,
cont = processValidate(qstate, vq, ve, id);
break;
case VAL_FINISHED_STATE:
cont = processFinished(qstate, vq, id);
break;
default:
log_warn("validator: invalid state %d",
vq->state);

View File

@ -112,9 +112,6 @@ struct val_qstate {
*/
struct reply_info* chase_reply;
/** This is the "final" state for the event. */
enum val_state final_state;
/** the trust anchor rrset */
struct trust_anchor* trust_anchor;