+4006. [security] A flaw in delegation handling could be exploited
+ to put named into an infinite loop. This has
+ been addressed by placing limits on the number
+ of levels of recursion named will allow (default 7),
+ and the number of iterative queries that it will
+ send (default 50) before terminating a recursive
+ query (CVE-2014-8500).
+
+ The recursion depth limit is configured via the
+ "max-recursion-depth" option. [RT #35780]
+
--- 9.9.6 released ---
3950. [port] Changed the bin/python Makefile to work around a
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: config.c,v 1.123 2012/01/06 23:46:41 tbox Exp $ */
-
/*! \file */
#include <config.h>
dnssec-accept-expired no;\n\
clients-per-query 10;\n\
max-clients-per-query 100;\n\
+ max-recursion-depth 7;\n\
zero-no-soa-ttl-cache no;\n\
nsec3-test-zone no;\n\
allow-new-zones no;\n\
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: query.h,v 1.45 2011/01/13 04:59:24 tbox Exp $ */
-
#ifndef NAMED_QUERY_H
#define NAMED_QUERY_H 1
peeraddr = &client->peeraddr;
else
peeraddr = NULL;
- result = dns_resolver_createfetch2(client->view->resolver,
+ result = dns_resolver_createfetch3(client->view->resolver,
qname, qtype, qdomain, nameservers,
NULL, peeraddr, client->message->id,
- client->query.fetchoptions,
- client->task,
- query_resume, client,
+ client->query.fetchoptions, 0,
+ client->task, query_resume, client,
rdataset, sigrdataset,
&client->query.fetch);
cfg_obj_asuint32(obj),
max_clients_per_query);
+ obj = NULL;
+ result = ns_config_get(maps, "max-recursion-depth", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_resolver_setmaxdepth(view->resolver, cfg_obj_asuint32(obj));
+
#ifdef ALLOW_FILTER_AAAA_ON_V4
obj = NULL;
result = ns_config_get(maps, "filter-aaaa-on-v4", &obj);
--- /dev/null
+rm -f ns1/[1-9]*example.tld?.db
+rm -f ns2/[1-9]*example.tld?.db
+rm -f ns1/zones.conf
+rm -f ns2/zones.conf
+rm -f */root.db
+rm -f ns3/tld1.db
+rm -f ns4/tld2.db
--- /dev/null
+/*
+ * Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+controls { /* empty */ };
+
+options {
+ query-source address 10.53.0.1;
+ notify-source 10.53.0.1;
+ transfer-source 10.53.0.1;
+ port 5300;
+ pid-file "named.pid";
+ listen-on { 10.53.0.1; };
+ listen-on-v6 { none; };
+ recursion no;
+};
+
+include "zones.conf";
+
+// zone "tld1" { type master; file "tld1.db"; };
+// zone "tld2" { type master; file "tld2.db"; };
--- /dev/null
+/*
+ * Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+controls { /* empty */ };
+
+options {
+ query-source address 10.53.0.2;
+ notify-source 10.53.0.2;
+ transfer-source 10.53.0.2;
+ port 5300;
+ pid-file "named.pid";
+ listen-on { 10.53.0.2; };
+ listen-on-v6 { none; };
+ recursion no;
+};
+
+include "zones.conf";
--- /dev/null
+/*
+ * Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+controls { /* empty */ };
+
+options {
+ query-source address 10.53.0.3;
+ notify-source 10.53.0.3;
+ transfer-source 10.53.0.3;
+ port 5300;
+ pid-file "named.pid";
+ listen-on { 10.53.0.3; };
+ listen-on-v6 { none; };
+ recursion no;
+};
+
+zone "." { type master; file "root.db"; };
+
+zone "tld1" { type master; file "tld1.db"; };
--- /dev/null
+/*
+ * Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+controls { /* empty */ };
+
+options {
+ query-source address 10.53.0.4;
+ notify-source 10.53.0.4;
+ transfer-source 10.53.0.4;
+ port 5300;
+ pid-file "named.pid";
+ listen-on { 10.53.0.4; };
+ listen-on-v6 { none; };
+ recursion no;
+};
+
+zone "tld2" { type master; file "tld2.db"; };
--- /dev/null
+. 60 in ns ns.nil.
+ns.nil. 60 in A 10.53.0.3
--- /dev/null
+/*
+ * Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+controls { /* empty */ };
+
+options {
+ query-source address 10.53.0.5;
+ notify-source 10.53.0.5;
+ transfer-source 10.53.0.5;
+ port 5300;
+ pid-file "named.pid";
+ listen-on { 10.53.0.5; };
+ listen-on-v6 { none; };
+};
+
+zone "." { type hint; file "hints.db"; };
--- /dev/null
+i=1
+
+cat > ns3/root.db << EOF
+. 60 in soa ns.nil. hostmaster.ns.nil. 1 0 0 0 0
+. 60 in ns ns.nil.
+ns.nil. 60 in a 10.53.0.3
+tld1. 60 in ns ns.tld1.
+ns.tld1. 60 in a 10.53.0.3
+tld2. 60 in ns ns.tld2.
+ns.tld2. 60 in a 10.53.0.4
+EOF
+
+cat > ns3/tld1.db << EOF
+tld1. 60 in soa ns.tld1. hostmaster.ns.tld1. 1 0 0 0 0
+tld1. 60 in ns ns.tld1.
+ns.tld1. 60 in a 10.53.0.1
+EOF
+
+cat > ns4/tld2.db << EOF
+tld2. 60 in soa ns.tld2. hostmaster.ns.tld4. 1 0 0 0 0
+tld2. 60 in ns ns.tld2.
+ns.tld2. 60 in a 10.53.0.1
+EOF
+
+: > ns1/zones.conf
+: > ns2/zones.conf
+
+while [ $i -lt 1000 ]
+do
+j=`expr $i + 1`
+s=`expr $j % 2 + 1`
+n=`expr $i % 2 + 1`
+t=`expr $s + 2`
+
+# i=1 j=2 s=1 n=2
+# i=2 j=3 s=1 n=2
+# i=3 j=4 s=1 n=2
+
+cat > ns1/${i}example.tld${s}.db << EOF
+${i}example.tld${s}. 60 in soa ns.${j}example.tld${n}. hostmaster 1 0 0 0 0
+${i}example.tld${s}. 60 in ns ns.${j}example.tld${n}.
+ns.${i}example.tld${s}. 60 in a 10.53.0.1
+EOF
+
+cat >> ns1/zones.conf << EOF
+zone "${i}example.tld${s}" { type master; file "${i}example.tld${s}.db"; };
+EOF
+
+cat >> ns${t}/tld${s}.db << EOF
+${i}example.tld${s}. 60 in ns ns.${j}example.tld${n}.
+EOF
+
+i=$j
+
+done
+
+j=`expr $i + 1`
+s=`expr $j % 2 + 1`
+n=`expr $s % 2 + 1`
+t=`expr $s + 2`
+
+cat > ns1/${i}example.tld${s}.db << EOF
+${i}example.tld${s}. 60 in soa ns.${i}example.tld${s}. hostmaster 1 0 0 0 0
+${i}example.tld${s}. 60 in ns ns.${i}example.tld${s}.
+ns.${i}example.tld${s}. 60 in a 10.53.0.1
+EOF
+
+cat >> ns1/zones.conf << EOF
+zone "${i}example.tld${s}" { type master; file "${i}example.tld${s}.db"; };
+EOF
+
+cat >> ns${t}/tld${s}.db << EOF
+${i}example.tld${s}. 60 in ns ns.${i}example.tld${s}.
+ns.${i}example.tld${s}. 60 in a 10.53.0.1
+EOF
--- /dev/null
+#!/bin/sh
+#
+# Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+SYSTEMTESTTOP=..
+. $SYSTEMTESTTOP/conf.sh
+
+status=0
+n=0
+
+n=`expr $n + 1`
+echo "I: attempt lookup 1example.tld2 soa ($n)"
+ret=0
+$DIG +tcp 1example.tld1 soa @10.53.0.5 -p 5300 > dig.out.test$n
+grep "status: SERVFAIL" dig.out.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: attempt lookup 992example.tld2 soa ($n)"
+ret=0
+$DIG +tcp 992example.tld2 soa @10.53.0.5 -p 5300 > dig.out.test$n
+grep "status: SERVFAIL" dig.out.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: attempt lookup 993example.tld1 soa ($n)"
+ret=0
+$DIG +tcp 993example.tld1 soa @10.53.0.5 -p 5300 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:exit status: $status"
+exit $status
<optional> max-acache-size <replaceable>size_spec</replaceable> ; </optional>
<optional> clients-per-query <replaceable>number</replaceable> ; </optional>
<optional> max-clients-per-query <replaceable>number</replaceable> ; </optional>
+ <optional> max-recursion-depth <replaceable>number</replaceable> ; </optional>
<optional> masterfile-format (<constant>text</constant>|<constant>raw</constant>) ; </optional>
<optional> empty-server <replaceable>name</replaceable> ; </optional>
<optional> empty-contact <replaceable>name</replaceable> ; </optional>
</listitem>
</varlistentry>
+ <varlistentry id="max-recursion-depth">
+ <term><command>max-recursion-depth</command></term>
+ <listitem>
+ <para>
+ Sets the maximum number of levels of recursion
+ permitted at any one time while resolving a name.
+ The default is 7.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><command>notify-delay</command></term>
<listitem>
unsigned int magic;
dns_fetch_t *fetch;
dns_rdataset_t rdataset;
+ unsigned int depth;
};
/*%
static isc_boolean_t clean_namehooks(dns_adb_t *, dns_adbnamehooklist_t *);
static void clean_target(dns_adb_t *, dns_name_t *);
static void clean_finds_at_name(dns_adbname_t *, isc_eventtype_t,
- unsigned int);
+ isc_uint32_t, unsigned int);
static isc_boolean_t check_expire_namehooks(dns_adbname_t *, isc_stdtime_t);
static isc_boolean_t check_expire_entry(dns_adb_t *, dns_adbentry_t **,
isc_stdtime_t);
static isc_result_t dbfind_name(dns_adbname_t *, isc_stdtime_t,
dns_rdatatype_t);
static isc_result_t fetch_name(dns_adbname_t *, isc_boolean_t,
- dns_rdatatype_t);
+ unsigned int, dns_rdatatype_t);
static inline void check_exit(dns_adb_t *);
static void destroy(dns_adb_t *);
static isc_boolean_t shutdown_names(dns_adb_t *);
* Clean up the name's various lists. These two are destructive
* in that they will always empty the list.
*/
- clean_finds_at_name(name, ev, DNS_ADBFIND_ADDRESSMASK);
+ clean_finds_at_name(name, ev, 0, DNS_ADBFIND_ADDRESSMASK);
result4 = clean_namehooks(adb, &name->v4);
result6 = clean_namehooks(adb, &name->v6);
clean_target(adb, &name->target);
*/
static void
clean_finds_at_name(dns_adbname_t *name, isc_eventtype_t evtype,
- unsigned int addrs)
+ isc_uint32_t qtotal, unsigned int addrs)
{
isc_event_t *ev;
isc_task_t *task;
ev->ev_sender = find;
find->result_v4 = find_err_map[name->fetch_err];
find->result_v6 = find_err_map[name->fetch6_err];
+ find->qtotal += qtotal;
ev->ev_type = evtype;
ev->ev_destroy = event_free;
ev->ev_destroy_arg = find;
h->flags = 0;
h->result_v4 = ISC_R_UNEXPECTED;
h->result_v6 = ISC_R_UNEXPECTED;
+ h->qtotal = 0;
ISC_LINK_INIT(h, publink);
ISC_LINK_INIT(h, plink);
ISC_LIST_INIT(h->list);
dns_rdatatype_t qtype, unsigned int options,
isc_stdtime_t now, dns_name_t *target,
in_port_t port, dns_adbfind_t **findp)
+{
+ return (dns_adb_createfind2(adb, task, action, arg, name,
+ qname, qtype, options, now,
+ target, port, 0, findp));
+}
+
+isc_result_t
+dns_adb_createfind2(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action,
+ void *arg, dns_name_t *name, dns_name_t *qname,
+ dns_rdatatype_t qtype, unsigned int options,
+ isc_stdtime_t now, dns_name_t *target,
+ in_port_t port, unsigned int depth,
+ dns_adbfind_t **findp)
{
dns_adbfind_t *find;
dns_adbname_t *adbname;
* Start V4.
*/
if (WANT_INET(wanted_fetches) &&
- fetch_name(adbname, start_at_zone,
+ fetch_name(adbname, start_at_zone, depth,
dns_rdatatype_a) == ISC_R_SUCCESS) {
DP(DEF_LEVEL,
"dns_adb_createfind: started A fetch for name %p",
* Start V6.
*/
if (WANT_INET6(wanted_fetches) &&
- fetch_name(adbname, start_at_zone,
+ fetch_name(adbname, start_at_zone, depth,
dns_rdatatype_aaaa) == ISC_R_SUCCESS) {
DP(DEF_LEVEL,
"dns_adb_createfind: "
isc_result_t result;
unsigned int address_type;
isc_boolean_t want_check_exit = ISC_FALSE;
+ isc_uint32_t qtotal = 0;
UNUSED(task);
adb = name->adb;
INSIST(DNS_ADB_VALID(adb));
+ qtotal = dev->qtotal;
+
bucket = name->lock_bucket;
LOCK(&adb->namelocks[bucket]);
DP(DEF_LEVEL, "adb: fetch of '%s' %s failed: %s",
buf, address_type == DNS_ADBFIND_INET ? "A" : "AAAA",
dns_result_totext(dev->result));
+ /*
+ * Don't record a failure unless this is the initial
+ * fetch of a chain.
+ */
+ if (fetch->depth > 1)
+ goto out;
/* XXXMLG Don't pound on bad servers. */
if (address_type == DNS_ADBFIND_INET) {
name->expire_v4 = ISC_MIN(name->expire_v4, now + 300);
free_adbfetch(adb, &fetch);
isc_event_free(&ev);
- clean_finds_at_name(name, ev_status, address_type);
+ clean_finds_at_name(name, ev_status, qtotal, address_type);
UNLOCK(&adb->namelocks[bucket]);
}
static isc_result_t
-fetch_name(dns_adbname_t *adbname,
- isc_boolean_t start_at_zone,
- dns_rdatatype_t type)
+fetch_name(dns_adbname_t *adbname, isc_boolean_t start_at_zone,
+ unsigned int depth, dns_rdatatype_t type)
{
isc_result_t result;
dns_adbfetch_t *fetch = NULL;
result = ISC_R_NOMEMORY;
goto cleanup;
}
-
- result = dns_resolver_createfetch(adb->view->resolver, &adbname->name,
- type, name, nameservers, NULL,
- options, adb->task, fetch_callback,
- adbname, &fetch->rdataset, NULL,
- &fetch->fetch);
+ fetch->depth = depth;
+
+ result = dns_resolver_createfetch3(adb->view->resolver, &adbname->name,
+ type, name, nameservers, NULL,
+ NULL, 0, options, depth, adb->task,
+ fetch_callback, adbname,
+ &fetch->rdataset, NULL,
+ &fetch->fetch);
if (result != ISC_R_SUCCESS)
goto cleanup;
isc_result_t result_v6; /*%< RO: v6 result */
ISC_LINK(dns_adbfind_t) publink; /*%< RW: client use */
+ isc_uint32_t qtotal;
+
/* Private */
isc_mutex_t lock; /* locks all below */
in_port_t port;
dns_rdatatype_t qtype, unsigned int options,
isc_stdtime_t now, dns_name_t *target,
in_port_t port, dns_adbfind_t **find);
+isc_result_t
+dns_adb_createfind2(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action,
+ void *arg, dns_name_t *name, dns_name_t *qname,
+ dns_rdatatype_t qtype, unsigned int options,
+ isc_stdtime_t now, dns_name_t *target, in_port_t port,
+ unsigned int depth, dns_adbfind_t **find);
/*%<
* Main interface for clients. The adb will look up the name given in
* "name" and will build up a list of found addresses, and perhaps start
isc_sockaddr_t * client;
dns_messageid_t id;
isc_result_t vresult;
+ isc_uint32_t qtotal;
} dns_fetchevent_t;
/*
dns_rdataset_t *rdataset,
dns_rdataset_t *sigrdataset,
dns_fetch_t **fetchp);
+isc_result_t
+dns_resolver_createfetch3(dns_resolver_t *res, dns_name_t *name,
+ dns_rdatatype_t type,
+ dns_name_t *domain, dns_rdataset_t *nameservers,
+ dns_forwarders_t *forwarders,
+ isc_sockaddr_t *client, isc_uint16_t id,
+ unsigned int options, unsigned int depth,
+ isc_task_t *task,
+ isc_taskaction_t action, void *arg,
+ dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset,
+ dns_fetch_t **fetchp);
/*%<
* Recurse to answer a question.
*
* \li resolver to be valid.
*/
+void
+dns_resolver_setmaxdepth(dns_resolver_t *resolver, unsigned int maxdepth);
+unsigned int
+dns_resolver_getmaxdepth(dns_resolver_t *resolver);
+/*%
+ * Get and set how many NS indirections will be followed when looking for
+ * nameserver addresses.
+ *
+ * Requires:
+ * \li resolver to be valid.
+ */
+
ISC_LANG_ENDDECLS
#endif /* DNS_RESOLVER_H */
#define MAXIMUM_QUERY_TIMEOUT 30 /* The maximum time in seconds for the whole query to live. */
#endif
+/* The default maximum number of recursions to follow before giving up. */
+#ifndef DEFAULT_RECURSION_DEPTH
+#define DEFAULT_RECURSION_DEPTH 7
+#endif
+
+/* The default maximum number of iterative queries to allow before giving up. */
+#ifndef DEFAULT_MAX_QUERIES
+#define DEFAULT_MAX_QUERIES 50
+#endif
+
/*%
* Maximum EDNS0 input packet size.
*/
isc_uint64_t duration;
isc_boolean_t logged;
unsigned int querysent;
+ unsigned int totalqueries;
unsigned int referrals;
unsigned int lamecount;
unsigned int neterr;
isc_boolean_t timeout;
dns_adbaddrinfo_t *addrinfo;
isc_sockaddr_t *client;
+ unsigned int depth;
};
#define FCTX_MAGIC ISC_MAGIC('F', '!', '!', '!')
isc_timer_t * spillattimer;
isc_boolean_t zero_no_soa_ttl;
unsigned int query_timeout;
+ unsigned int maxdepth;
/* Locked by lock. */
unsigned int references;
event->result == DNS_R_NCACHENXRRSET);
}
+ event->qtotal = fctx->totalqueries;
isc_task_sendanddetach(&task, ISC_EVENT_PTR(&event));
count++;
}
if (result != ISC_R_SUCCESS)
goto cleanup_dispatch;
}
+
fctx->querysent++;
+ fctx->totalqueries++;
ISC_LIST_APPEND(fctx->queries, query, link);
query->fctx->nqueries++;
*/
INSIST(!SHUTTINGDOWN(fctx));
fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
- if (event->ev_type == DNS_EVENT_ADBMOREADDRESSES)
+ if (event->ev_type == DNS_EVENT_ADBMOREADDRESSES) {
want_try = ISC_TRUE;
- else {
+ fctx->totalqueries += find->qtotal;
+ } else {
fctx->findfail++;
if (fctx->pending == 0) {
/*
* See what we know about this address.
*/
find = NULL;
- result = dns_adb_createfind(fctx->adb,
- res->buckets[fctx->bucketnum].task,
- fctx_finddone, fctx, name,
- &fctx->name, fctx->type,
- options, now, NULL,
- res->view->dstport, &find);
+ result = dns_adb_createfind2(fctx->adb,
+ res->buckets[fctx->bucketnum].task,
+ fctx_finddone, fctx, name,
+ &fctx->name, fctx->type,
+ options, now, NULL,
+ res->view->dstport,
+ fctx->depth + 1, &find);
if (result != ISC_R_SUCCESS) {
if (result == DNS_R_ALIAS) {
/*
res = fctx->res;
+ if (fctx->depth > res->maxdepth) {
+ FCTXTRACE("too much NS indirection");
+ return (DNS_R_SERVFAIL);
+ }
+
/*
* Forwarders.
*/
REQUIRE(!ADDRWAIT(fctx));
+ if (fctx->totalqueries > DEFAULT_MAX_QUERIES)
+ fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
+
addrinfo = fctx_nextaddress(fctx);
if (addrinfo == NULL) {
/*
* Normal fctx startup.
*/
fctx->state = fetchstate_active;
+ fctx->totalqueries = 0;
/*
* Reset the control event for later use in shutting down
* the fctx.
event->fetch = fetch;
event->client = client;
event->id = id;
+ event->qtotal = 0;
dns_fixedname_init(&event->foundname);
/*
static isc_result_t
fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
dns_name_t *domain, dns_rdataset_t *nameservers,
- unsigned int options, unsigned int bucketnum, fetchctx_t **fctxp)
+ unsigned int options, unsigned int bucketnum, unsigned int depth,
+ fetchctx_t **fctxp)
{
fetchctx_t *fctx;
isc_result_t result;
fctx->state = fetchstate_init;
fctx->want_shutdown = ISC_FALSE;
fctx->cloned = ISC_FALSE;
+ fctx->depth = depth;
ISC_LIST_INIT(fctx->queries);
ISC_LIST_INIT(fctx->finds);
ISC_LIST_INIT(fctx->altfinds);
fctx->pending = 0;
fctx->restarts = 0;
fctx->querysent = 0;
+ fctx->totalqueries = 0;
fctx->referrals = 0;
TIME_NOW(&fctx->start);
fctx->timeouts = 0;
res->spillattimer = NULL;
res->zero_no_soa_ttl = ISC_FALSE;
res->query_timeout = DEFAULT_QUERY_TIMEOUT;
+ res->maxdepth = DEFAULT_RECURSION_DEPTH;
res->nbuckets = ntasks;
res->activebuckets = ntasks;
res->buckets = isc_mem_get(view->mctx,
dns_rdataset_t *sigrdataset,
dns_fetch_t **fetchp)
{
- return (dns_resolver_createfetch2(res, name, type, domain,
+ return (dns_resolver_createfetch3(res, name, type, domain,
nameservers, forwarders, NULL, 0,
- options, task, action, arg,
+ options, 0, task, action, arg,
rdataset, sigrdataset, fetchp));
}
dns_rdataset_t *rdataset,
dns_rdataset_t *sigrdataset,
dns_fetch_t **fetchp)
+{
+ return (dns_resolver_createfetch3(res, name, type, domain,
+ nameservers, forwarders, client, id,
+ options, 0, task, action, arg,
+ rdataset, sigrdataset, fetchp));
+}
+
+isc_result_t
+dns_resolver_createfetch3(dns_resolver_t *res, dns_name_t *name,
+ dns_rdatatype_t type,
+ dns_name_t *domain, dns_rdataset_t *nameservers,
+ dns_forwarders_t *forwarders,
+ isc_sockaddr_t *client, dns_messageid_t id,
+ unsigned int options, unsigned int depth,
+ isc_task_t *task,
+ isc_taskaction_t action, void *arg,
+ dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset,
+ dns_fetch_t **fetchp)
{
dns_fetch_t *fetch;
fetchctx_t *fctx = NULL;
if (fctx == NULL) {
result = fctx_create(res, name, type, domain, nameservers,
- options, bucketnum, &fctx);
+ options, bucketnum, depth, &fctx);
if (result != ISC_R_SUCCESS)
goto unlock;
new_fctx = ISC_TRUE;
- }
+ } else if (fctx->depth > depth)
+ fctx->depth = depth;
result = fctx_join(fctx, task, client, id, action, arg,
rdataset, sigrdataset, fetch);
resolver->query_timeout = seconds;
}
+
+void
+dns_resolver_setmaxdepth(dns_resolver_t *resolver, unsigned int maxdepth) {
+ REQUIRE(VALID_RESOLVER(resolver));
+ resolver->maxdepth = maxdepth;
+}
+
+unsigned int
+dns_resolver_getmaxdepth(dns_resolver_t *resolver) {
+ REQUIRE(VALID_RESOLVER(resolver));
+ return (resolver->maxdepth);
+}
{ "max-cache-ttl", &cfg_type_uint32, 0 },
{ "max-clients-per-query", &cfg_type_uint32, 0 },
{ "max-ncache-ttl", &cfg_type_uint32, 0 },
+ { "max-recursion-depth", &cfg_type_uint32, 0 },
{ "max-udp-size", &cfg_type_uint32, 0 },
{ "min-roots", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP },
{ "minimal-responses", &cfg_type_boolean, 0 },