]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
answer non-recursive straight from cache, if possible.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Mon, 27 Aug 2007 09:53:16 +0000 (09:53 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Mon, 27 Aug 2007 09:53:16 +0000 (09:53 +0000)
git-svn-id: file:///svn/unbound/trunk@548 be551aaa-1e26-0410-a405-d3ace91eadb9

daemon/worker.c
doc/Changelog
iterator/iterator.c
services/cache/dns.c
services/cache/dns.h
util/data/msgencode.c

index d3cd0582651213b9c04122bcb56de9f7a91ac009..432e04013b5b79632d311577e5d8159c997d4b6c 100644 (file)
@@ -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; i<rep->rrset_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);
 
index f9c907edbf4d7e30765b6cd727f4c845699252b3..a520ece188d2eb5ecb5ced955fdf4f332be32c7f 100644 (file)
@@ -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.
index efccdea4bf0f7f9b6101366411f455e59c6ee9d2..6b3380db834432f08d9f7424241f1d45f83e72c4 100644 (file)
@@ -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. */
index 8dcabf015cb3470d41a900a6b1d4010a304ce830..6f8f998a6f38914f782e217ce9ef010af7fef67f 100644 (file)
@@ -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;
 }
 
index d694a86635fbaf7375c03b40aa8ed50618894cae..33303883c277b92491fac2b13418b1eb0b596693 100644 (file)
@@ -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 
index a17653d733f1751416ef6560478efdadff8886b1..f890944b319852538c7bda16926952e0ff9e2fba 100644 (file)
@@ -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));