mirror of
https://github.com/NLnetLabs/unbound.git
synced 2024-09-21 06:37:08 +00:00
Returns and caches validated replies.
git-svn-id: file:///svn/unbound/trunk@536 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
834a8fc30f
commit
272096d611
@ -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)) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
52
services/cache/dns.c
vendored
@ -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
18
services/cache/dns.h
vendored
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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. */
|
||||
|
@ -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];
|
||||
};
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user