#include <isc/counter.h>
#include <isc/hash.h>
#include <isc/hashmap.h>
+#include <isc/list.h>
#include <isc/log.h>
#include <isc/loop.h>
#include <isc/mutex.h>
#define DEFAULT_MAX_QUERIES 50
#endif /* ifndef DEFAULT_MAX_QUERIES */
-/*
- * After NS_FAIL_LIMIT attempts to fetch a name server address,
- * if the number of addresses in the NS RRset exceeds NS_RR_LIMIT,
- * stop trying to fetch, in order to avoid wasting resources.
- */
-#define NS_FAIL_LIMIT 4
-#define NS_RR_LIMIT 5
/*
* IP address lookups are performed for at most NS_PROCESSING_LIMIT NS RRs in
* any NS RRset encountered, to avoid excessive resource use while processing
*/
#define NS_PROCESSING_LIMIT 20
-STATIC_ASSERT(NS_PROCESSING_LIMIT > NS_RR_LIMIT,
- "The maximum number of NS RRs processed for each "
- "delegation (NS_PROCESSING_LIMIT) must be larger than the large "
- "delegation threshold (NS_RR_LIMIT).");
-
/* Hash table for zone counters */
#ifndef RES_DOMAIN_HASH_BITS
#define RES_DOMAIN_HASH_BITS 12
dns_name_t *fwdname;
/*%
- * The number of events we're waiting for.
+ * Used to track started ADB finds with event.
*/
- atomic_uint_fast32_t pending;
+ size_t pending_running;
+ dns_adbfindlist_t pending_finds;
/*%
* The number of times we've "restarted" the current
fctx->qmin_warning = ISC_R_SUCCESS;
+ /*
+ * Cancel all pending ADB finds if we have not been successful
+ * or we are shutting down.
+ */
+ if (result != ISC_R_SUCCESS) {
+ dns_adbfind_t *find = NULL;
+ for (find = ISC_LIST_HEAD(fctx->pending_finds); find != NULL;
+ find = ISC_LIST_NEXT(find, publink))
+ {
+ dns_adb_cancelfind(find);
+ }
+ }
+
fctx_cancelqueries(fctx, no_response, age_untried);
fctx_stoptimer(fctx);
resquery_detach(&query);
}
+static isc_result_t
+fctx_finddone_fail(fetchctx_t *fctx) {
+ fctx->findfail++;
+
+ /*
+ * There are still running ADB finds and these can be more successful.
+ */
+ if (!ISC_LIST_EMPTY(fctx->pending_finds)) {
+ return DNS_R_WAIT;
+ }
+
+ FCTX_ATTR_CLR(fctx, FCTX_ATTR_ADDRWAIT);
+
+ /*
+ * There's something on the alternate list. Try that.
+ */
+ if (!ISC_LIST_EMPTY(fctx->res->alternates)) {
+ return DNS_R_CONTINUE;
+ }
+
+ /*
+ * We've got nothing else to wait for and don't know the answer.
+ * There's nothing to do but fail the fctx.
+ */
+ return ISC_R_FAILURE;
+}
+
static void
fctx_finddone(void *arg) {
dns_adbfind_t *find = (dns_adbfind_t *)arg;
fetchctx_t *fctx = (fetchctx_t *)find->cbarg;
- bool want_try = false;
- bool want_done = false;
- uint_fast32_t pending;
+ isc_result_t result = ISC_R_SUCCESS;
REQUIRE(VALID_FCTX(fctx));
REQUIRE(fctx->tid == isc_tid());
LOCK(&fctx->lock);
- pending = atomic_fetch_sub_release(&fctx->pending, 1);
- INSIST(pending > 0);
+ if (ISC_LINK_LINKED(find, publink)) {
+ /*
+ * If we canceled the find directly in findname(),
+ * it won't be linked here as dns_adb_cancelfind()
+ * is not idempotent.
+ */
+ fctx->pending_running--;
+ ISC_LIST_UNLINK(fctx->pending_finds, find, publink);
+ }
if (ADDRWAIT(fctx)) {
/*
INSIST(!SHUTTINGDOWN(fctx));
if (dns_adb_findstatus(find) == DNS_ADB_MOREADDRESSES) {
FCTX_ATTR_CLR(fctx, FCTX_ATTR_ADDRWAIT);
- want_try = true;
+ result = DNS_R_CONTINUE;
} else {
- fctx->findfail++;
- if (atomic_load_acquire(&fctx->pending) == 0) {
- FCTX_ATTR_CLR(fctx, FCTX_ATTR_ADDRWAIT);
- if (!ISC_LIST_EMPTY(fctx->res->alternates)) {
- want_try = true;
- } else {
- /*
- * We've got nothing else to wait for
- * and don't know the answer. There's
- * nothing to do but fail the fctx.
- */
- want_done = true;
- }
- }
+ result = fctx_finddone_fail(fctx);
}
}
dns_adb_destroyfind(&find);
- if (want_done) {
- FCTXTRACE("fetch failed in finddone(); return "
- "ISC_R_FAILURE");
-
- fctx_done_unref(fctx, ISC_R_FAILURE);
- } else if (want_try) {
+ switch (result) {
+ case ISC_R_SUCCESS:
+ case DNS_R_WAIT:
+ break;
+ case DNS_R_CONTINUE:
fctx_try(fctx, true);
+ break;
+ default:
+ FCTXTRACE2("fetch failed in finddone()",
+ isc_result_totext(result));
+ fctx_done_unref(fctx, result);
+ break;
}
fetchctx_detach(&fctx);
static void
findname(fetchctx_t *fctx, const dns_name_t *name, in_port_t port,
unsigned int options, unsigned int flags, isc_stdtime_t now,
- bool *overquota, bool *need_alternate, unsigned int *no_addresses) {
+ bool *overquota, bool *need_alternate, bool *have_address) {
dns_adbaddrinfo_t *ai = NULL;
dns_adbfind_t *find = NULL;
dns_resolver_t *res = fctx->res;
} else {
ISC_LIST_APPEND(fctx->finds, find, publink);
}
+ SET_IF_NOT_NULL(have_address, true);
return;
}
"loop detected resolving '%s'", fctx->info);
if ((find->options & DNS_ADBFIND_WANTEVENT) != 0) {
- atomic_fetch_add_relaxed(&fctx->pending, 1);
dns_adb_cancelfind(find);
} else {
dns_adb_destroyfind(&find);
* we'll get an event later when the find has what it needs.
*/
if ((find->options & DNS_ADBFIND_WANTEVENT) != 0) {
- atomic_fetch_add_relaxed(&fctx->pending, 1);
+ fctx->pending_running++;
+ ISC_LIST_APPEND(fctx->pending_finds, find, publink);
/*
* Bootstrap.
{
*need_alternate = true;
}
- if (no_addresses != NULL) {
- (*no_addresses)++;
- }
return;
}
static isc_result_t
fctx_getaddresses(fetchctx_t *fctx) {
- dns_rdata_t rdata = DNS_RDATA_INIT;
isc_result_t result;
dns_resolver_t *res;
isc_stdtime_t now;
dns_rdata_ns_t ns;
bool need_alternate = false;
bool all_spilled = false;
- unsigned int no_addresses = 0;
+ bool have_address = false;
unsigned int ns_processed = 0;
+ size_t fetches_allowed = 0;
FCTXTRACE5("getaddresses", "fctx->depth=", fctx->depth);
INSIST(ISC_LIST_EMPTY(fctx->finds));
INSIST(ISC_LIST_EMPTY(fctx->altfinds));
- for (result = dns_rdataset_first(&fctx->nameservers);
- result == ISC_R_SUCCESS;
- result = dns_rdataset_next(&fctx->nameservers))
- {
- bool overquota = false;
- unsigned int static_stub = 0;
-
- dns_rdataset_current(&fctx->nameservers, &rdata);
- /*
- * Extract the name from the NS record.
- */
- result = dns_rdata_tostruct(&rdata, &ns, NULL);
- if (result != ISC_R_SUCCESS) {
- continue;
- }
+ switch (fctx->depth) {
+ case 0:
+ fetches_allowed = 3;
+ break;
+ case 1:
+ fetches_allowed = 2;
+ break;
+ default:
+ fetches_allowed = 1;
+ break;
+ }
- if (STATICSTUB(&fctx->nameservers) &&
- dns_name_equal(&ns.name, fctx->domain))
+ for (;;) {
+ for (result = dns_rdataset_first(&fctx->nameservers);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&fctx->nameservers))
{
- static_stub = DNS_ADBFIND_STATICSTUB;
- }
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ bool overquota = false;
+ unsigned int static_stub = 0;
+ unsigned int no_fetch = 0;
- if (no_addresses > NS_FAIL_LIMIT &&
- dns_rdataset_count(&fctx->nameservers) > NS_RR_LIMIT)
- {
- stdoptions |= DNS_ADBFIND_NOFETCH;
- }
- findname(fctx, &ns.name, 0, stdoptions | static_stub, 0, now,
- &overquota, &need_alternate, &no_addresses);
+ dns_rdataset_current(&fctx->nameservers, &rdata);
+ /*
+ * Extract the name from the NS record.
+ */
+ result = dns_rdata_tostruct(&rdata, &ns, NULL);
+ if (result != ISC_R_SUCCESS) {
+ continue;
+ }
- if (!overquota) {
- all_spilled = false;
- }
+ if (STATICSTUB(&fctx->nameservers) &&
+ dns_name_equal(&ns.name, fctx->domain))
+ {
+ static_stub = DNS_ADBFIND_STATICSTUB;
+ }
- dns_rdata_reset(&rdata);
- dns_rdata_freestruct(&ns);
+ /*
+ * Make sure we only launch a limited number of
+ * outgoing fetches.
+ */
+ if (fctx->pending_running >= fetches_allowed) {
+ no_fetch = DNS_ADBFIND_NOFETCH;
+ }
+
+ findname(fctx, &ns.name, 0,
+ stdoptions | static_stub | no_fetch, 0, now,
+ &overquota, &need_alternate, &have_address);
- if (++ns_processed >= NS_PROCESSING_LIMIT) {
+ if (!overquota) {
+ all_spilled = false;
+ }
+
+ dns_rdata_reset(&rdata);
+ dns_rdata_freestruct(&ns);
+
+ if (++ns_processed >= NS_PROCESSING_LIMIT) {
+ result = ISC_R_NOMORE;
+ break;
+ }
+ }
+
+ /*
+ * Don't start alternate fetch if we just started one above.
+ */
+ /*
+ * Don't start alternate fetch if we just started one above.
+ */
+ if (fctx->pending_running > 0) {
+ stdoptions |= DNS_ADBFIND_NOFETCH;
+ result = ISC_R_NOMORE;
+ } else if (have_address || fetches_allowed != 0) {
result = ISC_R_NOMORE;
+ }
+
+ if (result != ISC_R_SUCCESS) {
break;
}
+
+ /*
+ * We have no addresses and we haven't allowed any
+ * fetches to be started. Allow one extra fetch and try
+ * again.
+ */
+ fetches_allowed = 1;
}
if (result != ISC_R_NOMORE) {
return result;
/*
* We've got no addresses.
*/
- if (atomic_load_acquire(&fctx->pending) > 0) {
+ if (fctx->pending_running > 0) {
/*
* We're fetching the addresses, but don't have
* any yet. Tell the caller to wait for an
dns_adbaddrinfo_t *addrinfo = NULL;
dns_resolver_t *res = NULL;
- FCTXTRACE5("try", "fctx->qc=", isc_counter_used(fctx->qc));
- if (fctx->gqc != NULL) {
- FCTXTRACE5("try", "fctx->gqc=", isc_counter_used(fctx->gqc));
- }
-
REQUIRE(!ADDRWAIT(fctx));
REQUIRE(fctx->tid == isc_tid());
}
result = isc_counter_increment(fctx->qc);
+#if WANT_QUERYTRACE
+ FCTXTRACE5("query", "max-recursion-queries, querycount=",
+ isc_counter_used(fctx->qc));
+#endif
if (result != ISC_R_SUCCESS) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(3),
if (fctx->gqc != NULL) {
result = isc_counter_increment(fctx->gqc);
+#if WANT_QUERYTRACE
+ FCTXTRACE5("query", "max-query-count, querycount=",
+ isc_counter_used(fctx->gqc));
+#endif
if (result != ISC_R_SUCCESS) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(3),
goto cleanup;
}
fcount_decr(fctx);
-
dns_name_copy(fname, fctx->domain);
result = fcount_incr(fctx, true);
REQUIRE(ISC_LIST_EMPTY(fctx->queries));
REQUIRE(ISC_LIST_EMPTY(fctx->finds));
REQUIRE(ISC_LIST_EMPTY(fctx->altfinds));
- REQUIRE(atomic_load_acquire(&fctx->pending) == 0);
+ REQUIRE(ISC_LIST_EMPTY(fctx->pending_finds));
REQUIRE(ISC_LIST_EMPTY(fctx->validators));
REQUIRE(fctx->state != fetchstate_active);
+ REQUIRE(fctx->timer == NULL);
FCTXTRACE("destroy");
ISC_LIST_INIT(fctx->bad);
ISC_LIST_INIT(fctx->edns);
ISC_LIST_INIT(fctx->validators);
+ ISC_LIST_INIT(fctx->pending_finds);
atomic_init(&fctx->attributes, 0);