From: Evan Hunt Date: Thu, 9 Jul 2015 06:00:58 +0000 (-0700) Subject: [v9_9] DDoS mitigation features X-Git-Tag: v9.9.8b1~34 X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=ea36796f8222281f28aba9fc67428c03d043d244;p=thirdparty%2Fbind9.git [v9_9] DDoS mitigation features 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] --- diff --git a/CHANGES b/CHANGES index 59e1511686b..e3e02b56e76 100644 --- a/CHANGES +++ b/CHANGES @@ -189,6 +189,33 @@ 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] diff --git a/bin/named/client.c b/bin/named/client.c index 54da0c20240..de2d596c883 100644 --- a/bin/named/client.c +++ b/bin/named/client.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -111,6 +112,7 @@ */ #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; } diff --git a/bin/named/config.c b/bin/named/config.c index a32f12e660a..f61f0a85652 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -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\ diff --git a/bin/named/include/named/server.h b/bin/named/include/named/server.h index d3bec57f66a..a3696f1614c 100644 --- a/bin/named/include/named/server.h +++ b/bin/named/include/named/server.h @@ -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 diff --git a/bin/named/server.c b/bin/named/server.c index eb02aef08f5..ed301afe5fc 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -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: diff --git a/bin/named/statschannel.c b/bin/named/statschannel.c index 8d1a3e0cf0e..4bfd52176a9 100644 --- a/bin/named/statschannel.c +++ b/bin/named/statschannel.c @@ -14,8 +14,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: statschannel.c,v 1.28 2011/03/12 04:59:46 tbox Exp $ */ - /*! \file */ #include @@ -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; diff --git a/bin/rndc/rndc.c b/bin/rndc/rndc.c index 2803c34abd8..c7d8fe1f843 100644 --- a/bin/rndc/rndc.c +++ b/bin/rndc/rndc.c @@ -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\ diff --git a/bin/rndc/rndc.docbook b/bin/rndc/rndc.docbook index d49d5d88dc5..141ce5f37ca 100644 --- a/bin/rndc/rndc.docbook +++ b/bin/rndc/rndc.docbook @@ -267,7 +267,7 @@ - dumpdb -all|-cache|-zone view ... + dumpdb -all|-cache|-zone|-adb|-bad view ... Dump the server's caches (default) and/or zones to @@ -432,13 +432,17 @@ - recursing - - - Dump the list of queries named is currently - recursing on. - - + recursing + + + Dump the list of queries named 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.) + + diff --git a/bin/tests/system/Makefile.in b/bin/tests/system/Makefile.in index 7d7ecad6e87..80971670d48 100644 --- a/bin/tests/system/Makefile.in +++ b/bin/tests/system/Makefile.in @@ -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@ diff --git a/bin/tests/system/conf.sh.in b/bin/tests/system/conf.sh.in index bc8de7c8c87..30832f1c371 100644 --- a/bin/tests/system/conf.sh.in +++ b/bin/tests/system/conf.sh.in @@ -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 index 00000000000..6c48d82b591 --- /dev/null +++ b/bin/tests/system/ditch.pl @@ -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
] [-p ] [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; diff --git a/bin/tests/system/dlzexternal/driver.c b/bin/tests/system/dlzexternal/driver.c index f1dbfc954e0..8d8d8141d97 100644 --- a/bin/tests/system/dlzexternal/driver.c +++ b/bin/tests/system/dlzexternal/driver.c @@ -26,6 +26,7 @@ #include #include +#include #include #include #include diff --git a/bin/tests/system/fetchlimit/Makefile.in b/bin/tests/system/fetchlimit/Makefile.in new file mode 100644 index 00000000000..dd6912d5c3a --- /dev/null +++ b/bin/tests/system/fetchlimit/Makefile.in @@ -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 index 00000000000..d844502702b --- /dev/null +++ b/bin/tests/system/fetchlimit/ans4/ans.pl @@ -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 index 00000000000..4976b3ae986 --- /dev/null +++ b/bin/tests/system/fetchlimit/clean.sh @@ -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 index 00000000000..de308365d1f --- /dev/null +++ b/bin/tests/system/fetchlimit/fetchlimit.c @@ -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 +#include + +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 index 00000000000..d844502702b --- /dev/null +++ b/bin/tests/system/fetchlimit/lameserver/ans4/ans.pl @@ -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 index 00000000000..d1c822f2ea3 --- /dev/null +++ b/bin/tests/system/fetchlimit/lameserver/clean.sh @@ -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 index 00000000000..4354b46e3bd --- /dev/null +++ b/bin/tests/system/fetchlimit/lameserver/ns1/named.conf @@ -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 index 00000000000..cd74861619d --- /dev/null +++ b/bin/tests/system/fetchlimit/lameserver/ns1/root.db @@ -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 index 00000000000..128155420ee --- /dev/null +++ b/bin/tests/system/fetchlimit/lameserver/ns2/example.db @@ -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 index 00000000000..b276181ffe2 --- /dev/null +++ b/bin/tests/system/fetchlimit/lameserver/ns2/named.conf @@ -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 index 00000000000..e4a50391111 --- /dev/null +++ b/bin/tests/system/fetchlimit/lameserver/ns3/named1.conf @@ -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 index 00000000000..2a405129285 --- /dev/null +++ b/bin/tests/system/fetchlimit/lameserver/ns3/named2.conf @@ -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 index 00000000000..e017e420f43 --- /dev/null +++ b/bin/tests/system/fetchlimit/lameserver/ns3/named3.conf @@ -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 index 00000000000..2b369268d34 --- /dev/null +++ b/bin/tests/system/fetchlimit/lameserver/ns3/root.hint @@ -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 index 00000000000..89772208a2e --- /dev/null +++ b/bin/tests/system/fetchlimit/lameserver/setup.sh @@ -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 index 00000000000..e6e53be8895 --- /dev/null +++ b/bin/tests/system/fetchlimit/lameserver/tests.sh @@ -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 index 00000000000..4354b46e3bd --- /dev/null +++ b/bin/tests/system/fetchlimit/ns1/named.conf @@ -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 index 00000000000..cd74861619d --- /dev/null +++ b/bin/tests/system/fetchlimit/ns1/root.db @@ -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 index 00000000000..128155420ee --- /dev/null +++ b/bin/tests/system/fetchlimit/ns2/example.db @@ -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 index 00000000000..b276181ffe2 --- /dev/null +++ b/bin/tests/system/fetchlimit/ns2/named.conf @@ -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 index 00000000000..f34d1b80e44 --- /dev/null +++ b/bin/tests/system/fetchlimit/ns3/named.args @@ -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 index 00000000000..e4a50391111 --- /dev/null +++ b/bin/tests/system/fetchlimit/ns3/named1.conf @@ -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 index 00000000000..2a405129285 --- /dev/null +++ b/bin/tests/system/fetchlimit/ns3/named2.conf @@ -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 index 00000000000..e017e420f43 --- /dev/null +++ b/bin/tests/system/fetchlimit/ns3/named3.conf @@ -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 index 00000000000..2b369268d34 --- /dev/null +++ b/bin/tests/system/fetchlimit/ns3/root.hint @@ -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 index 00000000000..31ec7a43efd --- /dev/null +++ b/bin/tests/system/fetchlimit/prereq.sh @@ -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 index 00000000000..89772208a2e --- /dev/null +++ b/bin/tests/system/fetchlimit/setup.sh @@ -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 index 00000000000..83534e6501c --- /dev/null +++ b/bin/tests/system/fetchlimit/tests.sh @@ -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 diff --git a/bin/tests/system/resolver/tests.sh b/bin/tests/system/resolver/tests.sh index 9fbd899ed4e..e461d854b68 100755 --- a/bin/tests/system/resolver/tests.sh +++ b/bin/tests/system/resolver/tests.sh @@ -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 /' diff --git a/config.h.in b/config.h.in index eba5191f6cc..c336ba349d3 100644 --- a/config.h.in +++ b/config.h.in @@ -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 diff --git a/configure b/configure index 5fc806cdc59..c3fa3ddb532 100755 --- 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)" diff --git a/configure.in b/configure.in index fc38d1b46de..48cbc5d1c52 100644 --- a/configure.in +++ b/configure.in @@ -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)" diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml index e6248b9bc40..ceae588e346 100644 --- a/doc/arm/Bv9ARM-book.xml +++ b/doc/arm/Bv9ARM-book.xml @@ -4784,9 +4784,14 @@ badresp:1,adberr:0,findfail:0,valfail:0] max-transfer-time-out number; max-transfer-idle-in number; max-transfer-idle-out number; - tcp-clients number; reserved-sockets number; recursive-clients number; + tcp-clients number; + clients-per-query number ; + max-clients-per-query number ; + fetches-per-server number (drop | fail); + fetch-quota-params number fixedpoint fixedpoint fixedpoint ; + fetches-per-zonenumber (drop | fail); serial-query-rate number; serial-queries number; tcp-listen-queue number; @@ -4865,8 +4870,6 @@ badresp:1,adberr:0,findfail:0,valfail:0] acache-enable yes_or_no ; acache-cleaning-interval number; max-acache-size size_spec ; - clients-per-query number ; - max-clients-per-query number ; max-recursion-depth number ; max-recursion-queries number ; masterfile-format (text|raw) ; @@ -7933,17 +7936,33 @@ avoid-v6-udp-ports { 40000; range 50000 60000; }; recursive-clients - 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 1000. 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 recursive-clients option may - have to be decreased - on hosts with limited memory. - + have to be decreased on hosts with limited memory. + + + 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. + + + 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 is greater than + 1000, the soft quota is set to + minus 100; + otherwise it is set to 90% of + . + @@ -7958,6 +7977,187 @@ avoid-v6-udp-ports { 40000; range 50000 60000; }; + + clients-per-query + max-clients-per-query + + These set the + initial value (minimum) and maximum number of recursive + simultaneous clients for any given query + (<qname,qtype,qclass>) that the server will accept + before dropping additional clients. named will attempt to + self tune this value and changes will be logged. The + default values are 10 and 100. + + + 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, named 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. + + + If clients-per-query is set to zero, + then there is no limit on the number of clients per query + and no queries will be dropped. + + + If max-clients-per-query is set to zero, + then there is no upper bound other than imposed by + recursive-clients. + + + + + + fetches-per-zone + + + 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 . + + + When many clients simultaneously query for the + same name and type, the clients will all be attached + to the same fetch, up to the + limit, + and only one iterative query will be sent. + However, when clients are simultaneously + querying for different names + or types, multiple queries will be sent and + is not + effective as a limit. + + + Optionally, this value may be followed by the keyword + drop or fail, + indicating whether queries which exceed the fetch + quota for a zone will be dropped with no response, + or answered with SERVFAIL. The default is + drop. + + + If fetches-per-zone 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. + + + The current list of active fetches can be dumped by + running rndc recursing. 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 + 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.) + + + (Note: This option is only available when BIND is + built with configure --enable-fetchlimit.) + + + + + + fetches-per-server + + + 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 . + + + Optionally, this value may be followed by the keyword + drop or fail, + 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 + fail. + + + If fetches-per-server 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. + + + The fetches-per-server 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 fetches-per-server + is reduced for that server. If the timeout ratio + drops below a "low" threshold, then + fetches-per-server is increased. + The fetch-quota-params options + can be used to adjust the parameters for this + calculation. + + + (Note: This option is only available when BIND is + built with configure --enable-fetchlimit.) + + + + + + fetch-quota-params + + + Sets the parameters to use for dynamic resizing of + the quota in + response to detected congestion. + + + 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. + + + 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. + + + (Note: This option is only available when BIND is + built with configure --enable-fetchlimit.) + + + + reserved-sockets @@ -8658,72 +8858,6 @@ avoid-v6-udp-ports { 40000; range 50000 60000; }; - - clients-per-query - max-clients-per-query - - These set the - initial value (minimum) and maximum number of recursive - simultaneous clients for any given query - (<qname,qtype,qclass>) that the server will accept - before dropping additional clients. named will attempt to - self tune this value and changes will be logged. The - default values are 10 and 100. - - - 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, named 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. - - - If clients-per-query is set to zero, - then there is no limit on the number of clients per query - and no queries will be dropped. - - - If max-clients-per-query is set to zero, - then there is no upper bound other than imposed by - recursive-clients. - - - - - - max-recursion-depth - - - 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. - - - - - - max-recursion-queries - - - 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. - - - - notify-delay diff --git a/doc/arm/notes.xml b/doc/arm/notes.xml index d7b67a9d571..5b66d1bc969 100644 --- a/doc/arm/notes.xml +++ b/doc/arm/notes.xml @@ -55,7 +55,45 @@ New Features - None + + 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. + + + NOTE: These options are not available by default; use + configure --enable-fetchlimit to include + them in the build. + + + + + 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. + + + + + 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.) + + + + + Statistics counters have also been added to track the number + of queries affected by these quotas. + diff --git a/lib/dns/adb.c b/lib/dns/adb.c index 0a28171e26a..ec4af599da9 100644 --- a/lib/dns/adb.c +++ b/lib/dns/adb.c @@ -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 */ +} diff --git a/lib/dns/include/dns/adb.h b/lib/dns/include/dns/adb.h index 8222bd21100..571e859ec1b 100644 --- a/lib/dns/include/dns/adb.h +++ b/lib/dns/include/dns/adb.h @@ -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 */ diff --git a/lib/dns/include/dns/log.h b/lib/dns/include/dns/log.h index 488b48e33de..4b0e75b19f2 100644 --- a/lib/dns/include/dns/log.h +++ b/lib/dns/include/dns/log.h @@ -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 diff --git a/lib/dns/include/dns/resolver.h b/lib/dns/include/dns/resolver.h index a533f4ed2b2..61fd4ed91e5 100644 --- a/lib/dns/include/dns/resolver.h +++ b/lib/dns/include/dns/resolver.h @@ -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 #include +#include #include #include @@ -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 */ diff --git a/lib/dns/include/dns/stats.h b/lib/dns/include/dns/stats.h index 5364267272a..8ae5de59048 100644 --- a/lib/dns/include/dns/stats.h +++ b/lib/dns/include/dns/stats.h @@ -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. diff --git a/lib/dns/log.c b/lib/dns/log.c index 70055aaf887..05a83e1496f 100644 --- a/lib/dns/log.c +++ b/lib/dns/log.c @@ -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 } }; diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index 0ffb8606c32..e885015daed 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -15,8 +15,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ - /*! \file */ #include @@ -148,8 +146,9 @@ #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. */ @@ -162,6 +161,12 @@ #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 */ +} diff --git a/lib/dns/view.c b/lib/dns/view.c index 51f758f4279..da1136c075a 100644 --- a/lib/dns/view.c +++ b/lib/dns/view.c @@ -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, diff --git a/lib/dns/win32/libdns.def.in b/lib/dns/win32/libdns.def.in index ace82d4000e..1f21c037b97 100644 --- a/lib/dns/win32/libdns.def.in +++ b/lib/dns/win32/libdns.def.in @@ -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 diff --git a/lib/isc/include/isc/util.h b/lib/isc/include/isc/util.h index 0a7799cd6a3..0c1e94562f0 100644 --- a/lib/isc/include/isc/util.h +++ b/lib/isc/include/isc/util.h @@ -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 ... diff --git a/lib/isccfg/include/isccfg/cfg.h b/lib/isccfg/include/isccfg/cfg.h index a99c7637bb8..f2aa48a2a30 100644 --- a/lib/isccfg/include/isccfg/cfg.h +++ b/lib/isccfg/include/isccfg/cfg.h @@ -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); /*%< diff --git a/lib/isccfg/include/isccfg/grammar.h b/lib/isccfg/include/isccfg/grammar.h index ee76ff29ce7..2c0f9beea4f 100644 --- a/lib/isccfg/include/isccfg/grammar.h +++ b/lib/isccfg/include/isccfg/grammar.h @@ -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); diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 500e4c6619d..0787ec5e0dd 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -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 }, diff --git a/lib/isccfg/parser.c b/lib/isccfg/parser.c index 4a2c53c4fda..c66342c2913 100644 --- a/lib/isccfg/parser.c +++ b/lib/isccfg/parser.c @@ -15,12 +15,12 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ - /*! \file */ #include +#include + #include #include #include @@ -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 diff --git a/lib/isccfg/win32/libisccfg.def b/lib/isccfg/win32/libisccfg.def index 512b4cb1aff..51e76161654 100644 --- a/lib/isccfg/win32/libisccfg.def +++ b/lib/isccfg/win32/libisccfg.def @@ -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