diff --git a/bin/named/client.c b/bin/named/client.c index e68f96d..b467f06 100644 --- a/bin/named/client.c +++ b/bin/named/client.c @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -113,6 +114,9 @@ */ #endif + +#define NBUCKETS 251 + /*% nameserver client manager structure */ struct ns_clientmgr { /* Unlocked. */ @@ -133,9 +137,10 @@ struct ns_clientmgr { isc_mutex_t listlock; client_list_t clients; /*%< All active clients */ - /* Lock covers the recursing list */ + /* Lock covers the recursing list and hash table */ isc_mutex_t reclock; client_list_t recursing; /*%< Recursing clients */ + client_list_t rectbl[NBUCKETS]; #if NMCTXS > 0 /*%< mctx pool for clients. */ @@ -236,26 +241,68 @@ static isc_result_t get_client(ns_clientmgr_t *manager, ns_interface_t *ifp, void ns_client_recursing(ns_client_t *client) { + unsigned int h; + REQUIRE(NS_CLIENT_VALID(client)); REQUIRE(client->state == NS_CLIENTSTATE_WORKING); + h = isc_hash_calc((const unsigned char *) client, + sizeof(ns_client_t *), ISC_TRUE) % NBUCKETS; + LOCK(&client->manager->reclock); client->newstate = client->state = NS_CLIENTSTATE_RECURSING; + client->hash = h; ISC_LIST_APPEND(client->manager->recursing, client, rlink); + ISC_LIST_PREPEND(client->manager->rectbl[h], client, hlink); UNLOCK(&client->manager->reclock); } void -ns_client_killoldestquery(ns_client_t *client) { - ns_client_t *oldest; +ns_client_killoldquery(ns_client_t *client) { + isc_uint32_t r, t; + ns_client_t *old; REQUIRE(NS_CLIENT_VALID(client)); + /* + * kill either the oldest, newest, or a semi-randomly + * selected client from the middle of the queue, + * depending on client-drop-policy settings + */ + isc_random_get(&r); + t = r % 100; + LOCK(&client->manager->reclock); - oldest = ISC_LIST_HEAD(client->manager->recursing); - if (oldest != NULL) { - ISC_LIST_UNLINK(client->manager->recursing, oldest, rlink); + + if (t < ns_g_server->dropmiddle) { + /* the oldest oldest */ + old = ISC_LIST_HEAD(client->manager->recursing); + } else if (t < ns_g_server->dropnewest) { + /* drop from the middle */ + r %= NBUCKETS; + old = ISC_LIST_HEAD(client->manager->rectbl[r]); + if (old != NULL) { + ISC_LIST_UNLINK(client->manager->rectbl[r], old, hlink); + ISC_LIST_UNLINK(client->manager->recursing, old, rlink); + old->hash = 0; + UNLOCK(&client->manager->reclock); + ns_query_cancel(old); + return; + } else { + /* empty bucket; drop oldest instead */ + old = ISC_LIST_HEAD(client->manager->recursing); + } + } else { + /* drop the newest */ + old = ISC_LIST_TAIL(client->manager->recursing); + } + + if (old != NULL) { + ISC_LIST_UNLINK(client->manager->recursing, old, rlink); + ISC_LIST_UNLINK(client->manager->rectbl[old->hash], + old, hlink); + old->hash = 0; UNLOCK(&client->manager->reclock); - ns_query_cancel(oldest); + ns_query_cancel(old); } else UNLOCK(&client->manager->reclock); } @@ -352,13 +399,18 @@ exit_check(ns_client_t *client) { * * We need to check whether the client is still linked, * because it may already have been removed from the - * recursing list by ns_client_killoldestquery() + * recursing list by ns_client_killoldquery() */ if (client->state == NS_CLIENTSTATE_RECURSING) { LOCK(&manager->reclock); if (ISC_LINK_LINKED(client, rlink)) ISC_LIST_UNLINK(manager->recursing, client, rlink); + if (ISC_LINK_LINKED(client, hlink)) { + ISC_LIST_UNLINK(manager->rectbl[client->hash], + client, hlink); + client->hash = 0; + } UNLOCK(&manager->reclock); } ns_client_endrequest(client); @@ -2158,8 +2210,10 @@ client_create(ns_clientmgr_t *manager, ns_client_t **clientp) { isc_sockaddr_any(&client->formerrcache.addr); client->formerrcache.time = 0; client->formerrcache.id = 0; + client->hash = 0; ISC_LINK_INIT(client, link); ISC_LINK_INIT(client, rlink); + ISC_LINK_INIT(client, hlink); ISC_QLINK_INIT(client, ilink); /* @@ -2489,9 +2543,7 @@ ns_clientmgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, { ns_clientmgr_t *manager; isc_result_t result; -#if NMCTXS > 0 int i; -#endif manager = isc_mem_get(mctx, sizeof(*manager)); if (manager == NULL) @@ -2515,6 +2567,8 @@ ns_clientmgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, manager->exiting = ISC_FALSE; ISC_LIST_INIT(manager->clients); ISC_LIST_INIT(manager->recursing); + for (i = 0; i < NBUCKETS; i++) + ISC_LIST_INIT(manager->rectbl[i]); ISC_QUEUE_INIT(manager->inactive, ilink); #if NMCTXS > 0 manager->nextmctx = 0; diff --git a/bin/named/config.c b/bin/named/config.c index 2782720..7d51ed8 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -62,6 +62,8 @@ options {\n\ "# session-keyfile \"" NS_LOCALSTATEDIR "/run/named/session.key\";\n\ session-keyname local-ddns;\n\ session-keyalg hmac-sha256;\n\ + client-drop-policy 0 50 50;\n\ + client-soft-quota 0;\n\ deallocate-on-exit true;\n\ # directory \n\ dump-file \"named_dump.db\";\n\ @@ -158,11 +160,15 @@ options {\n\ dnssec-enable yes;\n\ dnssec-validation yes; \n\ dnssec-accept-expired no;\n\ + fetches-per-zone 200;\n\ clients-per-query 10;\n\ max-clients-per-query 100;\n\ zero-no-soa-ttl-cache no;\n\ nsec3-test-zone no;\n\ allow-new-zones no;\n\ + holddown-time 0;\n\ + holddown-threshold 10;\n\ + holddown-log-only no;\n\ " #ifdef ALLOW_FILTER_AAAA_ON_V4 " filter-aaaa-on-v4 no;\n\ diff --git a/bin/named/include/named/client.h b/bin/named/include/named/client.h index 98e79df..64dc549 100644 --- a/bin/named/include/named/client.h +++ b/bin/named/include/named/client.h @@ -156,8 +156,12 @@ struct ns_client { dns_messageid_t id; } formerrcache; + /* hash bucket in client manager; must be set when recursing */ + unsigned int hash; + ISC_LINK(ns_client_t) link; ISC_LINK(ns_client_t) rlink; + ISC_LINK(ns_client_t) hlink; ISC_QLINK(ns_client_t) ilink; }; @@ -356,9 +360,13 @@ ns_client_recursing(ns_client_t *client); */ void -ns_client_killoldestquery(ns_client_t *client); +ns_client_killoldquery(ns_client_t *client); /*% - * Kill the oldest recursive query (recursing list head). + * Kill an existing recursive query. + * - 50% chance: Kill the oldest recursive query (recursing list head). + * - 50% chance: Kill a semi-random recursive query (head of a + * randomly-chosen bucket; if the bucket selected is empty, + * fall back to killing the oldest query). */ void diff --git a/bin/named/include/named/server.h b/bin/named/include/named/server.h index 52ba94d..def6d79 100644 --- a/bin/named/include/named/server.h +++ b/bin/named/include/named/server.h @@ -51,6 +51,20 @@ struct ns_server { isc_quota_t xfroutquota; isc_quota_t tcpquota; isc_quota_t recursionquota; + + /*% + * Threshold probabilities of dropping the oldest, newest, or a + * random query from the middle of the queue when recursive + * clients quota is exceeded. + * + * For a random number r between 0 and 99: + * r >= 0 && r < dropmiddle: drop the oldest + * r >= dropmiddle && r < dropnewest: drop from the middle + * r >= dropnewest: drop the newest + */ + isc_uint32_t dropmiddle; + isc_uint32_t dropnewest; + dns_acl_t *blackholeacl; char * statsfile; /*%< Statistics file name */ char * dumpfile; /*%< Dump file name */ diff --git a/bin/named/query.c b/bin/named/query.c index c357f83..894e25d 100644 --- a/bin/named/query.c +++ b/bin/named/query.c @@ -3811,7 +3811,7 @@ query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname, client->recursionquota->soft, client->recursionquota->max); } - ns_client_killoldestquery(client); + ns_client_killoldquery(client); result = ISC_R_SUCCESS; } else if (result == ISC_R_QUOTA) { static isc_stdtime_t last = 0; @@ -3829,7 +3829,7 @@ query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname, ns_g_server->recursionquota.max, isc_result_totext(result)); } - ns_client_killoldestquery(client); + ns_client_killoldquery(client); } if (result == ISC_R_SUCCESS && !client->mortal && (client->attributes & NS_CLIENTATTR_TCP) == 0) { diff --git a/bin/named/server.c b/bin/named/server.c index 77a4e44..db49770 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -2082,6 +2082,8 @@ configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig, size_t max_acache_size; size_t max_adb_size; isc_uint32_t lame_ttl; + isc_uint32_t holddown, holddown_threshold; + isc_boolean_t holddown_logonly; dns_tsig_keyring_t *ring = NULL; dns_view_t *pview = NULL; /* Production view */ isc_mem_t *cmctx = NULL, *hmctx = NULL; @@ -2731,6 +2733,30 @@ configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig, dns_adb_setadbsize(view->adb, max_adb_size); /* + * Set up ADB holddowns + */ + obj = NULL; + result = ns_config_get(maps, "holddown-threshold", &obj); + INSIST(result == ISC_R_SUCCESS); + holddown_threshold = cfg_obj_asuint32(obj); + + obj = NULL; + result = ns_config_get(maps, "holddown-time", &obj); + INSIST(result == ISC_R_SUCCESS); + holddown = cfg_obj_asuint32(obj); + if (holddown > 30) + holddown = 30; + + obj = NULL; + result = ns_config_get(maps, "holddown-log-only", &obj); + INSIST(result == ISC_R_SUCCESS); + holddown_logonly = cfg_obj_asboolean(obj); + + if (holddown != 0 && holddown_threshold != 0) + dns_adb_setholddown(view->adb, holddown, + holddown_threshold, holddown_logonly); + + /* * Set resolver's lame-ttl. */ obj = NULL; @@ -3141,6 +3167,11 @@ configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig, cfg_obj_asuint32(obj), max_clients_per_query); + obj = NULL; + result = ns_config_get(maps, "fetches-per-zone", &obj); + INSIST(result == ISC_R_SUCCESS); + dns_resolver_setfetchesperzone(view->resolver, cfg_obj_asuint32(obj)); + #ifdef ALLOW_FILTER_AAAA_ON_V4 obj = NULL; result = ns_config_get(maps, "filter-aaaa-on-v4", &obj); @@ -4907,6 +4938,7 @@ load_configuration(const char *filename, ns_server_t *server, ns_cachelist_t cachelist, tmpcachelist; struct cfg_context *nzctx; unsigned int maxsocks; + isc_uint32_t softquota = 0; ISC_LIST_INIT(viewlist); ISC_LIST_INIT(builtin_viewlist); @@ -5068,12 +5100,38 @@ 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) + + obj = NULL; + result = ns_config_get(maps, "client-soft-quota", &obj); + INSIST(result == ISC_R_SUCCESS); + softquota = (server->recursionquota.max * cfg_obj_asuint32(obj) / 100); + + if (softquota != 0) + isc_quota_soft(&server->recursionquota, softquota); + else if (server->recursionquota.max > 1000) isc_quota_soft(&server->recursionquota, server->recursionquota.max - 100); else isc_quota_soft(&server->recursionquota, 0); + obj = NULL; + result = ns_config_get(maps, "client-drop-policy", &obj); + INSIST(result == ISC_R_SUCCESS); + { + const cfg_obj_t *new, *middle, *old; + + new = cfg_tuple_get(obj, "new"); + middle = cfg_tuple_get(obj, "middle"); + old = cfg_tuple_get(obj, "old"); + + INSIST(new != NULL && middle != NULL && old != NULL); + + /* dropoldest threshold is zero */ + server->dropmiddle = cfg_obj_asuint32(old); + server->dropnewest = server->dropmiddle + + cfg_obj_asuint32(middle); + } + CHECK(configure_view_acl(NULL, config, "blackhole", NULL, ns_g_aclconfctx, ns_g_mctx, &server->blackholeacl)); @@ -6100,6 +6158,14 @@ ns_server_create(isc_mem_t *mctx, ns_server_t **serverp) { result = isc_quota_init(&server->recursionquota, 100); RUNTIME_CHECK(result == ISC_R_SUCCESS); + /* + * the drop-oldest threshold is always 0. + * the default drop-middle threshold is 50, + * and default drop-newest is 100 (i.e. never happens) + */ + server->dropmiddle = 50; + server->dropnewest = 100; + result = dns_aclenv_init(mctx, &server->aclenv); RUNTIME_CHECK(result == ISC_R_SUCCESS); @@ -7254,12 +7320,24 @@ ns_server_dumpsecroots(ns_server_t *server, char *args) { isc_result_t ns_server_dumprecursing(ns_server_t *server) { FILE *fp = NULL; + dns_view_t *view; isc_result_t result; 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); + + 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, + isc_statsformat_file, fp); + } + fprintf(fp, "; Dump complete\n"); cleanup: diff --git a/bin/rndc/rndc.docbook b/bin/rndc/rndc.docbook index 2b91d2d..448b900 100644 --- a/bin/rndc/rndc.docbook +++ b/bin/rndc/rndc.docbook @@ -535,8 +535,12 @@ recursing - Dump the list of queries named is currently recursing - on. + 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/checkconf/bad-drop.conf b/bin/tests/system/checkconf/bad-drop.conf new file mode 100644 index 0000000..69a66de --- /dev/null +++ b/bin/tests/system/checkconf/bad-drop.conf @@ -0,0 +1,20 @@ +/* + * Copyright (C) 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. + */ + +options { + client-soft-quota 150; + client-drop-policy 50 50 50; +}; diff --git a/bin/tests/system/conf.sh.in b/bin/tests/system/conf.sh.in index 0a157e7..250b545 100644 --- a/bin/tests/system/conf.sh.in +++ b/bin/tests/system/conf.sh.in @@ -61,9 +61,9 @@ SAMPLE=$TOP/lib/export/samples/sample 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 - formerr forward glue gost ixfr inline limits logfileconfig - lwresd masterfile masterformat metadata notify nsupdate pending + emptyzones formerr forward glue gost ixfr inline + lameserver limits logfileconfig lwresd + masterfile masterformat metadata notify nsupdate pending pkcs11 redirect resolver rndc rpz rrl rrsetorder rsabigexponent smartsign sortlist spf staticstub stub tkey tsig tsiggss unknown upforwd verify views wildcard xfer xferquota zero zonechecks" diff --git a/bin/tests/system/lameserver/ans4/ans.pl b/bin/tests/system/lameserver/ans4/ans.pl new file mode 100644 index 0000000..d844502 --- /dev/null +++ b/bin/tests/system/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/lameserver/clean.sh b/bin/tests/system/lameserver/clean.sh new file mode 100644 index 0000000..97ff5a7 --- /dev/null +++ b/bin/tests/system/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 diff --git a/bin/tests/system/lameserver/ns1/named.conf b/bin/tests/system/lameserver/ns1/named.conf new file mode 100644 index 0000000..4354b46 --- /dev/null +++ b/bin/tests/system/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/lameserver/ns1/root.db b/bin/tests/system/lameserver/ns1/root.db new file mode 100644 index 0000000..cd74861 --- /dev/null +++ b/bin/tests/system/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/lameserver/ns2/example.db b/bin/tests/system/lameserver/ns2/example.db new file mode 100644 index 0000000..1281554 --- /dev/null +++ b/bin/tests/system/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/lameserver/ns2/named.conf b/bin/tests/system/lameserver/ns2/named.conf new file mode 100644 index 0000000..b276181 --- /dev/null +++ b/bin/tests/system/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/lameserver/ns3/named1.conf b/bin/tests/system/lameserver/ns3/named1.conf new file mode 100644 index 0000000..2f73f9b --- /dev/null +++ b/bin/tests/system/lameserver/ns3/named1.conf @@ -0,0 +1,46 @@ +/* + * 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; + holddown-threshold 10; + holddown-time 5; +}; + +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/lameserver/ns3/named3.conf b/bin/tests/system/lameserver/ns3/named3.conf new file mode 100644 index 0000000..d7e3ceb --- /dev/null +++ b/bin/tests/system/lameserver/ns3/named3.conf @@ -0,0 +1,46 @@ +/* + * 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 200; + client-soft-quota 75; +}; + +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/lameserver/ns3/root.hint b/bin/tests/system/lameserver/ns3/root.hint new file mode 100644 index 0000000..2b36926 --- /dev/null +++ b/bin/tests/system/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/lameserver/setup.sh b/bin/tests/system/lameserver/setup.sh new file mode 100644 index 0000000..8977220 --- /dev/null +++ b/bin/tests/system/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/lameserver/tests.sh b/bin/tests/system/lameserver/tests.sh new file mode 100644 index 0000000..2fd859b --- /dev/null +++ b/bin/tests/system/lameserver/tests.sh @@ -0,0 +1,122 @@ +#!/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 don't grow too large with holddown set" +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 + 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: checking holddown expires after 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 +for try in 1 2 3 4 5; do + burst c $try + stat 20 || ret=1 + [ $ret -eq 1 ] && break + sleep 1 +done +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` + +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 300 + $DIGCMD a ${try}.example > dig.out.ns3.$try + stat 150 || 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/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml index 0238065..b740f42 100644 --- a/doc/arm/Bv9ARM-book.xml +++ b/doc/arm/Bv9ARM-book.xml @@ -4762,9 +4762,17 @@ 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 ; + client-drop-policy + number + number + number; + client-soft-quota + number; serial-query-rate number; serial-queries number; tcp-listen-queue number; @@ -4843,8 +4851,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 ; masterfile-format (text|raw) ; empty-server name ; empty-contact name ; @@ -7838,34 +7844,165 @@ 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 - 1000. Because each recursing - client uses a fair - 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. - - - + + recursive-clients + + + 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 + recursive-clients option may + 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. + + + - - tcp-clients - - - The maximum number of simultaneous client TCP - connections that the server will accept. - The default is 100. - - - + + client-soft-quota + + + Defines a "soft quota" limit for + . When this lower + quota is exceeded, incoming requests are accepted, but + for each one, a pending request will be dropped. + The default value of zero means there is no soft + quota unless + exceeds 1000, at which point the soft quota is set to + minus 100. + Any other value sets the soft quota to the specified + percentage of . + + + + + + + client-drop-policy + + + Defines policy for dropping clients when the + quota is reached. + + + The arguments define percentage probabilities for + "drop newest", "drop random" and "drop oldest", + in that order. All three values must be set, and they + must sum to exactly 100. When the server drops an + existing query due to exceeding either the hard quota + set by or the soft + quota set by , + it will randomly choose drop the most recent query, + the least recent query, or a randomly-selected query + from the middle of the queue. By default, the + probabilities of these are 0% for drop + newest, 50% for drop random, and 50% for drop + oldest. + + + + + + tcp-clients + + + The maximum number of simultaneous client TCP + connections that the server will accept. + The default is 100. + + + + + + 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. + + + 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 200. + + + 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.) + + + reserved-sockets diff --git a/lib/bind9/check.c b/lib/bind9/check.c index af1b87a..e10cb3d 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -683,6 +683,7 @@ check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx, isc_result_t tresult; unsigned int i; const cfg_obj_t *obj = NULL; + const cfg_obj_t *new = NULL, *middle = NULL, *old = NULL; const cfg_obj_t *resignobj = NULL; const cfg_listelt_t *element; isc_symtab_t *symtab = NULL; @@ -1039,6 +1040,36 @@ check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx, result = ISC_R_FAILURE; } + obj = NULL; + (void)cfg_map_get(options, "client-soft-quota", &obj); + if (obj != NULL && cfg_obj_asuint32(obj) > 100) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "'client-soft-quota' percent exceeds 100"); + result = ISC_R_RANGE; + } + + obj = NULL; + (void)cfg_map_get(options, "client-drop-policy", &obj); + if (obj != NULL) { + isc_uint32_t n, m, o; + + new = cfg_tuple_get(obj, "new"); + middle = cfg_tuple_get(obj, "middle"); + old = cfg_tuple_get(obj, "old"); + + n = cfg_obj_asuint32(new); + m = cfg_obj_asuint32(middle); + o = cfg_obj_asuint32(old); + + if (n + m + o != 100) { + cfg_obj_log(new, logctx, ISC_LOG_ERROR, + "'client-drop-policy' drop-newest, " + "drop-middle and drop-oldest " + "probabilities must sum to 100"); + result = ISC_R_FAILURE; + } + } + return (result); } diff --git a/lib/dns/adb.c b/lib/dns/adb.c index 10d51bc..d5b014f 100644 --- a/lib/dns/adb.c +++ b/lib/dns/adb.c @@ -164,6 +164,10 @@ struct dns_adb { isc_boolean_t growentries_sent; isc_event_t grownames; isc_boolean_t grownames_sent; + + isc_uint32_t holddown_time; + isc_uint32_t holddown_threshold; + isc_boolean_t holddown_logonly; }; /* @@ -242,6 +246,7 @@ struct dns_adbentry { unsigned int flags; unsigned int srtt; + unsigned int timeouts; isc_sockaddr_t sockaddr; isc_stdtime_t expires; @@ -253,6 +258,10 @@ struct dns_adbentry { * name. */ + isc_stdtime_t holddown; + isc_boolean_t was_held; + isc_boolean_t logonly; + ISC_LIST(dns_adblameinfo_t) lameinfo; ISC_LINK(dns_adbentry_t) plink; @@ -321,6 +330,8 @@ 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 log_holddown(dns_adbentry_t *entry, const char *fmt, ...) + ISC_FORMAT_PRINTF(2, 3); /* * MUST NOT overlap DNS_ADBFIND_* flags! @@ -1757,6 +1768,10 @@ new_adbentry(dns_adb_t *adb) { isc_random_get(&r); e->srtt = (r & 0x1f) + 1; e->expires = 0; + e->timeouts = 0; + e->holddown = 0; + e->logonly = adb->holddown_logonly; + e->was_held = ISC_FALSE; ISC_LIST_INIT(e->lameinfo); ISC_LINK_INIT(e, plink); LOCK(&adb->entriescntlock); @@ -2066,6 +2081,26 @@ entry_is_lame(dns_adb_t *adb, dns_adbentry_t *entry, dns_name_t *qname, } static void +log_holddown(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_SPILL, + DNS_LOGMODULE_ADB, ISC_LOG_INFO, + "adb: holddown %s: %s", + addrbuf, msgbuf); +} + +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, isc_stdtime_t now) @@ -2085,6 +2120,20 @@ copy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, dns_name_t *qname, INSIST(bucket != DNS_ADB_INVALIDBUCKET); LOCK(&adb->entrylocks[bucket]); + if (entry->holddown >= now) { + if (!adb->holddown_logonly) { + find->options |= + (DNS_ADBFIND_LAMEPRUNED| + DNS_ADBFIND_HELDDOWN); + goto nextv4; + } + } else if (entry->holddown != 0) { + entry->timeouts = 0; + entry->holddown = 0; + entry->was_held = ISC_TRUE; + log_holddown(entry, "cleared"); + } + if (!FIND_RETURNLAME(find) && entry_is_lame(adb, entry, qname, qtype, now)) { find->options |= DNS_ADBFIND_LAMEPRUNED; @@ -2116,6 +2165,20 @@ copy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, dns_name_t *qname, INSIST(bucket != DNS_ADB_INVALIDBUCKET); LOCK(&adb->entrylocks[bucket]); + if (entry->holddown >= now) { + if (!adb->holddown_logonly) { + find->options |= + (DNS_ADBFIND_LAMEPRUNED| + DNS_ADBFIND_HELDDOWN); + goto nextv6; + } + } else if (entry->holddown != 0) { + entry->timeouts = 0; + entry->holddown = 0; + entry->was_held = ISC_TRUE; + log_holddown(entry, "cleared"); + } + if (!FIND_RETURNLAME(find) && entry_is_lame(adb, entry, qname, qtype, now)) { find->options |= DNS_ADBFIND_LAMEPRUNED; @@ -2449,6 +2512,10 @@ dns_adb_create(isc_mem_t *mem, dns_view_t *view, isc_timermgr_t *timermgr, adb, NULL, NULL); adb->growentries_sent = ISC_FALSE; + adb->holddown_time = 0; + adb->holddown_threshold = 0; + adb->holddown_logonly = ISC_FALSE; + adb->nnames = nbuckets[0]; adb->namescnt = 0; adb->names = NULL; @@ -3967,6 +4034,60 @@ dns_adb_changeflags(dns_adb_t *adb, dns_adbaddrinfo_t *addr, UNLOCK(&adb->entrylocks[bucket]); } +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]); + + /* + * Success; clear the timeout counter + */ + addr->entry->timeouts = 0; + addr->entry->holddown = 0; + addr->entry->was_held = ISC_FALSE; + + UNLOCK(&adb->entrylocks[bucket]); +} + +void +dns_adb_timeout(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]); + + /* + * Track successive timeouts; when they reach a configured + * number, start a holddown period. If we have recently + * lifted a holddown timer, then we don't wait for the + * threshold, just reassert it immediately. + */ + addr->entry->timeouts++; + if (addr->entry->holddown == 0 && + (addr->entry->timeouts > adb->holddown_threshold || + addr->entry->was_held)) + { + isc_stdtime_t now; + + isc_stdtime_get(&now); + addr->entry->holddown = now + adb->holddown_time; + + log_holddown(addr->entry, + "%ssetting for %d seconds after %d timeouts", + addr->entry->was_held ? "re" : "", + adb->holddown_time, addr->entry->timeouts); + } + UNLOCK(&adb->entrylocks[bucket]); +} + isc_result_t dns_adb_findaddrinfo(dns_adb_t *adb, isc_sockaddr_t *sa, dns_adbaddrinfo_t **addrp, isc_stdtime_t now) @@ -4146,3 +4267,21 @@ dns_adb_setadbsize(dns_adb_t *adb, size_t size) { else isc_mem_setwater(adb->mctx, water, adb, hiwater, lowater); } + +void +dns_adb_setholddown(dns_adb_t *adb, isc_uint32_t duration, + isc_uint32_t threshold, isc_boolean_t logonly) +{ + REQUIRE(DNS_ADB_VALID(adb)); + + adb->holddown_time = duration; + adb->holddown_threshold = threshold; + adb->holddown_logonly = logonly; +} + +isc_boolean_t +dns_adbentry_helddown(dns_adbentry_t *entry, isc_stdtime_t now) { + REQUIRE(DNS_ADBENTRY_VALID(entry)); + + return (ISC_TF(!entry->logonly && entry->holddown >= now)); +} diff --git a/lib/dns/include/dns/adb.h b/lib/dns/include/dns/adb.h index a5a3124..637f8e9 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 +/*% + * Server has timed out too often; it temporarily be presumed + * lame for all queries. + */ +#define DNS_ADBFIND_HELDDOWN 0x00000400 /*% * The answers to queries come back as a list of these. @@ -565,6 +570,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); @@ -629,6 +658,30 @@ dns_adb_flushname(dns_adb_t *adb, dns_name_t *name); *\li 'name' is valid. */ +void +dns_adb_setholddown(dns_adb_t *adb, isc_uint32_t duration, + isc_uint32_t threshold, isc_boolean_t logonly); +/*%< + * Set holddown duration and threshold. If queries to any address + * time out more than 'threshold' times in a row, then the server + * will be treated as lame for all queries for 'duration' seconds. + * If 'logonly' is true, holddowns will not be enforced but will + * still be logged. + * + * Requires: + *\li 'adb' is valid. + */ + +isc_boolean_t +dns_adbentry_helddown(dns_adbentry_t *entry, isc_stdtime_t now); +/*%< + * Returns true if the specified ADB has an active holddown as of time + * 'now' + * + * Requires: + *\li 'entry' is 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 e8c8c10..cd99954 100644 --- a/lib/dns/include/dns/log.h +++ b/lib/dns/include/dns/log.h @@ -44,6 +44,7 @@ LIBDNS_EXTERNAL_DATA extern isc_logmodule_t dns_modules[]; #define DNS_LOGCATEGORY_EDNS_DISABLED (&dns_categories[11]) #define DNS_LOGCATEGORY_RPZ (&dns_categories[12]) #define DNS_LOGCATEGORY_RRL (&dns_categories[13]) +#define DNS_LOGCATEGORY_SPILL (&dns_categories[14]) /* 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 095269e..9febbcc 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 @@ -519,6 +518,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, @@ -575,6 +576,10 @@ dns_resolver_printbadcache(dns_resolver_t *resolver, FILE *fp); * \li resolver to be valid. */ +void +dns_resolver_dumpfetches(dns_resolver_t *resolver, + isc_statsformat_t format, FILE *fp); + ISC_LANG_ENDDECLS #endif /* DNS_RESOLVER_H */ diff --git a/lib/dns/log.c b/lib/dns/log.c index 75e0d79..ca19387 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 */ @@ -46,6 +44,7 @@ LIBDNS_EXTERNAL_DATA isc_logcategory_t dns_categories[] = { { "edns-disabled", 0 }, { "rpz", 0 }, { "rate-limit", 0 }, + { "spill", 0 }, { NULL, 0 } }; diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index 2e60cd8..54fb4cd 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -15,8 +15,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ - /*! \file */ #include @@ -127,9 +125,16 @@ #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 + +/* 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. @@ -200,6 +205,7 @@ struct fetchctx { dns_rdatatype_t type; unsigned int options; unsigned int bucketnum; + unsigned int dbucketnum; char * info; isc_mem_t * mctx; @@ -299,6 +305,7 @@ struct fetchctx { unsigned int querysent; unsigned int referrals; unsigned int lamecount; + unsigned int helddown; unsigned int neterr; unsigned int badresp; unsigned int adberr; @@ -357,6 +364,23 @@ typedef struct fctxbucket { isc_mem_t * mctx; } fctxbucket_t; +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; + typedef struct alternate { isc_boolean_t isaddress; union { @@ -402,6 +426,7 @@ struct dns_resolver { isc_boolean_t exclusivev6; unsigned int nbuckets; fctxbucket_t * buckets; + zonebucket_t * dbuckets; isc_uint32_t lame_ttl; ISC_LIST(alternate_t) alternates; isc_uint16_t udpsize; @@ -426,6 +451,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; @@ -849,16 +875,11 @@ fctx_cancelquery(resquery_t **queryp, dns_dispatchevent_t **deventp, * 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); - } - /* * Age RTTs of servers not tried. */ @@ -1040,6 +1061,126 @@ fctx_stopeverything(fetchctx_t *fctx, isc_boolean_t no_response) { fctx_stoptimer(fctx); } +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); +} + static inline void fctx_sendevents(fetchctx_t *fctx, isc_result_t result, int line) { dns_fetchevent_t *event, *next_event; @@ -2217,7 +2358,7 @@ fctx_finddone(isc_task_t *task, isc_event_t *event) { else if (want_done) fctx_done(fctx, ISC_R_FAILURE, __LINE__); else if (destroy) { - fctx_destroy(fctx); + fctx_destroy(fctx); if (bucket_empty) empty_bucket(res); } @@ -2527,7 +2668,9 @@ findname(fetchctx_t *fctx, dns_name_t *name, in_port_t port, find->result_v4 != DNS_R_NXDOMAIN))) *need_alternate = ISC_TRUE; } else { - if ((find->options & DNS_ADBFIND_LAMEPRUNED) != 0) + if ((find->options & DNS_ADBFIND_HELDDOWN) != 0) + fctx->helddown++; /* too many timeouts */ + else if ((find->options & DNS_ADBFIND_LAMEPRUNED) != 0) fctx->lamecount++; /* cached lame server */ else fctx->adberr++; /* unreachable server, etc. */ @@ -2626,12 +2769,16 @@ fctx_getaddresses(fetchctx_t *fctx, isc_boolean_t badcache) { fctx->fwdpolicy = forwarders->fwdpolicy; if (fctx->fwdpolicy == dns_fwdpolicy_only && isstrictsubdomain(domain, &fctx->domain)) { + fcount_decr(fctx); 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); + result = fcount_incr(fctx, ISC_TRUE); + if (result != ISC_R_SUCCESS) + return (result); } } } @@ -3016,13 +3163,20 @@ 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; + isc_stdtime_t now; FCTXTRACE("try"); REQUIRE(!ADDRWAIT(fctx)); - addrinfo = fctx_nextaddress(fctx); + isc_stdtime_get(&now); + + /* Try to find an address that isn't held down */ + while ((addrinfo = fctx_nextaddress(fctx)) != NULL) + if (! dns_adbentry_helddown(addrinfo->entry, now)) + break; + if (addrinfo == NULL) { /* * We have no more addresses. Start over. @@ -3048,7 +3202,9 @@ fctx_try(fetchctx_t *fctx, isc_boolean_t retrying, isc_boolean_t badcache) { return; } - addrinfo = fctx_nextaddress(fctx); + while ((addrinfo = fctx_nextaddress(fctx)) != NULL) + if (! dns_adbentry_helddown(addrinfo->entry, now)) + break; /* * While we may have addresses from the ADB, they * might be bad ones. In this case, return SERVFAIL. @@ -3157,6 +3313,8 @@ fctx_destroy(fetchctx_t *fctx) { isc_mem_put(fctx->mctx, sa, sizeof(*sa)); } + fcount_decr(fctx); + isc_timer_detach(&fctx->timer); dns_message_destroy(&fctx->rmessage); dns_message_destroy(&fctx->qmessage); @@ -3485,7 +3643,8 @@ log_ns_ttl(fetchctx_t *fctx, const char *where) { static isc_result_t fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, dns_name_t *domain, dns_rdataset_t *nameservers, - unsigned int options, unsigned int bucketnum, fetchctx_t **fctxp) + unsigned int options, unsigned int bucketnum, + fetchctx_t **fctxp) { fetchctx_t *fctx; isc_result_t result; @@ -3534,6 +3693,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; @@ -3559,6 +3719,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->helddown = 0; fctx->adberr = 0; fctx->neterr = 0; fctx->badresp = 0; @@ -3647,6 +3808,15 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, fctx->ns_ttl_ok = ISC_TRUE; } + /* + * Are there too many simultaneous queries for this domain? + */ + result = fcount_incr(fctx, ISC_FALSE); + if (result != ISC_R_SUCCESS) { + result = DNS_R_DROP; + goto cleanup_domain; + } + log_ns_ttl(fctx, "fctx_create"); INSIST(dns_name_issubdomain(&fctx->name, &fctx->domain)); @@ -3656,7 +3826,7 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, &fctx->qmessage); if (result != ISC_R_SUCCESS) - goto cleanup_domain; + goto cleanup_fcount; fctx->rmessage = NULL; result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, @@ -3732,6 +3902,9 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, cleanup_qmessage: dns_message_destroy(&fctx->qmessage); + cleanup_fcount: + fcount_decr(fctx); + cleanup_domain: if (dns_name_countlabels(&fctx->domain) > 0) dns_name_free(&fctx->domain, mctx); @@ -5973,6 +6146,7 @@ noanswer_response(fetchctx_t *fctx, dns_name_t *oqname, * if so we should bail out. */ INSIST(dns_name_countlabels(&fctx->domain) > 0); + fcount_decr(fctx); dns_name_free(&fctx->domain, fctx->mctx); if (dns_rdataset_isassociated(&fctx->nameservers)) dns_rdataset_disassociate(&fctx->nameservers); @@ -5980,6 +6154,9 @@ 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); + result = fcount_incr(fctx, ISC_TRUE); + if (result != ISC_R_SUCCESS) + return (result); fctx->attributes |= FCTX_ATTR_WANTCACHE; fctx->ns_ttl_ok = ISC_FALSE; log_ns_ttl(fctx, "DELEGATION"); @@ -6535,6 +6712,8 @@ 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"); + + fcount_decr(fctx); dns_name_free(&fctx->domain, fctx->mctx); dns_name_init(&fctx->domain, NULL); result = dns_name_dup(&fctx->nsname, fctx->mctx, &fctx->domain); @@ -6542,6 +6721,11 @@ resume_dslookup(isc_task_t *task, isc_event_t *event) { fctx_done(fctx, DNS_R_SERVFAIL, __LINE__); goto cleanup; } + result = fcount_incr(fctx, ISC_TRUE); + if (result != ISC_R_SUCCESS) { + fctx_done(fctx, DNS_R_SERVFAIL, __LINE__); + goto cleanup; + } /* * Try again. */ @@ -6910,6 +7094,8 @@ resquery_response(isc_task_t *task, isc_event_t *event) { goto done; } + 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) { switch (result) { @@ -7339,6 +7525,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) { fctx->referrals++; fctx->querysent = 0; fctx->lamecount = 0; + fctx->helddown = 0; fctx->neterr = 0; fctx->badresp = 0; fctx->adberr = 0; @@ -7455,6 +7642,8 @@ resquery_response(isc_task_t *task, isc_event_t *event) { fctx_done(fctx, DNS_R_SERVFAIL, __LINE__); return; } + + fcount_decr(fctx); dns_name_free(&fctx->domain, fctx->mctx); dns_name_init(&fctx->domain, NULL); result = dns_name_dup(fname, fctx->mctx, &fctx->domain); @@ -7462,6 +7651,11 @@ resquery_response(isc_task_t *task, isc_event_t *event) { fctx_done(fctx, DNS_R_SERVFAIL, __LINE__); return; } + result = fcount_incr(fctx, ISC_TRUE); + if (result != ISC_R_SUCCESS) { + fctx_done(fctx, DNS_R_SERVFAIL, __LINE__); + return; + } fctx->ns_ttl = fctx->nameservers.ttl; fctx->ns_ttl_ok = ISC_TRUE; fctx_cancelqueries(fctx, ISC_TRUE); @@ -7584,6 +7778,13 @@ destroy(dns_resolver_t *res) { } isc_mem_put(res->mctx, res->buckets, res->nbuckets * sizeof(fctxbucket_t)); + 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)); if (res->dispatches4 != NULL) dns_dispatchset_destroy(&res->dispatches4); if (res->dispatches6 != NULL) @@ -7689,7 +7890,7 @@ dns_resolver_create(dns_view_t *view, { dns_resolver_t *res; isc_result_t result = ISC_R_SUCCESS; - unsigned int i, buckets_created = 0; + unsigned int i, buckets_created = 0, dbuckets_created = 0; isc_task_t *task = NULL; char name[16]; unsigned dispattr; @@ -7729,6 +7930,7 @@ dns_resolver_create(dns_view_t *view, res->spillatmin = res->spillat = 10; res->spillatmax = 100; res->spillattimer = NULL; + res->zspill = 100; res->zero_no_soa_ttl = ISC_FALSE; res->query_timeout = DEFAULT_QUERY_TIMEOUT; res->nbuckets = ntasks; @@ -7773,6 +7975,24 @@ dns_resolver_create(dns_view_t *view, buckets_created++; } + 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++; + } + res->dispatches4 = NULL; if (dispatchv4 != NULL) { dns_dispatchset_create(view->mctx, socketmgr, taskmgr, @@ -7866,6 +8086,14 @@ dns_resolver_create(dns_view_t *view, if (res->dispatches4 != NULL) dns_dispatchset_destroy(&res->dispatches4); + 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)); + cleanup_buckets: for (i = 0; i < buckets_created; i++) { isc_mem_detach(&res->buckets[i].mctx); @@ -8434,15 +8662,16 @@ 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,helddown:%u,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, isc_result_totext(fctx->result), isc_result_totext(fctx->vresult), domainbuf, fctx->referrals, fctx->restarts, - fctx->querysent, fctx->timeouts, fctx->lamecount, + fctx->querysent, fctx->timeouts, + fctx->lamecount, fctx->helddown, fctx->neterr, fctx->badresp, fctx->adberr, fctx->findfail, fctx->valfail); fctx->logged = ISC_TRUE; @@ -9008,6 +9237,17 @@ 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) +{ + REQUIRE(VALID_RESOLVER(resolver)); + + LOCK(&resolver->lock); + resolver->zspill = clients; + UNLOCK(&resolver->lock); +} + + isc_boolean_t dns_resolver_getzeronosoattl(dns_resolver_t *resolver) { REQUIRE(VALID_RESOLVER(resolver)); @@ -9049,3 +9289,28 @@ dns_resolver_settimeout(dns_resolver_t *resolver, unsigned int seconds) { resolver->query_timeout = seconds; } + +void +dns_resolver_dumpfetches(dns_resolver_t *resolver, + isc_statsformat_t format, FILE *fp) +{ + int i; + + REQUIRE(VALID_RESOLVER(resolver)); + REQUIRE(fp != NULL); + REQUIRE(format == isc_statsformat_file); + + 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 slipped, %d passed)\n", + fc->count, fc->dropped, fc->allowed); + } + UNLOCK(&resolver->dbuckets[i].lock); + } +} diff --git a/lib/dns/view.c b/lib/dns/view.c index 142b09e..7c223ef 100644 --- a/lib/dns/view.c +++ b/lib/dns/view.c @@ -685,7 +685,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 ede70a5..53b2c04 100644 --- a/lib/dns/win32/libdns.def.in +++ b/lib/dns/win32/libdns.def.in @@ -631,6 +631,7 @@ dns_resolver_printbadcache dns_resolver_reset_algorithms dns_resolver_resetmustbesecure dns_resolver_setclientsperquery +dns_resolver_setfetchesperzone dns_resolver_setlamettl dns_resolver_setmustbesecure dns_resolver_settimeout diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index f11e293..8da84e6 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -107,6 +107,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; @@ -870,6 +871,22 @@ static cfg_type_t cfg_type_bracketed_portlist = { }; /*% + * client-drop-policy + */ + +static cfg_tuplefielddef_t clientdrop_fields[] = { + { "new", &cfg_type_uint32, 0 }, + { "middle", &cfg_type_uint32, 0 }, + { "old", &cfg_type_uint32, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_type_t cfg_type_clientdrop = { + "clientdrop", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, + &cfg_rep_tuple, clientdrop_fields +}; + +/*% * Clauses that can be found within the top level of the named.conf * file only. */ @@ -923,6 +940,8 @@ options_clauses[] = { { "bindkeys-file", &cfg_type_qstring, 0 }, { "blackhole", &cfg_type_bracketed_aml, 0 }, { "coresize", &cfg_type_size, 0 }, + { "client-drop-policy", &cfg_type_clientdrop, 0 }, + { "client-soft-quota", &cfg_type_uint32, 0 }, { "datasize", &cfg_type_size, 0 }, { "session-keyfile", &cfg_type_qstringornone, 0 }, { "session-keyname", &cfg_type_astring, 0 }, @@ -1379,6 +1398,9 @@ view_clauses[] = { { "acache-enable", &cfg_type_boolean, 0 }, { "additional-from-auth", &cfg_type_boolean, 0 }, { "additional-from-cache", &cfg_type_boolean, 0 }, + { "holddown-threshold", &cfg_type_uint32, 0 }, + { "holddown-time", &cfg_type_uint32, 0 }, + { "holddown-log-only", &cfg_type_boolean, 0 }, { "allow-new-zones", &cfg_type_boolean, 0 }, { "allow-query-cache", &cfg_type_bracketed_aml, 0 }, { "allow-query-cache-on", &cfg_type_bracketed_aml, 0 }, @@ -1412,6 +1434,7 @@ view_clauses[] = { { "empty-server", &cfg_type_astring, 0 }, { "empty-zones-enable", &cfg_type_boolean, 0 }, { "fetch-glue", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, + { "fetches-per-zone", &cfg_type_uint32, 0 }, { "ixfr-from-differences", &cfg_type_ixfrdifftype, 0 }, { "lame-ttl", &cfg_type_uint32, 0 }, { "max-acache-size", &cfg_type_sizenodefault, 0 },