summaryrefslogtreecommitdiffstats
path: root/contrib/unbound/iterator/iterator.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/unbound/iterator/iterator.c')
-rw-r--r--contrib/unbound/iterator/iterator.c146
1 files changed, 138 insertions, 8 deletions
diff --git a/contrib/unbound/iterator/iterator.c b/contrib/unbound/iterator/iterator.c
index 2037cc8..96918fa 100644
--- a/contrib/unbound/iterator/iterator.c
+++ b/contrib/unbound/iterator/iterator.c
@@ -61,10 +61,11 @@
#include "util/data/msgencode.h"
#include "util/fptr_wlist.h"
#include "util/config_file.h"
-#include "ldns/rrdef.h"
-#include "ldns/wire2str.h"
-#include "ldns/parseutil.h"
-#include "ldns/sbuffer.h"
+#include "util/random.h"
+#include "sldns/rrdef.h"
+#include "sldns/wire2str.h"
+#include "sldns/parseutil.h"
+#include "sldns/sbuffer.h"
int
iter_init(struct module_env* env, int id)
@@ -83,6 +84,16 @@ iter_init(struct module_env* env, int id)
return 1;
}
+/** delete caps_whitelist element */
+static void
+caps_free(struct rbnode_t* n, void* ATTR_UNUSED(d))
+{
+ if(n) {
+ free(((struct name_tree_node*)n)->name);
+ free(n);
+ }
+}
+
void
iter_deinit(struct module_env* env, int id)
{
@@ -93,6 +104,10 @@ iter_deinit(struct module_env* env, int id)
free(iter_env->target_fetch_policy);
priv_delete(iter_env->priv);
donotq_delete(iter_env->donotq);
+ if(iter_env->caps_white) {
+ traverse_postorder(iter_env->caps_white, caps_free, NULL);
+ free(iter_env->caps_white);
+ }
free(iter_env);
env->modinfo[id] = NULL;
}
@@ -120,6 +135,7 @@ iter_new(struct module_qstate* qstate, int id)
iq->query_restart_count = 0;
iq->referral_count = 0;
iq->sent_count = 0;
+ iq->ratelimit_ok = 0;
iq->target_count = NULL;
iq->wait_priming_stub = 0;
iq->refetch_glue = 0;
@@ -308,6 +324,8 @@ iter_prepend(struct iter_qstate* iq, struct dns_msg* msg,
if(num_an + num_ns == 0)
return 1;
verbose(VERB_ALGO, "prepending %d rrsets", (int)num_an + (int)num_ns);
+ if(num_an > RR_COUNT_MAX || num_ns > RR_COUNT_MAX ||
+ msg->rep->rrset_count > RR_COUNT_MAX) return 0; /* overflow */
sets = regional_alloc(region, (num_an+num_ns+msg->rep->rrset_count) *
sizeof(struct ub_packed_rrset_key*));
if(!sets)
@@ -455,6 +473,16 @@ handle_cname_response(struct module_qstate* qstate, struct iter_qstate* iq,
return 1;
}
+/** see if target name is caps-for-id whitelisted */
+static int
+is_caps_whitelisted(struct iter_env* ie, struct iter_qstate* iq)
+{
+ if(!ie->caps_white) return 0; /* no whitelist, or no capsforid */
+ return name_tree_lookup(ie->caps_white, iq->qchase.qname,
+ iq->qchase.qname_len, dname_count_labels(iq->qchase.qname),
+ iq->qchase.qclass) != NULL;
+}
+
/** create target count structure for this query */
static void
target_count_create(struct iter_qstate* iq)
@@ -1123,6 +1151,32 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
* results of priming. */
return 0;
}
+ if(!iq->ratelimit_ok && qstate->prefetch_leeway)
+ iq->ratelimit_ok = 1; /* allow prefetches, this keeps
+ otherwise valid data in the cache */
+ if(!iq->ratelimit_ok && infra_ratelimit_exceeded(
+ qstate->env->infra_cache, iq->dp->name,
+ iq->dp->namelen, *qstate->env->now)) {
+ /* and increment the rate, so that the rate for time
+ * now will also exceed the rate, keeping cache fresh */
+ (void)infra_ratelimit_inc(qstate->env->infra_cache,
+ iq->dp->name, iq->dp->namelen,
+ *qstate->env->now);
+ /* see if we are passed through with slip factor */
+ if(qstate->env->cfg->ratelimit_factor != 0 &&
+ ub_random_max(qstate->env->rnd,
+ qstate->env->cfg->ratelimit_factor) == 1) {
+ iq->ratelimit_ok = 1;
+ log_nametypeclass(VERB_ALGO, "ratelimit allowed through for "
+ "delegation point", iq->dp->name,
+ LDNS_RR_TYPE_NS, LDNS_RR_CLASS_IN);
+ } else {
+ log_nametypeclass(VERB_ALGO, "ratelimit exceeded with "
+ "delegation point", iq->dp->name,
+ LDNS_RR_TYPE_NS, LDNS_RR_CLASS_IN);
+ return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+ }
+ }
/* see if this dp not useless.
* It is useless if:
@@ -1787,11 +1841,13 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
* the original query is one that matched too, so we have
* caps_server+1 number of matching queries now */
if(iq->caps_server+1 >= naddr*3 ||
- iq->caps_server+1 >= MAX_SENT_COUNT) {
+ iq->caps_server*2+2 >= MAX_SENT_COUNT) {
+ /* *2 on sentcount check because ipv6 may fail */
/* we're done, process the response */
verbose(VERB_ALGO, "0x20 fallback had %d responses "
"match for %d wanted, done.",
(int)iq->caps_server+1, (int)naddr*3);
+ iq->response = iq->caps_response;
iq->caps_fallback = 0;
iter_dec_attempts(iq->dp, 3); /* space for fallback */
iq->num_current_queries++; /* RespState decrements it*/
@@ -1866,6 +1922,24 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
/* Since a target query might have been made, we
* need to check again. */
if(iq->num_target_queries == 0) {
+ /* if in capsforid fallback, instead of last
+ * resort, we agree with the current reply
+ * we have (if any) (our count of addrs bad)*/
+ if(iq->caps_fallback && iq->caps_reply) {
+ /* we're done, process the response */
+ verbose(VERB_ALGO, "0x20 fallback had %d responses, "
+ "but no more servers except "
+ "last resort, done.",
+ (int)iq->caps_server+1);
+ iq->response = iq->caps_response;
+ iq->caps_fallback = 0;
+ iter_dec_attempts(iq->dp, 3); /* space for fallback */
+ iq->num_current_queries++; /* RespState decrements it*/
+ iq->referral_count++; /* make sure we don't loop */
+ iq->sent_count = 0;
+ iq->state = QUERY_RESP_STATE;
+ return 1;
+ }
return processLastResort(qstate, iq, ie, id);
}
}
@@ -1892,6 +1966,15 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
return 0;
}
+ /* if not forwarding, check ratelimits per delegationpoint name */
+ if(!(iq->chase_flags & BIT_RD) && !iq->ratelimit_ok) {
+ if(!infra_ratelimit_inc(qstate->env->infra_cache, iq->dp->name,
+ iq->dp->namelen, *qstate->env->now)) {
+ verbose(VERB_ALGO, "query exceeded ratelimits");
+ return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+ }
+ }
+
/* We have a valid target. */
if(verbosity >= VERB_QUERY) {
log_query_info(VERB_QUERY, "sending query:", &iq->qchase);
@@ -1906,11 +1989,15 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
iq->qchase.qname, iq->qchase.qname_len,
iq->qchase.qtype, iq->qchase.qclass,
iq->chase_flags | (iq->chase_to_rd?BIT_RD:0), EDNS_DO|BIT_CD,
- iq->dnssec_expected, iq->caps_fallback, &target->addr,
- target->addrlen, iq->dp->name, iq->dp->namelen, qstate);
+ iq->dnssec_expected, iq->caps_fallback || is_caps_whitelisted(
+ ie, iq), &target->addr, target->addrlen, iq->dp->name,
+ iq->dp->namelen, qstate);
if(!outq) {
log_addr(VERB_DETAIL, "error sending query to auth server",
&target->addr, target->addrlen);
+ if(!(iq->chase_flags & BIT_RD) && !iq->ratelimit_ok)
+ infra_ratelimit_dec(qstate->env->infra_cache, iq->dp->name,
+ iq->dp->namelen, *qstate->env->now);
return next_state(iq, QUERYTARGETS_STATE);
}
outbound_list_insert(&iq->outlist, outq);
@@ -2061,6 +2148,14 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
* delegation point, and back to the QUERYTARGETS_STATE. */
verbose(VERB_DETAIL, "query response was REFERRAL");
+ if(!(iq->chase_flags & BIT_RD) && !iq->ratelimit_ok) {
+ /* we have a referral, no ratelimit, we can send
+ * our queries to the given name */
+ infra_ratelimit_dec(qstate->env->infra_cache,
+ iq->dp->name, iq->dp->namelen,
+ *qstate->env->now);
+ }
+
/* if hardened, only store referral if we asked for it */
if(!qstate->env->cfg->harden_referral_path ||
( qstate->qinfo.qtype == LDNS_RR_TYPE_NS
@@ -2529,6 +2624,12 @@ processClassResponse(struct module_qstate* qstate, int id,
/* copy appropriate rcode */
to->rep->flags = from->rep->flags;
/* copy rrsets */
+ if(from->rep->rrset_count > RR_COUNT_MAX ||
+ to->rep->rrset_count > RR_COUNT_MAX) {
+ log_err("malloc failed (too many rrsets) in collect ANY");
+ foriq->state = FINISHED_STATE;
+ return; /* integer overflow protection */
+ }
dest = regional_alloc(forq->region, sizeof(dest[0])*n);
if(!dest) {
log_err("malloc failed in collect ANY");
@@ -2825,6 +2926,7 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq,
iq->caps_fallback = 1;
iq->caps_server = 0;
iq->caps_reply = NULL;
+ iq->caps_response = NULL;
iq->state = QUERYTARGETS_STATE;
iq->num_current_queries--;
/* need fresh attempts for the 0x20 fallback, if
@@ -2867,8 +2969,19 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq,
/* normalize and sanitize: easy to delete items from linked lists */
if(!scrub_message(pkt, prs, &iq->qchase, iq->dp->name,
- qstate->env->scratch, qstate->env, ie))
+ qstate->env->scratch, qstate->env, ie)) {
+ /* if 0x20 enabled, start fallback, but we have no message */
+ if(event == module_event_capsfail && !iq->caps_fallback) {
+ iq->caps_fallback = 1;
+ iq->caps_server = 0;
+ iq->caps_reply = NULL;
+ iq->caps_response = NULL;
+ iq->state = QUERYTARGETS_STATE;
+ iq->num_current_queries--;
+ verbose(VERB_DETAIL, "Capsforid: scrub failed, starting fallback with no response");
+ }
goto handle_it;
+ }
/* allocate response dns_msg in region */
iq->response = dns_alloc_msg(pkt, prs, qstate->region);
@@ -2890,6 +3003,7 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq,
iq->caps_fallback = 1;
iq->caps_server = 0;
iq->caps_reply = iq->response->rep;
+ iq->caps_response = iq->response;
iq->state = QUERYTARGETS_STATE;
iq->num_current_queries--;
verbose(VERB_DETAIL, "Capsforid: starting fallback");
@@ -2898,8 +3012,24 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq,
/* check if reply is the same, otherwise, fail */
if(!iq->caps_reply) {
iq->caps_reply = iq->response->rep;
+ iq->caps_response = iq->response;
iq->caps_server = -1; /*become zero at ++,
so that we start the full set of trials */
+ } else if(caps_failed_rcode(iq->caps_reply) &&
+ !caps_failed_rcode(iq->response->rep)) {
+ /* prefer to upgrade to non-SERVFAIL */
+ iq->caps_reply = iq->response->rep;
+ iq->caps_response = iq->response;
+ } else if(!caps_failed_rcode(iq->caps_reply) &&
+ caps_failed_rcode(iq->response->rep)) {
+ /* if we have non-SERVFAIL as answer then
+ * we can ignore SERVFAILs for the equality
+ * comparison */
+ /* no instructions here, skip other else */
+ } else if(caps_failed_rcode(iq->caps_reply) &&
+ caps_failed_rcode(iq->response->rep)) {
+ /* failure is same as other failure in fallbk*/
+ /* no instructions here, skip other else */
} else if(!reply_equal(iq->response->rep, iq->caps_reply,
qstate->env->scratch)) {
verbose(VERB_DETAIL, "Capsforid fallback: "
OpenPOWER on IntegriCloud