diff --git a/bin/named/client.c b/bin/named/client.c index 4acc456..a0789f4 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. */ @@ -238,26 +243,68 @@ allowed(isc_netaddr_t *addr, dns_name_t *signer, dns_acl_t *acl); 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); } @@ -354,13 +401,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); @@ -2259,8 +2311,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); /* @@ -2590,9 +2644,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) @@ -2616,6 +2668,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 074f7cb..33047bd 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 " filter-aaaa-on-v4 no;\n\ diff --git a/bin/named/include/named/client.h b/bin/named/include/named/client.h index 2571010..e854d38 100644 --- a/bin/named/include/named/client.h +++ b/bin/named/include/named/client.h @@ -158,8 +158,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; }; @@ -358,9 +362,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 22559dd..c2bf68b 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 a228a5c..8b8aeb3 100644 --- a/bin/named/query.c +++ b/bin/named/query.c @@ -3825,7 +3825,7 @@ query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname, isc_stats_increment(ns_g_server->nsstats, dns_nsstatscounter_recursclients); - if (result == ISC_R_SOFTQUOTA) { + if (result == ISC_R_SOFTQUOTA) { static isc_stdtime_t last = 0; isc_stdtime_t now; isc_stdtime_get(&now); @@ -3841,7 +3841,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; @@ -3859,7 +3859,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 && !TCP_CLIENT(client)) { diff --git a/bin/named/server.c b/bin/named/server.c index 32a6ca9..665f9de 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -2388,6 +2388,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; @@ -3040,6 +3042,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; @@ -3458,6 +3484,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 obj = NULL; result = ns_config_get(maps, "filter-aaaa-on-v4", &obj); @@ -5286,6 +5317,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); @@ -5465,12 +5497,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)); @@ -6514,6 +6572,13 @@ 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); @@ -7690,12 +7755,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 ff4c188..80365c4 100644 --- a/bin/rndc/rndc.docbook +++ b/bin/rndc/rndc.docbook @@ -534,8 +534,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 5c3ce33..722e7da 100644 --- a/bin/tests/system/conf.sh.in +++ b/bin/tests/system/conf.sh.in @@ -64,8 +64,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 dlzredir dname dns64 dnssec - dscp ecdsa emptyzones formerr forward glue gost ixfr inline limits - logfileconfig lwresd masterfile masterformat metadata notify + dscp ecdsa emptyzones formerr forward glue gost ixfr inline + lameserver limits logfileconfig lwresd + masterfile masterformat metadata notify nsupdate pending @PKCS11_TEST@ redirect resolver rndc rpz rrsetorder rrl rsabigexponent smartsign sortlist spf staticstub statistics stub tkey tsig tsiggss unknown upforwd verify views wildcard 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 d7acf62..4b04442 100644 --- a/doc/arm/Bv9ARM-book.xml +++ b/doc/arm/Bv9ARM-book.xml @@ -4837,9 +4837,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; @@ -4919,8 +4927,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 ; @@ -7981,23 +7987,73 @@ 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. + + + + + + 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 @@ -8010,6 +8066,87 @@ 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. + + + 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 @@ -9704,7 +9841,6 @@ ok.domain.com CNAME rpz-passthru. ; redirect x.bzone.domain.com to x.bzone.domain.com.garden.example.com *.bzone.domain.com CNAME *.garden.example.com. - ; IP policy records that rewrite all responses containing A records in 127/8 ; except 127.0.0.1 8.0.0.0.127.rpz-ip CNAME . @@ -10439,7 +10575,7 @@ rate-limit { - + <command>trusted-keys</command> Statement Grammar trusted-keys { diff --git a/lib/bind9/check.c b/lib/bind9/check.c index c98cfae..d7df217 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -948,6 +948,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; @@ -1308,6 +1309,36 @@ check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx, if (tresult != ISC_R_SUCCESS) result = tresult; + 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 3b423e0..6f61782 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! @@ -1778,6 +1789,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); @@ -2090,6 +2105,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) @@ -2109,6 +2144,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; @@ -2140,6 +2189,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; @@ -2473,6 +2536,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; @@ -3997,6 +4064,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) @@ -4206,3 +4327,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 0aa6e0c..c741af0 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. @@ -567,6 +572,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); @@ -642,6 +671,30 @@ dns_adb_flushnames(dns_adb_t *adb, dns_name_t *name); */ +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 2d00ec0..4e68a64 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, @@ -602,6 +603,10 @@ dns_resolver_getquerydscp6(dns_resolver_t *resolver); * \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 4b32c24..6294e70 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. @@ -201,6 +206,7 @@ struct fetchctx { dns_rdatatype_t type; unsigned int options; unsigned int bucketnum; + unsigned int dbucketnum; char * info; isc_mem_t * mctx; @@ -300,6 +306,7 @@ struct fetchctx { unsigned int querysent; unsigned int referrals; unsigned int lamecount; + unsigned int helddown; unsigned int neterr; unsigned int badresp; unsigned int adberr; @@ -358,6 +365,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 { @@ -405,6 +429,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; @@ -429,6 +454,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; @@ -858,16 +884,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. */ @@ -1049,6 +1070,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; @@ -2256,7 +2397,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); } @@ -2566,7 +2707,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. */ @@ -2665,12 +2808,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); } } } @@ -3054,13 +3201,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. @@ -3086,7 +3240,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. @@ -3196,6 +3352,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); @@ -3524,7 +3682,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; @@ -3573,6 +3732,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; @@ -3598,6 +3758,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; @@ -3686,6 +3847,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)); @@ -3695,7 +3865,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, @@ -3772,6 +3942,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); @@ -6013,6 +6186,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); @@ -6020,6 +6194,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"); @@ -6575,6 +6752,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); @@ -6582,6 +6761,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. */ @@ -6950,6 +7134,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) { @@ -7382,6 +7568,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; @@ -7498,6 +7685,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); @@ -7505,6 +7694,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); @@ -7627,6 +7821,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) @@ -7732,7 +7933,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; @@ -7772,6 +7973,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; @@ -7819,6 +8021,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, @@ -7914,6 +8134,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); @@ -8482,15 +8710,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; @@ -9096,6 +9325,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)); @@ -9164,3 +9404,27 @@ dns_resolver_getquerydscp6(dns_resolver_t *resolver) { return (resolver->querydscp6); } +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 3b54784..f13388b 100644 --- a/lib/dns/view.c +++ b/lib/dns/view.c @@ -703,7 +703,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 0fcdd09..2fe19ff 100644 --- a/lib/dns/win32/libdns.def.in +++ b/lib/dns/win32/libdns.def.in @@ -652,6 +652,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_setquerydscp4 diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 19fd196..b7d5aad 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -120,6 +120,7 @@ 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_dscp; +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; @@ -905,6 +906,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. */ @@ -957,6 +974,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 }, @@ -1487,6 +1506,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 }, @@ -1520,6 +1542,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 },