From: Wouter Wijngaards Date: Mon, 27 Aug 2007 09:53:16 +0000 (+0000) Subject: answer non-recursive straight from cache, if possible. X-Git-Tag: release-0.5~93 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c9107bfb1a4f1b7b93f32e79b44f5b9583fe364b;p=thirdparty%2Funbound.git answer non-recursive straight from cache, if possible. git-svn-id: file:///svn/unbound/trunk@548 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/daemon/worker.c b/daemon/worker.c index d3cd05826..432e04013 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -55,6 +55,7 @@ #include "services/outbound_list.h" #include "services/cache/rrset.h" #include "services/cache/infra.h" +#include "services/cache/dns.h" #include "services/mesh.h" #include "util/data/msgparse.h" #include "util/data/msgencode.h" @@ -302,6 +303,96 @@ worker_handle_control_cmd(struct comm_point* c, void* arg, int error, return 0; } +/** check if a delegation is secure */ +static enum sec_status +check_delegation_secure(struct reply_info *rep) +{ + /* return smallest security status */ + size_t i; + enum sec_status sec = sec_status_secure; + enum sec_status s; + for(i=0; irrset_count; i++) { + s = ((struct packed_rrset_data*)rep->rrsets[i])->security; + if(s < sec) + sec = s; + } + return sec; +} + +/** answer nonrecursive query from the cache */ +static int +answer_norec_from_cache(struct worker* worker, struct query_info* qinfo, + uint16_t id, uint16_t flags, struct comm_reply* repinfo, + struct edns_data* edns) +{ + /* for a nonrecursive query return either: + * o an error (servfail; we try to avoid this) + * o a delegation (closest we have; this routine tries that) + * o the answer (checked by answer_from_cache) + * + * So, grab a delegation from the rrset cache. + * Then check if it needs validation, if so, this routine fails, + * so that iterator can prime and validator can verify rrsets. + */ + uint16_t udpsize = edns->udp_size; + int secure = 0; + uint32_t timenow = (uint32_t)time(0); + int must_validate = !(flags&BIT_CD) && worker->env.need_to_validate; + struct dns_msg *msg = NULL; + struct delegpt *dp; + + dp = dns_cache_find_delegation(&worker->env, qinfo->qname, + qinfo->qname_len, qinfo->qtype, qinfo->qclass, + worker->scratchpad, &msg, timenow); + if(!dp) { /* no delegation, need to reprime */ + region_free_all(worker->scratchpad); + return 0; + } + if(must_validate) { + switch(check_delegation_secure(msg->rep)) { + case sec_status_unchecked: + /* some rrsets have not been verified yet, go and + * let validator do that */ + region_free_all(worker->scratchpad); + return 0; + case sec_status_bogus: + /* some rrsets are bogus, reply servfail */ + edns->edns_version = EDNS_ADVERTISED_VERSION; + edns->udp_size = EDNS_ADVERTISED_SIZE; + edns->ext_rcode = 0; + edns->bits &= EDNS_DO; + error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, + &msg->qinfo, id, flags, edns); + region_free_all(worker->scratchpad); + return 1; + case sec_status_secure: + /* all rrsets are secure */ + secure = 1; + break; + case sec_status_indeterminate: + case sec_status_insecure: + default: + /* not secure */ + secure = 0; + break; + } + } + /* return this delegation from the cache */ + edns->edns_version = EDNS_ADVERTISED_VERSION; + edns->udp_size = EDNS_ADVERTISED_SIZE; + edns->ext_rcode = 0; + edns->bits &= EDNS_DO; + msg->rep->flags |= BIT_QR|BIT_RA; + if(!reply_info_answer_encode(&msg->qinfo, msg->rep, id, flags, + repinfo->c->buffer, timenow, 1, worker->scratchpad, + udpsize, edns, (int)(edns->bits & EDNS_DO), secure)) { + error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, + &msg->qinfo, id, flags, edns); + } + region_free_all(worker->scratchpad); + return 1; +} + /** check cname chain in cache reply */ static int check_cache_chain(struct reply_info* rep) { @@ -358,10 +449,6 @@ answer_from_cache(struct worker* worker, struct lruhash_entry* e, uint16_t id, */ return 0; } - edns->edns_version = EDNS_ADVERTISED_VERSION; - edns->udp_size = EDNS_ADVERTISED_SIZE; - edns->ext_rcode = 0; - edns->bits &= EDNS_DO; if(!rrset_array_lock(rep->ref, rep->rrset_count, timenow)) return 0; /* locked and ids and ttls are OK. */ @@ -382,6 +469,10 @@ answer_from_cache(struct worker* worker, struct lruhash_entry* e, uint16_t id, /* check security status of the cached answer */ if( rep->security == sec_status_bogus && must_validate) { /* BAD cached */ + edns->edns_version = EDNS_ADVERTISED_VERSION; + edns->udp_size = EDNS_ADVERTISED_SIZE; + edns->ext_rcode = 0; + edns->bits &= EDNS_DO; error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, &mrentry->key, id, flags, edns); rrset_array_unlock_touch(worker->env.rrset_cache, @@ -405,6 +496,10 @@ answer_from_cache(struct worker* worker, struct lruhash_entry* e, uint16_t id, } } else secure = 0; + edns->edns_version = EDNS_ADVERTISED_VERSION; + edns->udp_size = EDNS_ADVERTISED_SIZE; + edns->ext_rcode = 0; + edns->bits &= EDNS_DO; if(!reply_info_answer_encode(&mrentry->key, rep, id, flags, repinfo->c->buffer, timenow, 1, worker->scratchpad, udpsize, edns, (int)(edns->bits & EDNS_DO), secure)) { @@ -589,7 +684,6 @@ worker_handle_request(struct comm_point* c, void* arg, int error, edns.udp_size = 65535; /* max size for TCP replies */ if(qinfo.qclass == LDNS_RR_CLASS_CH && answer_chaos(worker, &qinfo, &edns, c->buffer)) { - verbose(VERB_ALGO, "class CH reply"); return 1; } h = query_info_hash(&qinfo); @@ -605,6 +699,16 @@ worker_handle_request(struct comm_point* c, void* arg, int error, verbose(VERB_DETAIL, "answer from the cache -- data has timed out"); lock_rw_unlock(&e->lock); } + if(!LDNS_RD_WIRE(ldns_buffer_begin(c->buffer))) { + if(answer_norec_from_cache(worker, &qinfo, + *(uint16_t*)ldns_buffer_begin(c->buffer), + ldns_buffer_read_u16_at(c->buffer, 2), repinfo, + &edns)) { + return 1; + } + verbose(VERB_DETAIL, "answer norec from cache -- " + "need to validate or not primed"); + } ldns_buffer_rewind(c->buffer); server_stats_querymiss(&worker->stats, worker); diff --git a/doc/Changelog b/doc/Changelog index f9c907edb..a520ece18 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,7 @@ +27 August 2007: Wouter + - do not garble the edns if a cache answer fails. + - answer norecursive from cache if possible. + 24 August 2007: Wouter - message is bogus if unsecure authority rrsets are present. - val-clean-additional option, so you can turn it off. diff --git a/iterator/iterator.c b/iterator/iterator.c index efccdea4b..6b3380db8 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -652,7 +652,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, * to be primed for the qclass. */ iq->dp = dns_cache_find_delegation(qstate->env, delname, delnamelen, iq->qchase.qtype, iq->qchase.qclass, qstate->region, - &iq->deleg_msg); + &iq->deleg_msg, (uint32_t)time(NULL)); /* If the cache has returned nothing, then we have a root priming * situation. */ @@ -668,6 +668,10 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, * of priming. */ return 0; } + if(verbosity >= VERB_ALGO) { + log_info("dns_cache_find_delegation returns delegpt"); + delegpt_log(iq->dp); + } /* Reset the RD flag. If this is a query restart, then the RD * will have been turned off. */ diff --git a/services/cache/dns.c b/services/cache/dns.c index 8dcabf015..6f8f998a6 100644 --- a/services/cache/dns.c +++ b/services/cache/dns.c @@ -316,13 +316,12 @@ create_msg(uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, struct delegpt* dns_cache_find_delegation(struct module_env* env, uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, - struct region* region, struct dns_msg** msg) + struct region* region, struct dns_msg** msg, uint32_t now) { /* try to find closest NS rrset */ struct ub_packed_rrset_key* nskey; struct packed_rrset_data* nsdata; struct delegpt* dp; - uint32_t now = (uint32_t)time(NULL); nskey = find_closest_of_type(env, qname, qnamelen, qclass, now, LDNS_RR_TYPE_NS); @@ -355,8 +354,6 @@ dns_cache_find_delegation(struct module_env* env, uint8_t* qname, /* find and add A entries */ if(!find_add_addrs(env, qclass, region, dp, now, msg)) log_err("find_delegation: addrs out of memory"); - log_info("dns_cache_find_delegation returns delegpt"); - delegpt_log(dp); return dp; } diff --git a/services/cache/dns.h b/services/cache/dns.h index d694a8663..33303883c 100644 --- a/services/cache/dns.h +++ b/services/cache/dns.h @@ -102,11 +102,12 @@ void dns_cache_store_msg(struct module_env* env, struct query_info* qinfo, * @param region: where to allocate result delegation. * @param msg: if not NULL, delegation message is returned here, synthesized * from the cache. + * @param timenow: the time now, for checking if TTL on cache entries is OK. * @return new delegation or NULL on error or if not found in cache. */ struct delegpt* dns_cache_find_delegation(struct module_env* env, uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, - struct region* region, struct dns_msg** msg); + struct region* region, struct dns_msg** msg, uint32_t timenow); /** * Find cached message diff --git a/util/data/msgencode.c b/util/data/msgencode.c index a17653d73..f890944b3 100644 --- a/util/data/msgencode.c +++ b/util/data/msgencode.c @@ -712,8 +712,8 @@ reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep, int attach_edns = 1; if(!cached) { - /* original flags, copy RD bit from query. */ - flags = rep->flags | (qflags & BIT_RD); + /* original flags, copy RD and CD bits from query. */ + flags = rep->flags | (qflags & (BIT_RD|BIT_CD)); } else { /* remove AA bit, copy RD and CD bits from query. */ flags = (rep->flags & ~BIT_AA) | (qflags & (BIT_RD|BIT_CD));