]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
[v9_9] DDoS mitigation features
authorEvan Hunt <each@isc.org>
Thu, 9 Jul 2015 06:00:58 +0000 (23:00 -0700)
committerEvan Hunt <each@isc.org>
Thu, 9 Jul 2015 06:00:58 +0000 (23:00 -0700)
3938. [func] Added quotas to be used in recursive resolvers
that are under high query load for names in zones
whose authoritative servers are nonresponsive or
are experiencing a denial of service attack.

- "fetches-per-server" limits the number of
  simultaneous queries that can be sent to any
  single authoritative server.  The configured
  value is a starting point; it is automatically
  adjusted downward if the server is partially or
  completely non-responsive. The algorithm used to
  adjust the quota can be configured via the
  "fetch-quota-params" option.
- "fetches-per-zone" limits the number of
  simultaneous queries that can be sent for names
  within a single domain.  (Note: Unlike
  "fetches-per-server", this value is not
  self-tuning.)
- New stats counters have been added to count
  queries spilled due to these quotas.

These options are not available by default;
use "configure --enable-fetchlimit" (or
--enable-developer) to include them in the build.

See the ARM for details of these options. [RT #37125]

61 files changed:
CHANGES
bin/named/client.c
bin/named/config.c
bin/named/include/named/server.h
bin/named/server.c
bin/named/statschannel.c
bin/rndc/rndc.c
bin/rndc/rndc.docbook
bin/tests/system/Makefile.in
bin/tests/system/conf.sh.in
bin/tests/system/ditch.pl [new file with mode: 0644]
bin/tests/system/dlzexternal/driver.c
bin/tests/system/fetchlimit/Makefile.in [new file with mode: 0644]
bin/tests/system/fetchlimit/ans4/ans.pl [new file with mode: 0644]
bin/tests/system/fetchlimit/clean.sh [new file with mode: 0644]
bin/tests/system/fetchlimit/fetchlimit.c [new file with mode: 0644]
bin/tests/system/fetchlimit/lameserver/ans4/ans.pl [new file with mode: 0644]
bin/tests/system/fetchlimit/lameserver/clean.sh [new file with mode: 0644]
bin/tests/system/fetchlimit/lameserver/ns1/named.conf [new file with mode: 0644]
bin/tests/system/fetchlimit/lameserver/ns1/root.db [new file with mode: 0644]
bin/tests/system/fetchlimit/lameserver/ns2/example.db [new file with mode: 0644]
bin/tests/system/fetchlimit/lameserver/ns2/named.conf [new file with mode: 0644]
bin/tests/system/fetchlimit/lameserver/ns3/named1.conf [new file with mode: 0644]
bin/tests/system/fetchlimit/lameserver/ns3/named2.conf [new file with mode: 0644]
bin/tests/system/fetchlimit/lameserver/ns3/named3.conf [new file with mode: 0644]
bin/tests/system/fetchlimit/lameserver/ns3/root.hint [new file with mode: 0644]
bin/tests/system/fetchlimit/lameserver/setup.sh [new file with mode: 0644]
bin/tests/system/fetchlimit/lameserver/tests.sh [new file with mode: 0644]
bin/tests/system/fetchlimit/ns1/named.conf [new file with mode: 0644]
bin/tests/system/fetchlimit/ns1/root.db [new file with mode: 0644]
bin/tests/system/fetchlimit/ns2/example.db [new file with mode: 0644]
bin/tests/system/fetchlimit/ns2/named.conf [new file with mode: 0644]
bin/tests/system/fetchlimit/ns3/named.args [new file with mode: 0644]
bin/tests/system/fetchlimit/ns3/named1.conf [new file with mode: 0644]
bin/tests/system/fetchlimit/ns3/named2.conf [new file with mode: 0644]
bin/tests/system/fetchlimit/ns3/named3.conf [new file with mode: 0644]
bin/tests/system/fetchlimit/ns3/root.hint [new file with mode: 0644]
bin/tests/system/fetchlimit/prereq.sh [new file with mode: 0644]
bin/tests/system/fetchlimit/setup.sh [new file with mode: 0644]
bin/tests/system/fetchlimit/tests.sh [new file with mode: 0644]
bin/tests/system/resolver/tests.sh
config.h.in
configure
configure.in
doc/arm/Bv9ARM-book.xml
doc/arm/notes.xml
lib/dns/adb.c
lib/dns/include/dns/adb.h
lib/dns/include/dns/log.h
lib/dns/include/dns/resolver.h
lib/dns/include/dns/stats.h
lib/dns/log.c
lib/dns/resolver.c
lib/dns/view.c
lib/dns/win32/libdns.def.in
lib/isc/include/isc/util.h
lib/isccfg/include/isccfg/cfg.h
lib/isccfg/include/isccfg/grammar.h
lib/isccfg/namedconf.c
lib/isccfg/parser.c
lib/isccfg/win32/libisccfg.def

diff --git a/CHANGES b/CHANGES
index 59e1511686bc014bfc8df75e099ebbd27fe331a9..e3e02b56e7635ab46410a762b043d1d76c444b76 100644 (file)
--- a/CHANGES
+++ b/CHANGES
                        during operation. If the read failed, named
                        could segfault. [RT #38559]
 
+3938.  [func]          Added quotas to be used in recursive resolvers
+                       that are under high query load for names in zones
+                       whose authoritative servers are nonresponsive or
+                       are experiencing a denial of service attack.
+
+                       - "fetches-per-server" limits the number of
+                         simultaneous queries that can be sent to any
+                         single authoritative server.  The configured
+                         value is a starting point; it is automatically
+                         adjusted downward if the server is partially or
+                         completely non-responsive. The algorithm used to
+                         adjust the quota can be configured via the
+                         "fetch-quota-params" option.
+                       - "fetches-per-zone" limits the number of
+                         simultaneous queries that can be sent for names
+                         within a single domain.  (Note: Unlike
+                         "fetches-per-server", this value is not
+                         self-tuning.)
+                       - New stats counters have been added to count
+                         queries spilled due to these quotas.
+
+                       These options are not available by default;
+                       use "configure --enable-fetchlimit" (or
+                       --enable-developer) to include them in the build.
+
+                       See the ARM for details of these options. [RT #37125]
+
 3937.  [func]          Added some debug logging to better indicate the
                        conditions causing SERVFAILs when resolving.
                        [RT #35538]
index 54da0c2024000d49da282ff4278ebbf7c7dbba9b..de2d596c8832dfa961ab03886d17194d2ef874ac 100644 (file)
@@ -23,6 +23,7 @@
 #include <isc/platform.h>
 #include <isc/print.h>
 #include <isc/queue.h>
+#include <isc/random.h>
 #include <isc/stats.h>
 #include <isc/stdio.h>
 #include <isc/string.h>
  */
 #endif
 
+
 /*% nameserver client manager structure */
 struct ns_clientmgr {
        /* Unlocked. */
@@ -1649,7 +1651,7 @@ client_request(isc_task_t *task, isc_event_t *event) {
        }
        if (TCP_CLIENT(client))
                isc_stats_increment(ns_g_server->nsstats,
-                                   dns_nsstatscounter_tcp);
+                                   dns_nsstatscounter_requesttcp);
 
        /*
         * It's a request.  Parse it.
@@ -1662,6 +1664,11 @@ client_request(isc_task_t *task, isc_event_t *event) {
                 */
                if (result == DNS_R_OPTERR)
                        (void)client_addopt(client);
+
+               ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+                             NS_LOGMODULE_CLIENT, ISC_LOG_WARNING,
+                             "message parsing failed: %s",
+                             isc_result_totext(result));
                ns_client_error(client, result);
                goto cleanup;
        }
index a32f12e660ae398d820adfbe41f25705e176e1c2..f61f0a856526e6ec1a1f3be66cd3321bcad9a13f 100644 (file)
@@ -156,7 +156,14 @@ options {\n\
        dnssec-enable yes;\n\
        dnssec-validation yes; \n\
        dnssec-accept-expired no;\n\
-       clients-per-query 10;\n\
+"
+#ifdef ENABLE_FETCHLIMIT
+"      fetches-per-server 0;\n\
+       fetches-per-zone 0;\n\
+       fetch-quota-params 100 0.1 0.3 0.7;\n\
+"
+#endif /* ENABLE_FETCHLIMIT */
+"      clients-per-query 10;\n\
        max-clients-per-query 100;\n\
        max-recursion-depth 7;\n\
        max-recursion-queries 50;\n\
index d3bec57f66a3351552e65eda534ef7ce484fd6f9..a3696f1614c15ad2bbf0e81fe89c1d74acb0d259 100644 (file)
@@ -51,6 +51,7 @@ struct ns_server {
        isc_quota_t             xfroutquota;
        isc_quota_t             tcpquota;
        isc_quota_t             recursionquota;
+
        dns_acl_t               *blackholeacl;
        char *                  statsfile;      /*%< Statistics file name */
        char *                  dumpfile;       /*%< Dump file name */
@@ -130,7 +131,7 @@ enum {
        dns_nsstatscounter_tsigin = 4,
        dns_nsstatscounter_sig0in = 5,
        dns_nsstatscounter_invalidsig = 6,
-       dns_nsstatscounter_tcp = 7,
+       dns_nsstatscounter_requesttcp = 7,
 
        dns_nsstatscounter_authrej = 8,
        dns_nsstatscounter_recurserej = 9,
@@ -165,16 +166,31 @@ enum {
        dns_nsstatscounter_updatefail = 34,
        dns_nsstatscounter_updatebadprereq = 35,
 
-       dns_nsstatscounter_rpz_rewrites = 36,
+       dns_nsstatscounter_recursclients = 36,
+
+       dns_nsstatscounter_dns64 = 37,
+
+       dns_nsstatscounter_ratedropped = 38,
+       dns_nsstatscounter_rateslipped = 39,
+
+       dns_nsstatscounter_rpz_rewrites = 40,
+
+       dns_nsstatscounter_udp = 41,
+       dns_nsstatscounter_tcp = 42,
+
+       dns_nsstatscounter_nsidopt = 43,
+       dns_nsstatscounter_expireopt = 44,
+       dns_nsstatscounter_otheropt = 45,
+       dns_nsstatscounter_ecsopt = 46,
 
-#ifdef USE_RRL
-       dns_nsstatscounter_ratedropped = 37,
-       dns_nsstatscounter_rateslipped = 38,
+       dns_nsstatscounter_sitopt = 47,
+       dns_nsstatscounter_sitbadsize = 48,
+       dns_nsstatscounter_sitbadtime = 49,
+       dns_nsstatscounter_sitnomatch = 50,
+       dns_nsstatscounter_sitmatch = 51,
+       dns_nsstatscounter_sitnew = 52,
 
-       dns_nsstatscounter_max = 39
-#else /* USE_RRL */
-       dns_nsstatscounter_max = 37
-#endif /* USE_RRL */
+       dns_nsstatscounter_max = 53
 };
 
 void
index eb02aef08f54cf15532d05e3757300f6c5640255..ed301afe5fcfa698e6e1fa6047e0e1ae9fd385a7 100644 (file)
@@ -195,6 +195,8 @@ struct dumpcontext {
        isc_mem_t                       *mctx;
        isc_boolean_t                   dumpcache;
        isc_boolean_t                   dumpzones;
+       isc_boolean_t                   dumpadb;
+       isc_boolean_t                   dumpbad;
        FILE                            *fp;
        ISC_LIST(struct viewlistentry)  viewlist;
        struct viewlistentry            *view;
@@ -2093,6 +2095,9 @@ configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
        char **dlzargv;
        const cfg_obj_t *disabled;
        const cfg_obj_t *obj;
+#ifdef ENABLE_FETCHLIMIT
+       const cfg_obj_t *obj2;
+#endif /* ENABLE_FETCHLIMIT */
        const cfg_listelt_t *element;
        in_port_t port;
        dns_cache_t *cache = NULL;
@@ -2749,6 +2754,55 @@ configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
        }
        dns_adb_setadbsize(view->adb, max_adb_size);
 
+#ifdef ENABLE_FETCHLIMIT
+       /*
+        * Set up ADB quotas
+        */
+       {
+               isc_uint32_t fps, freq;
+               double low, high, discount;
+
+               obj = NULL;
+               result = ns_config_get(maps, "fetches-per-server", &obj);
+               INSIST(result == ISC_R_SUCCESS);
+               obj2 = cfg_tuple_get(obj, "fetches");
+               fps = cfg_obj_asuint32(obj2);
+               obj2 = cfg_tuple_get(obj, "response");
+               if (!cfg_obj_isvoid(obj2)) {
+                       const char *resp = cfg_obj_asstring(obj2);
+                       isc_result_t r;
+
+                       if (strcasecmp(resp, "drop") == 0)
+                               r = DNS_R_DROP;
+                       else if (strcasecmp(resp, "fail") == 0)
+                               r = DNS_R_SERVFAIL;
+                       else
+                               INSIST(0);
+
+                       dns_resolver_setquotaresponse(view->resolver,
+                                                     dns_quotatype_server, r);
+               }
+
+               obj = NULL;
+               result = ns_config_get(maps, "fetch-quota-params", &obj);
+               INSIST(result == ISC_R_SUCCESS);
+
+               obj2 = cfg_tuple_get(obj, "frequency");
+               freq = cfg_obj_asuint32(obj2);
+
+               obj2 = cfg_tuple_get(obj, "low");
+               low = (double) cfg_obj_asfixedpoint(obj2) / 100.0; 
+
+               obj2 = cfg_tuple_get(obj, "high");
+               high = (double) cfg_obj_asfixedpoint(obj2) / 100.0;
+
+               obj2 = cfg_tuple_get(obj, "discount");
+               discount = (double) cfg_obj_asfixedpoint(obj2) / 100.0;
+
+               dns_adb_setquota(view->adb, fps, freq, low, high, discount);
+       }
+#endif /* ENABLE_FETCHLIMIT */
+
        /*
         * Set resolver's lame-ttl.
         */
@@ -3176,6 +3230,29 @@ configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
        INSIST(result == ISC_R_SUCCESS);
        dns_resolver_setmaxqueries(view->resolver, cfg_obj_asuint32(obj));
 
+#ifdef ENABLE_FETCHLIMIT
+       obj = NULL;
+       result = ns_config_get(maps, "fetches-per-zone", &obj);
+       INSIST(result == ISC_R_SUCCESS);
+       obj2 = cfg_tuple_get(obj, "fetches");
+       dns_resolver_setfetchesperzone(view->resolver, cfg_obj_asuint32(obj2));
+       obj2 = cfg_tuple_get(obj, "response");
+       if (!cfg_obj_isvoid(obj2)) {
+               const char *resp = cfg_obj_asstring(obj2);
+               isc_result_t r;
+
+               if (strcasecmp(resp, "drop") == 0)
+                       r = DNS_R_DROP;
+               else if (strcasecmp(resp, "fail") == 0)
+                       r = DNS_R_SERVFAIL;
+               else
+                       INSIST(0);
+
+               dns_resolver_setquotaresponse(view->resolver,
+                                             dns_quotatype_zone, r);
+       }
+#endif /* ENABLE_FETCHLIMIT */
+
 #ifdef ALLOW_FILTER_AAAA_ON_V4
        obj = NULL;
        result = ns_config_get(maps, "filter-aaaa-on-v4", &obj);
@@ -4929,6 +5006,9 @@ load_configuration(const char *filename, ns_server_t *server,
        ns_cachelist_t cachelist, tmpcachelist;
        struct cfg_context *nzctx;
        unsigned int maxsocks;
+#ifdef ENABLE_FETCHLIMIT
+       isc_uint32_t softquota = 0;
+#endif /* ENABLE_FETCHLIMIT */
 
        ISC_LIST_INIT(viewlist);
        ISC_LIST_INIT(builtin_viewlist);
@@ -5090,11 +5170,30 @@ load_configuration(const char *filename, ns_server_t *server,
        configure_server_quota(maps, "tcp-clients", &server->tcpquota);
        configure_server_quota(maps, "recursive-clients",
                               &server->recursionquota);
-       if (server->recursionquota.max > 1000)
+
+#ifdef ENABLE_FETCHLIMIT
+       if (server->recursionquota.max > 1000) {
+               int margin = ISC_MAX(100, ns_g_cpus + 1);
+               if (margin > server->recursionquota.max - 100) {
+                       isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+                                     NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+                                     "'recursive-clients %d' too low when "
+                                     "running with %d worker threads", 
+                                     server->recursionquota.max, ns_g_cpus);
+                       CHECK(ISC_R_RANGE);
+               }
+               softquota = server->recursionquota.max - margin;
+       } else
+               softquota = (server->recursionquota.max * 90) / 100;
+
+       isc_quota_soft(&server->recursionquota, softquota);
+#else
+       if (server->recursionquota.max > 1000) {
                isc_quota_soft(&server->recursionquota,
                               server->recursionquota.max - 100);
-       else
+       else
                isc_quota_soft(&server->recursionquota, 0);
+#endif /* !ENABLE_FETCHLIMIT */
 
        CHECK(configure_view_acl(NULL, config, "blackhole", NULL,
                                 ns_g_aclconfctx, ns_g_mctx,
@@ -7064,10 +7163,17 @@ dumpdone(void *arg, isc_result_t result) {
                                goto cleanup;
                }
        }
+
+       if ((dctx->dumpadb || dctx->dumpbad) &&
+           dctx->cache == NULL && dctx->view->view->cachedb != NULL)
+               dns_db_attach(dctx->view->view->cachedb, &dctx->cache);
+
        if (dctx->cache != NULL) {
-               dns_adb_dump(dctx->view->view->adb, dctx->fp);
-               dns_resolver_printbadcache(dctx->view->view->resolver,
-                                          dctx->fp);
+               if (dctx->dumpadb)
+                       dns_adb_dump(dctx->view->view->adb, dctx->fp);
+               if (dctx->dumpbad)
+                       dns_resolver_printbadcache(dctx->view->view->resolver,
+                                                  dctx->fp);
                dns_db_detach(&dctx->cache);
        }
        if (dctx->dumpzones) {
@@ -7151,6 +7257,8 @@ ns_server_dumpdb(ns_server_t *server, char *args) {
 
        dctx->mctx = server->mctx;
        dctx->dumpcache = ISC_TRUE;
+       dctx->dumpadb = ISC_TRUE;
+       dctx->dumpbad = ISC_TRUE;
        dctx->dumpzones = ISC_FALSE;
        dctx->fp = NULL;
        ISC_LIST_INIT(dctx->viewlist);
@@ -7174,17 +7282,31 @@ ns_server_dumpdb(ns_server_t *server, char *args) {
 
        ptr = next_token(&args, " \t");
        if (ptr != NULL && strcmp(ptr, "-all") == 0) {
+               /* also dump zones */
                dctx->dumpzones = ISC_TRUE;
-               dctx->dumpcache = ISC_TRUE;
                ptr = next_token(&args, " \t");
        } else if (ptr != NULL && strcmp(ptr, "-cache") == 0) {
-               dctx->dumpzones = ISC_FALSE;
-               dctx->dumpcache = ISC_TRUE;
+               /* this is the default */
                ptr = next_token(&args, " \t");
        } else if (ptr != NULL && strcmp(ptr, "-zones") == 0) {
+               /* only dump zones, suppress caches */
+               dctx->dumpadb = ISC_FALSE;
+               dctx->dumpbad = ISC_FALSE;
+               dctx->dumpcache = ISC_FALSE;
                dctx->dumpzones = ISC_TRUE;
+               ptr = next_token(&args, " \t");
+#ifdef ENABLE_FETCHLIMIT
+       } else if (ptr != NULL && strcmp(ptr, "-adb") == 0) {
+               /* only dump adb, suppress other caches */
+               dctx->dumpbad = ISC_FALSE;
+               dctx->dumpcache = ISC_FALSE;
+               ptr = next_token(&args, " \t");
+       } else if (ptr != NULL && strcmp(ptr, "-bad") == 0) {
+               /* only dump badcache, suppress other caches */
+               dctx->dumpadb = ISC_FALSE;
                dctx->dumpcache = ISC_FALSE;
                ptr = next_token(&args, " \t");
+#endif /* ENABLE_FETCHLIMIT */
        }
 
  nextview:
@@ -7278,11 +7400,26 @@ isc_result_t
 ns_server_dumprecursing(ns_server_t *server) {
        FILE *fp = NULL;
        isc_result_t result;
+#ifdef ENABLE_FETCHLIMIT
+       dns_view_t *view;
+#endif /* ENABLE_FETCHLIMIT */
 
        CHECKMF(isc_stdio_open(server->recfile, "w", &fp),
                "could not open dump file", server->recfile);
-       fprintf(fp,";\n; Recursing Queries\n;\n");
+       fprintf(fp, ";\n; Recursing Queries\n;\n");
        ns_interfacemgr_dumprecursing(fp, server->interfacemgr);
+
+#ifdef ENABLE_FETCHLIMIT
+       for (view = ISC_LIST_HEAD(server->viewlist);
+            view != NULL;
+            view = ISC_LIST_NEXT(view, link))
+       {
+               fprintf(fp, ";\n; Active fetch domains [view: %s]\n;\n",
+                       view->name);
+               dns_resolver_dumpfetches(view->resolver, fp);
+       }
+#endif /* ENABLE_FETCHLIMIT */
+
        fprintf(fp, "; Dump complete\n");
 
  cleanup:
index 8d1a3e0cf0e158be51d33e9b1b1e4e8edddc3074..4bfd52176a9ae217754b4cb77f2a29a7acec9f77 100644 (file)
@@ -14,8 +14,6 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: statschannel.c,v 1.28 2011/03/12 04:59:46 tbox Exp $ */
-
 /*! \file */
 
 #include <config.h>
@@ -123,6 +121,8 @@ set_desc(int counter, int maxcounter, const char *fdesc, const char **fdescs,
         const char *xdesc, const char **xdescs)
 {
        REQUIRE(counter < maxcounter);
+if (fdescs[counter] != NULL)
+fprintf(stderr, "fdescs[%d] == %s\n", counter, fdescs[counter]);
        REQUIRE(fdescs[counter] == NULL);
 #ifdef HAVE_LIBXML2
        REQUIRE(xdescs[counter] == NULL);
@@ -168,7 +168,7 @@ init_desc(void) {
        SET_NSSTATDESC(sig0in, "requests with SIG(0) received", "ReqSIG0");
        SET_NSSTATDESC(invalidsig, "requests with invalid signature",
                       "ReqBadSIG");
-       SET_NSSTATDESC(tcp, "TCP requests received", "ReqTCP");
+       SET_NSSTATDESC(requesttcp, "TCP requests received", "ReqTCP");
        SET_NSSTATDESC(authrej, "auth queries rejected", "AuthQryRej");
        SET_NSSTATDESC(recurserej, "recursive queries rejected", "RecQryRej");
        SET_NSSTATDESC(xfrrej, "transfer requests rejected", "XfrRej");
@@ -207,14 +207,32 @@ init_desc(void) {
        SET_NSSTATDESC(updatebadprereq,
                       "updates rejected due to prerequisite failure",
                       "UpdateBadPrereq");
-       SET_NSSTATDESC(rpz_rewrites, "response policy zone rewrites",
-                      "RPZRewrites");
-#ifdef USE_RRL
+       SET_NSSTATDESC(recursclients, "recursing clients",
+                       "RecursClients");
+       SET_NSSTATDESC(dns64, "queries answered by DNS64", "DNS64");
        SET_NSSTATDESC(ratedropped, "responses dropped for rate limits",
                       "RateDropped");
        SET_NSSTATDESC(rateslipped, "responses truncated for rate limits",
                       "RateSlipped");
-#endif /* USE_RRL */
+       SET_NSSTATDESC(rpz_rewrites, "response policy zone rewrites",
+                      "RPZRewrites");
+       SET_NSSTATDESC(udp, "UDP queries received", "QryUDP");
+       SET_NSSTATDESC(tcp, "TCP queries received", "QryTCP");
+       SET_NSSTATDESC(nsidopt, "NSID option received", "NSIDOpt");
+       SET_NSSTATDESC(expireopt, "Expire option received", "ExpireOpt");
+       SET_NSSTATDESC(otheropt, "Other EDNS option received", "OtherOpt");
+       SET_NSSTATDESC(sitopt, "source identity token option received",
+                      "SitOpt");
+       SET_NSSTATDESC(sitnew, "new source identity token requested",
+                      "SitNew");
+       SET_NSSTATDESC(sitbadsize, "source identity token - bad size",
+                      "SitBadSize");
+       SET_NSSTATDESC(sitbadtime, "source identity token - bad time",
+                      "SitBadTime");
+       SET_NSSTATDESC(sitnomatch, "source identity token - no match",
+                      "SitNoMatch");
+       SET_NSSTATDESC(sitmatch, "source identity token - match", "SitMatch");
+       SET_NSSTATDESC(ecsopt, "EDNS client subnet option recieved", "ECSOpt");
        INSIST(i == dns_nsstatscounter_max);
 
        /* Initialize resolver statistics */
@@ -285,6 +303,10 @@ init_desc(void) {
        SET_RESSTATDESC(queryrtt5, "queries with RTT > "
                        DNS_RESOLVER_QRYRTTCLASS4STR "ms",
                        "QryRTT" DNS_RESOLVER_QRYRTTCLASS4STR "+");
+       SET_RESSTATDESC(zonequota, "spilled due to zone quota", "ZoneQuota");
+       SET_RESSTATDESC(serverquota, "spilled due to server quota",
+                       "ServerQuota");
+
        INSIST(i == dns_resstatscounter_max);
 
        /* Initialize zone statistics */
@@ -1055,7 +1077,7 @@ generatexml(ns_server_t *server, int *buflen, xmlChar **buf) {
                        ISC_XMLCHAR "type=\"text/xsl\" href=\"/bind9.ver3.xsl\""));
        TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "statistics"));
        TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "version",
-                                        ISC_XMLCHAR "3.3"));
+                                        ISC_XMLCHAR "3.6"));
 
        /* Set common fields for statistics dump */
        dumparg.type = statsformat_xml;
index 2803c34abd838571f5a7e2145f5e3f873d162c7a..c7d8fe1f84314ae07f903cf0d93ad67ef97863b9 100644 (file)
@@ -104,7 +104,7 @@ command is one of the following:\n\
                Add zone to given view. Requires new-zone-file option.\n\
   delzone zone [class [view]]\n\
                Removes zone from given view. Requires new-zone-file option.\n\
-  dumpdb [-all|-cache|-zones] [view ...]\n\
+  dumpdb [-all|-cache|-zones|-adb|-bad|-fail] [view ...]\n\
                Dump cache(s) to the dump file (named_dump.db).\n\
   flush        Flushes all of the server's caches.\n\
   flush [view] Flushes the server's cache for a view.\n\
index d49d5d88dc5685372cf0de5fff66195d3abfcaa5..141ce5f37ca22190013c84c0085be99efaff76f6 100644 (file)
       </varlistentry>
 
       <varlistentry>
-       <term><userinput>dumpdb <optional>-all|-cache|-zone</optional> <optional><replaceable>view ...</replaceable></optional></userinput></term>
+       <term><userinput>dumpdb <optional>-all|-cache|-zone|-adb|-bad</optional> <optional><replaceable>view ...</replaceable></optional></userinput></term>
        <listitem>
          <para>
            Dump the server's caches (default) and/or zones to
       </varlistentry>
 
       <varlistentry>
-       <term><userinput>recursing</userinput></term>
-       <listitem>
-         <para>
-           Dump the list of queries <command>named</command> is currently
-           recursing on.
-         </para>
-       </listitem>
+        <term><userinput>recursing</userinput></term>
+        <listitem>
+          <para>
+            Dump the list of queries <command>named</command> is currently
+            recursing on, and the list of domains to which iterative
+            queries are currently being sent.  (The second list includes
+            the number of fetches currently active for the given domain,
+            and how many have been passed or dropped because of the
+            <option>fetches-per-zone</option> option.)
+          </para>
+        </listitem>
       </varlistentry>
 
       <varlistentry>
index 7d7ecad6e8783250338bab7bc979e266a3e60160..80971670d483b935bced236ce48e4878ab0e0569 100644 (file)
@@ -19,8 +19,8 @@ top_srcdir =  @top_srcdir@
 
 @BIND9_MAKE_INCLUDES@
 
-SUBDIRS =      builtin dlzexternal filter-aaaa lwresd statistics rpz rrl \
-               rsabigexponent tkey tsiggss
+SUBDIRS =      builtin dlzexternal fetchlimit filter-aaaa lwresd \
+               statistics rpz rrl rsabigexponent tkey tsiggss
 TARGETS =
 
 @BIND9_MAKE_RULES@
index bc8de7c8c87f9ce795946aae853f7c3a0a4fbd9a..30832f1c371ad9b73ba58301c4f3cfab37b3077b 100644 (file)
@@ -65,8 +65,9 @@ RANDFILE=$TOP/bin/tests/system/random.data
 SUBDIRS="acl additional allow_query addzone autosign builtin
         cacheclean case checkconf @CHECKDS@ checknames checkzone
         @COVERAGE@ database dlv dlvauto dlz dlzexternal dname dns64
-        dnssec ecdsa emptyzones filter-aaaa formerr forward glue
-        gost ixfr inline legacy limits logfileconfig lwresd masterfile
+        dnssec ecdsa emptyzones
+         fetchlimit filter-aaaa formerr forward glue gost ixfr inline
+         legacy limits logfileconfig lwresd masterfile
         masterformat metadata notify nslookup nsupdate pending pkcs11
         reclimit redirect resolver rndc rpz rrl rrsetorder rsabigexponent
         smartsign sortlist spf staticstub stub tkey tsig tsiggss
diff --git a/bin/tests/system/ditch.pl b/bin/tests/system/ditch.pl
new file mode 100644 (file)
index 0000000..6c48d82
--- /dev/null
@@ -0,0 +1,90 @@
+#!/usr/bin/perl
+#
+# Copyright (C) 2011, 2012  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.
+
+# This is a tool for sending queries via UDP to specified address and
+# port, then exiting without waiting for a response.
+#
+# Usage: ditch.pl [-s <address>] [-p <port>] [filename]
+#
+# Input (in filename, if specified, otherwise stdin) is a series of one
+# or more DNS names and types to send as queries, e.g.:
+#
+# www.example.com A
+# www.example.org MX
+#
+# If not specified, address defaults to 127.0.0.1, port to 53.
+
+require 5.006.001;
+
+use strict;
+use Getopt::Std;
+use Net::DNS;
+use Net::DNS::Packet;
+use IO::File;
+use IO::Socket;
+
+sub usage {
+    print ("Usage: ditch.pl [-s address] [-p port] [file]\n");
+    exit 1;
+}
+
+my %options={};
+getopts("s:p:t:", \%options);
+
+my $addr = "127.0.0.1";
+$addr = $options{s} if defined $options{s};
+
+my $port = 53;
+$port = $options{p} if defined $options{p};
+
+my $file = "STDIN";
+if (@ARGV >= 1) {
+    my $filename = shift @ARGV;
+    open FH, "<$filename" or die "$filename: $!";
+    $file = "FH";
+}
+
+my $input = "";
+while (defined(my $line = <$file>) ) {
+    chomp $line;
+    next if ($line =~ m/^ *#/);
+    my @tokens = split (' ', $line);
+
+    my $packet;
+    if ($Net::DNS::VERSION > 0.68) {
+            $packet = new Net::DNS::Packet();
+            $@ and die $@;
+    } else {
+            my $err;
+            ($packet, $err) = new Net::DNS::Packet();
+            $err and die $err;
+    }
+
+    my $q = new Net::DNS::Question($tokens[0], $tokens[1], "IN");
+    $packet->header->rd(1);
+    $packet->push(question => $q);
+
+    my $sock = IO::Socket::INET->new(PeerAddr => $addr, PeerPort => $port,
+                                     Proto => "udp",) or die "$!";
+
+    my $bytes = $sock->send($packet->data);
+    #print ("sent $bytes bytes to $addr:$port:\n");
+    #print ("  ", unpack("H* ", $packet->data), "\n");
+
+    $sock->close;
+}
+
+close $file;
index f1dbfc954e0d5c2470965fcddae21f3fa4cce4f9..8d8d8141d97be5ba1bb0cd49d6a482d6d2c92687 100644 (file)
@@ -26,6 +26,7 @@
 #include <stdarg.h>
 
 #include <isc/log.h>
+#include <isc/print.h>
 #include <isc/result.h>
 #include <isc/string.h>
 #include <isc/types.h>
diff --git a/bin/tests/system/fetchlimit/Makefile.in b/bin/tests/system/fetchlimit/Makefile.in
new file mode 100644 (file)
index 0000000..dd6912d
--- /dev/null
@@ -0,0 +1,53 @@
+# Copyright (C) 2010-2012  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.
+
+srcdir =       @srcdir@
+VPATH =                @srcdir@
+top_srcdir =   @top_srcdir@
+
+@BIND9_VERSION@
+
+@BIND9_MAKE_INCLUDES@
+
+CINCLUDES =    ${ISC_INCLUDES}
+
+CDEFINES =
+CWARNINGS =
+
+DNSLIBS =      
+ISCLIBS =      .
+
+DNSDEPLIBS =
+ISCDEPLIBS =
+
+DEPLIBS =
+
+LIBS =         @LIBS@
+
+TARGETS =      fetchlimit@EXEEXT@
+
+FILTEROBJS =   fetchlimit.@O@
+
+SRCS =         fetchlimit.c
+
+@BIND9_MAKE_RULES@
+
+all: fetchlimit@EXEEXT@
+
+fetchlimit@EXEEXT@: ${FILTEROBJS}
+       ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ ${FILTEROBJS} ${LIBS}
+
+clean distclean::
+       rm -f ${TARGETS}
+
diff --git a/bin/tests/system/fetchlimit/ans4/ans.pl b/bin/tests/system/fetchlimit/ans4/ans.pl
new file mode 100644 (file)
index 0000000..d844502
--- /dev/null
@@ -0,0 +1,86 @@
+#!/usr/bin/perl -w
+#
+# 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.
+
+#
+# Don't respond if the "norespond" file exists; otherwise respond to
+# any A or AAAA query.
+#
+
+use IO::File;
+use IO::Socket;
+use Net::DNS;
+use Net::DNS::Packet;
+
+my $sock = IO::Socket::INET->new(LocalAddr => "10.53.0.4",
+   LocalPort => 5300, Proto => "udp") or die "$!";
+
+my $pidf = new IO::File "ans.pid", "w" or die "cannot open pid file: $!";
+print $pidf "$$\n" or die "cannot write pid file: $!";
+$pidf->close or die "cannot close pid file: $!";
+sub rmpid { unlink "ans.pid"; exit 1; };
+
+$SIG{INT} = \&rmpid;
+$SIG{TERM} = \&rmpid;
+
+for (;;) {
+       $sock->recv($buf, 512);
+
+       print "**** request from " , $sock->peerhost, " port ", $sock->peerport, "\n";
+
+       my $packet;
+
+       if ($Net::DNS::VERSION > 0.68) {
+               $packet = new Net::DNS::Packet(\$buf, 0);
+               $@ and die $@;
+       } else {
+               my $err;
+               ($packet, $err) = new Net::DNS::Packet(\$buf, 0);
+               $err and die $err;
+       }
+
+       print "REQUEST:\n";
+       $packet->print;
+
+       $packet->header->qr(1);
+
+       my @questions = $packet->question;
+       my $qname = $questions[0]->qname;
+       my $qtype = $questions[0]->qtype;
+
+       my $donotrespond = 0;
+
+       if (-e 'norespond') {
+               $donotrespond = 1;
+       } else {
+               $packet->header->aa(1);
+               if ($qtype eq "A") {
+                       $packet->push("answer",
+                                     new Net::DNS::RR($qname .
+                                                      " 300 A 192.0.2.1"));
+               } elsif ($qtype eq "AAAA") {
+                       $packet->push("answer",
+                                     new Net::DNS::RR($qname .
+                                               " 300 AAAA 2001:db8:beef::1"));
+               }
+       }
+
+       if ($donotrespond == 0) {
+               $sock->send($packet->data);
+               print "RESPONSE:\n";
+               $packet->print;
+               print "\n";
+       }
+}
diff --git a/bin/tests/system/fetchlimit/clean.sh b/bin/tests/system/fetchlimit/clean.sh
new file mode 100644 (file)
index 0000000..4976b3a
--- /dev/null
@@ -0,0 +1,21 @@
+#!/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.
+
+rm -f */named.memstats */ans.run */named.recursing
+rm -f dig.out*
+rm -f ans4/norespond
+rm -f ns3/named.conf ns3/named.stats ns3/named_dump.db
+rm -f burst.input.*
diff --git a/bin/tests/system/fetchlimit/fetchlimit.c b/bin/tests/system/fetchlimit/fetchlimit.c
new file mode 100644 (file)
index 0000000..de30836
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015  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.
+ */
+
+#include <config.h>
+#include <isc/util.h>
+
+int
+main(int argc, char **argv) {
+
+       UNUSED(argc);
+       UNUSED(argv);
+
+#ifdef ENABLE_FETCHLIMIT
+       return (0);
+#else
+       return (1);
+#endif
+}
diff --git a/bin/tests/system/fetchlimit/lameserver/ans4/ans.pl b/bin/tests/system/fetchlimit/lameserver/ans4/ans.pl
new file mode 100644 (file)
index 0000000..d844502
--- /dev/null
@@ -0,0 +1,86 @@
+#!/usr/bin/perl -w
+#
+# 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.
+
+#
+# Don't respond if the "norespond" file exists; otherwise respond to
+# any A or AAAA query.
+#
+
+use IO::File;
+use IO::Socket;
+use Net::DNS;
+use Net::DNS::Packet;
+
+my $sock = IO::Socket::INET->new(LocalAddr => "10.53.0.4",
+   LocalPort => 5300, Proto => "udp") or die "$!";
+
+my $pidf = new IO::File "ans.pid", "w" or die "cannot open pid file: $!";
+print $pidf "$$\n" or die "cannot write pid file: $!";
+$pidf->close or die "cannot close pid file: $!";
+sub rmpid { unlink "ans.pid"; exit 1; };
+
+$SIG{INT} = \&rmpid;
+$SIG{TERM} = \&rmpid;
+
+for (;;) {
+       $sock->recv($buf, 512);
+
+       print "**** request from " , $sock->peerhost, " port ", $sock->peerport, "\n";
+
+       my $packet;
+
+       if ($Net::DNS::VERSION > 0.68) {
+               $packet = new Net::DNS::Packet(\$buf, 0);
+               $@ and die $@;
+       } else {
+               my $err;
+               ($packet, $err) = new Net::DNS::Packet(\$buf, 0);
+               $err and die $err;
+       }
+
+       print "REQUEST:\n";
+       $packet->print;
+
+       $packet->header->qr(1);
+
+       my @questions = $packet->question;
+       my $qname = $questions[0]->qname;
+       my $qtype = $questions[0]->qtype;
+
+       my $donotrespond = 0;
+
+       if (-e 'norespond') {
+               $donotrespond = 1;
+       } else {
+               $packet->header->aa(1);
+               if ($qtype eq "A") {
+                       $packet->push("answer",
+                                     new Net::DNS::RR($qname .
+                                                      " 300 A 192.0.2.1"));
+               } elsif ($qtype eq "AAAA") {
+                       $packet->push("answer",
+                                     new Net::DNS::RR($qname .
+                                               " 300 AAAA 2001:db8:beef::1"));
+               }
+       }
+
+       if ($donotrespond == 0) {
+               $sock->send($packet->data);
+               print "RESPONSE:\n";
+               $packet->print;
+               print "\n";
+       }
+}
diff --git a/bin/tests/system/fetchlimit/lameserver/clean.sh b/bin/tests/system/fetchlimit/lameserver/clean.sh
new file mode 100644 (file)
index 0000000..d1c822f
--- /dev/null
@@ -0,0 +1,20 @@
+#!/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.
+
+rm -f */named.memstats */ans.run */named.recursing
+rm -f dig.out*
+rm -f ans4/norespond
+rm -f ns3/named.conf ns3/named.stats ns3/named_dump.db
diff --git a/bin/tests/system/fetchlimit/lameserver/ns1/named.conf b/bin/tests/system/fetchlimit/lameserver/ns1/named.conf
new file mode 100644 (file)
index 0000000..4354b46
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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;
+       notify yes;
+};
+
+zone "." {
+       type master;
+       file "root.db";
+};
+
+zone "example.info." {
+       type master;
+       file "example-info.db";
+};
diff --git a/bin/tests/system/fetchlimit/lameserver/ns1/root.db b/bin/tests/system/fetchlimit/lameserver/ns1/root.db
new file mode 100644 (file)
index 0000000..cd74861
--- /dev/null
@@ -0,0 +1,29 @@
+; 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.
+
+; $Id$
+
+$TTL 300
+.                      IN SOA  gson.nominum.com. a.root.servers.nil. (
+                               2000042100      ; serial
+                               600             ; refresh
+                               600             ; retry
+                               1200            ; expire
+                               600             ; minimum
+                               )
+.                      NS      a.root-servers.nil.
+a.root-servers.nil.    A       10.53.0.1
+
+example.               NS      ns2.example.
+ns2.example.           A       10.53.0.2
diff --git a/bin/tests/system/fetchlimit/lameserver/ns2/example.db b/bin/tests/system/fetchlimit/lameserver/ns2/example.db
new file mode 100644 (file)
index 0000000..1281554
--- /dev/null
@@ -0,0 +1,40 @@
+; 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.
+
+$ORIGIN .
+$TTL 300       ; 5 minutes
+example                        IN SOA  mname1. . (
+                               1          ; serial
+                               20         ; refresh (20 seconds)
+                               20         ; retry (20 seconds)
+                               1814400    ; expire (3 weeks)
+                               3600       ; minimum (1 hour)
+                               )
+example                        NS      ns2.example.
+ns2.example.           A       10.53.0.2
+
+a.example.             A       10.0.0.1
+                       MX      10 mail.example.
+
+mail.example.          A       10.0.0.2
+
+lamesub.example.        NS     ns4.example.
+ns4.example.           A       10.53.0.4
+
+0.example.              A       10.53.1.0
+1.example.              A       10.53.1.1
+2.example.              A       10.53.1.2
+3.example.              A       10.53.1.3
+4.example.              A       10.53.1.4
+5.example.              A       10.53.1.5
diff --git a/bin/tests/system/fetchlimit/lameserver/ns2/named.conf b/bin/tests/system/fetchlimit/lameserver/ns2/named.conf
new file mode 100644 (file)
index 0000000..b276181
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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;
+       notify yes;
+};
+
+include "../../common/controls.conf";
+
+zone "example" {
+       type master;
+       file "example.db";
+       allow-update { any; };
+};
diff --git a/bin/tests/system/fetchlimit/lameserver/ns3/named1.conf b/bin/tests/system/fetchlimit/lameserver/ns3/named1.conf
new file mode 100644 (file)
index 0000000..e4a5039
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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;
+       directory ".";
+       pid-file "named.pid";
+       listen-on { 10.53.0.3; };
+       listen-on-v6 { none; };
+       recursion yes;
+       notify yes;
+       fetches-per-server 400;
+};
+
+key rndc_key {
+       secret "1234abcd8765";
+       algorithm hmac-sha256;
+};
+
+controls {
+       inet 10.53.0.3 port 9953 allow { any; } keys { rndc_key; };
+};
+
+zone "." {
+       type hint;
+       file "root.hint";
+};
diff --git a/bin/tests/system/fetchlimit/lameserver/ns3/named2.conf b/bin/tests/system/fetchlimit/lameserver/ns3/named2.conf
new file mode 100644 (file)
index 0000000..2a40512
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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;
+       directory ".";
+       pid-file "named.pid";
+       listen-on { 10.53.0.3; };
+       listen-on-v6 { none; };
+       recursion yes;
+       notify yes;
+       fetches-per-zone 40;
+};
+
+key rndc_key {
+       secret "1234abcd8765";
+       algorithm hmac-sha256;
+};
+
+controls {
+       inet 10.53.0.3 port 9953 allow { any; } keys { rndc_key; };
+};
+
+zone "." {
+       type hint;
+       file "root.hint";
+};
diff --git a/bin/tests/system/fetchlimit/lameserver/ns3/named3.conf b/bin/tests/system/fetchlimit/lameserver/ns3/named3.conf
new file mode 100644 (file)
index 0000000..e017e42
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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;
+       directory ".";
+       pid-file "named.pid";
+       listen-on { 10.53.0.3; };
+       listen-on-v6 { none; };
+       recursion yes;
+       notify yes;
+       recursive-clients 400;
+};
+
+key rndc_key {
+       secret "1234abcd8765";
+       algorithm hmac-sha256;
+};
+
+controls {
+       inet 10.53.0.3 port 9953 allow { any; } keys { rndc_key; };
+};
+
+zone "." {
+       type hint;
+       file "root.hint";
+};
diff --git a/bin/tests/system/fetchlimit/lameserver/ns3/root.hint b/bin/tests/system/fetchlimit/lameserver/ns3/root.hint
new file mode 100644 (file)
index 0000000..2b36926
--- /dev/null
@@ -0,0 +1,19 @@
+; 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.
+
+; $Id$
+
+$TTL 999999
+.                       IN NS  a.root-servers.nil.
+a.root-servers.nil.     IN A   10.53.0.1
diff --git a/bin/tests/system/fetchlimit/lameserver/setup.sh b/bin/tests/system/fetchlimit/lameserver/setup.sh
new file mode 100644 (file)
index 0000000..8977220
--- /dev/null
@@ -0,0 +1,22 @@
+#!/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
+
+$SHELL clean.sh 
+
+cp -f ns3/named1.conf ns3/named.conf
diff --git a/bin/tests/system/fetchlimit/lameserver/tests.sh b/bin/tests/system/fetchlimit/lameserver/tests.sh
new file mode 100644 (file)
index 0000000..e6e53be
--- /dev/null
@@ -0,0 +1,183 @@
+#!/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
+
+DIGCMD="$DIG @10.53.0.3 -p 5300 +tries=1 +time=1"
+RNDCCMD="$RNDC -p 9953 -s 10.53.0.3 -c ../common/rndc.conf"
+
+burst() {
+    num=${3:-20}
+    while [ $num -gt 0 ]; do
+        num=`expr $num - 1`
+        $DIGCMD ${num}${1}${2}.lamesub.example a > /dev/null 2>&1 &
+    done
+}
+
+stat() {
+    clients=`$RNDCCMD status | grep "recursive clients" |
+            sed 's;.*: \([^/][^/]*\)/.*;\1;'`
+    echo "I: clients: $clients"
+    [ "$clients" = "" ] && return 1
+    [ "$clients" -le $1 ]
+}
+
+status=0
+
+echo "I: checking recursing clients are dropped at the per-server limit"
+ret=0
+# make the server lame and restart
+$RNDCCMD flush
+touch ans4/norespond
+for try in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20; do
+    burst a $try
+    # fetches-per-server is at 400, but at 20qps against a lame server,
+    # we'll reach 200 at the tenth second, and the quota should have been
+    # tuned to less than that by then
+    stat 200 || ret=1
+    [ $ret -eq 1 ] && break
+    sleep 1
+done
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+echo "I: dumping ADB data"
+$RNDCCMD dumpdb -adb
+info=`grep '10.53.0.4' ns3/named_dump.db | sed 's/.*\(atr [.0-9]*\).*\(quota [0-9]*\).*/I: \1 \2/'`
+echo $info
+set -- $info
+quota=$5
+[ $5 -lt 200 ] || ret=1
+
+echo "I: checking servfail statistics"
+rm -f ns3/named.stats
+$RNDCCMD stats
+for try in 1 2 3 4 5; do
+    [ -f ns3/named.stats ] && break
+    sleep 1
+done
+sspill=`grep 'spilled due to server' ns3/named.stats | sed 's/\([0-9][0-9]*\) spilled.*/\1/'`
+[ -z "$sspill" ] && sspill=0
+fails=`grep 'queries resulted in SERVFAIL' ns3/named.stats | sed 's/\([0-9][0-9]*\) queries.*/\1/'`
+[ -z "$fails" ] && fails=0
+[ "$fails" -ge "$sspill" ] || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+echo "I: checking lame server recovery"
+ret=0
+rm -f ans4/norespond
+for try in 1 2 3 4 5; do
+    burst b $try
+    stat 200 || ret=1
+    [ $ret -eq 1 ] && break
+    sleep 1
+done
+
+echo "I: dumping ADB data"
+$RNDCCMD dumpdb -adb
+info=`grep '10.53.0.4' ns3/named_dump.db | sed 's/.*\(atr [.0-9]*\).*\(quota [0-9]*\).*/I: \1 \2/'`
+echo $info
+set -- $info
+[ $5 -lt $quota ] || ret=1
+quota=$5
+
+for try in 1 2 3 4 5 6 7 8 9 10; do
+    burst c $try
+    stat 20 || ret=1
+    [ $ret -eq 1 ] && break
+    sleep 1
+done
+
+echo "I: dumping ADB data"
+$RNDCCMD dumpdb -adb
+info=`grep '10.53.0.4' ns3/named_dump.db | sed 's/.*\(atr [.0-9]*\).*\(quota [0-9]*\).*/I: \1 \2/'`
+echo $info
+set -- $info
+[ $5 -gt $quota ] || ret=1
+quota=$5
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+cp -f ns3/named2.conf ns3/named.conf
+$RNDCCMD reconfig 2>&1 | sed 's/^/I:ns3 /'
+
+echo "I: checking lame server clients are dropped at the per-domain limit"
+ret=0
+fail=0
+success=0
+touch ans4/norespond
+for try in 1 2 3 4 5; do
+    burst b $try 300
+    $DIGCMD a ${try}.example > dig.out.ns3.$try
+    grep "status: NOERROR" dig.out.ns3.$try > /dev/null 2>&1 && \
+            success=`expr $success + 1`
+    grep "status: SERVFAIL" dig.out.ns3.$try > /dev/null 2>&1 && \
+            fail=`expr $fail + 1`
+    stat 50 || ret=1
+    [ $ret -eq 1 ] && break
+    $RNDCCMD recursing 2>&1 | sed 's/^/I:ns3 /'
+    sleep 1
+done
+echo "I: $success successful valid queries, $fail SERVFAIL"
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+echo "I: checking drop statsistics"
+rm -f ns3/named.stats
+$RNDCCMD stats
+for try in 1 2 3 4 5; do
+    [ -f ns3/named.stats ] && break
+    sleep 1
+done
+zspill=`grep 'spilled due to zone' ns3/named.stats | sed 's/\([0-9][0-9]*\) spilled.*/\1/'`
+[ -z "$zspill" ] && zspill=0
+drops=`grep 'queries dropped' ns3/named.stats | sed 's/\([0-9][0-9]*\) queries.*/\1/'`
+[ -z "$drops" ] && drops=0
+[ "$drops" -ge "$zspill" ] || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+cp -f ns3/named3.conf ns3/named.conf
+$RNDCCMD reconfig 2>&1 | sed 's/^/I:ns3 /'
+
+echo "I: checking lame server clients are dropped at the soft limit"
+ret=0
+fail=0
+success=0
+touch ans4/norespond
+for try in 1 2 3 4 5; do
+    burst b $try 400
+    $DIGCMD a ${try}.example > dig.out.ns3.$try
+    stat 360 || ret=1
+    grep "status: NOERROR" dig.out.ns3.$try > /dev/null 2>&1 && \
+            success=`expr $success + 1`
+    grep "status: SERVFAIL" dig.out.ns3.$try > /dev/null 2>&1 && \
+            fail=`expr $fail + 1`
+    [ $ret -eq 1 ] && break
+    sleep 1
+done
+echo "I: $success successful valid queries, $fail SERVFAIL"
+[ "$success" -eq 5 ] || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+echo "I:exit status: $status"
+exit $status
diff --git a/bin/tests/system/fetchlimit/ns1/named.conf b/bin/tests/system/fetchlimit/ns1/named.conf
new file mode 100644 (file)
index 0000000..4354b46
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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;
+       notify yes;
+};
+
+zone "." {
+       type master;
+       file "root.db";
+};
+
+zone "example.info." {
+       type master;
+       file "example-info.db";
+};
diff --git a/bin/tests/system/fetchlimit/ns1/root.db b/bin/tests/system/fetchlimit/ns1/root.db
new file mode 100644 (file)
index 0000000..cd74861
--- /dev/null
@@ -0,0 +1,29 @@
+; 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.
+
+; $Id$
+
+$TTL 300
+.                      IN SOA  gson.nominum.com. a.root.servers.nil. (
+                               2000042100      ; serial
+                               600             ; refresh
+                               600             ; retry
+                               1200            ; expire
+                               600             ; minimum
+                               )
+.                      NS      a.root-servers.nil.
+a.root-servers.nil.    A       10.53.0.1
+
+example.               NS      ns2.example.
+ns2.example.           A       10.53.0.2
diff --git a/bin/tests/system/fetchlimit/ns2/example.db b/bin/tests/system/fetchlimit/ns2/example.db
new file mode 100644 (file)
index 0000000..1281554
--- /dev/null
@@ -0,0 +1,40 @@
+; 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.
+
+$ORIGIN .
+$TTL 300       ; 5 minutes
+example                        IN SOA  mname1. . (
+                               1          ; serial
+                               20         ; refresh (20 seconds)
+                               20         ; retry (20 seconds)
+                               1814400    ; expire (3 weeks)
+                               3600       ; minimum (1 hour)
+                               )
+example                        NS      ns2.example.
+ns2.example.           A       10.53.0.2
+
+a.example.             A       10.0.0.1
+                       MX      10 mail.example.
+
+mail.example.          A       10.0.0.2
+
+lamesub.example.        NS     ns4.example.
+ns4.example.           A       10.53.0.4
+
+0.example.              A       10.53.1.0
+1.example.              A       10.53.1.1
+2.example.              A       10.53.1.2
+3.example.              A       10.53.1.3
+4.example.              A       10.53.1.4
+5.example.              A       10.53.1.5
diff --git a/bin/tests/system/fetchlimit/ns2/named.conf b/bin/tests/system/fetchlimit/ns2/named.conf
new file mode 100644 (file)
index 0000000..b276181
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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;
+       notify yes;
+};
+
+include "../../common/controls.conf";
+
+zone "example" {
+       type master;
+       file "example.db";
+       allow-update { any; };
+};
diff --git a/bin/tests/system/fetchlimit/ns3/named.args b/bin/tests/system/fetchlimit/ns3/named.args
new file mode 100644 (file)
index 0000000..f34d1b8
--- /dev/null
@@ -0,0 +1,2 @@
+# Don't specify '-T clienttest' as it consumes lots of memory with this test
+-m record,size,mctx -c named.conf -d 99 -g -U 4
diff --git a/bin/tests/system/fetchlimit/ns3/named1.conf b/bin/tests/system/fetchlimit/ns3/named1.conf
new file mode 100644 (file)
index 0000000..e4a5039
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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;
+       directory ".";
+       pid-file "named.pid";
+       listen-on { 10.53.0.3; };
+       listen-on-v6 { none; };
+       recursion yes;
+       notify yes;
+       fetches-per-server 400;
+};
+
+key rndc_key {
+       secret "1234abcd8765";
+       algorithm hmac-sha256;
+};
+
+controls {
+       inet 10.53.0.3 port 9953 allow { any; } keys { rndc_key; };
+};
+
+zone "." {
+       type hint;
+       file "root.hint";
+};
diff --git a/bin/tests/system/fetchlimit/ns3/named2.conf b/bin/tests/system/fetchlimit/ns3/named2.conf
new file mode 100644 (file)
index 0000000..2a40512
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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;
+       directory ".";
+       pid-file "named.pid";
+       listen-on { 10.53.0.3; };
+       listen-on-v6 { none; };
+       recursion yes;
+       notify yes;
+       fetches-per-zone 40;
+};
+
+key rndc_key {
+       secret "1234abcd8765";
+       algorithm hmac-sha256;
+};
+
+controls {
+       inet 10.53.0.3 port 9953 allow { any; } keys { rndc_key; };
+};
+
+zone "." {
+       type hint;
+       file "root.hint";
+};
diff --git a/bin/tests/system/fetchlimit/ns3/named3.conf b/bin/tests/system/fetchlimit/ns3/named3.conf
new file mode 100644 (file)
index 0000000..e017e42
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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;
+       directory ".";
+       pid-file "named.pid";
+       listen-on { 10.53.0.3; };
+       listen-on-v6 { none; };
+       recursion yes;
+       notify yes;
+       recursive-clients 400;
+};
+
+key rndc_key {
+       secret "1234abcd8765";
+       algorithm hmac-sha256;
+};
+
+controls {
+       inet 10.53.0.3 port 9953 allow { any; } keys { rndc_key; };
+};
+
+zone "." {
+       type hint;
+       file "root.hint";
+};
diff --git a/bin/tests/system/fetchlimit/ns3/root.hint b/bin/tests/system/fetchlimit/ns3/root.hint
new file mode 100644 (file)
index 0000000..2b36926
--- /dev/null
@@ -0,0 +1,19 @@
+; 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.
+
+; $Id$
+
+$TTL 999999
+.                       IN NS  a.root-servers.nil.
+a.root-servers.nil.     IN A   10.53.0.1
diff --git a/bin/tests/system/fetchlimit/prereq.sh b/bin/tests/system/fetchlimit/prereq.sh
new file mode 100644 (file)
index 0000000..31ec7a4
--- /dev/null
@@ -0,0 +1,40 @@
+#!/bin/sh
+#
+# Copyright (C) 2015  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
+
+if ./fetchlimit
+then
+    :
+else
+    echo "I:This test requires --enable-fetchlimit at compile time." >&2
+    exit 255
+fi
+
+if $PERL -e 'use Net::DNS;' 2>/dev/null
+then
+    if $PERL -e 'use Net::DNS; die if ($Net::DNS::VERSION >= 0.76 && $Net::DNS::VERSION <= 0.77);' 2>/dev/null
+    then
+       :
+    else
+       echo "I:Net::DNS version 0.76 and 0.77 have a bug that causes this test to fail: please update." >&2
+       exit 1
+    fi
+else
+    echo "I:This test requires the Net::DNS library." >&2
+    exit 1
+fi
diff --git a/bin/tests/system/fetchlimit/setup.sh b/bin/tests/system/fetchlimit/setup.sh
new file mode 100644 (file)
index 0000000..8977220
--- /dev/null
@@ -0,0 +1,22 @@
+#!/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
+
+$SHELL clean.sh 
+
+cp -f ns3/named1.conf ns3/named.conf
diff --git a/bin/tests/system/fetchlimit/tests.sh b/bin/tests/system/fetchlimit/tests.sh
new file mode 100644 (file)
index 0000000..83534e6
--- /dev/null
@@ -0,0 +1,186 @@
+#!/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
+
+DIGCMD="$DIG @10.53.0.3 -p 5300 +tries=1 +time=1"
+RNDCCMD="$RNDC -p 9953 -s 10.53.0.3 -c ../common/rndc.conf"
+
+burst() {
+    num=${3:-20}
+    rm -f burst.input.$$
+    while [ $num -gt 0 ]; do
+        num=`expr $num - 1`
+        echo "${num}${1}${2}.lamesub.example A" >> burst.input.$$
+    done
+    $PERL ../ditch.pl -p 5300 -s 10.53.0.3 burst.input.$$
+    rm -f burst.input.$$
+}
+
+stat() {
+    clients=`$RNDCCMD status | grep "recursive clients" | 
+            sed 's;.*: \([^/][^/]*\)/.*;\1;'`
+    echo "I: clients: $clients"
+    [ "$clients" = "" ] && return 1
+    [ "$clients" -le $1 ]
+}
+
+status=0
+
+echo "I: checking recursing clients are dropped at the per-server limit"
+ret=0
+# make the server lame and restart
+$RNDCCMD flush
+touch ans4/norespond
+for try in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20; do
+    burst a $try
+    # fetches-per-server is at 400, but at 20qps against a lame server,
+    # we'll reach 200 at the tenth second, and the quota should have been
+    # tuned to less than that by then
+    stat 200 || ret=1
+    [ $ret -eq 1 ] && break
+    sleep 1
+done
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+echo "I: dumping ADB data"
+$RNDCCMD dumpdb -adb
+info=`grep '10.53.0.4' ns3/named_dump.db | sed 's/.*\(atr [.0-9]*\).*\(quota [0-9]*\).*/I: \1 \2/'`
+echo $info
+set -- $info
+quota=$5
+[ ${5:-200} -lt 200 ] || ret=1
+
+echo "I: checking servfail statistics"
+rm -f ns3/named.stats
+$RNDCCMD stats
+for try in 1 2 3 4 5; do
+    [ -f ns3/named.stats ] && break
+    sleep 1
+done
+sspill=`grep 'spilled due to server' ns3/named.stats | sed 's/\([0-9][0-9]*\) spilled.*/\1/'`
+[ -z "$sspill" ] && sspill=0
+fails=`grep 'queries resulted in SERVFAIL' ns3/named.stats | sed 's/\([0-9][0-9]*\) queries.*/\1/'`
+[ -z "$fails" ] && fails=0
+[ "$fails" -ge "$sspill" ] || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+echo "I: checking lame server recovery"
+ret=0
+rm -f ans4/norespond
+for try in 1 2 3 4 5; do
+    burst b $try
+    stat 200 || ret=1
+    [ $ret -eq 1 ] && break
+    sleep 1
+done
+
+echo "I: dumping ADB data"
+$RNDCCMD dumpdb -adb
+info=`grep '10.53.0.4' ns3/named_dump.db | sed 's/.*\(atr [.0-9]*\).*\(quota [0-9]*\).*/I: \1 \2/'`
+echo $info
+set -- $info
+[ ${5:-${quota}} -lt $quota ] || ret=1
+quota=$5
+
+for try in 1 2 3 4 5 6 7 8 9 10; do
+    burst c $try
+    stat 20 || ret=1
+    [ $ret -eq 1 ] && break
+    sleep 1
+done
+
+echo "I: dumping ADB data"
+$RNDCCMD dumpdb -adb
+info=`grep '10.53.0.4' ns3/named_dump.db | sed 's/.*\(atr [.0-9]*\).*\(quota [0-9]*\).*/I: \1 \2/'`
+echo $info
+set -- $info
+[ ${5:-${quota}} -gt $quota ] || ret=1
+quota=$5
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+cp -f ns3/named2.conf ns3/named.conf
+$RNDCCMD reconfig 2>&1 | sed 's/^/I:ns3 /'
+
+echo "I: checking lame server clients are dropped at the per-domain limit"
+ret=0
+fail=0
+success=0
+touch ans4/norespond
+for try in 1 2 3 4 5; do
+    burst b $try 300
+    $DIGCMD a ${try}.example > dig.out.ns3.$try
+    grep "status: NOERROR" dig.out.ns3.$try > /dev/null 2>&1 && \
+            success=`expr $success + 1`
+    grep "status: SERVFAIL" dig.out.ns3.$try > /dev/null 2>&1 && \
+            fail=`expr $fail + 1`
+    stat 50 || ret=1
+    [ $ret -eq 1 ] && break
+    $RNDCCMD recursing 2>&1 | sed 's/^/I:ns3 /'
+    sleep 1
+done
+echo "I: $success successful valid queries, $fail SERVFAIL"
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+echo "I: checking drop statistics"
+rm -f ns3/named.stats
+$RNDCCMD stats
+for try in 1 2 3 4 5; do
+    [ -f ns3/named.stats ] && break
+    sleep 1
+done
+zspill=`grep 'spilled due to zone' ns3/named.stats | sed 's/\([0-9][0-9]*\) spilled.*/\1/'`
+[ -z "$zspill" ] && zspill=0
+drops=`grep 'queries dropped' ns3/named.stats | sed 's/\([0-9][0-9]*\) queries.*/\1/'`
+[ -z "$drops" ] && drops=0
+[ "$drops" -ge "$zspill" ] || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+cp -f ns3/named3.conf ns3/named.conf
+$RNDCCMD reconfig 2>&1 | sed 's/^/I:ns3 /'
+
+echo "I: checking lame server clients are dropped at the soft limit"
+ret=0
+fail=0
+success=0
+touch ans4/norespond
+for try in 1 2 3 4 5; do
+    burst b $try 400
+    $DIG @10.53.0.3 -p 5300 a ${try}.example > dig.out.ns3.$try
+    stat 360 || ret=1
+    grep "status: NOERROR" dig.out.ns3.$try > /dev/null 2>&1 && \
+            success=`expr $success + 1`
+    grep "status: SERVFAIL" dig.out.ns3.$try > /dev/null 2>&1 && \
+            fail=`expr $fail + 1`
+    [ $ret -eq 1 ] && break
+    sleep 1
+done
+echo "I: $success successful valid queries, $fail SERVFAIL"
+[ "$success" -eq 5 ] || ret=1
+if [ $ret != 0 ]; then echo "I: failed"; fi
+status=`expr $status + $ret`
+
+echo "I:exit status: $status"
+exit $status
index 9fbd899ed4ea38275ec22d4050762459890a0b98..e461d854b6894cbf6da3e5f7b84db872bf93b5aa 100755 (executable)
@@ -15,8 +15,6 @@
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id$
-
 SYSTEMTESTTOP=..
 . $SYSTEMTESTTOP/conf.sh
 
@@ -63,7 +61,7 @@ if [ -x ${SAMPLE} ] ; then
     echo "I:checking handling of bogus referrals using dns_client"
     ret=0
     ${SAMPLE} -p 5300 -t a 10.53.0.1 www.example.com 2> sample.out || ret=1
-    grep "resolution failed: failure" sample.out > /dev/null || ret=1
+    egrep "resolution failed: (failure|SERVFAIL)" sample.out > /dev/null || ret=1
     if [ $ret != 0 ]; then echo "I:failed"; fi
     status=`expr $status + $ret`
 fi
@@ -387,7 +385,6 @@ grep "not subdomain of zone" ns1/named.run > /dev/null || ret=1
 if [ $ret != 0 ]; then echo "I:failed"; fi
 status=`expr $status + $ret`
 
-#HERE <<<
 cp ns7/named2.conf ns7/named.conf
 $RNDC -c ../common/rndc.conf -s 10.53.0.7 -p 9953 reconfig 2>&1 | sed 's/^/I:ns7 /'
 
index eba5191f6cc8ee3fefe864193169a1d714fe3172..c336ba349d388bb150146948d61cbe7922aa9c53 100644 (file)
@@ -169,6 +169,10 @@ int sigwait(const unsigned int *set, int *sig);
 /* Define to enable "rrset-order fixed" syntax. */
 #undef DNS_RDATASET_FIXED
 
+/* Define to enable the "fetches-per-server" and "fetches-per-zone" options.
+   */
+#undef ENABLE_FETCHLIMIT
+
 /* Define to enable rpz-nsdname rules. */
 #undef ENABLE_RPZ_NSDNAME
 
index 5fc806cdc599690d55eaf3226f2786fc602bdb7f..c3fa3ddb5327594fb60829c56bd217d086b7d4b7 100755 (executable)
--- a/configure
+++ b/configure
@@ -1002,6 +1002,7 @@ enable_atomic
 enable_fixed_rrset
 enable_rpz_nsip
 enable_rpz_nsdname
+enable_fetchlimit
 enable_filter_aaaa
 with_docbook_xsl
 with_idn
@@ -1681,6 +1682,7 @@ Optional Features:
                          [default=no]
   --disable-rpz-nsip     disable rpz-nsip rules [default=enabled]
   --disable-rpz-nsdname          disable rpz-nsdname rules [default=enabled]
+  --enable-fetchlimit     enable recursive fetch limits [default=no]
   --enable-filter-aaaa    enable filtering of AAAA records over IPv4
                          [default=no]
   --enable-querytrace     enable very verbose query trace logging [default=no]
@@ -11434,6 +11436,7 @@ yes)
                ;;
        esac
        test "${enable_symtable+set}" = set || enable_symtable=all
+       test "${enable_fetchlimit+set}" = set || enable_fetchlimit=yes
        test "${enable_warn_error+set}" = set || enable_warn_error=yes
        test "${enable_warn_shadow+set}" = set || enable_warn_shadow=yes
        ;;
@@ -18761,7 +18764,29 @@ $as_echo "#define ENABLE_RPZ_NSDNAME 1" >>confdefs.h
 esac
 
 #
-# Activate "filter-aaaa-on-v4" or not?
+# Activate recursive fetch limits
+#
+# Check whether --enable-fetchlimit was given.
+if test "${enable_fetchlimit+set}" = set; then :
+  enableval=$enable_fetchlimit; enable_fetchlimit="$enableval"
+else
+  enable_fetchlimit="no"
+fi
+
+case "$enable_fetchlimit" in
+       yes)
+
+$as_echo "#define ENABLE_FETCHLIMIT 1" >>confdefs.h
+
+               ;;
+       no)
+               ;;
+       *)
+               ;;
+esac
+
+#
+# Activate "filter-aaaa" or not?
 #
 # Check whether --enable-filter-aaaa was given.
 if test "${enable_filter_aaaa+set}" = set; then :
@@ -20882,7 +20907,7 @@ ac_config_commands="$ac_config_commands chmod"
 # elsewhere if there's a good reason for doing so.
 #
 
-ac_config_files="$ac_config_files make/Makefile make/mkdep Makefile bin/Makefile bin/check/Makefile bin/confgen/Makefile bin/confgen/unix/Makefile bin/dig/Makefile bin/dnssec/Makefile bin/named/Makefile bin/named/unix/Makefile bin/nsupdate/Makefile bin/pkcs11/Makefile bin/python/Makefile bin/python/dnssec-checkds.py bin/python/dnssec-coverage.py bin/rndc/Makefile bin/tests/Makefile bin/tests/atomic/Makefile bin/tests/db/Makefile bin/tests/dst/Makefile bin/tests/dst/Kdh.+002+18602.key bin/tests/dst/Kdh.+002+18602.private bin/tests/dst/Kdh.+002+48957.key bin/tests/dst/Kdh.+002+48957.private bin/tests/dst/Ktest.+001+00002.key bin/tests/dst/Ktest.+001+54622.key bin/tests/dst/Ktest.+001+54622.private bin/tests/dst/Ktest.+003+23616.key bin/tests/dst/Ktest.+003+23616.private bin/tests/dst/Ktest.+003+49667.key bin/tests/dst/dst_2_data bin/tests/dst/t2_data_1 bin/tests/dst/t2_data_2 bin/tests/dst/t2_dsasig bin/tests/dst/t2_rsasig bin/tests/hashes/Makefile bin/tests/headerdep_test.sh bin/tests/master/Makefile bin/tests/mem/Makefile bin/tests/names/Makefile bin/tests/net/Makefile bin/tests/rbt/Makefile bin/tests/resolver/Makefile bin/tests/sockaddr/Makefile bin/tests/system/Makefile bin/tests/system/conf.sh bin/tests/system/builtin/Makefile bin/tests/system/dlz/prereq.sh bin/tests/system/dlzexternal/Makefile bin/tests/system/dlzexternal/ns1/named.conf bin/tests/system/filter-aaaa/Makefile bin/tests/system/inline/checkdsa.sh bin/tests/system/lwresd/Makefile bin/tests/system/statistics/Makefile bin/tests/system/rpz/Makefile bin/tests/system/rrl/Makefile bin/tests/system/rsabigexponent/Makefile bin/tests/system/tkey/Makefile bin/tests/system/tsiggss/Makefile bin/tests/tasks/Makefile bin/tests/timers/Makefile bin/tests/virtual-time/Makefile bin/tests/virtual-time/conf.sh bin/tools/Makefile contrib/check-secure-delegation.pl contrib/zone-edit.sh doc/Makefile doc/arm/Makefile doc/doxygen/Doxyfile doc/doxygen/Makefile doc/doxygen/doxygen-input-filter doc/misc/Makefile doc/xsl/Makefile doc/xsl/isc-docbook-chunk.xsl doc/xsl/isc-docbook-html.xsl doc/xsl/isc-docbook-latex.xsl doc/xsl/isc-manpage.xsl doc/xsl/isc-notes-html.xsl doc/xsl/isc-notes-latex.xsl isc-config.sh lib/Makefile lib/bind9/Makefile lib/bind9/include/Makefile lib/bind9/include/bind9/Makefile lib/dns/Makefile lib/dns/include/Makefile lib/dns/include/dns/Makefile lib/dns/include/dst/Makefile lib/dns/tests/Makefile lib/export/Makefile lib/export/dns/Makefile lib/export/dns/include/Makefile lib/export/dns/include/dns/Makefile lib/export/dns/include/dst/Makefile lib/export/irs/Makefile lib/export/irs/include/Makefile lib/export/irs/include/irs/Makefile lib/export/isc/$thread_dir/Makefile lib/export/isc/$thread_dir/include/Makefile lib/export/isc/$thread_dir/include/isc/Makefile lib/export/isc/Makefile lib/export/isc/include/Makefile lib/export/isc/include/isc/Makefile lib/export/isc/nls/Makefile lib/export/isc/unix/Makefile lib/export/isc/unix/include/Makefile lib/export/isc/unix/include/isc/Makefile lib/export/isccfg/Makefile lib/export/isccfg/include/Makefile lib/export/isccfg/include/isccfg/Makefile lib/export/samples/Makefile lib/export/samples/Makefile-postinstall lib/irs/Makefile lib/irs/include/Makefile lib/irs/include/irs/Makefile lib/irs/include/irs/netdb.h lib/irs/include/irs/platform.h lib/isc/$arch/Makefile lib/isc/$arch/include/Makefile lib/isc/$arch/include/isc/Makefile lib/isc/$thread_dir/Makefile lib/isc/$thread_dir/include/Makefile lib/isc/$thread_dir/include/isc/Makefile lib/isc/Makefile lib/isc/include/Makefile lib/isc/include/isc/Makefile lib/isc/include/isc/platform.h lib/isc/tests/Makefile lib/isc/nls/Makefile lib/isc/unix/Makefile lib/isc/unix/include/Makefile lib/isc/unix/include/isc/Makefile lib/isccc/Makefile lib/isccc/include/Makefile lib/isccc/include/isccc/Makefile lib/isccfg/Makefile lib/isccfg/include/Makefile lib/isccfg/include/isccfg/Makefile lib/lwres/Makefile lib/lwres/include/Makefile lib/lwres/include/lwres/Makefile lib/lwres/include/lwres/netdb.h lib/lwres/include/lwres/platform.h lib/lwres/man/Makefile lib/lwres/tests/Makefile lib/lwres/unix/Makefile lib/lwres/unix/include/Makefile lib/lwres/unix/include/lwres/Makefile lib/tests/Makefile lib/tests/include/Makefile lib/tests/include/tests/Makefile unit/Makefile unit/unittest.sh"
+ac_config_files="$ac_config_files make/Makefile make/mkdep Makefile bin/Makefile bin/check/Makefile bin/confgen/Makefile bin/confgen/unix/Makefile bin/dig/Makefile bin/dnssec/Makefile bin/named/Makefile bin/named/unix/Makefile bin/nsupdate/Makefile bin/pkcs11/Makefile bin/python/Makefile bin/python/dnssec-checkds.py bin/python/dnssec-coverage.py bin/rndc/Makefile bin/tests/Makefile bin/tests/atomic/Makefile bin/tests/db/Makefile bin/tests/dst/Makefile bin/tests/dst/Kdh.+002+18602.key bin/tests/dst/Kdh.+002+18602.private bin/tests/dst/Kdh.+002+48957.key bin/tests/dst/Kdh.+002+48957.private bin/tests/dst/Ktest.+001+00002.key bin/tests/dst/Ktest.+001+54622.key bin/tests/dst/Ktest.+001+54622.private bin/tests/dst/Ktest.+003+23616.key bin/tests/dst/Ktest.+003+23616.private bin/tests/dst/Ktest.+003+49667.key bin/tests/dst/dst_2_data bin/tests/dst/t2_data_1 bin/tests/dst/t2_data_2 bin/tests/dst/t2_dsasig bin/tests/dst/t2_rsasig bin/tests/hashes/Makefile bin/tests/headerdep_test.sh bin/tests/master/Makefile bin/tests/mem/Makefile bin/tests/names/Makefile bin/tests/net/Makefile bin/tests/rbt/Makefile bin/tests/resolver/Makefile bin/tests/sockaddr/Makefile bin/tests/system/Makefile bin/tests/system/conf.sh bin/tests/system/builtin/Makefile bin/tests/system/dlz/prereq.sh bin/tests/system/dlzexternal/Makefile bin/tests/system/dlzexternal/ns1/named.conf bin/tests/system/fetchlimit/Makefile bin/tests/system/filter-aaaa/Makefile bin/tests/system/inline/checkdsa.sh bin/tests/system/lwresd/Makefile bin/tests/system/statistics/Makefile bin/tests/system/rpz/Makefile bin/tests/system/rrl/Makefile bin/tests/system/rsabigexponent/Makefile bin/tests/system/tkey/Makefile bin/tests/system/tsiggss/Makefile bin/tests/tasks/Makefile bin/tests/timers/Makefile bin/tests/virtual-time/Makefile bin/tests/virtual-time/conf.sh bin/tools/Makefile contrib/check-secure-delegation.pl contrib/zone-edit.sh doc/Makefile doc/arm/Makefile doc/doxygen/Doxyfile doc/doxygen/Makefile doc/doxygen/doxygen-input-filter doc/misc/Makefile doc/xsl/Makefile doc/xsl/isc-docbook-chunk.xsl doc/xsl/isc-docbook-html.xsl doc/xsl/isc-docbook-latex.xsl doc/xsl/isc-manpage.xsl doc/xsl/isc-notes-html.xsl doc/xsl/isc-notes-latex.xsl isc-config.sh lib/Makefile lib/bind9/Makefile lib/bind9/include/Makefile lib/bind9/include/bind9/Makefile lib/dns/Makefile lib/dns/include/Makefile lib/dns/include/dns/Makefile lib/dns/include/dst/Makefile lib/dns/tests/Makefile lib/export/Makefile lib/export/dns/Makefile lib/export/dns/include/Makefile lib/export/dns/include/dns/Makefile lib/export/dns/include/dst/Makefile lib/export/irs/Makefile lib/export/irs/include/Makefile lib/export/irs/include/irs/Makefile lib/export/isc/$thread_dir/Makefile lib/export/isc/$thread_dir/include/Makefile lib/export/isc/$thread_dir/include/isc/Makefile lib/export/isc/Makefile lib/export/isc/include/Makefile lib/export/isc/include/isc/Makefile lib/export/isc/nls/Makefile lib/export/isc/unix/Makefile lib/export/isc/unix/include/Makefile lib/export/isc/unix/include/isc/Makefile lib/export/isccfg/Makefile lib/export/isccfg/include/Makefile lib/export/isccfg/include/isccfg/Makefile lib/export/samples/Makefile lib/export/samples/Makefile-postinstall lib/irs/Makefile lib/irs/include/Makefile lib/irs/include/irs/Makefile lib/irs/include/irs/netdb.h lib/irs/include/irs/platform.h lib/isc/$arch/Makefile lib/isc/$arch/include/Makefile lib/isc/$arch/include/isc/Makefile lib/isc/$thread_dir/Makefile lib/isc/$thread_dir/include/Makefile lib/isc/$thread_dir/include/isc/Makefile lib/isc/Makefile lib/isc/include/Makefile lib/isc/include/isc/Makefile lib/isc/include/isc/platform.h lib/isc/tests/Makefile lib/isc/nls/Makefile lib/isc/unix/Makefile lib/isc/unix/include/Makefile lib/isc/unix/include/isc/Makefile lib/isccc/Makefile lib/isccc/include/Makefile lib/isccc/include/isccc/Makefile lib/isccfg/Makefile lib/isccfg/include/Makefile lib/isccfg/include/isccfg/Makefile lib/lwres/Makefile lib/lwres/include/Makefile lib/lwres/include/lwres/Makefile lib/lwres/include/lwres/netdb.h lib/lwres/include/lwres/platform.h lib/lwres/man/Makefile lib/lwres/tests/Makefile lib/lwres/unix/Makefile lib/lwres/unix/include/Makefile lib/lwres/unix/include/lwres/Makefile lib/tests/Makefile lib/tests/include/Makefile lib/tests/include/tests/Makefile unit/Makefile unit/unittest.sh"
 
 
 #
@@ -21927,6 +21952,7 @@ do
     "bin/tests/system/dlz/prereq.sh") CONFIG_FILES="$CONFIG_FILES bin/tests/system/dlz/prereq.sh" ;;
     "bin/tests/system/dlzexternal/Makefile") CONFIG_FILES="$CONFIG_FILES bin/tests/system/dlzexternal/Makefile" ;;
     "bin/tests/system/dlzexternal/ns1/named.conf") CONFIG_FILES="$CONFIG_FILES bin/tests/system/dlzexternal/ns1/named.conf" ;;
+    "bin/tests/system/fetchlimit/Makefile") CONFIG_FILES="$CONFIG_FILES bin/tests/system/fetchlimit/Makefile" ;;
     "bin/tests/system/filter-aaaa/Makefile") CONFIG_FILES="$CONFIG_FILES bin/tests/system/filter-aaaa/Makefile" ;;
     "bin/tests/system/inline/checkdsa.sh") CONFIG_FILES="$CONFIG_FILES bin/tests/system/inline/checkdsa.sh" ;;
     "bin/tests/system/lwresd/Makefile") CONFIG_FILES="$CONFIG_FILES bin/tests/system/lwresd/Makefile" ;;
@@ -23334,6 +23360,8 @@ $use_threads && echo "    Multiprocessing support (--enable-threads)"
 
 test "$enable_rrl" = "yes" && \
     echo "    Response Rate Limiting (--enable-rrl)"
+test "$enable_fetchlimit" = "yes" && \
+    echo "    Recursive fetch limits for DoS attack mitigation (--enable-fetchlimit)"
 test "$use_gssapi" = "no" || echo "    GSS-API (--with-gssapi)"
 test "$use_pkcs11" = "no" || echo "    PKCS#11/Cryptoki support (--with-pkcs11)"
 test "$enable_newstats" = "yes" && \
@@ -23390,6 +23418,8 @@ test "$enable_ipv6" = "no" -o "$found_ipv6" = "no" && \
        echo "    IPv6 support (--enable-ipv6)"
 test "$enable_rrl" = "yes" || \
     echo "    Response Rate Limiting (--enable-rrl)"
+test "$enable_fetchlimit" = "no" && \
+        echo "    Recursive fetch limits for DoS attack mitigation (--enable-fetchlimit)"
 test "$use_gssapi" = "no" && echo "    GSS-API (--with-gssapi)"
 test "$use_pkcs11" = "no" && echo "    PKCS#11/Cryptoki support (--with-pkcs11)"
 test "X$enable_newstats" = "X" && echo "    New statistics (--enable-newstats)"
index fc38d1b46def86c1b4f0ae6269ac212c0bef79a0..48cbc5d1c5263fa411578561ec9aae97e90c67e8 100644 (file)
@@ -86,6 +86,7 @@ yes)
                ;;
        esac
        test "${enable_symtable+set}" = set || enable_symtable=all
+       test "${enable_fetchlimit+set}" = set || enable_fetchlimit=yes
        test "${enable_warn_error+set}" = set || enable_warn_error=yes
        test "${enable_warn_shadow+set}" = set || enable_warn_shadow=yes
        ;;
@@ -3282,7 +3283,25 @@ case "$enable_nsdname" in
 esac
 
 #
-# Activate "filter-aaaa-on-v4" or not?
+# Activate recursive fetch limits
+#
+AC_ARG_ENABLE(fetchlimit,
+       [  --enable-fetchlimit     enable recursive fetch limits [[default=no]]],
+                       enable_fetchlimit="$enableval",
+                       enable_fetchlimit="no")
+case "$enable_fetchlimit" in
+       yes)
+               AC_DEFINE(ENABLE_FETCHLIMIT, 1,
+                         [Define to enable the "fetches-per-server" and "fetches-per-zone" options.])
+               ;;
+       no)
+               ;;
+       *)
+               ;;
+esac
+
+#
+# Activate "filter-aaaa" or not?
 #
 AC_ARG_ENABLE(filter-aaaa,
        [  --enable-filter-aaaa    enable filtering of AAAA records over IPv4
@@ -4022,6 +4041,7 @@ AC_CONFIG_FILES([
        bin/tests/system/dlz/prereq.sh
        bin/tests/system/dlzexternal/Makefile
        bin/tests/system/dlzexternal/ns1/named.conf
+       bin/tests/system/fetchlimit/Makefile
        bin/tests/system/filter-aaaa/Makefile
        bin/tests/system/inline/checkdsa.sh
        bin/tests/system/lwresd/Makefile
@@ -4156,6 +4176,8 @@ $use_threads && echo "    Multiprocessing support (--enable-threads)"
 
 test "$enable_rrl" = "yes" && \
     echo "    Response Rate Limiting (--enable-rrl)"
+test "$enable_fetchlimit" = "yes" && \
+    echo "    Recursive fetch limits for DoS attack mitigation (--enable-fetchlimit)"
 test "$use_gssapi" = "no" || echo "    GSS-API (--with-gssapi)"
 test "$use_pkcs11" = "no" || echo "    PKCS#11/Cryptoki support (--with-pkcs11)"
 test "$enable_newstats" = "yes" && \
@@ -4212,6 +4234,8 @@ test "$enable_ipv6" = "no" -o "$found_ipv6" = "no" && \
        echo "    IPv6 support (--enable-ipv6)"
 test "$enable_rrl" = "yes" || \
     echo "    Response Rate Limiting (--enable-rrl)"
+test "$enable_fetchlimit" = "no" && \
+        echo "    Recursive fetch limits for DoS attack mitigation (--enable-fetchlimit)"
 test "$use_gssapi" = "no" && echo "    GSS-API (--with-gssapi)"
 test "$use_pkcs11" = "no" && echo "    PKCS#11/Cryptoki support (--with-pkcs11)"
 test "X$enable_newstats" = "X" && echo "    New statistics (--enable-newstats)"
index e6248b9bc407108d0c83d8a05682d654466f83a0..ceae588e3461c1e9c7990a2939dc23f6f466f933 100644 (file)
@@ -4784,9 +4784,14 @@ badresp:1,adberr:0,findfail:0,valfail:0]
     <optional> max-transfer-time-out <replaceable>number</replaceable>; </optional>
     <optional> max-transfer-idle-in <replaceable>number</replaceable>; </optional>
     <optional> max-transfer-idle-out <replaceable>number</replaceable>; </optional>
-    <optional> tcp-clients <replaceable>number</replaceable>; </optional>
     <optional> reserved-sockets <replaceable>number</replaceable>; </optional>
     <optional> recursive-clients <replaceable>number</replaceable>; </optional>
+    <optional> tcp-clients <replaceable>number</replaceable>; </optional>
+    <optional> clients-per-query <replaceable>number</replaceable> ; </optional>
+    <optional> max-clients-per-query <replaceable>number</replaceable> ; </optional>
+    <optional> fetches-per-server <replaceable>number</replaceable> <optional><replaceable>(drop | fail)</replaceable></optional>; </optional>
+    <optional> fetch-quota-params <replaceable>number fixedpoint fixedpoint fixedpoint</replaceable> ; </optional>
+    <optional> fetches-per-zone<replaceable>number</replaceable> <optional><replaceable>(drop | fail)</replaceable></optional>; </optional>
     <optional> serial-query-rate <replaceable>number</replaceable>; </optional>
     <optional> serial-queries <replaceable>number</replaceable>; </optional>
     <optional> tcp-listen-queue <replaceable>number</replaceable>; </optional>
@@ -4865,8 +4870,6 @@ badresp:1,adberr:0,findfail:0,valfail:0]
     <optional> acache-enable <replaceable>yes_or_no</replaceable> ; </optional>
     <optional> acache-cleaning-interval <replaceable>number</replaceable>; </optional>
     <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> max-recursion-queries <replaceable>number</replaceable> ; </optional>
     <optional> masterfile-format (<constant>text</constant>|<constant>raw</constant>) ; </optional>
@@ -7933,17 +7936,33 @@ avoid-v6-udp-ports { 40000; range 50000 60000; };
              <term><command>recursive-clients</command></term>
              <listitem>
                <para>
-                 The maximum number of simultaneous recursive lookups
-                 the server will perform on behalf of clients.  The default
-                 is
+                 The maximum number ("hard quota") of simultaneous
+                 recursive lookups the server will perform on behalf
+                 of clients.  The default is
                  <literal>1000</literal>.  Because each recursing
                  client uses a fair
-                 bit of memory, on the order of 20 kilobytes, the value of
-                 the
+                 bit of memory (on the order of 20 kilobytes), the
+                 value of the
                  <command>recursive-clients</command> option may
-                 have to be decreased
-                 on hosts with limited memory.
-               </para>
+                 have to be decreased on hosts with limited memory.
+               </para>
+               <para>
+                 <option>recursive-clients</option> defines a "hard
+                 quota" limit for pending recursive clients: when more
+                 clients than this are pending, new incoming requests
+                 will not be accepted, and for each incoming request
+                 a previous pending request will also be dropped.
+               </para>
+                <para>
+                  A "soft quota" is also set.  When this lower
+                 quota is exceeded, incoming requests are accepted, but
+                 for each one, a pending request will be dropped. 
+                  If <option>recursive-clients</option> is greater than
+                  1000, the soft quota is set to
+                  <option>recursive-clients</option> minus 100;
+                  otherwise it is set to 90% of
+                  <option>recursive-clients</option>.
+                </para>
              </listitem>
            </varlistentry>
 
@@ -7958,6 +7977,187 @@ avoid-v6-udp-ports { 40000; range 50000 60000; };
              </listitem>
            </varlistentry>
 
+           <varlistentry id="clients-per-query">
+             <term><command>clients-per-query</command></term>
+             <term><command>max-clients-per-query</command></term>
+             <listitem>
+               <para>These set the
+                 initial value (minimum) and maximum number of recursive
+                 simultaneous clients for any given query
+                 (&lt;qname,qtype,qclass&gt;) that the server will accept
+                 before dropping additional clients.  <command>named</command> will attempt to
+                 self tune this value and changes will be logged.  The
+                 default values are 10 and 100.
+               </para>
+               <para>
+                 This value should reflect how many queries come in for
+                 a given name in the time it takes to resolve that name.
+                 If the number of queries exceed this value, <command>named</command> will
+                 assume that it is dealing with a non-responsive zone
+                 and will drop additional queries.  If it gets a response
+                 after dropping queries, it will raise the estimate.  The
+                 estimate will then be lowered in 20 minutes if it has
+                 remained unchanged.
+               </para>
+               <para>
+                 If <command>clients-per-query</command> is set to zero,
+                 then there is no limit on the number of clients per query
+                 and no queries will be dropped.
+               </para>
+               <para>
+                 If <command>max-clients-per-query</command> is set to zero,
+                 then there is no upper bound other than imposed by
+                 <command>recursive-clients</command>.
+               </para>
+             </listitem>
+           </varlistentry>
+
+           <varlistentry id="fetches-per-zone">
+             <term><command>fetches-per-zone</command></term>
+             <listitem>
+               <para>
+                 The maximum number of simultaneous iterative
+                 queries to any one domain that the server will
+                 permit before blocking new queries for data
+                 in or beneath that zone.
+                 This value should reflect how many fetches would
+                 normally be sent to any one zone in the time it
+                 would take to resolve them.  It should be smaller
+                 than <option>recursive-clients</option>.
+               </para>
+               <para>
+                 When many clients simultaneously query for the
+                 same name and type, the clients will all be attached
+                 to the same fetch, up to the
+                 <option>max-clients-per-query</option> limit,
+                 and only one iterative query will be sent.
+                 However, when clients are simultaneously
+                 querying for <emphasis>different</emphasis> names
+                 or types, multiple queries will be sent and
+                 <option>max-clients-per-query</option> is not
+                 effective as a limit.
+               </para>
+               <para>
+                 Optionally, this value may be followed by the keyword
+                 <literal>drop</literal> or <literal>fail</literal>,
+                 indicating whether queries which exceed the fetch
+                 quota for a zone will be dropped with no response,
+                 or answered with SERVFAIL.  The default is
+                 <literal>drop</literal>.
+               </para>
+               <para>
+                 If <command>fetches-per-zone</command> is set to zero,
+                 then there is no limit on the number of fetches per query
+                 and no queries will be dropped.  The default is zero.
+               </para>
+               <para>
+                 The current list of active fetches can be dumped by
+                 running <command>rndc recursing</command>.  The list
+                 includes the number of active fetches for each
+                 domain and the number of queries that have been
+                 passed or dropped as a result of the
+                 <option>fetches-per-zone</option> limit.  (Note:
+                 these counters are not cumulative over time; whenever
+                 the number of active fetches for a domain drops to
+                 zero, the counter for that domain is deleted, and the
+                 next time a fetch is sent to that domain, it is
+                 recreated with the counters set to zero.)
+               </para>
+               <para>
+                 (Note: This option is only available when BIND is
+                 built with <command>configure --enable-fetchlimit</command>.)
+               </para>
+             </listitem>
+           </varlistentry>
+
+           <varlistentry id="fetches-per-server">
+             <term><command>fetches-per-server</command></term>
+             <listitem>
+               <para>
+                 The maximum number of simultaneous iterative
+                  queries that the server will allow to be sent to
+                  a single upstream name server before blocking
+                  additional queries.
+                 This value should reflect how many fetches would
+                 normally be sent to any one server in the time it
+                 would take to resolve them.  It should be smaller
+                  than <option>recursive-clients</option>.
+               </para>
+               <para>
+                 Optionally, this value may be followed by the keyword
+                 <literal>drop</literal> or <literal>fail</literal>,
+                 indicating whether queries will be dropped with no
+                 response, or answered with SERVFAIL, when all of the
+                 servers authoritative for a zone are found to have
+                 exceeded the per-server quota.  The default is
+                 <literal>fail</literal>.
+               </para>
+               <para>
+                 If <command>fetches-per-server</command> is set to zero,
+                 then there is no limit on the number of fetches per query
+                 and no queries will be dropped.  The default is zero.
+               </para>
+               <para>
+                 The <command>fetches-per-server</command> quota is
+                 dynamically adjusted in response to detected
+                 congestion. As queries are sent to a server
+                 and are either answered or time out, an
+                 exponentially weighted moving average is calculated
+                 of the ratio of timeouts to responses.  If the
+                 current average timeout ratio rises above a "high"
+                 threshold, then <command>fetches-per-server</command>
+                 is reduced for that server.  If the timeout ratio
+                 drops below a "low" threshold, then
+                 <command>fetches-per-server</command> is increased.
+                 The <command>fetch-quota-params</command> options
+                 can be used to adjust the parameters for this
+                 calculation.
+               </para>
+               <para>
+                 (Note: This option is only available when BIND is
+                 built with <command>configure --enable-fetchlimit</command>.)
+               </para>
+             </listitem>
+           </varlistentry>
+
+           <varlistentry>
+             <term><command>fetch-quota-params</command></term>
+             <listitem>
+               <para>
+                 Sets the parameters to use for dynamic resizing of
+                 the <option>fetches-per-server</option> quota in
+                 response to detected congestion.
+               </para>
+               <para>
+                 The first argument is an integer value indicating
+                 how frequently to recalculate the moving average
+                 of the ratio of timeouts to responses for each
+                 server.  The default is 100, meaning we recalculate
+                 the average ratio after every 100 queries have either
+                 been answered or timed out.
+               </para>
+               <para>
+                 The remaining three arguments represent the "low"
+                 threshold (defaulting to a timeout ratio of 0.1),
+                 the "high" threshold (defaulting to a timeout
+                 ratio of 0.3), and the discount rate for
+                 the moving average (defaulting to 0.7).
+                 A higher discount rate causes recent events to
+                 weigh more heavily when calculating the moving
+                 average; a lower discount rate causes past
+                 events to weigh more heavily, smoothing out
+                 short-term blips in the timeout ratio.
+                 These arguments are all fixed-point numbers with
+                 precision of 1/100: at most two places after
+                 the decimal point are significant.
+               </para>
+               <para>
+                 (Note: This option is only available when BIND is
+                 built with <command>configure --enable-fetchlimit</command>.)
+               </para>
+             </listitem>
+           </varlistentry>
+
            <varlistentry>
              <term><command>reserved-sockets</command></term>
              <listitem>
@@ -8658,72 +8858,6 @@ avoid-v6-udp-ports { 40000; range 50000 60000; };
              </listitem>
            </varlistentry>
 
-           <varlistentry id="clients-per-query">
-             <term><command>clients-per-query</command></term>
-             <term><command>max-clients-per-query</command></term>
-             <listitem>
-               <para>These set the
-                 initial value (minimum) and maximum number of recursive
-                 simultaneous clients for any given query
-                 (&lt;qname,qtype,qclass&gt;) that the server will accept
-                 before dropping additional clients.  <command>named</command> will attempt to
-                 self tune this value and changes will be logged.  The
-                 default values are 10 and 100.
-               </para>
-               <para>
-                 This value should reflect how many queries come in for
-                 a given name in the time it takes to resolve that name.
-                 If the number of queries exceed this value, <command>named</command> will
-                 assume that it is dealing with a non-responsive zone
-                 and will drop additional queries.  If it gets a response
-                 after dropping queries, it will raise the estimate.  The
-                 estimate will then be lowered in 20 minutes if it has
-                 remained unchanged.
-               </para>
-               <para>
-                 If <command>clients-per-query</command> is set to zero,
-                 then there is no limit on the number of clients per query
-                 and no queries will be dropped.
-               </para>
-               <para>
-                 If <command>max-clients-per-query</command> is set to zero,
-                 then there is no upper bound other than imposed by
-                 <command>recursive-clients</command>.
-               </para>
-             </listitem>
-           </varlistentry>
-
-           <varlistentry id="max-recursion-depth">
-             <term><command>max-recursion-depth</command></term>
-             <listitem>
-               <para>
-                 Sets the maximum number of levels of recursion
-                 that are permitted at any one time while servicing
-                 a recursive query. Resolving a name may require
-                 looking up a name server address, which in turn
-                 requires resolving another name, etc; if the number
-                 of indirections exceeds this value, the recursive
-                 query is terminated and returns SERVFAIL.  The
-                 default is 7.
-               </para>
-             </listitem>
-           </varlistentry>
-
-           <varlistentry id="max-recursion-queries">
-             <term><command>max-recursion-queries</command></term>
-             <listitem>
-               <para>
-                 Sets the maximum number of iterative queries that
-                 may be sent while servicing a recursive query.
-                 If more queries are sent, the recursive query
-                 is terminated and returns SERVFAIL. Queries to
-                 look up top level comains such as "com" and "net"
-                 and the DNS root zone are exempt from this limitation.
-                 The default is 50.
-               </para>
-             </listitem>
-           </varlistentry>
-
            <varlistentry>
              <term><command>notify-delay</command></term>
              <listitem>
index d7b67a9d5715165a514fcb34cd0616aa28d27349..5b66d1bc9690fa93fac64e3df905a80d42d8860b 100644 (file)
     <title>New Features</title>
     <itemizedlist>
       <listitem>
-       <para>None</para>
+       <para>
+         New quotas have been added to limit the queries that are
+         sent by recursive resolvers to authoritative servers
+         experiencing denial-of-service attacks. When configured,
+         these options can both reduce the harm done to authoritative
+         servers and also avoid the resource exhaustion that can be
+         experienced by recursives when they are being used as a
+         vehicle for such an attack.
+       </para>
+       <para>
+         NOTE: These options are not available by default; use
+         <command>configure --enable-fetchlimit</command> to include
+         them in the build.
+       </para>
+       <itemizedlist>
+          <listitem>
+           <para>
+             <option>fetches-per-server</option> limits the number of
+             simultaneous queries that can be sent to any single
+             authoritative server.  The configured value is a starting
+             point; it is automatically adjusted downward if the server is
+             partially or completely non-responsive. The algorithm used to
+             adjust the quota can be configured via the
+             <option>fetch-quota-params</option> option.
+           </para>
+         </listitem>
+          <listitem>
+           <para>
+             <option>fetches-per-zone</option> limits the number of
+             simultaneous queries that can be sent for names within a
+             single domain.  (Note: Unlike "fetches-per-server", this
+             value is not self-tuning.)
+           </para>
+         </listitem>
+       </itemizedlist>
+       <para>
+         Statistics counters have also been added to track the number
+         of queries affected by these quotas.
+       </para>
       </listitem>
       <listitem>
        <para>
index 0a28171e26ad7f1c45797c4836f1cf7238567bcf..ec4af599da93560498d5398d2462ce394b968a1b 100644 (file)
@@ -163,6 +163,14 @@ struct dns_adb {
        isc_boolean_t                   growentries_sent;
        isc_event_t                     grownames;
        isc_boolean_t                   grownames_sent;
+
+#ifdef ENABLE_FETCHLIMIT
+       isc_uint32_t                    quota;
+       isc_uint32_t                    atr_freq;
+       double                          atr_low;
+       double                          atr_high;
+       double                          atr_discount;
+#endif /* ENABLE_FETCHLIMIT */
 };
 
 /*
@@ -243,6 +251,17 @@ struct dns_adbentry {
 
        unsigned int                    flags;
        unsigned int                    srtt;
+
+       unsigned int                    timeouts;
+       unsigned int                    completed;
+
+#ifdef ENABLE_FETCHLIMIT
+       isc_uint8_t                     mode;
+       isc_uint32_t                    quota;
+       isc_uint32_t                    active;
+       double                          atr;
+#endif /* ENABLE_FETCHLIMIT */
+
        isc_sockaddr_t                  sockaddr;
 
        isc_stdtime_t                   expires;
@@ -287,6 +306,7 @@ static inline dns_adbentry_t *find_entry_and_lock(dns_adb_t *,
 static void dump_adb(dns_adb_t *, FILE *, isc_boolean_t debug, isc_stdtime_t);
 static void print_dns_name(FILE *, dns_name_t *);
 static void print_namehook_list(FILE *, const char *legend,
+                               dns_adb_t *adb,
                                dns_adbnamehooklist_t *list,
                                isc_boolean_t debug,
                                isc_stdtime_t now);
@@ -322,10 +342,15 @@ static inline void link_entry(dns_adb_t *, int, dns_adbentry_t *);
 static inline isc_boolean_t unlink_entry(dns_adb_t *, dns_adbentry_t *);
 static isc_boolean_t kill_name(dns_adbname_t **, isc_eventtype_t);
 static void water(void *, int);
-static void dump_entry(FILE *, dns_adbentry_t *, isc_boolean_t, isc_stdtime_t);
+static void dump_entry(FILE *, dns_adb_t *, dns_adbentry_t *,
+                      isc_boolean_t, isc_stdtime_t);
 static void adjustsrtt(dns_adbaddrinfo_t *addr, unsigned int rtt,
                       unsigned int factor, isc_stdtime_t now);
 static void shutdown_task(isc_task_t *task, isc_event_t *ev);
+#ifdef ENABLE_FETCHLIMIT
+static void log_quota(dns_adbentry_t *entry, const char *fmt, ...)
+     ISC_FORMAT_PRINTF(2, 3);
+#endif /* ENABLE_FETCHLIMIT */
 
 /*
  * MUST NOT overlap DNS_ADBFIND_* flags!
@@ -1767,10 +1792,18 @@ new_adbentry(dns_adb_t *adb) {
        e->refcnt = 0;
        e->nh = 0;
        e->flags = 0;
+       e->completed = 0;
+       e->timeouts = 0;
        isc_random_get(&r);
        e->srtt = (r & 0x1f) + 1;
        e->lastage = 0;
        e->expires = 0;
+#ifdef ENABLE_FETCHLIMIT
+       e->active = 0;
+       e->mode = 0;
+       e->quota = adb->quota;
+       e->atr = 0.0;
+#endif /* ENABLE_FETCHLIMIT */
        ISC_LIST_INIT(e->lameinfo);
        ISC_LINK_INIT(e, plink);
        LOCK(&adb->entriescntlock);
@@ -2079,6 +2112,27 @@ entry_is_lame(dns_adb_t *adb, dns_adbentry_t *entry, dns_name_t *qname,
        return (is_bad);
 }
 
+#ifdef ENABLE_FETCHLIMIT
+static void
+log_quota(dns_adbentry_t *entry, const char *fmt, ...) {
+       va_list ap;
+       char msgbuf[2048];
+       char addrbuf[ISC_NETADDR_FORMATSIZE];
+       isc_netaddr_t netaddr;
+
+       va_start(ap, fmt);
+       vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
+       va_end(ap);
+
+       isc_netaddr_fromsockaddr(&netaddr, &entry->sockaddr);
+       isc_netaddr_format(&netaddr, addrbuf, sizeof(addrbuf));
+
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ADB,
+                     ISC_LOG_INFO, "adb: quota %s (%d/%d): %s",
+                     addrbuf, entry->active, entry->quota, msgbuf);
+}
+#endif /* ENABLE_FETCHLIMIT */
+
 static void
 copy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, dns_name_t *qname,
                    dns_rdatatype_t qtype, dns_adbname_t *name,
@@ -2099,6 +2153,17 @@ copy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, dns_name_t *qname,
                        INSIST(bucket != DNS_ADB_INVALIDBUCKET);
                        LOCK(&adb->entrylocks[bucket]);
 
+#ifdef ENABLE_FETCHLIMIT
+                       if (entry->quota != 0 &&
+                           entry->active >= entry->quota)
+                       {
+                               find->options |=
+                                       (DNS_ADBFIND_LAMEPRUNED|
+                                        DNS_ADBFIND_OVERQUOTA);
+                               goto nextv4;
+                       }
+#endif /* ENABLE_FETCHLIMIT */
+
                        if (!FIND_RETURNLAME(find)
                            && entry_is_lame(adb, entry, qname, qtype, now)) {
                                find->options |= DNS_ADBFIND_LAMEPRUNED;
@@ -2130,6 +2195,17 @@ copy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, dns_name_t *qname,
                        INSIST(bucket != DNS_ADB_INVALIDBUCKET);
                        LOCK(&adb->entrylocks[bucket]);
 
+#ifdef ENABLE_FETCHLIMIT
+                       if (entry->quota != 0 &&
+                           entry->active >= entry->quota)
+                       {
+                               find->options |=
+                                       (DNS_ADBFIND_LAMEPRUNED|
+                                        DNS_ADBFIND_OVERQUOTA);
+                               goto nextv6;
+                       }
+#endif /* ENABLE_FETCHLIMIT */
+
                        if (!FIND_RETURNLAME(find)
                            && entry_is_lame(adb, entry, qname, qtype, now)) {
                                find->options |= DNS_ADBFIND_LAMEPRUNED;
@@ -2462,6 +2538,14 @@ dns_adb_create(isc_mem_t *mem, dns_view_t *view, isc_timermgr_t *timermgr,
                       adb, NULL, NULL);
        adb->growentries_sent = ISC_FALSE;
 
+#ifdef ENABLE_FETCHLIMIT
+       adb->quota = 0;
+       adb->atr_freq = 0;
+       adb->atr_low = 0.0;
+       adb->atr_high = 0.0;
+       adb->atr_discount = 0.0;
+#endif /* ENABLE_FETCHLIMIT */
+
        adb->nnames = nbuckets[0];
        adb->namescnt = 0;
        adb->names = NULL;
@@ -3357,8 +3441,10 @@ dump_adb(dns_adb_t *adb, FILE *f, isc_boolean_t debug, isc_stdtime_t now) {
 
                        fprintf(f, "\n");
 
-                       print_namehook_list(f, "v4", &name->v4, debug, now);
-                       print_namehook_list(f, "v6", &name->v6, debug, now);
+                       print_namehook_list(f, "v4", adb,
+                                           &name->v4, debug, now);
+                       print_namehook_list(f, "v6", adb,
+                                           &name->v6, debug, now);
 
                        if (debug)
                                print_fetch_list(f, name);
@@ -3373,7 +3459,7 @@ dump_adb(dns_adb_t *adb, FILE *f, isc_boolean_t debug, isc_stdtime_t now) {
                entry = ISC_LIST_HEAD(adb->entries[i]);
                while (entry != NULL) {
                        if (entry->nh == 0)
-                               dump_entry(f, entry, debug, now);
+                               dump_entry(f, adb, entry, debug, now);
                        entry = ISC_LIST_NEXT(entry, plink);
                }
        }
@@ -3388,14 +3474,18 @@ dump_adb(dns_adb_t *adb, FILE *f, isc_boolean_t debug, isc_stdtime_t now) {
 }
 
 static void
-dump_entry(FILE *f, dns_adbentry_t *entry, isc_boolean_t debug,
-          isc_stdtime_t now)
+dump_entry(FILE *f, dns_adb_t *adb, dns_adbentry_t *entry,
+          isc_boolean_t debug, isc_stdtime_t now)
 {
        char addrbuf[ISC_NETADDR_FORMATSIZE];
        char typebuf[DNS_RDATATYPE_FORMATSIZE];
        isc_netaddr_t netaddr;
        dns_adblameinfo_t *li;
 
+#ifndef ENABLE_FETCHLIMIT
+       UNUSED(adb);
+#endif /* !ENABLE_FETCHLIMIT */
+
        isc_netaddr_fromsockaddr(&netaddr, &entry->sockaddr);
        isc_netaddr_format(&netaddr, addrbuf, sizeof(addrbuf));
 
@@ -3406,10 +3496,19 @@ dump_entry(FILE *f, dns_adbentry_t *entry, isc_boolean_t debug,
                addrbuf, entry->srtt, entry->flags);
        if (entry->expires != 0)
                fprintf(f, " [ttl %d]", entry->expires - now);
+
+#ifdef ENABLE_FETCHLIMIT
+       if (adb != NULL && adb->quota != 0 && adb->atr_freq != 0) {
+               fprintf(f, " [atr %0.2f] [quota %d]",
+                       entry->atr, entry->quota);
+       }
+#endif /* ENABLE_FETCHLIMIT */
+
        fprintf(f, "\n");
        for (li = ISC_LIST_HEAD(entry->lameinfo);
             li != NULL;
-            li = ISC_LIST_NEXT(li, plink)) {
+            li = ISC_LIST_NEXT(li, plink))
+       {
                fprintf(f, ";\t\t");
                print_dns_name(f, &li->qname);
                dns_rdatatype_format(li->qtype, typebuf, sizeof(typebuf));
@@ -3481,7 +3580,8 @@ print_dns_name(FILE *f, dns_name_t *name) {
 }
 
 static void
-print_namehook_list(FILE *f, const char *legend, dns_adbnamehooklist_t *list,
+print_namehook_list(FILE *f, const char *legend,
+                   dns_adb_t *adb, dns_adbnamehooklist_t *list,
                    isc_boolean_t debug, isc_stdtime_t now)
 {
        dns_adbnamehook_t *nh;
@@ -3492,7 +3592,7 @@ print_namehook_list(FILE *f, const char *legend, dns_adbnamehooklist_t *list,
        {
                if (debug)
                        fprintf(f, ";\tHook(%s) %p\n", legend, nh);
-               dump_entry(f, nh->entry, debug, now);
+               dump_entry(f, adb, nh->entry, debug, now);
        }
 }
 
@@ -4058,6 +4158,114 @@ dns_adb_changeflags(dns_adb_t *adb, dns_adbaddrinfo_t *addr,
        UNLOCK(&adb->entrylocks[bucket]);
 }
 
+#ifdef ENABLE_FETCHLIMIT
+/*
+ * (10000 / ((10 + n) / 10)^(3/2)) for n in 0..99.
+ * These will be used to make quota adjustments.
+ */
+static int quota_adj[] = {
+       10000, 8668, 7607, 6747, 6037, 5443, 4941, 4512, 4141,
+       3818, 3536, 3286, 3065, 2867, 2690, 2530, 2385, 2254,
+       2134, 2025, 1925, 1832, 1747, 1668, 1595, 1527, 1464,
+       1405, 1350, 1298, 1250, 1205, 1162, 1121, 1083, 1048,
+       1014, 981, 922, 894, 868, 843, 820, 797, 775, 755,
+       735, 716, 698, 680, 664, 648, 632, 618, 603, 590, 577,
+       564, 552, 540, 529, 518, 507, 497, 487, 477, 468, 459,
+       450, 442, 434, 426, 418, 411, 404, 397, 390, 383, 377,
+       370, 364, 358, 353, 347, 342, 336, 331, 326, 321, 316,
+       312, 307, 303, 298, 294, 290, 286, 282, 278
+};
+
+/*
+ * Caller must hold adbentry lock
+ */
+static void
+maybe_adjust_quota(dns_adb_t *adb, dns_adbaddrinfo_t *addr,
+                  isc_boolean_t timeout)
+{
+       double tr;
+
+       UNUSED(adb);
+
+       if (adb->quota == 0 || adb->atr_freq == 0)
+               return;
+
+       if (timeout)
+               addr->entry->timeouts++;
+
+       if (addr->entry->completed++ <= adb->atr_freq)
+               return;
+
+       /*
+        * Calculate an exponential rolling average of the timeout ratio
+        *
+        * XXX: Integer arithmetic might be better than floating point
+        */
+       tr = (double) addr->entry->timeouts / addr->entry->completed;
+       addr->entry->timeouts = addr->entry->completed = 0;
+       INSIST(addr->entry->atr >= 0.0);
+       INSIST(addr->entry->atr <= 1.0);
+       INSIST(adb->atr_discount >= 0.0);
+       INSIST(adb->atr_discount <= 1.0);
+       addr->entry->atr *= 1.0 - adb->atr_discount;
+       addr->entry->atr += tr * adb->atr_discount;
+       addr->entry->atr = ISC_CLAMP(addr->entry->atr, 0.0, 1.0);
+
+       if (addr->entry->atr < adb->atr_low && addr->entry->mode > 0) {
+               addr->entry->quota = adb->quota *
+                       quota_adj[--addr->entry->mode] / 10000;
+               log_quota(addr->entry, "atr %0.2f, quota increased to %d",
+                         addr->entry->atr, addr->entry->quota);
+       } else if (addr->entry->atr > adb->atr_high && addr->entry->mode < 99) {
+               addr->entry->quota = adb->quota *
+                       quota_adj[++addr->entry->mode] / 10000;
+               log_quota(addr->entry, "atr %0.2f, quota decreased to %d",
+                         addr->entry->atr, addr->entry->quota);
+       }
+
+       /* Ensure we don't drop to zero */
+       if (addr->entry->quota == 0)
+               addr->entry->quota = 1;
+}
+#endif /* ENABLE_FETCHLIMIT */
+
+void
+dns_adb_plainresponse(dns_adb_t *adb, dns_adbaddrinfo_t *addr) {
+       int bucket;
+
+       REQUIRE(DNS_ADB_VALID(adb));
+       REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+
+       bucket = addr->entry->lock_bucket;
+       LOCK(&adb->entrylocks[bucket]);
+
+#ifdef ENABLE_FETCHLIMIT
+       maybe_adjust_quota(adb, addr, ISC_FALSE);
+#endif /* ENABLE_FETCHLIMIT */
+
+       UNLOCK(&adb->entrylocks[bucket]);
+}
+
+void
+dns_adb_timeout(dns_adb_t *adb, dns_adbaddrinfo_t *addr) {
+#ifdef ENABLE_FETCHLIMIT
+       int bucket;
+
+       REQUIRE(DNS_ADB_VALID(adb));
+       REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+
+       bucket = addr->entry->lock_bucket;
+       LOCK(&adb->entrylocks[bucket]);
+       maybe_adjust_quota(adb, addr, ISC_TRUE);
+       UNLOCK(&adb->entrylocks[bucket]);
+#else
+       UNUSED(adb);
+       UNUSED(addr);
+
+       return;
+#endif /* !ENABLE_FETCHLIMIT */
+}
+
 isc_result_t
 dns_adb_findaddrinfo(dns_adb_t *adb, isc_sockaddr_t *sa,
                     dns_adbaddrinfo_t **addrp, isc_stdtime_t now)
@@ -4237,3 +4445,84 @@ dns_adb_setadbsize(dns_adb_t *adb, size_t size) {
        else
                isc_mem_setwater(adb->mctx, water, adb, hiwater, lowater);
 }
+
+void
+dns_adb_setquota(dns_adb_t *adb, isc_uint32_t quota, isc_uint32_t freq,
+                double low, double high, double discount)
+{
+#ifdef ENABLE_FETCHLIMIT
+       REQUIRE(DNS_ADB_VALID(adb));
+
+       adb->quota = quota;
+       adb->atr_freq = freq;
+       adb->atr_low = low;
+       adb->atr_high = high;
+       adb->atr_discount = discount;
+#else
+       UNUSED(adb);
+       UNUSED(quota);
+       UNUSED(freq);
+       UNUSED(low);
+       UNUSED(high);
+       UNUSED(discount);
+
+       return;
+#endif /* !ENABLE_FETCHLIMIT */
+}
+
+isc_boolean_t
+dns_adbentry_overquota(dns_adbentry_t *entry) {
+#ifdef ENABLE_FETCHLIMIT
+       isc_boolean_t block;
+       REQUIRE(DNS_ADBENTRY_VALID(entry));
+       block = ISC_TF(entry->quota != 0 && entry->active >= entry->quota);
+       return (block);
+#else
+       UNUSED(entry);
+
+       return (ISC_FALSE);
+#endif /* !ENABLE_FETCHLIMIT */
+}
+
+void
+dns_adb_beginudpfetch(dns_adb_t *adb, dns_adbaddrinfo_t *addr) {
+#ifdef ENABLE_FETCHLIMIT
+       int bucket;
+
+       REQUIRE(DNS_ADB_VALID(adb));
+       REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+
+       bucket = addr->entry->lock_bucket;
+
+       LOCK(&adb->entrylocks[bucket]);
+       addr->entry->active++;
+       UNLOCK(&adb->entrylocks[bucket]);
+#else
+       UNUSED(adb);
+       UNUSED(addr);
+
+       return;
+#endif /* !ENABLE_FETCHLIMIT */
+}
+
+void
+dns_adb_endudpfetch(dns_adb_t *adb, dns_adbaddrinfo_t *addr) {
+#ifdef ENABLE_FETCHLIMIT
+       int bucket;
+
+       REQUIRE(DNS_ADB_VALID(adb));
+       REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+
+       bucket = addr->entry->lock_bucket;
+
+       LOCK(&adb->entrylocks[bucket]);
+       if (addr->entry->active > 0)
+               addr->entry->active--;
+       UNLOCK(&adb->entrylocks[bucket]);
+#else
+       UNUSED(adb);
+       UNUSED(addr);
+
+       return;
+#endif /* !ENABLE_FETCHLIMIT */
+}
index 8222bd211006347369fb353c9f8fbddf90125292..571e859ec1bc171392819f34c35649eb447597c0 100644 (file)
@@ -206,6 +206,11 @@ struct dns_adbfind {
  *      Must set _WANTEVENT for this to be meaningful.
  */
 #define DNS_ADBFIND_LAMEPRUNED         0x00000200
+/*%
+ *      The server's fetch quota is exceeded; it will be treated as
+ *      lame for this query.
+ */
+#define DNS_ADBFIND_OVERQUOTA          0x00000400
 
 /*%
  * The answers to queries come back as a list of these.
@@ -583,6 +588,30 @@ dns_adb_changeflags(dns_adb_t *adb, dns_adbaddrinfo_t *addr,
  *\li  addr be valid.
  */
 
+void
+dns_adb_plainresponse(dns_adb_t *adb, dns_adbaddrinfo_t *addr);
+/*%
+ * Record a successful DNS response.
+ *
+ * Requires:
+ *
+ *\li  adb be valid.
+ *
+ *\li  addr be valid.
+ */
+
+void
+dns_adb_timeout(dns_adb_t *adb, dns_adbaddrinfo_t *addr);
+/*%
+ * Record a DNS UDP query failed.
+ *
+ * Requires:
+ *
+ *\li  adb be valid.
+ *
+ *\li  addr be valid.
+ */
+
 isc_result_t
 dns_adb_findaddrinfo(dns_adb_t *adb, isc_sockaddr_t *sa,
                     dns_adbaddrinfo_t **addrp, isc_stdtime_t now);
@@ -647,6 +676,64 @@ dns_adb_flushname(dns_adb_t *adb, dns_name_t *name);
  *\li  'name' is valid.
  */
 
+void
+dns_adb_setquota(dns_adb_t *adb, isc_uint32_t quota, isc_uint32_t freq,
+                double low, double high, double discount);
+/*%<
+ * Set the baseline ADB quota, and configure parameters for the
+ * quota adjustment algorithm.
+ *
+ * If the number of fetches currently waiting for responses from this
+ * address exceeds the current quota, then additional fetches are spilled.
+ *
+ * 'quota' is the highest permissible quota; it will adjust itself
+ * downward in response to detected congestion.
+ *
+ * After every 'freq' fetches have either completed or timed out, an
+ * exponentially weighted moving average of the ratio of timeouts
+ * to responses is calculated.  If the EWMA goes above a 'high'
+ * threshold, then the quota is adjusted down one step; if it drops
+ * below a 'low' threshold, then the quota is adjusted back up one
+ * step.
+ *
+ * The quota adjustment is based on the function (1 / 1 + (n/10)^(3/2)),
+ * for values of n from 0 to 99.  It starts at 100% of the baseline
+ * quota, and descends after 100 steps to 2%.
+ *
+ * 'discount' represents the discount rate of the moving average. Higher
+ * values cause older values to be discounted sooner, providing a faster
+ * response to changes in the timeout ratio.
+ *
+ * Requires:
+ *\li  'adb' is valid.
+ */
+
+isc_boolean_t
+dns_adbentry_overquota(dns_adbentry_t *entry);
+/*%<
+ * Returns true if the specified ADB has too many active fetches.
+ *
+ * Requires:
+ *\li  'entry' is valid.
+ */
+
+void
+dns_adb_beginudpfetch(dns_adb_t *adb, dns_adbaddrinfo_t *addr);
+void
+dns_adb_endudpfetch(dns_adb_t *adb, dns_adbaddrinfo_t *addr);
+/*%
+ * Begin/end a UDP fetch on a particular address.
+ *
+ * These functions increment or decrement the fetch counter for
+ * the ADB entry so that the fetch quota can be enforced.
+ *
+ * Requires:
+ *
+ *\li  adb be valid.
+ *
+ *\li  addr be valid.
+ */
+
 ISC_LANG_ENDDECLS
 
 #endif /* DNS_ADB_H */
index 488b48e33de2e2eb31ba342e2c4726e7a7ca44a5..4b0e75b19f2dfbc8a2384e6f5bfcd0ecc9718b85 100644 (file)
@@ -15,8 +15,6 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: log.h,v 1.47 2011/10/13 22:48:24 tbox Exp $ */
-
 /*! \file dns/log.h
  * \author  Principal Authors: DCL */
 
@@ -45,6 +43,7 @@ LIBDNS_EXTERNAL_DATA extern isc_logmodule_t dns_modules[];
 #define DNS_LOGCATEGORY_RPZ            (&dns_categories[12])
 #define DNS_LOGCATEGORY_RRL            (&dns_categories[13])
 #define DNS_LOGCATEGORY_CNAME          (&dns_categories[14])
+#define DNS_LOGCATEGORY_SPILL          (&dns_categories[15])
 
 /* Backwards compatibility. */
 #define DNS_LOGCATEGORY_GENERAL                ISC_LOGCATEGORY_GENERAL
index a533f4ed2b24f8be6b6579cb88fd331bb846a16f..61fd4ed91e50d172e9f6fd01791c40d686d01261 100644 (file)
@@ -15,8 +15,6 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: resolver.h,v 1.72 2011/12/05 17:10:51 each Exp $ */
-
 #ifndef DNS_RESOLVER_H
 #define DNS_RESOLVER_H 1
 
@@ -54,6 +52,7 @@
 
 #include <isc/lang.h>
 #include <isc/socket.h>
+#include <isc/stats.h>
 
 #include <dns/types.h>
 #include <dns/fixedname.h>
@@ -84,6 +83,14 @@ typedef struct dns_fetchevent {
        isc_result_t                    vresult;
 } dns_fetchevent_t;
 
+/*%
+ * The two quota types (fetches-per-zone and fetches-per-server)
+ */
+typedef enum {
+       dns_quotatype_zone = 0,
+       dns_quotatype_server
+} dns_quotatype_t;
+
 /*
  * Options that modify how a 'fetch' is done.
  */
@@ -532,6 +539,8 @@ dns_resolver_gettimeout(dns_resolver_t *resolver);
 void
 dns_resolver_setclientsperquery(dns_resolver_t *resolver,
                                isc_uint32_t min, isc_uint32_t max);
+void
+dns_resolver_setfetchesperzone(dns_resolver_t *resolver, isc_uint32_t clients);
 
 void
 dns_resolver_getclientsperquery(dns_resolver_t *resolver, isc_uint32_t *cur,
@@ -612,6 +621,30 @@ dns_resolver_getmaxqueries(dns_resolver_t *resolver);
  * \li resolver to be valid.
  */
 
+void
+dns_resolver_dumpfetches(dns_resolver_t *resolver, FILE *fp);
+
+void
+dns_resolver_setquotaresponse(dns_resolver_t *resolver,
+                            dns_quotatype_t which, isc_result_t resp);
+isc_result_t
+dns_resolver_getquotaresponse(dns_resolver_t *resolver, dns_quotatype_t which);
+/*%
+ * Get and set the result code that will be used when quotas
+ * are exceeded. If 'which' is set to quotatype "zone", then the
+ * result specified in 'resp' will be used when the fetches-per-zone
+ * quota is exceeded by a fetch.  If 'which' is set to quotatype "server",
+ * then the reuslt specified in 'resp' will be used when the 
+ * fetches-per-server quota has been exceeded for all the
+ * authoritative servers for a zone.  Valid choices are
+ * DNS_R_DROP or DNS_R_SERVFAIL.
+ *
+ * Requires:
+ * \li 'resolver' to be valid.
+ * \li 'which' to be dns_quotatype_zone or dns_quotatype_server
+ * \li 'resp' to be DNS_R_DROP or DNS_R_SERVFAIL.
+ */
+
 ISC_LANG_ENDDECLS
 
 #endif /* DNS_RESOLVER_H */
index 5364267272a02b6c4fea07a2a282e65ceb1cf187..8ae5de59048ba305e6c1dc255c1deeb0ba60123f 100644 (file)
@@ -61,8 +61,10 @@ enum {
        dns_resstatscounter_queryrtt3 = 27,
        dns_resstatscounter_queryrtt4 = 28,
        dns_resstatscounter_queryrtt5 = 29,
+       dns_resstatscounter_zonequota = 30,
+       dns_resstatscounter_serverquota = 31,
 
-       dns_resstatscounter_max = 30,
+       dns_resstatscounter_max = 32,
 
        /*
         * DNSSEC stats.
index 70055aaf887b0b207e71bf8cbd5d260e782ceec8..05a83e1496f4babb6230129a2d777ce4463c97eb 100644 (file)
@@ -15,8 +15,6 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: log.c,v 1.49 2011/10/13 22:48:24 tbox Exp $ */
-
 /*! \file */
 
 /* Principal Authors: DCL */
@@ -47,6 +45,7 @@ LIBDNS_EXTERNAL_DATA isc_logcategory_t dns_categories[] = {
        { "rpz",        0 },
        { "rate-limit", 0 },
        { "cname",      0 },
+       { "spill",      0 },
        { NULL,         0 }
 };
 
index 0ffb8606c32c9e7d36a21e8ad7857f93d0c8f08c..e885015daedea296e37ffb071be388ab5998be54 100644 (file)
@@ -15,8 +15,6 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id$ */
-
 /*! \file */
 
 #include <config.h>
 #define DEFAULT_QUERY_TIMEOUT MINIMUM_QUERY_TIMEOUT
 #endif
 
+/* The maximum time in seconds for the whole query to live. */
 #ifndef MAXIMUM_QUERY_TIMEOUT
-#define MAXIMUM_QUERY_TIMEOUT 30 /* The maximum time in seconds for the whole query to live. */
+#define MAXIMUM_QUERY_TIMEOUT 30
 #endif
 
 /* The default maximum number of recursions to follow before giving up. */
 #define DEFAULT_MAX_QUERIES 50
 #endif
 
+/* Number of hash buckets for zone counters */
+#ifndef RES_DOMAIN_BUCKETS
+#define RES_DOMAIN_BUCKETS     523
+#endif
+#define RES_NOBUCKET           0xffffffff
+
 /*%
  * Maximum EDNS0 input packet size.
  */
@@ -231,6 +236,7 @@ struct fetchctx {
        dns_rdatatype_t                 type;
        unsigned int                    options;
        unsigned int                    bucketnum;
+       unsigned int                    dbucketnum;
        char *                          info;
        isc_mem_t *                     mctx;
 
@@ -331,6 +337,7 @@ struct fetchctx {
        unsigned int                    querysent;
        unsigned int                    referrals;
        unsigned int                    lamecount;
+       unsigned int                    quotacount;
        unsigned int                    neterr;
        unsigned int                    badresp;
        unsigned int                    adberr;
@@ -391,6 +398,25 @@ typedef struct fctxbucket {
        isc_mem_t *                     mctx;
 } fctxbucket_t;
 
+#ifdef ENABLE_FETCHLIMIT
+typedef struct fctxcount fctxcount_t;
+struct fctxcount {
+       dns_fixedname_t                 fdname;
+       dns_name_t                      *domain;
+       isc_uint32_t                    count;
+       isc_uint32_t                    allowed;
+       isc_uint32_t                    dropped;
+       isc_stdtime_t                   logged;
+       ISC_LINK(fctxcount_t)           link;
+};
+
+typedef struct zonebucket {
+       isc_mutex_t                     lock;
+       isc_mem_t                       *mctx;
+       ISC_LIST(fctxcount_t)           list;
+} zonebucket_t;
+#endif /* ENABLE_FETCHLIMIT */
+
 typedef struct alternate {
        isc_boolean_t                   isaddress;
        union   {
@@ -436,6 +462,9 @@ struct dns_resolver {
        isc_boolean_t                   exclusivev6;
        unsigned int                    nbuckets;
        fctxbucket_t *                  buckets;
+#ifdef ENABLE_FETCHLIMIT
+       zonebucket_t *                  dbuckets;
+#endif /* ENABLE_FETCHLIMIT */
        isc_uint32_t                    lame_ttl;
        ISC_LIST(alternate_t)           alternates;
        isc_uint16_t                    udpsize;
@@ -454,6 +483,7 @@ struct dns_resolver {
        unsigned int                    query_timeout;
        unsigned int                    maxdepth;
        unsigned int                    maxqueries;
+       isc_result_t                    quotaresp[2];
 
        /* Locked by lock. */
        unsigned int                    references;
@@ -462,6 +492,7 @@ struct dns_resolver {
        unsigned int                    activebuckets;
        isc_boolean_t                   priming;
        unsigned int                    spillat;        /* clients-per-query */
+       unsigned int                    zspill;         /* fetches-per-zone */
 
        /* Bad cache. */
        dns_badcache_t  **              badcache;
@@ -898,19 +929,20 @@ fctx_cancelquery(resquery_t **queryp, dns_dispatchevent_t **deventp,
                        rtt = query->addrinfo->srtt + 200000;
                        if (rtt > MAX_SINGLE_QUERY_TIMEOUT_US)
                                rtt = MAX_SINGLE_QUERY_TIMEOUT_US;
+
                        /*
                         * Replace the current RTT with our value.
                         */
                        factor = DNS_ADB_RTTADJREPLACE;
+                       dns_adb_timeout(fctx->adb, query->addrinfo);
                }
+
                dns_adb_adjustsrtt(fctx->adb, query->addrinfo, rtt, factor);
        }
 
-       /* Remember that the server has been tried. */
-       if (!TRIED(query->addrinfo)) {
-               dns_adb_changeflags(fctx->adb, query->addrinfo,
-                                   FCTX_ADDRINFO_TRIED, FCTX_ADDRINFO_TRIED);
-       }
+#ifdef ENABLE_FETCHLIMIT
+       dns_adb_endudpfetch(fctx->adb, query->addrinfo);
+#endif /* ENABLE_FETCHLIMIT */
 
        /*
         * Age RTTs of servers not tried.
@@ -1091,6 +1123,128 @@ fctx_stopeverything(fetchctx_t *fctx, isc_boolean_t no_response) {
        fctx_stoptimer(fctx);
 }
 
+#ifdef ENABLE_FETCHLIMIT
+static void
+fcount_logspill(fetchctx_t *fctx, fctxcount_t *counter) {
+       char dbuf[DNS_NAME_FORMATSIZE];
+       isc_stdtime_t now;
+
+       if (! isc_log_wouldlog(dns_lctx, ISC_LOG_INFO))
+               return;
+
+       isc_stdtime_get(&now);
+       if (counter->logged > now - 60)
+               return;
+
+       dns_name_format(&fctx->domain, dbuf, sizeof(dbuf));
+
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_SPILL,
+                     DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO,
+                     "too many simultaneous fetches for %s "
+                     "(allowed %d spilled %d)",
+                     dbuf, counter->allowed, counter->dropped);
+
+       counter->logged = now;
+}
+
+static isc_result_t
+fcount_incr(fetchctx_t *fctx, isc_boolean_t force) {
+       isc_result_t result = ISC_R_SUCCESS;
+       zonebucket_t *dbucket;
+       fctxcount_t *counter;
+       unsigned int bucketnum, spill;
+
+       REQUIRE(fctx != NULL);
+       REQUIRE(fctx->res != NULL);
+
+       INSIST(fctx->dbucketnum == RES_NOBUCKET);
+       bucketnum = dns_name_fullhash(&fctx->domain, ISC_FALSE)
+                       % RES_DOMAIN_BUCKETS;
+
+       LOCK(&fctx->res->lock);
+       spill = fctx->res->zspill;
+       UNLOCK(&fctx->res->lock);
+
+       dbucket = &fctx->res->dbuckets[bucketnum];
+
+       LOCK(&dbucket->lock);
+       for (counter = ISC_LIST_HEAD(dbucket->list);
+            counter != NULL;
+            counter = ISC_LIST_NEXT(counter, link))
+       {
+               if (dns_name_equal(counter->domain, &fctx->domain))
+                       break;
+       }
+
+       if (counter == NULL) {
+               counter = isc_mem_get(dbucket->mctx, sizeof(fctxcount_t));
+               if (counter == NULL)
+                       result = ISC_R_NOMEMORY;
+               else {
+                       ISC_LINK_INIT(counter, link);
+                       counter->count = 1;
+                       counter->logged = 0;
+                       counter->allowed = 1;
+                       counter->dropped = 0;
+                       dns_fixedname_init(&counter->fdname);
+                       counter->domain = dns_fixedname_name(&counter->fdname);
+                       dns_name_copy(&fctx->domain, counter->domain, NULL);
+                       ISC_LIST_APPEND(dbucket->list, counter, link);
+               }
+       } else {
+               if (!force && spill != 0 && counter->count >= spill) {
+                       counter->dropped++;
+                       fcount_logspill(fctx, counter);
+                       result = ISC_R_QUOTA;
+               } else {
+                       counter->count++;
+                       counter->allowed++;
+               }
+       }
+       UNLOCK(&dbucket->lock);
+
+       if (result == ISC_R_SUCCESS)
+               fctx->dbucketnum = bucketnum;
+
+       return (result);
+}
+
+static void
+fcount_decr(fetchctx_t *fctx) {
+       zonebucket_t *dbucket;
+       fctxcount_t *counter;
+
+       REQUIRE(fctx != NULL);
+
+       if (fctx->dbucketnum == RES_NOBUCKET)
+               return;
+
+       dbucket = &fctx->res->dbuckets[fctx->dbucketnum];
+
+       LOCK(&dbucket->lock);
+       for (counter = ISC_LIST_HEAD(dbucket->list);
+            counter != NULL;
+            counter = ISC_LIST_NEXT(counter, link))
+       {
+               if (dns_name_equal(counter->domain, &fctx->domain))
+                       break;
+       }
+
+       if (counter != NULL) {
+               INSIST(counter->count != 0);
+               counter->count--;
+               fctx->dbucketnum = RES_NOBUCKET;
+
+               if (counter->count == 0) {
+                       ISC_LIST_UNLINK(dbucket->list, counter, link);
+                       isc_mem_put(dbucket->mctx, counter, sizeof(*counter));
+               }
+       }
+
+       UNLOCK(&dbucket->lock);
+}
+#endif /* ENABLE_FETCHLIMIT */
+
 static inline void
 fctx_sendevents(fetchctx_t *fctx, isc_result_t result, int line) {
        dns_fetchevent_t *event, *next_event;
@@ -1592,6 +1746,14 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
                query->connects++;
                QTRACE("connecting via TCP");
        } else {
+#ifdef ENABLE_FETCHLIMIT
+               if (dns_adbentry_overquota(addrinfo->entry))
+                       goto cleanup_dispatch;
+
+               /* Inform the ADB that we're starting a fetch */
+               dns_adb_beginudpfetch(fctx->adb, addrinfo);
+#endif /* ENABLE_FETCHLIMIT */
+
                result = resquery_send(query);
                if (result != ISC_R_SUCCESS)
                        goto cleanup_dispatch;
@@ -2552,7 +2714,7 @@ sort_finds(dns_adbfindlist_t *findlist) {
 static void
 findname(fetchctx_t *fctx, dns_name_t *name, in_port_t port,
         unsigned int options, unsigned int flags, isc_stdtime_t now,
-        isc_boolean_t *need_alternate)
+        isc_boolean_t *overquota, isc_boolean_t *need_alternate)
 {
        dns_adbaddrinfo_t *ai;
        dns_adbfind_t *find;
@@ -2560,6 +2722,10 @@ findname(fetchctx_t *fctx, dns_name_t *name, in_port_t port,
        isc_boolean_t unshared;
        isc_result_t result;
 
+#ifndef ENABLE_FETCHLIMIT
+       UNUSED(overquota);
+#endif /* !ENABLE_FETCHLIMIT */
+
        res = fctx->res;
        unshared = ISC_TF((fctx->options & DNS_FETCHOPT_UNSHARED) != 0);
        /*
@@ -2643,6 +2809,14 @@ findname(fetchctx_t *fctx, dns_name_t *name, in_port_t port,
                              find->result_v4 != DNS_R_NXDOMAIN)))
                                *need_alternate = ISC_TRUE;
                } else {
+#ifdef ENABLE_FETCHLIMIT
+                       if ((find->options & DNS_ADBFIND_OVERQUOTA) != 0) {
+                               if (overquota != NULL)
+                                       *overquota = ISC_TRUE;
+                               fctx->quotacount++; /* quota exceeded */
+                       }
+                       else
+#endif /* ENABLE_FETCHLIMIT */
                        if ((find->options & DNS_ADBFIND_LAMEPRUNED) != 0)
                                fctx->lamecount++; /* cached lame server */
                        else
@@ -2686,6 +2860,9 @@ fctx_getaddresses(fetchctx_t *fctx, isc_boolean_t badcache) {
        isc_boolean_t all_bad;
        dns_rdata_ns_t ns;
        isc_boolean_t need_alternate = ISC_FALSE;
+#ifdef ENABLE_FETCHLIMIT
+       isc_boolean_t all_spilled = ISC_TRUE;
+#endif /* ENABLE_FETCHLIMIT */
 
        FCTXTRACE("getaddresses");
 
@@ -2743,19 +2920,27 @@ fctx_getaddresses(fetchctx_t *fctx, isc_boolean_t badcache) {
 
                dns_fixedname_init(&fixed);
                domain = dns_fixedname_name(&fixed);
-               result = dns_fwdtable_find2(fctx->res->view->fwdtable, name,
+               result = dns_fwdtable_find2(res->view->fwdtable, name,
                                            domain, &forwarders);
                if (result == ISC_R_SUCCESS) {
                        sa = ISC_LIST_HEAD(forwarders->addrs);
                        fctx->fwdpolicy = forwarders->fwdpolicy;
                        if (fctx->fwdpolicy == dns_fwdpolicy_only &&
                            isstrictsubdomain(domain, &fctx->domain)) {
+#ifdef ENABLE_FETCHLIMIT
+                               fcount_decr(fctx);
+#endif /* ENABLE_FETCHLIMIT */
                                dns_name_free(&fctx->domain, fctx->mctx);
                                dns_name_init(&fctx->domain, NULL);
                                result = dns_name_dup(domain, fctx->mctx,
                                                      &fctx->domain);
                                if (result != ISC_R_SUCCESS)
                                        return (result);
+#ifdef ENABLE_FETCHLIMIT
+                               result = fcount_incr(fctx, ISC_TRUE);
+                               if (result != ISC_R_SUCCESS)
+                                       return (result);
+#endif /* ENABLE_FETCHLIMIT */
                        }
                }
        }
@@ -2829,6 +3014,8 @@ fctx_getaddresses(fetchctx_t *fctx, isc_boolean_t badcache) {
             result == ISC_R_SUCCESS;
             result = dns_rdataset_next(&fctx->nameservers))
        {
+               isc_boolean_t overquota = ISC_FALSE;
+
                dns_rdataset_current(&fctx->nameservers, &rdata);
                /*
                 * Extract the name from the NS record.
@@ -2838,7 +3025,13 @@ fctx_getaddresses(fetchctx_t *fctx, isc_boolean_t badcache) {
                        continue;
 
                findname(fctx, &ns.name, 0, stdoptions, 0, now,
-                        &need_alternate);
+                        &overquota, &need_alternate);
+
+#ifdef ENABLE_FETCHLIMIT
+               if (!overquota)
+                       all_spilled = ISC_FALSE;
+#endif /* ENABLE_FETCHLIMIT */
+
                dns_rdata_reset(&rdata);
                dns_rdata_freestruct(&ns);
        }
@@ -2852,13 +3045,13 @@ fctx_getaddresses(fetchctx_t *fctx, isc_boolean_t badcache) {
                int family;
                alternate_t *a;
                family = (res->dispatches6 != NULL) ? AF_INET6 : AF_INET;
-               for (a = ISC_LIST_HEAD(fctx->res->alternates);
+               for (a = ISC_LIST_HEAD(res->alternates);
                     a != NULL;
                     a = ISC_LIST_NEXT(a, link)) {
                        if (!a->isaddress) {
                                findname(fctx, &a->_u._n.name, a->_u._n.port,
                                         stdoptions, FCTX_ADDRINFO_FORWARDER,
-                                        now, NULL);
+                                        now, NULL, NULL);
                                continue;
                        }
                        if (isc_sockaddr_pf(&a->_u.addr) != family)
@@ -2917,10 +3110,21 @@ fctx_getaddresses(fetchctx_t *fctx, isc_boolean_t badcache) {
                             fctx->type == dns_rdatatype_dlv ||
                             fctx->type == dns_rdatatype_ds) &&
                             result == ISC_R_SUCCESS)
-                               dns_resolver_addbadcache(fctx->res,
-                                                        &fctx->name,
+                               dns_resolver_addbadcache(res, &fctx->name,
                                                         fctx->type, &expire);
-                       result = ISC_R_FAILURE;
+
+#ifdef ENABLE_FETCHLIMIT
+                       /*
+                        * If all of the addresses found were over the
+                        * fetches-per-server quota, return the configured
+                        * response.
+                        */
+                       if (all_spilled) {
+                               result = res->quotaresp[dns_quotatype_server];
+                               inc_stats(res, dns_resstatscounter_serverquota);
+                       } else
+                               result = ISC_R_FAILURE;
+#endif /* ENABLE_FETCHLIMIT */
                }
        } else {
                /*
@@ -3144,14 +3348,17 @@ fctx_nextaddress(fetchctx_t *fctx) {
 static void
 fctx_try(fetchctx_t *fctx, isc_boolean_t retrying, isc_boolean_t badcache) {
        isc_result_t result;
-       dns_adbaddrinfo_t *addrinfo;
+       dns_adbaddrinfo_t *addrinfo = NULL;
+       dns_resolver_t *res;
 
        FCTXTRACE("try");
 
        REQUIRE(!ADDRWAIT(fctx));
 
+       res = fctx->res;
+
        /* We've already exceeded maximum query count */
-       if (isc_counter_used(fctx->qc) > fctx->res->maxqueries) {
+       if (isc_counter_used(fctx->qc) > res->maxqueries) {
                isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
                              DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(3),
                              "exceeded max queries resolving '%s'",
@@ -3160,11 +3367,15 @@ fctx_try(fetchctx_t *fctx, isc_boolean_t retrying, isc_boolean_t badcache) {
                return;
        }
 
-       addrinfo = fctx_nextaddress(fctx);
+#ifdef ENABLE_FETCHLIMIT
+       /* Try to find an address that isn't over quota */
+       while ((addrinfo = fctx_nextaddress(fctx)) != NULL)
+               if (! dns_adbentry_overquota(addrinfo->entry))
+                       break;
+#endif /* ENABLE_FETCHLIMIT */
+
        if (addrinfo == NULL) {
-               /*
-                * We have no more addresses.  Start over.
-                */
+               /* We have no more addresses.  Start over. */
                fctx_cancelqueries(fctx, ISC_TRUE);
                fctx_cleanupfinds(fctx);
                fctx_cleanupaltfinds(fctx);
@@ -3186,7 +3397,15 @@ fctx_try(fetchctx_t *fctx, isc_boolean_t retrying, isc_boolean_t badcache) {
                        return;
                }
 
+#ifdef ENABLE_FETCHLIMIT
+               while ((addrinfo = fctx_nextaddress(fctx)) != NULL) {
+                       if (! dns_adbentry_overquota(addrinfo->entry))
+                               break;
+               }
+#else
                addrinfo = fctx_nextaddress(fctx);
+#endif /* !ENABLE_FETCHLIMIT */
+
                /*
                 * While we may have addresses from the ADB, they
                 * might be bad ones.  In this case, return SERVFAIL.
@@ -3213,7 +3432,7 @@ fctx_try(fetchctx_t *fctx, isc_boolean_t retrying, isc_boolean_t badcache) {
        if (result != ISC_R_SUCCESS)
                fctx_done(fctx, result, __LINE__);
        else if (retrying)
-               inc_stats(fctx->res, dns_resstatscounter_retry);
+               inc_stats(res, dns_resstatscounter_retry);
 }
 
 static isc_boolean_t
@@ -3308,6 +3527,11 @@ fctx_destroy(fetchctx_t *fctx) {
        }
 
        isc_counter_detach(&fctx->qc);
+
+#ifdef ENABLE_FETCHLIMIT
+       fcount_decr(fctx);
+#endif /* ENABLE_FETCHLIMIT */
+
        isc_timer_detach(&fctx->timer);
        dns_message_destroy(&fctx->rmessage);
        dns_message_destroy(&fctx->qmessage);
@@ -3705,6 +3929,7 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
        fctx->res = res;
        fctx->references = 0;
        fctx->bucketnum = bucketnum;
+       fctx->dbucketnum = RES_NOBUCKET;
        fctx->state = fetchstate_init;
        fctx->want_shutdown = ISC_FALSE;
        fctx->cloned = ISC_FALSE;
@@ -3731,6 +3956,7 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
        TIME_NOW(&fctx->start);
        fctx->timeouts = 0;
        fctx->lamecount = 0;
+       fctx->quotacount = 0;
        fctx->adberr = 0;
        fctx->neterr = 0;
        fctx->badresp = 0;
@@ -3823,6 +4049,18 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
                fctx->ns_ttl_ok = ISC_TRUE;
        }
 
+#ifdef ENABLE_FETCHLIMIT
+       /*
+        * Are there too many simultaneous queries for this domain?
+        */
+       result = fcount_incr(fctx, ISC_FALSE);
+       if (result != ISC_R_SUCCESS) {
+               result = fctx->res->quotaresp[dns_quotatype_zone];
+               inc_stats(res, dns_resstatscounter_zonequota);
+               goto cleanup_domain;
+       }
+#endif /* ENABLE_FETCHLIMIT */
+
        log_ns_ttl(fctx, "fctx_create");
 
        INSIST(dns_name_issubdomain(&fctx->name, &fctx->domain));
@@ -3832,7 +4070,11 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
                                    &fctx->qmessage);
 
        if (result != ISC_R_SUCCESS)
+#ifdef ENABLE_FETCHLIMIT
+               goto cleanup_fcount;
+#else
                goto cleanup_domain;
+#endif /* !ENABLE_FETCHLIMIT */
 
        fctx->rmessage = NULL;
        result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE,
@@ -3908,6 +4150,11 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
  cleanup_qmessage:
        dns_message_destroy(&fctx->qmessage);
 
+#ifdef ENABLE_FETCHLIMIT
+ cleanup_fcount:
+       fcount_decr(fctx);
+#endif /* ENABLE_FETCHLIMIT */
+
  cleanup_domain:
        if (dns_name_countlabels(&fctx->domain) > 0)
                dns_name_free(&fctx->domain, mctx);
@@ -6165,6 +6412,11 @@ noanswer_response(fetchctx_t *fctx, dns_name_t *oqname,
                 *              if so we should bail out.
                 */
                INSIST(dns_name_countlabels(&fctx->domain) > 0);
+
+#ifdef ENABLE_FETCHLIMIT
+               fcount_decr(fctx);
+#endif /* ENABLE_FETCHLIMIT */
+
                dns_name_free(&fctx->domain, fctx->mctx);
                if (dns_rdataset_isassociated(&fctx->nameservers))
                        dns_rdataset_disassociate(&fctx->nameservers);
@@ -6172,6 +6424,13 @@ noanswer_response(fetchctx_t *fctx, dns_name_t *oqname,
                result = dns_name_dup(ns_name, fctx->mctx, &fctx->domain);
                if (result != ISC_R_SUCCESS)
                        return (result);
+
+#ifdef ENABLE_FETCHLIMIT
+               result = fcount_incr(fctx, ISC_TRUE);
+               if (result != ISC_R_SUCCESS)
+                       return (result);
+#endif /* ENABLE_FETCHLIMIT */
+
                fctx->attributes |= FCTX_ATTR_WANTCACHE;
                fctx->ns_ttl_ok = ISC_FALSE;
                log_ns_ttl(fctx, "DELEGATION");
@@ -6738,6 +6997,11 @@ resume_dslookup(isc_task_t *task, isc_event_t *event) {
                fctx->ns_ttl = fctx->nameservers.ttl;
                fctx->ns_ttl_ok = ISC_TRUE;
                log_ns_ttl(fctx, "resume_dslookup");
+
+#ifdef ENABLE_FETCHLIMIT
+               fcount_decr(fctx);
+#endif /* ENABLE_FETCHLIMIT */
+
                dns_name_free(&fctx->domain, fctx->mctx);
                dns_name_init(&fctx->domain, NULL);
                result = dns_name_dup(&fctx->nsname, fctx->mctx, &fctx->domain);
@@ -6745,6 +7009,15 @@ resume_dslookup(isc_task_t *task, isc_event_t *event) {
                        fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
                        goto cleanup;
                }
+
+#ifdef ENABLE_FETCHLIMIT
+               result = fcount_incr(fctx, ISC_TRUE);
+               if (result != ISC_R_SUCCESS) {
+                       fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
+                       goto cleanup;
+               }
+#endif /* ENABLE_FETCHLIMIT */
+
                /*
                 * Try again.
                 */
@@ -7119,6 +7392,8 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
                }
        }
 
+       if ((options & DNS_FETCHOPT_TCP) == 0)
+               dns_adb_plainresponse(fctx->adb, query->addrinfo);
        result = dns_message_parse(message, &devent->buffer, 0);
        if (result != ISC_R_SUCCESS) {
                FCTXTRACE3("message failed to parse", result);
@@ -7600,6 +7875,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
                        fctx->referrals++;
                        fctx->querysent = 0;
                        fctx->lamecount = 0;
+                       fctx->quotacount = 0;
                        fctx->neterr = 0;
                        fctx->badresp = 0;
                        fctx->adberr = 0;
@@ -7731,6 +8007,11 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
                                fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
                                return;
                        }
+
+#ifdef ENABLE_FETCHLIMIT
+                       fcount_decr(fctx);
+#endif /* ENABLE_FETCHLIMIT */
+
                        dns_name_free(&fctx->domain, fctx->mctx);
                        dns_name_init(&fctx->domain, NULL);
                        result = dns_name_dup(fname, fctx->mctx, &fctx->domain);
@@ -7738,6 +8019,15 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
                                fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
                                return;
                        }
+
+#ifdef ENABLE_FETCHLIMIT
+                       result = fcount_incr(fctx, ISC_TRUE);
+                       if (result != ISC_R_SUCCESS) {
+                               fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
+                               return;
+                       }
+#endif /* ENABLE_FETCHLIMIT */
+
                        fctx->ns_ttl = fctx->nameservers.ttl;
                        fctx->ns_ttl_ok = ISC_TRUE;
                        fctx_cancelqueries(fctx, ISC_TRUE);
@@ -7858,6 +8148,15 @@ destroy(dns_resolver_t *res) {
        }
        isc_mem_put(res->mctx, res->buckets,
                    res->nbuckets * sizeof(fctxbucket_t));
+#ifdef ENABLE_FETCHLIMIT
+       for (i = 0; i < RES_DOMAIN_BUCKETS; i++) {
+               INSIST(ISC_LIST_EMPTY(res->dbuckets[i].list));
+               isc_mem_detach(&res->dbuckets[i].mctx);
+               DESTROYLOCK(&res->dbuckets[i].lock);
+       }
+       isc_mem_put(res->mctx, res->dbuckets,
+                   RES_DOMAIN_BUCKETS * sizeof(zonebucket_t));
+#endif /* ENABLE_FETCHLIMIT */
        if (res->dispatches4 != NULL)
                dns_dispatchset_destroy(&res->dispatches4);
        if (res->dispatches6 != NULL)
@@ -7967,6 +8266,9 @@ dns_resolver_create(dns_view_t *view,
        isc_task_t *task = NULL;
        char name[16];
        unsigned dispattr;
+#ifdef ENABLE_FETCHLIMIT
+       unsigned int dbuckets_created = 0;
+#endif /* ENABLE_FETCHLIMIT */
 
        /*
         * Create a resolver.
@@ -8003,10 +8305,13 @@ dns_resolver_create(dns_view_t *view,
        res->spillatmin = res->spillat = 10;
        res->spillatmax = 100;
        res->spillattimer = NULL;
+       res->zspill = 0;
        res->zero_no_soa_ttl = ISC_FALSE;
        res->query_timeout = DEFAULT_QUERY_TIMEOUT;
        res->maxdepth = DEFAULT_RECURSION_DEPTH;
        res->maxqueries = DEFAULT_MAX_QUERIES;
+       res->quotaresp[dns_quotatype_zone] = DNS_R_DROP;
+       res->quotaresp[dns_quotatype_server] = DNS_R_SERVFAIL;
        res->nbuckets = ntasks;
        res->activebuckets = ntasks;
        res->buckets = isc_mem_get(view->mctx,
@@ -8049,6 +8354,26 @@ dns_resolver_create(dns_view_t *view,
                buckets_created++;
        }
 
+#ifdef ENABLE_FETCHLIMIT
+       res->dbuckets = isc_mem_get(view->mctx,
+                                   RES_DOMAIN_BUCKETS * sizeof(zonebucket_t));
+       if (res->dbuckets == NULL) {
+               result = ISC_R_NOMEMORY;
+               goto cleanup_buckets;
+       }
+       for (i = 0; i < RES_DOMAIN_BUCKETS; i++) {
+               ISC_LIST_INIT(res->dbuckets[i].list);
+               res->dbuckets[i].mctx = NULL;
+               isc_mem_attach(view->mctx, &res->dbuckets[i].mctx);
+               result = isc_mutex_init(&res->dbuckets[i].lock);
+               if (result != ISC_R_SUCCESS) {
+                       isc_mem_detach(&res->dbuckets[i].mctx);
+                       goto cleanup_dbuckets;
+               }
+               dbuckets_created++;
+       }
+#endif /* ENABLE_FETCHLIMIT */
+
        res->dispatches4 = NULL;
        if (dispatchv4 != NULL) {
                dns_dispatchset_create(view->mctx, socketmgr, taskmgr,
@@ -8142,6 +8467,16 @@ dns_resolver_create(dns_view_t *view,
        if (res->dispatches4 != NULL)
                dns_dispatchset_destroy(&res->dispatches4);
 
+#ifdef ENABLE_FETCHLIMIT
+ cleanup_dbuckets:
+       for (i = 0; i < dbuckets_created; i++) {
+               DESTROYLOCK(&res->dbuckets[i].lock);
+               isc_mem_detach(&res->dbuckets[i].mctx);
+       }
+       isc_mem_put(view->mctx, res->dbuckets,
+                   RES_DOMAIN_BUCKETS * sizeof(zonebucket_t));
+#endif /* ENABLE_FETCHLIMIT*/
+
  cleanup_buckets:
        for (i = 0; i < buckets_created; i++) {
                isc_mem_detach(&res->buckets[i].mctx);
@@ -8736,8 +9071,12 @@ dns_resolver_logfetch(dns_fetch_t *fetch, isc_log_t *lctx,
                              "%" ISC_PRINT_QUADFORMAT "u."
                              "%06" ISC_PRINT_QUADFORMAT "u: %s/%s "
                              "[domain:%s,referral:%u,restart:%u,qrysent:%u,"
-                             "timeout:%u,lame:%u,neterr:%u,badresp:%u,"
-                             "adberr:%u,findfail:%u,valfail:%u]",
+                             "timeout:%u,lame:%u,"
+#ifdef ENABLE_FETCHLIMIT
+                             "quota:%u,"
+#endif /* ENABLE_FETCHLIMIT */
+                             "neterr:%u,"
+                             "badresp:%u,adberr:%u,findfail:%u,valfail:%u]",
                              __FILE__, fctx->exitline, fctx->info,
                              fctx->duration / US_PER_SEC,
                              fctx->duration % US_PER_SEC,
@@ -8745,6 +9084,9 @@ dns_resolver_logfetch(dns_fetch_t *fetch, isc_log_t *lctx,
                              isc_result_totext(fctx->vresult), domainbuf,
                              fctx->referrals, fctx->restarts,
                              fctx->querysent, fctx->timeouts, fctx->lamecount,
+#ifdef ENABLE_FETCHLIMIT
+                             fctx->quotacount,
+#endif /* ENABLE_FETCHLIMIT */
                              fctx->neterr, fctx->badresp, fctx->adberr,
                              fctx->findfail, fctx->valfail);
                fctx->logged = ISC_TRUE;
@@ -9366,6 +9708,24 @@ dns_resolver_setclientsperquery(dns_resolver_t *resolver, isc_uint32_t min,
        UNLOCK(&resolver->lock);
 }
 
+void
+dns_resolver_setfetchesperzone(dns_resolver_t *resolver, isc_uint32_t clients)
+{
+#ifdef ENABLE_FETCHLIMIT
+       REQUIRE(VALID_RESOLVER(resolver));
+
+       LOCK(&resolver->lock);
+       resolver->zspill = clients;
+       UNLOCK(&resolver->lock);
+#else
+       UNUSED(resolver);
+       UNUSED(clients);
+
+       return;
+#endif /* !ENABLE_FETCHLIMIT */
+}
+
+
 isc_boolean_t
 dns_resolver_getzeronosoattl(dns_resolver_t *resolver) {
        REQUIRE(VALID_RESOLVER(resolver));
@@ -9431,3 +9791,67 @@ dns_resolver_getmaxqueries(dns_resolver_t *resolver) {
        REQUIRE(VALID_RESOLVER(resolver));
        return (resolver->maxqueries);
 }
+
+void
+dns_resolver_dumpfetches(dns_resolver_t *resolver, FILE *fp) {
+#ifdef ENABLE_FETCHLIMIT
+       int i;
+
+       REQUIRE(VALID_RESOLVER(resolver));
+       REQUIRE(fp != NULL);
+
+       for (i = 0; i < RES_DOMAIN_BUCKETS; i++) {
+               fctxcount_t *fc;
+               LOCK(&resolver->dbuckets[i].lock);
+               for (fc = ISC_LIST_HEAD(resolver->dbuckets[i].list);
+                    fc != NULL;
+                    fc = ISC_LIST_NEXT(fc, link))
+               {
+                       dns_name_print(fc->domain, fp);
+                       fprintf(fp, ": %d active (%d spilled, %d allowed)\n",
+                               fc->count, fc->dropped, fc->allowed);
+               }
+               UNLOCK(&resolver->dbuckets[i].lock);
+       }
+#else
+       UNUSED(resolver);
+       UNUSED(fp);
+
+       return;
+#endif /* !ENABLE_FETCHLIMIT */
+}
+
+void
+dns_resolver_setquotaresponse(dns_resolver_t *resolver,
+                             dns_quotatype_t which, isc_result_t resp)
+{
+#ifdef ENABLE_FETCHLIMIT
+       REQUIRE(VALID_RESOLVER(resolver));
+       REQUIRE(which == dns_quotatype_zone || which == dns_quotatype_server);
+       REQUIRE(resp == DNS_R_DROP || resp == DNS_R_SERVFAIL);
+
+       resolver->quotaresp[which] = resp;
+#else
+       UNUSED(resolver);
+       UNUSED(which);
+       UNUSED(resp);
+
+       return;
+#endif /* !ENABLE_FETCHLIMIT */
+}
+
+isc_result_t
+dns_resolver_getquotaresponse(dns_resolver_t *resolver, dns_quotatype_t which)
+{
+#ifdef ENABLE_FETCHLIMIT
+       REQUIRE(VALID_RESOLVER(resolver));
+       REQUIRE(which == dns_quotatype_zone || which == dns_quotatype_server);
+
+       return (resolver->quotaresp[which]);
+#else
+       UNUSED(resolver);
+       UNUSED(which);
+
+       return (ISC_R_NOTIMPLEMENTED);
+#endif /* !ENABLE_FETCHLIMIT */
+}
index 51f758f4279c61aca701220867a6684df5d1c012..da1136c075acaeb5b2d9cf452142f3bfb6e545a1 100644 (file)
@@ -686,7 +686,8 @@ req_shutdown(isc_task_t *task, isc_event_t *event) {
 isc_result_t
 dns_view_createresolver(dns_view_t *view,
                        isc_taskmgr_t *taskmgr,
-                       unsigned int ntasks, unsigned int ndisp,
+                       unsigned int ntasks,
+                       unsigned int ndisp,
                        isc_socketmgr_t *socketmgr,
                        isc_timermgr_t *timermgr,
                        unsigned int options,
index ace82d4000e0b99c03cd0d09b616217376a5e588..1f21c037b9762f9b6442f89c55f28b13804eb74a 100644 (file)
@@ -35,6 +35,7 @@ dns_aclenv_init
 dns_adb_agesrtt
 dns_adb_adjustsrtt
 dns_adb_attach
+dns_adb_beginudpfetch
 dns_adb_cancelfind
 dns_adb_changeflags
 dns_adb_create
@@ -44,14 +45,17 @@ dns_adb_destroyfind
 dns_adb_detach
 dns_adb_dump
 dns_adb_dumpfind
+dns_adb_endudpfetch
 dns_adb_findaddrinfo
 dns_adb_flush
 dns_adb_flushname
 dns_adb_freeaddrinfo
 dns_adb_marklame
 dns_adb_setadbsize
+dns_adb_setquota
 dns_adb_shutdown
 dns_adb_whenshutdown
+dns_adbentry_overquota
 dns_byaddr_cancel
 dns_byaddr_create
 dns_byaddr_createptrname
@@ -741,10 +745,14 @@ dns_resolver_printbadcache
 dns_resolver_reset_algorithms
 dns_resolver_resetmustbesecure
 dns_resolver_setclientsperquery
+dns_resolver_setfetchesperzone
 dns_resolver_setlamettl
 dns_resolver_setmustbesecure
 dns_resolver_setmaxdepth
 dns_resolver_setmaxqueries
+dns_resolver_setquerydscp4
+dns_resolver_setquerydscp6
+dns_resolver_setquotaresponse
 dns_resolver_settimeout
 dns_resolver_setudpsize
 dns_resolver_setzeronosoattl
index 0a7799cd6a387b6bff6bf86d5b8e6e1b436245d9..0c1e94562f09ed05ebd71a04dbf93c8a7b313042 100644 (file)
@@ -56,6 +56,8 @@
 #define ISC_MAX(a, b)  ((a) > (b) ? (a) : (b))
 #define ISC_MIN(a, b)  ((a) < (b) ? (a) : (b))
 
+#define ISC_CLAMP(v, x, y) ((v) < (x) ? (x) : ((v) > (y) ? (y) : (v)))
+
 /*%
  * Use this to remove the const qualifier of a variable to assign it to
  * a non-const variable or pass it as a non-const function argument ...
index a99c7637bb85ecdc0d517640f73ba5557f07603c..f2aa48a2a30a9a2767ec360a7dbfe1b2b7e0e4b2 100644 (file)
@@ -247,6 +247,18 @@ cfg_obj_asuint64(const cfg_obj_t *obj);
  * \li     A 64-bit unsigned integer.
  */
 
+isc_uint32_t
+cfg_obj_asfixedpoint(const cfg_obj_t *obj);
+/*%<
+ * Returns the value of a configuration object of fixed point number.
+ *
+ * Requires:
+ * \li     'obj' points to a valid configuration object of fixed point type.
+ *
+ * Returns:
+ * \li     A 32-bit unsigned integer.
+ */
+
 isc_boolean_t
 cfg_obj_isstring(const cfg_obj_t *obj);
 /*%<
index ee76ff29ce7df9b19a1c7e7877a584f3b5dcbe60..2c0f9beea4f224b5ea5bd88b51bbbd6fccd77ce2 100644 (file)
@@ -255,6 +255,7 @@ LIBISCCFG_EXTERNAL_DATA extern cfg_rep_t cfg_rep_tuple;
 LIBISCCFG_EXTERNAL_DATA extern cfg_rep_t cfg_rep_sockaddr;
 LIBISCCFG_EXTERNAL_DATA extern cfg_rep_t cfg_rep_netprefix;
 LIBISCCFG_EXTERNAL_DATA extern cfg_rep_t cfg_rep_void;
+LIBISCCFG_EXTERNAL_DATA extern cfg_rep_t cfg_rep_fixedpoint;
 /*@}*/
 
 /*@{*/
@@ -278,6 +279,7 @@ LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_netprefix;
 LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_void;
 LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_token;
 LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_unsupported;
+LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_fixedpoint;
 /*@}*/
 
 isc_result_t
@@ -437,6 +439,12 @@ cfg_print_void(cfg_printer_t *pctx, const cfg_obj_t *obj);
 void
 cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type);
 
+isc_result_t
+cfg_parse_fixedpoint(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
+
+void
+cfg_print_fixedpoint(cfg_printer_t *pctx, const cfg_obj_t *obj);
+
 isc_result_t
 cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
 
index 500e4c6619d4bce7fe9b9cc2739d4c141f6a0995..0787ec5e0ddcd9e32035bc7758ed9cf93a5187f5 100644 (file)
@@ -105,6 +105,7 @@ static cfg_type_t cfg_type_optional_class;
 static cfg_type_t cfg_type_optional_facility;
 static cfg_type_t cfg_type_optional_keyref;
 static cfg_type_t cfg_type_optional_port;
+static cfg_type_t cfg_type_optional_uint32;
 static cfg_type_t cfg_type_options;
 static cfg_type_t cfg_type_portiplist;
 static cfg_type_t cfg_type_querysource4;
@@ -844,6 +845,58 @@ static cfg_type_t cfg_type_bracketed_portlist = {
        &cfg_rep_list, &cfg_type_portrange
 };
 
+#ifdef ENABLE_FETCHLIMIT
+/*%
+ * fetch-quota-params
+ */
+static cfg_tuplefielddef_t fetchquota_fields[] = {
+       { "frequency", &cfg_type_uint32, 0 },
+       { "low", &cfg_type_fixedpoint, 0 },
+       { "high", &cfg_type_fixedpoint, 0 },
+       { "discount", &cfg_type_fixedpoint, 0 },
+       { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_fetchquota = {
+       "fetchquota", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
+       &cfg_rep_tuple, fetchquota_fields
+};
+
+/*%
+ * fetches-per-server or fetches-per-zone
+ */
+static const char *response_enums[] = { "drop", "fail", NULL };
+
+static isc_result_t
+parse_optional_response(cfg_parser_t *pctx, const cfg_type_t *type,
+                       cfg_obj_t **ret)
+{
+       return (parse_enum_or_other(pctx, type, &cfg_type_void, ret));
+}
+
+static void
+doc_optional_response(cfg_printer_t *pctx, const cfg_type_t *type) {
+       UNUSED(type);
+       cfg_print_cstr(pctx, "[ ( drop | fail ) ]");
+}
+
+static cfg_type_t cfg_type_responsetype = {
+       "responsetype", parse_optional_response, cfg_print_ustring,
+       doc_optional_response, &cfg_rep_string, response_enums
+};
+
+static cfg_tuplefielddef_t fetchesper_fields[] = {
+       { "fetches", &cfg_type_uint32, 0 },
+       { "response", &cfg_type_responsetype, 0 },
+       { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_fetchesper = {
+       "fetchesper", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
+       &cfg_rep_tuple, fetchesper_fields
+};
+#endif /* ENABLE_FETCHLIMIT */
+
 /*%
  * Clauses that can be found within the top level of the named.conf
  * file only.
@@ -1091,7 +1144,7 @@ cleanup:
 }
 
 /*
- * Parse a tuple consisting of any kind of  required field followed
+ * Parse a tuple consisting of any kind of required field followed
  * by 2 or more optional keyvalues that can be in any order.
  */
 static isc_result_t
@@ -1283,8 +1336,7 @@ static cfg_type_t cfg_type_rrl = {
  */
 
 static void
-print_lookaside(cfg_printer_t *pctx, const cfg_obj_t *obj)
-{
+print_lookaside(cfg_printer_t *pctx, const cfg_obj_t *obj) {
        const cfg_obj_t *domain = obj->value.tuple[0];
 
        if (domain->value.string.length == 4 &&
@@ -1387,6 +1439,11 @@ view_clauses[] = {
        { "empty-server", &cfg_type_astring, 0 },
        { "empty-zones-enable", &cfg_type_boolean, 0 },
        { "fetch-glue", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
+#ifdef ENABLE_FETCHLIMIT
+       { "fetch-quota-params", &cfg_type_fetchquota, 0 },
+       { "fetches-per-server", &cfg_type_fetchesper, 0 },
+       { "fetches-per-zone", &cfg_type_fetchesper, 0 },
+#endif /* ENABLE_FETCHLIMIT */
        { "ixfr-from-differences", &cfg_type_ixfrdifftype, 0 },
        { "lame-ttl", &cfg_type_uint32, 0 },
        { "max-acache-size", &cfg_type_sizenodefault, 0 },
index 4a2c53c4fda8978ea64559d797d79a321fff6f60..c66342c291366e19291ec85e73bfa3ad1348bbd7 100644 (file)
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id$ */
-
 /*! \file */
 
 #include <config.h>
 
+#include <stdlib.h>
+
 #include <isc/buffer.h>
 #include <isc/dir.h>
 #include <isc/formatcheck.h>
@@ -122,6 +122,7 @@ cfg_rep_t cfg_rep_tuple = { "tuple", free_tuple };
 cfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_noop };
 cfg_rep_t cfg_rep_netprefix = { "netprefix", free_noop };
 cfg_rep_t cfg_rep_void = { "void", free_noop };
+cfg_rep_t cfg_rep_fixedpoint = { "fixedpoint", free_noop };
 
 /*
  * Configuration type definitions.
@@ -601,6 +602,80 @@ cfg_type_t cfg_type_void = {
        "void", cfg_parse_void, cfg_print_void, cfg_doc_void, &cfg_rep_void,
        NULL };
 
+/*
+ * Fixed point
+ */
+isc_result_t
+cfg_parse_fixedpoint(cfg_parser_t *pctx, const cfg_type_t *type,
+                    cfg_obj_t **ret)
+{
+       isc_result_t result;
+       cfg_obj_t *obj = NULL;
+       UNUSED(type);
+       size_t n1, n2, n3, l;
+       const char *p;
+
+       UNUSED(type);
+
+       CHECK(cfg_gettoken(pctx, 0));
+       if (pctx->token.type != isc_tokentype_string) {
+               cfg_parser_error(pctx, CFG_LOG_NEAR,
+                                "expected fixed point number");
+               return (ISC_R_UNEXPECTEDTOKEN);
+       }
+
+
+       p = TOKEN_STRING(pctx);
+       l = strlen(p);
+       n1 = strspn(p, "0123456789");
+       n2 = strspn(p + n1, ".");
+       n3 = strspn(p + n1 + n2, "0123456789");
+
+       if ((n1 + n2 + n3 != l) || (n1 + n3 == 0) ||
+           n1 > 5 || n2 > 1 || n3 > 2) {
+               cfg_parser_error(pctx, CFG_LOG_NEAR,
+                                "expected fixed point number");
+               return (ISC_R_UNEXPECTEDTOKEN);
+       }
+
+       CHECK(cfg_create_obj(pctx, &cfg_type_fixedpoint, &obj));
+
+       obj->value.uint32 = strtoul(p, NULL, 10) * 100;
+       switch (n3) {
+       case 2:
+               obj->value.uint32 += strtoul(p + n1 + n2, NULL, 10);
+               break;
+       case 1:
+               obj->value.uint32 += strtoul(p + n1 + n2, NULL, 10) * 10;
+               break;
+       }
+       *ret = obj;
+
+ cleanup:
+       return (result);
+}
+
+void
+cfg_print_fixedpoint(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+       char buf[64];
+       int n;
+
+       n = snprintf(buf, sizeof(buf), "%u.%02u",
+                    obj->value.uint32/100, obj->value.uint32%100);
+       INSIST(n > 0 && (size_t)n < sizeof(buf));
+       cfg_print_chars(pctx, buf, strlen(buf));
+}
+
+isc_uint32_t
+cfg_obj_asfixedpoint(const cfg_obj_t *obj) {
+       REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_fixedpoint);
+       return (obj->value.uint32);
+}
+
+cfg_type_t cfg_type_fixedpoint = {
+       "fixedpoint", cfg_parse_fixedpoint, cfg_print_fixedpoint,
+       cfg_doc_terminal, &cfg_rep_fixedpoint, NULL
+};
 
 /*
  * uint32
index 512b4cb1aff582d1e212f6fcbe294ad06486de8f..51e76161654c1a1c5d29005cccd7288b30215bcd 100644 (file)
@@ -31,6 +31,7 @@ cfg_lookingat_netaddr
 cfg_map_get
 cfg_map_getname
 cfg_obj_asboolean
+cfg_obj_asfixedpoint
 cfg_obj_asnetprefix
 cfg_obj_assockaddr
 cfg_obj_asstring
@@ -59,6 +60,7 @@ cfg_parse_bracketed_list
 cfg_parse_buffer
 cfg_parse_enum
 cfg_parse_file
+cfg_parse_fixedpoint
 cfg_parse_listelt
 cfg_parse_map
 cfg_parse_mapbody
@@ -88,6 +90,7 @@ cfg_print_boolean
 cfg_print_bracketed_list
 cfg_print_chars
 cfg_print_cstr
+cfg_print_fixedpoint
 cfg_print_grammar
 cfg_print_map
 cfg_print_mapbody