]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
unit test for auth zone lookup
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Tue, 30 Jan 2018 15:44:49 +0000 (15:44 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Tue, 30 Jan 2018 15:44:49 +0000 (15:44 +0000)
git-svn-id: file:///svn/unbound/trunk@4469 be551aaa-1e26-0410-a405-d3ace91eadb9

Makefile.in
iterator/iter_delegpt.h
iterator/iterator.c
iterator/iterator.h
services/authzone.c
services/authzone.h
testcode/replay.h
testcode/testbound.c
testcode/unitauth.c
testdata/auth_zonefile.rpl [new file with mode: 0644]

index fe587f7de5281752c2932a98942cb5c76aec4c36..f555d598107ea36a3cd3d3b08c4c938b774524be 100644 (file)
@@ -680,9 +680,9 @@ iterator.lo iterator.o: $(srcdir)/iterator/iterator.c config.h $(srcdir)/iterato
  $(srcdir)/iterator/iter_delegpt.h $(srcdir)/iterator/iter_scrub.h $(srcdir)/iterator/iter_priv.h \
  $(srcdir)/validator/val_neg.h $(srcdir)/services/cache/dns.h $(srcdir)/services/cache/infra.h \
  $(srcdir)/util/rtt.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
-  $(srcdir)/dnscrypt/cert.h $(srcdir)/util/net_help.h \
- $(srcdir)/util/regional.h $(srcdir)/util/data/dname.h $(srcdir)/util/data/msgencode.h \
- $(srcdir)/util/fptr_wlist.h $(srcdir)/util/tube.h $(srcdir)/services/mesh.h $(srcdir)/services/modstack.h \
+  $(srcdir)/dnscrypt/cert.h $(srcdir)/services/authzone.h \
+ $(srcdir)/services/mesh.h $(srcdir)/services/modstack.h $(srcdir)/util/net_help.h $(srcdir)/util/regional.h \
+ $(srcdir)/util/data/dname.h $(srcdir)/util/data/msgencode.h $(srcdir)/util/fptr_wlist.h $(srcdir)/util/tube.h \
  $(srcdir)/util/config_file.h $(srcdir)/util/random.h $(srcdir)/sldns/wire2str.h $(srcdir)/sldns/str2wire.h \
  $(srcdir)/sldns/parseutil.h $(srcdir)/sldns/sbuffer.h
 iter_delegpt.lo iter_delegpt.o: $(srcdir)/iterator/iter_delegpt.c config.h $(srcdir)/iterator/iter_delegpt.h \
index 4bd79c81af09f7fa2f38a79a963c4591764068f0..24f0574901d9ed1898453ea4e3440c7a5c85da30 100644 (file)
@@ -83,6 +83,8 @@ struct delegpt {
        uint8_t dp_type_mlc;
        /** use SSL for upstream query */
        uint8_t ssl_upstream;
+       /** delegpt from authoritative zone that is locally hosted */
+       uint8_t auth_dp;
 };
 
 /**
index ddad1da9f1201fa8dda2e6585284296b3b740d23..0caaf987547693c94ebfcbb8a882fceda94f8382 100644 (file)
@@ -53,6 +53,7 @@
 #include "validator/val_neg.h"
 #include "services/cache/dns.h"
 #include "services/cache/infra.h"
+#include "services/authzone.h"
 #include "util/module.h"
 #include "util/netevent.h"
 #include "util/net_help.h"
@@ -771,6 +772,11 @@ prime_stub(struct module_qstate* qstate, struct iter_qstate* iq, int id,
        if(!stub)
                return 0;
        stub_dp = stub->dp;
+       /* if we have an auth_zone dp, and stub is equal, don't prime stub
+        * yet, unless we want to fallback and avoid the auth_zone */
+       if(!iq->auth_zone_avoid && iq->dp && iq->dp->auth_dp && 
+               query_dname_compare(iq->dp->name, stub_dp->name) == 0)
+               return 0;
 
        /* is it a noprime stub (always use) */
        if(stub->noprime) {
@@ -831,6 +837,66 @@ prime_stub(struct module_qstate* qstate, struct iter_qstate* iq, int id,
        return 1;
 }
 
+/**
+ * Generate a delegation point for an auth zone (unless cached dp is better)
+ * false on alloc failure.
+ */
+static int
+auth_zone_delegpt(struct module_qstate* qstate, struct iter_qstate* iq,
+       uint8_t* delname, size_t delnamelen)
+{
+       struct auth_zone* z;
+       if(iq->auth_zone_avoid)
+               return 1;
+       if(!delname) {
+               delname = iq->qchase.qname;
+               delnamelen = iq->qchase.qname_len;
+       }
+       lock_rw_rdlock(&qstate->env->auth_zones->lock);
+       z = auth_zones_find_zone(qstate->env->auth_zones, delname, delnamelen,
+               qstate->qinfo.qclass);
+       if(!z) {
+               lock_rw_unlock(&qstate->env->auth_zones->lock);
+               return 1;
+       }
+       lock_rw_rdlock(&z->lock);
+       lock_rw_unlock(&qstate->env->auth_zones->lock);
+       if(z->for_upstream) {
+               if(iq->dp==NULL || dname_subdomain_c(z->name, iq->dp->name)) {
+                       struct delegpt* dp;
+                       dp = (struct delegpt*)regional_alloc_zero(
+                               qstate->region, sizeof(*dp));
+                       if(!dp) {
+                               log_err("alloc failure");
+                               if(z->fallback_enabled) {
+                                       lock_rw_unlock(&z->lock);
+                                       return 1; /* just fallback */
+                               }
+                               lock_rw_unlock(&z->lock);
+                               return 0;
+                       }
+                       dp->name = regional_alloc_init(qstate->region,
+                               z->name, z->namelen);
+                       if(!dp->name) {
+                               log_err("alloc failure");
+                               if(z->fallback_enabled) {
+                                       lock_rw_unlock(&z->lock);
+                                       return 1; /* just fallback */
+                               }
+                               lock_rw_unlock(&z->lock);
+                               return 0;
+                       }
+                       dp->namelen = z->namelen;
+                       dp->namelabs = z->namelabs;
+                       dp->auth_dp = 1;
+                       iq->dp = dp;
+               }
+       }
+
+       lock_rw_unlock(&z->lock);
+       return 1;
+}
+
 /**
  * Generate A and AAAA checks for glue that is in-zone for the referral
  * we just got to obtain authoritative information on the addresses.
@@ -1167,7 +1233,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
                iq->response = msg;
                return final_state(iq);
        }
-       
+
        /* attempt to forward the request */
        if(forward_request(qstate, iq))
        {
@@ -1228,8 +1294,15 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
                /* If the cache has returned nothing, then we have a 
                 * root priming situation. */
                if(iq->dp == NULL) {
+                       int r;
+                       /* if under auth zone, no prime needed */
+                       if(!auth_zone_delegpt(qstate, iq, delname, delnamelen))
+                               return error_response(qstate, id, 
+                                       LDNS_RCODE_SERVFAIL);
+                       if(iq->dp) /* use auth zone dp */
+                               return next_state(iq, INIT_REQUEST_2_STATE);
                        /* if there is a stub, then no root prime needed */
-                       int r = prime_stub(qstate, iq, id, delname,
+                       r = prime_stub(qstate, iq, id, delname,
                                iq->qchase.qclass);
                        if(r == 2)
                                break; /* got noprime-stub-zone, continue */
@@ -1398,6 +1471,12 @@ processInitRequest2(struct module_qstate* qstate, struct iter_qstate* iq,
                        dname_remove_label(&delname, &delnamelen);
                iq->refetch_glue = 0; /* if CNAME causes restart, no refetch */
        }
+
+       /* see if we have an auth zone to answer from, improves dp from cache
+        * (if any dp from cache) with auth zone dp, if that is lower */
+       if(!auth_zone_delegpt(qstate, iq, delname, delnamelen))
+               return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+
        /* Check to see if we need to prime a stub zone. */
        if(prime_stub(qstate, iq, id, delname, iq->qchase.qclass)) {
                /* A priming sub request was made */
@@ -1882,6 +1961,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
        int tf_policy;
        struct delegpt_addr* target;
        struct outbound_entry* outq;
+       int auth_fallback = 0;
 
        /* NOTE: a request will encounter this state for each target it 
         * needs to send a query to. That is, at least one per referral, 
@@ -1926,6 +2006,152 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
                return 0;
        }
 
+       if(iq->minimisation_state == INIT_MINIMISE_STATE) {
+               /* (Re)set qinfo_out to (new) delegation point, except when
+                * qinfo_out is already a subdomain of dp. This happens when
+                * increasing by more than one label at once (QNAMEs with more
+                * than MAX_MINIMISE_COUNT labels). */
+               if(!(iq->qinfo_out.qname_len 
+                       && dname_subdomain_c(iq->qchase.qname, 
+                               iq->qinfo_out.qname)
+                       && dname_subdomain_c(iq->qinfo_out.qname, 
+                               iq->dp->name))) {
+                       iq->qinfo_out.qname = iq->dp->name;
+                       iq->qinfo_out.qname_len = iq->dp->namelen;
+                       iq->qinfo_out.qtype = LDNS_RR_TYPE_A;
+                       iq->qinfo_out.qclass = iq->qchase.qclass;
+                       iq->qinfo_out.local_alias = NULL;
+                       iq->minimise_count = 0;
+               }
+
+               iq->minimisation_state = MINIMISE_STATE;
+       }
+       if(iq->minimisation_state == MINIMISE_STATE) {
+               int qchaselabs = dname_count_labels(iq->qchase.qname);
+               int labdiff = qchaselabs -
+                       dname_count_labels(iq->qinfo_out.qname);
+
+               iq->qinfo_out.qname = iq->qchase.qname;
+               iq->qinfo_out.qname_len = iq->qchase.qname_len;
+               iq->minimise_count++;
+               iq->minimise_timeout_count = 0;
+
+               iter_dec_attempts(iq->dp, 1);
+
+               /* Limit number of iterations for QNAMEs with more
+                * than MAX_MINIMISE_COUNT labels. Send first MINIMISE_ONE_LAB
+                * labels of QNAME always individually.
+                */
+               if(qchaselabs > MAX_MINIMISE_COUNT && labdiff > 1 && 
+                       iq->minimise_count > MINIMISE_ONE_LAB) {
+                       if(iq->minimise_count < MAX_MINIMISE_COUNT) {
+                               int multilabs = qchaselabs - 1 - 
+                                       MINIMISE_ONE_LAB;
+                               int extralabs = multilabs / 
+                                       MINIMISE_MULTIPLE_LABS;
+
+                               if (MAX_MINIMISE_COUNT - iq->minimise_count >= 
+                                       multilabs % MINIMISE_MULTIPLE_LABS)
+                                       /* Default behaviour is to add 1 label
+                                        * every iteration. Therefore, decrement
+                                        * the extralabs by 1 */
+                                       extralabs--;
+                               if (extralabs < labdiff)
+                                       labdiff -= extralabs;
+                               else
+                                       labdiff = 1;
+                       }
+                       /* Last minimised iteration, send all labels with
+                        * QTYPE=NS */
+                       else
+                               labdiff = 1;
+               }
+
+               if(labdiff > 1) {
+                       verbose(VERB_QUERY, "removing %d labels", labdiff-1);
+                       dname_remove_labels(&iq->qinfo_out.qname, 
+                               &iq->qinfo_out.qname_len, 
+                               labdiff-1);
+               }
+               if(labdiff < 1 || (labdiff < 2 
+                       && (iq->qchase.qtype == LDNS_RR_TYPE_DS
+                       || iq->qchase.qtype == LDNS_RR_TYPE_A)))
+                       /* Stop minimising this query, resolve "as usual" */
+                       iq->minimisation_state = DONOT_MINIMISE_STATE;
+               else if(!qstate->no_cache_lookup) {
+                       struct dns_msg* msg = dns_cache_lookup(qstate->env, 
+                               iq->qinfo_out.qname, iq->qinfo_out.qname_len, 
+                               iq->qinfo_out.qtype, iq->qinfo_out.qclass, 
+                               qstate->query_flags, qstate->region, 
+                               qstate->env->scratch, 0);
+                       if(msg && msg->rep->an_numrrsets == 0
+                               && FLAGS_GET_RCODE(msg->rep->flags) == 
+                               LDNS_RCODE_NOERROR)
+                               /* no need to send query if it is already 
+                                * cached as NOERROR/NODATA */
+                               return 1;
+               }
+       }
+       if(iq->minimisation_state == SKIP_MINIMISE_STATE) {
+               if(iq->minimise_timeout_count < MAX_MINIMISE_TIMEOUT_COUNT)
+                       /* Do not increment qname, continue incrementing next 
+                        * iteration */
+                       iq->minimisation_state = MINIMISE_STATE;
+               else if(!qstate->env->cfg->qname_minimisation_strict)
+                       /* Too many time-outs detected for this QNAME and QTYPE.
+                        * We give up, disable QNAME minimisation. */
+                       iq->minimisation_state = DONOT_MINIMISE_STATE;
+       }
+       if(iq->minimisation_state == DONOT_MINIMISE_STATE)
+               iq->qinfo_out = iq->qchase;
+
+       /* now find an answer to this query */
+       /* see if authority zones have an answer */
+       /* now we know the dp, we can check the auth zone for locally hosted
+        * contents */
+       if(!iq->auth_zone_avoid && qstate->blacklist) {
+               if(auth_zones_can_fallback(qstate->env->auth_zones,
+                       iq->dp->name, iq->dp->namelen, iq->qinfo_out.qclass)) {
+                       /* if cache is blacklisted and this zone allows us
+                        * to fallback to the internet, then do so, and
+                        * fetch results from the internet servers */
+                       iq->auth_zone_avoid = 1;
+               }
+       }
+       if(iq->auth_zone_avoid) {
+               iq->auth_zone_avoid = 0;
+               auth_fallback = 1;
+       } else if(auth_zones_lookup(qstate->env->auth_zones, &iq->qinfo_out,
+               qstate->region, &iq->response, &auth_fallback, iq->dp->name,
+               iq->dp->namelen)) {
+               /* use this as a response to be processed by the iterator */
+               if(verbosity >= VERB_ALGO) {
+                       log_dns_msg("msg from auth zone",
+                               &iq->response->qinfo, iq->response->rep);
+               }
+               iq->num_current_queries++;
+               iq->chase_to_rd = 0;
+               iq->dnssec_lame_query = 0;
+               iq->auth_zone_response = 1;
+               return next_state(iq, QUERY_RESP_STATE);
+       }
+       iq->auth_zone_response = 0;
+       if(auth_fallback == 0) {
+               /* like we got servfail from the auth zone lookup, and
+                * no internet fallback */
+               verbose(VERB_ALGO, "auth zone lookup failed, no fallback,"
+                       " servfail");
+               return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+       }
+       if(iq->dp && iq->dp->auth_dp) {
+               /* we wanted to fallback, but had no delegpt, only the
+                * auth zone generated delegpt, create an actual one */
+               iq->auth_zone_avoid = 1;
+               return next_state(iq, INIT_REQUEST_STATE);
+       }
+       /* but mostly, fallback==1 (like, when no such auth zone exists)
+        * and we continue with lookups */
+
        tf_policy = 0;
        /* < not <=, because although the array is large enough for <=, the
         * generated query will immediately be discarded due to depth and
@@ -2093,105 +2319,6 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
                }
        }
 
-       if(iq->minimisation_state == INIT_MINIMISE_STATE) {
-               /* (Re)set qinfo_out to (new) delegation point, except when
-                * qinfo_out is already a subdomain of dp. This happens when
-                * increasing by more than one label at once (QNAMEs with more
-                * than MAX_MINIMISE_COUNT labels). */
-               if(!(iq->qinfo_out.qname_len 
-                       && dname_subdomain_c(iq->qchase.qname, 
-                               iq->qinfo_out.qname)
-                       && dname_subdomain_c(iq->qinfo_out.qname, 
-                               iq->dp->name))) {
-                       iq->qinfo_out.qname = iq->dp->name;
-                       iq->qinfo_out.qname_len = iq->dp->namelen;
-                       iq->qinfo_out.qtype = LDNS_RR_TYPE_A;
-                       iq->qinfo_out.qclass = iq->qchase.qclass;
-                       iq->qinfo_out.local_alias = NULL;
-                       iq->minimise_count = 0;
-               }
-
-               iq->minimisation_state = MINIMISE_STATE;
-       }
-       if(iq->minimisation_state == MINIMISE_STATE) {
-               int qchaselabs = dname_count_labels(iq->qchase.qname);
-               int labdiff = qchaselabs -
-                       dname_count_labels(iq->qinfo_out.qname);
-
-               iq->qinfo_out.qname = iq->qchase.qname;
-               iq->qinfo_out.qname_len = iq->qchase.qname_len;
-               iq->minimise_count++;
-               iq->minimise_timeout_count = 0;
-
-               iter_dec_attempts(iq->dp, 1);
-
-               /* Limit number of iterations for QNAMEs with more
-                * than MAX_MINIMISE_COUNT labels. Send first MINIMISE_ONE_LAB
-                * labels of QNAME always individually.
-                */
-               if(qchaselabs > MAX_MINIMISE_COUNT && labdiff > 1 && 
-                       iq->minimise_count > MINIMISE_ONE_LAB) {
-                       if(iq->minimise_count < MAX_MINIMISE_COUNT) {
-                               int multilabs = qchaselabs - 1 - 
-                                       MINIMISE_ONE_LAB;
-                               int extralabs = multilabs / 
-                                       MINIMISE_MULTIPLE_LABS;
-
-                               if (MAX_MINIMISE_COUNT - iq->minimise_count >= 
-                                       multilabs % MINIMISE_MULTIPLE_LABS)
-                                       /* Default behaviour is to add 1 label
-                                        * every iteration. Therefore, decrement
-                                        * the extralabs by 1 */
-                                       extralabs--;
-                               if (extralabs < labdiff)
-                                       labdiff -= extralabs;
-                               else
-                                       labdiff = 1;
-                       }
-                       /* Last minimised iteration, send all labels with
-                        * QTYPE=NS */
-                       else
-                               labdiff = 1;
-               }
-
-               if(labdiff > 1) {
-                       verbose(VERB_QUERY, "removing %d labels", labdiff-1);
-                       dname_remove_labels(&iq->qinfo_out.qname, 
-                               &iq->qinfo_out.qname_len, 
-                               labdiff-1);
-               }
-               if(labdiff < 1 || (labdiff < 2 
-                       && (iq->qchase.qtype == LDNS_RR_TYPE_DS
-                       || iq->qchase.qtype == LDNS_RR_TYPE_A)))
-                       /* Stop minimising this query, resolve "as usual" */
-                       iq->minimisation_state = DONOT_MINIMISE_STATE;
-               else if(!qstate->no_cache_lookup) {
-                       struct dns_msg* msg = dns_cache_lookup(qstate->env, 
-                               iq->qinfo_out.qname, iq->qinfo_out.qname_len, 
-                               iq->qinfo_out.qtype, iq->qinfo_out.qclass, 
-                               qstate->query_flags, qstate->region, 
-                               qstate->env->scratch, 0);
-                       if(msg && msg->rep->an_numrrsets == 0
-                               && FLAGS_GET_RCODE(msg->rep->flags) == 
-                               LDNS_RCODE_NOERROR)
-                               /* no need to send query if it is already 
-                                * cached as NOERROR/NODATA */
-                               return 1;
-               }
-       }
-       if(iq->minimisation_state == SKIP_MINIMISE_STATE) {
-               if(iq->minimise_timeout_count < MAX_MINIMISE_TIMEOUT_COUNT)
-                       /* Do not increment qname, continue incrementing next 
-                        * iteration */
-                       iq->minimisation_state = MINIMISE_STATE;
-               else if(!qstate->env->cfg->qname_minimisation_strict)
-                       /* Too many time-outs detected for this QNAME and QTYPE.
-                        * We give up, disable QNAME minimisation. */
-                       iq->minimisation_state = DONOT_MINIMISE_STATE;
-       }
-       if(iq->minimisation_state == DONOT_MINIMISE_STATE)
-               iq->qinfo_out = iq->qchase;
-
        /* We have a valid target. */
        if(verbosity >= VERB_QUERY) {
                log_query_info(VERB_QUERY, "sending query:", &iq->qinfo_out);
@@ -2584,6 +2711,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
                iq->deleg_msg = NULL;
                iq->dp = NULL;
                iq->dsns_point = NULL;
+               iq->auth_zone_response = 0;
                /* Note the query restart. */
                iq->query_restart_count++;
                iq->sent_count = 0;
@@ -2656,6 +2784,25 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
        if (qstate->env->cfg->qname_minimisation &&
                !qstate->env->cfg->qname_minimisation_strict)
                iq->minimisation_state = DONOT_MINIMISE_STATE;
+       if(iq->auth_zone_response) {
+               /* can we fallback? */
+               iq->auth_zone_response = 0;
+               if(!auth_zones_can_fallback(qstate->env->auth_zones,
+                       iq->dp->name, iq->dp->namelen, qstate->qinfo.qclass)) {
+                       verbose(VERB_ALGO, "auth zone response bad, and no"
+                               " fallback possible, servfail");
+                       return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+               }
+               verbose(VERB_ALGO, "auth zone response was bad, "
+                       "fallback enabled");
+               iq->auth_zone_avoid = 1;
+               if(iq->dp->auth_dp) {
+                       /* we are using a dp for the auth zone, with no
+                        * nameservers, get one first */
+                       iq->dp = NULL;
+                       return next_state(iq, INIT_REQUEST_STATE);
+               }
+       }
        return next_state(iq, QUERYTARGETS_STATE);
 }
 
index 841a3643669ca7770e0bb4c1e7c2ad7e1c00fedc..67ffeb14763180519bdd212e344176a156d7cf9e 100644 (file)
@@ -387,6 +387,11 @@ struct iter_qstate {
         * Count number of time-outs. Used to prevent resolving failures when
         * the QNAME minimisation QTYPE is blocked. */
        int minimise_timeout_count;
+
+       /** True if the current response is from auth_zone */
+       int auth_zone_response;
+       /** True if the auth_zones should not be consulted for the query */
+       int auth_zone_avoid;
 };
 
 /**
index c9fc2eb0bf0ce3478738ea13a7b070cd47ff07ac..ded654d54c91920e78c5dd85436be99652cde44b 100644 (file)
@@ -441,14 +441,16 @@ auth_zone_find_less_equal(struct auth_zones* az, uint8_t* nm, size_t nmlen,
        return rbtree_find_less_equal(&az->ztree, &key, (rbnode_type**)z);
 }
 
+
 /** find the auth zone that is above the given qname */
 struct auth_zone*
-auth_zones_find_zone(struct auth_zones* az, struct query_info* qinfo)
+auth_zones_find_zone(struct auth_zones* az, uint8_t* qname, size_t qname_len,
+       uint16_t qclass)
 {
-       uint8_t* nm = qinfo->qname;
-       size_t nmlen = qinfo->qname_len;
+       uint8_t* nm = qname;
+       size_t nmlen = qname_len;
        struct auth_zone* z;
-       if(auth_zone_find_less_equal(az, nm, nmlen, qinfo->qclass, &z)) {
+       if(auth_zone_find_less_equal(az, nm, nmlen, qclass, &z)) {
                /* exact match */
                return z;
        } else {
@@ -456,13 +458,13 @@ auth_zones_find_zone(struct auth_zones* az, struct query_info* qinfo)
                if(!z) return NULL; /* nothing smaller, nothing above it */
                /* we found smaller name; smaller may be above the qname,
                 * but not below it. */
-               nm = dname_get_shared_topdomain(z->name, qinfo->qname);
+               nm = dname_get_shared_topdomain(z->name, qname);
                dname_count_size_labels(nm, &nmlen);
        }
        /* search up */
        while(!z && !dname_is_root(nm)) {
                dname_remove_label(&nm, &nmlen);
-               z = auth_zone_find(az, nm, nmlen, qinfo->qclass);
+               z = auth_zone_find(az, nm, nmlen, qclass);
        }
        return z;
 }
@@ -3010,9 +3012,6 @@ int auth_zones_lookup(struct auth_zones* az, struct query_info* qinfo,
 {
        int r;
        struct auth_zone* z;
-       /* TODO: in iterator, after cache lookup, before network lookup,
-        * call this to get answer */
-
        /* find the zone that should contain the answer. */
        lock_rw_rdlock(&az->lock);
        z = auth_zone_find(az, dp_nm, dp_nmlen, qinfo->qclass);
@@ -3026,6 +3025,12 @@ int auth_zones_lookup(struct auth_zones* az, struct query_info* qinfo,
        lock_rw_rdlock(&z->lock);
        lock_rw_unlock(&az->lock);
 
+       /* if not for upstream queries, fallback */
+       if(!z->for_upstream) {
+               lock_rw_unlock(&z->lock);
+               *fallback = 1;
+               return 0;
+       }
        /* see what answer that zone would generate */
        r = auth_zone_generate_answer(z, qinfo, region, msg, fallback);
        lock_rw_unlock(&z->lock);
@@ -3094,7 +3099,16 @@ int auth_zones_answer(struct auth_zones* az, struct module_env* env,
                lock_rw_unlock(&az->lock);
                return 0;
        }
-       z = auth_zones_find_zone(az, qinfo);
+       if(qinfo->qtype == LDNS_RR_TYPE_DS) {
+               uint8_t* delname = qinfo->qname;
+               size_t delnamelen = qinfo->qname_len;
+               dname_remove_label(&delname, &delnamelen);
+               z = auth_zones_find_zone(az, delname, delnamelen,
+                       qinfo->qclass);
+       } else {
+               z = auth_zones_find_zone(az, qinfo->qname, qinfo->qname_len,
+                       qinfo->qclass);
+       }
        if(!z) {
                /* no zone above it */
                lock_rw_unlock(&az->lock);
@@ -3123,6 +3137,25 @@ int auth_zones_answer(struct auth_zones* az, struct module_env* env,
        return 1;
 }
 
+int auth_zones_can_fallback(struct auth_zones* az, uint8_t* nm, size_t nmlen,
+       uint16_t dclass)
+{
+       int r;
+       struct auth_zone* z;
+       lock_rw_rdlock(&az->lock);
+       z = auth_zone_find(az, nm, nmlen, dclass);
+       if(!z) {
+               lock_rw_unlock(&az->lock);
+               /* no such auth zone, fallback */
+               return 1;
+       }
+       lock_rw_rdlock(&z->lock);
+       lock_rw_unlock(&az->lock);
+       r = z->fallback_enabled || (!z->for_upstream);
+       lock_rw_unlock(&z->lock);
+       return r;
+}
+
 /** set a zone expired */
 static void
 auth_xfer_set_expired(struct auth_xfer* xfr, struct module_env* env,
index 0f2fa39fbc0fc85b04a7577f429f48a965bedf21..dc2db6c6bd15ac46e8232d11f531e7d0558c10fc 100644 (file)
@@ -483,11 +483,14 @@ int auth_zones_answer(struct auth_zones* az, struct module_env* env,
  * Return NULL when there is no auth_zone above the give name, otherwise
  * returns the closest auth_zone above the qname that pertains to it.
  * @param az: auth zones structure.
+ * @param name: query to look up for.
+ * @param namelen: length of name.
+ * @param dclass: class of zone to find.
  * @param qinfo: query info to lookup.
  * @return NULL or auth_zone that pertains to the query.
  */
 struct auth_zone* auth_zones_find_zone(struct auth_zones* az,
-       struct query_info* qinfo);
+       uint8_t* name, size_t namelen, uint16_t dclass);
 
 /** find an auth zone by name (exact match by name or NULL returned) */
 struct auth_zone* auth_zone_find(struct auth_zones* az, uint8_t* nm,
@@ -497,7 +500,6 @@ struct auth_zone* auth_zone_find(struct auth_zones* az, uint8_t* nm,
 struct auth_xfer* auth_xfer_find(struct auth_zones* az, uint8_t* nm,
        size_t nmlen, uint16_t dclass);
 
-
 /** create an auth zone. returns wrlocked zone. caller must have wrlock
  * on az. returns NULL on malloc failure */
 struct auth_zone* auth_zone_create(struct auth_zones* az, uint8_t* nm,
@@ -510,6 +512,18 @@ int auth_zone_set_zonefile(struct auth_zone* z, char* zonefile);
  * fallbackstr is "yes" or "no". false on parse failure. */
 int auth_zone_set_fallback(struct auth_zone* z, char* fallbackstr);
 
+/** see if the auth zone for the name can fallback
+ * @param az: auth zones
+ * @param nm: name of delegation point.
+ * @param nmlen: length of nm.
+ * @param dclass: class of zone to look for.
+ * @return true if fallback_enabled is true. false if not.
+ * if the zone does not exist, fallback is true (more lenient)
+ * also true if zone does not do upstream requests.
+ */
+int auth_zones_can_fallback(struct auth_zones* az, uint8_t* nm, size_t nmlen,
+       uint16_t dclass);
+
 /** read auth zone from zonefile. caller must lock zone. false on failure */
 int auth_zone_read_zonefile(struct auth_zone* z);
 
index b33950304d12ba52b1452643425278c3d6de2fe7..acf4ca97ea65ad83356f47d8452043c04f368e89 100644 (file)
  * AUTOTRUST_FILE id
  * ; contents of that file
  * AUTOTRUST_END
+ * ; temp file names are echoed as "tmp/xxx.fname"
+ * TEMPFILE_NAME fname
+ * ; temp file contents, inline, deleted at end of run
+ * TEMPFILE_CONTENTS fname
+ * ; contents of that file
+ * ; this creates $INCLUDE /tmp/xxx.fname
+ * $INCLUDE_TEMPFILE fname
+ * TEMPFILE_END
  * CONFIG_END
  * ; comment line.
  * SCENARIO_BEGIN name_of_scenario
index 20c99608fdd7f2550f4c9d3b62f2ef1d8bf449fa..5ec51ab3df52e298f119eae4f47ceded18922bc7 100644 (file)
@@ -135,6 +135,65 @@ echo_cmdline(int argc, char* argv[])
        fprintf(stderr, "\n");
 }
 
+/** spool temp file name */
+static void
+spool_temp_file_name(int* lineno, FILE* cfg, char* id)
+{
+       char line[MAX_LINE_LEN];
+       /* find filename for new file */
+       while(isspace((unsigned char)*id))
+               id++;
+       if(*id == '\0') 
+               fatal_exit("TEMPFILE_NAME must have id, line %d", *lineno);
+       id[strlen(id)-1]=0; /* remove newline */
+       fake_temp_file("_temp_", id, line, sizeof(line));
+       fprintf(cfg, "\"%s\"\n", line);
+}
+
+/** spool temp file */
+static void
+spool_temp_file(FILE* in, int* lineno, char* id)
+{
+       char line[MAX_LINE_LEN];
+       char* parse;
+       FILE* spool;
+       /* find filename for new file */
+       while(isspace((unsigned char)*id))
+               id++;
+       if(*id == '\0') 
+               fatal_exit("TEMPFILE_CONTENTS must have id, line %d", *lineno);
+       id[strlen(id)-1]=0; /* remove newline */
+       fake_temp_file("_temp_", id, line, sizeof(line));
+       /* open file and spool to it */
+       spool = fopen(line, "w");
+       if(!spool) fatal_exit("could not open %s: %s", line, strerror(errno));
+       fprintf(stderr, "testbound is spooling temp file: %s\n", line);
+       if(!cfg_strlist_insert(&cfgfiles, strdup(line))) 
+               fatal_exit("out of memory");
+       line[sizeof(line)-1] = 0;
+       while(fgets(line, MAX_LINE_LEN-1, in)) {
+               parse = line;
+               (*lineno)++;
+               while(isspace((unsigned char)*parse))
+                       parse++;
+               if(strncmp(parse, "$INCLUDE_TEMPFILE", 17) == 0) {
+                       char l2[MAX_LINE_LEN];
+                       char* tid = parse+17;
+                       while(isspace((unsigned char)*tid))
+                               tid++;
+                       tid[strlen(tid)-1]=0; /* remove newline */
+                       fake_temp_file("_temp_", tid, l2, sizeof(l2));
+                       snprintf(line, sizeof(line), "$INCLUDE %s\n", l2);
+               }
+               if(strncmp(parse, "TEMPFILE_END", 12) == 0) {
+                       fclose(spool);
+                       return;
+               }
+               fputs(line, spool);
+       }
+       fatal_exit("no TEMPFILE_END in input file");
+}
+
 /** spool autotrust file */
 static void
 spool_auto_file(FILE* in, int* lineno, FILE* cfg, char* id)
@@ -213,6 +272,14 @@ setup_config(FILE* in, int* lineno, int* pass_argc, char* pass_argv[])
                        spool_auto_file(in, lineno, cfg, parse+14);
                        continue;
                }
+               if(strncmp(parse, "TEMPFILE_NAME", 13) == 0) {
+                       spool_temp_file_name(lineno, cfg, parse+13);
+                       continue;
+               }
+               if(strncmp(parse, "TEMPFILE_CONTENTS", 17) == 0) {
+                       spool_temp_file(in, lineno, parse+17);
+                       continue;
+               }
                if(strncmp(parse, "CONFIG_END", 10) == 0) {
                        fclose(cfg);
                        return;
index 6ca0be9d8d25788a86ba8152ba2563104248d19a..7aa42acfeddaafbaccff0322786b0547162387fb 100644 (file)
@@ -521,6 +521,7 @@ addzone(struct auth_zones* az, const char* name, char* fname)
        lock_rw_unlock(&az->lock);
        if(!z) fatal_exit("cannot find zone");
        auth_zone_set_zonefile(z, fname);
+       z->for_upstream = 1;
 
        if(!auth_zone_read_zonefile(z)) {
                fatal_exit("parse failure for auth zone %s", name);
@@ -685,8 +686,12 @@ msgtostr(struct dns_msg* msg)
        char* str;
        sldns_buffer* buf = sldns_buffer_new(65535);
        if(!buf) fatal_exit("out of memory");
-       pr_flags(buf, msg->rep->flags);
-       pr_rrs(buf, msg->rep);
+       if(!msg) {
+               sldns_buffer_printf(buf, "null packet\n");
+       } else {
+               pr_flags(buf, msg->rep->flags);
+               pr_rrs(buf, msg->rep);
+       }
 
        str = strdup((char*)sldns_buffer_begin(buf));
        if(!str) fatal_exit("out of memory");
diff --git a/testdata/auth_zonefile.rpl b/testdata/auth_zonefile.rpl
new file mode 100644 (file)
index 0000000..5ca6a5c
--- /dev/null
@@ -0,0 +1,187 @@
+; config options
+server:
+       target-fetch-policy: "0 0 0 0 0"
+
+auth-zone:
+       name: "example.com."
+       ## zonefile (or none).
+       ## zonefile: "example.com.zone"
+       ## master by IP address or hostname
+       ## can list multiple masters, each on one line.
+       ## master:
+       ## url for http fetch
+       ## url:
+       ## queries from downstream clients get authoritative answers.
+       ## for-downstream: yes
+       ## queries are used to fetch authoritative answers from this zone,
+       ## instead of unbound itself sending queries there.
+       ## for-upstream: yes
+       ## on failures with for-upstream, fallback to sending queries to
+       ## the authority servers
+       ## fallback-enabled: no
+
+       ## this line generates zonefile: \n"/tmp/xxx.example.com"\n
+       zonefile:
+TEMPFILE_NAME example.com
+       ## this is the inline file /tmp/xxx.example.com
+       ## the tempfiles are deleted when the testrun is over.
+TEMPFILE_CONTENTS example.com
+$ORIGIN com.
+example        3600    IN      SOA     dns.example.de. hostmaster.dns.example.de. (
+               1379078166 28800 7200 604800 7200 )
+       3600    IN      NS      ns1.example.com.
+       3600    IN      NS      ns2.example.com.
+$ORIGIN example.com.
+www    3600    IN      A       1.2.3.4
+mail   3600    IN      A       1.2.3.5
+       3600    IN      AAAA    ::5
+ns1    3600    IN      A       1.2.3.4
+ns2    3600    IN      AAAA    ::2
+$INCLUDE_TEMPFILE example.inc
+TEMPFILE_END
+TEMPFILE_CONTENTS example.inc
+other  7200    IN      A       1.2.3.6
+TEMPFILE_END
+
+stub-zone:
+       name: "."
+       stub-addr: 193.0.14.129         # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test authority zone with zonefile
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+       ADDRESS 193.0.14.129 
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS        K.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+K.ROOT-SERVERS.NET.    IN      A       193.0.14.129
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+SECTION AUTHORITY
+com.   IN NS   a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net.    IN      A       192.5.6.30
+ENTRY_END
+RANGE_END
+
+; a.gtld-servers.net.
+RANGE_BEGIN 0 100
+       ADDRESS 192.5.6.30
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+SECTION ANSWER
+com.   IN NS   a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net.    IN      A       192.5.6.30
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION AUTHORITY
+example.com.   IN NS   ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.44
+ENTRY_END
+RANGE_END
+
+; ns.example.net.
+RANGE_BEGIN 0 100
+       ADDRESS 1.2.3.44
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.net. IN NS
+SECTION ANSWER
+example.net.   IN NS   ns.example.net.
+SECTION ADDITIONAL
+ns.example.net.                IN      A       1.2.3.44
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+ns.example.net. IN A
+SECTION ANSWER
+ns.example.net. IN A   1.2.3.44
+SECTION AUTHORITY
+example.net.   IN NS   ns.example.net.
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+ns.example.net. IN AAAA
+SECTION AUTHORITY
+example.net.   IN NS   ns.example.net.
+SECTION ADDITIONAL
+www.example.net. IN A  1.2.3.44
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION ANSWER
+example.com.   IN NS   ns.example.net.
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A  10.20.30.40
+ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; recursion happens here.
+STEP 20 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A  1.2.3.4
+ENTRY_END
+
+SCENARIO_END