mirror of
https://github.com/NLnetLabs/unbound.git
synced 2026-01-05 22:39:35 -05:00
unit test for auth zone lookup
git-svn-id: file:///svn/unbound/trunk@4469 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
7d5dc75dc5
commit
bea3b6b72d
10 changed files with 586 additions and 118 deletions
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,14 @@
|
|||
* 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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
187
testdata/auth_zonefile.rpl
vendored
Normal file
187
testdata/auth_zonefile.rpl
vendored
Normal file
|
|
@ -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
|
||||
Loading…
Reference in a new issue