[master] DDoS mitigation features

3938.	[func]		Added quotas to be used in recursive resolvers
			that are under high query load for names in zones
			whose authoritative servers are nonresponsive or
			are experiencing a denial of service attack.

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

			See the ARM for details of these options. [RT #37125]
This commit is contained in:
Evan Hunt 2015-07-08 22:53:39 -07:00
parent e8f98ec8d4
commit 1479200aa0
41 changed files with 1976 additions and 102 deletions

23
CHANGES
View file

@ -767,7 +767,28 @@
3939. [func] Improve UPDATE forwarding performance by allowing TCP
connections to be shared. [RT #37039]
3938. [placeholder]
3938. [func] Added quotas to be used in recursive resolvers
that are under high query load for names in zones
whose authoritative servers are nonresponsive or
are experiencing a denial of service attack.
- "fetches-per-server" limits the number of
simultaneous queries that can be sent to any
single authoritative server. The configured
value is a starting point; it is automatically
adjusted downward if the server is partially or
completely non-responsive. The algorithm used to
adjust the quota can be configured via the
"fetch-quota-params" option.
- "fetches-per-zone" limits the number of
simultaneous queries that can be sent for names
within a single domain. (Note: Unlike
"fetches-per-server", this value is not
self-tuning.)
- New stats counters have been added to count
queries spilled due to these quotas.
See the ARM for details of these options. [RT #37125]
3937. [func] Added some debug logging to better indicate the
conditions causing SERVFAILs when resolving.

View file

@ -2216,6 +2216,11 @@ client_request(isc_task_t *task, isc_event_t *event) {
if (result == DNS_R_OPTERR)
(void)ns_client_addopt(client, client->message,
&client->opt);
ns_client_log(client, NS_LOGCATEGORY_CLIENT,
NS_LOGMODULE_CLIENT, ISC_LOG_WARNING,
"message parsing failed: %s",
isc_result_totext(result));
ns_client_error(client, result);
goto cleanup;
}

View file

@ -173,6 +173,8 @@ options {\n\
dnssec-enable yes;\n\
dnssec-validation yes; \n\
dnssec-accept-expired no;\n\
fetches-per-zone 0;\n\
fetch-quota-params 100 0.1 0.3 0.7;\n\
clients-per-query 10;\n\
max-clients-per-query 100;\n\
max-recursion-depth 7;\n\
@ -180,6 +182,7 @@ options {\n\
zero-no-soa-ttl-cache no;\n\
nsec3-test-zone no;\n\
allow-new-zones no;\n\
fetches-per-server 0;\n\
"
#ifdef HAVE_GEOIP
"\

View file

@ -15,8 +15,6 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: server.h,v 1.118 2012/01/31 23:47:31 tbox Exp $ */
#ifndef NAMED_SERVER_H
#define NAMED_SERVER_H 1
@ -51,6 +49,7 @@ struct ns_server {
isc_quota_t xfroutquota;
isc_quota_t tcpquota;
isc_quota_t recursionquota;
dns_acl_t *blackholeacl;
dns_acl_t *keepresporder;
char * statsfile; /*%< Statistics file name */

View file

@ -217,6 +217,9 @@ struct dumpcontext {
isc_mem_t *mctx;
isc_boolean_t dumpcache;
isc_boolean_t dumpzones;
isc_boolean_t dumpadb;
isc_boolean_t dumpbad;
isc_boolean_t dumpfail;
FILE *fp;
ISC_LIST(struct viewlistentry) viewlist;
struct viewlistentry *view;
@ -2385,7 +2388,7 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
unsigned int dlzargc;
char **dlzargv;
const cfg_obj_t *disabled;
const cfg_obj_t *obj;
const cfg_obj_t *obj, *obj2;
const cfg_listelt_t *element;
in_port_t port;
dns_cache_t *cache = NULL;
@ -3044,6 +3047,53 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
}
dns_adb_setadbsize(view->adb, max_adb_size);
/*
* Set up ADB quotas
*/
{
isc_uint32_t fps, freq;
double low, high, discount;
obj = NULL;
result = ns_config_get(maps, "fetches-per-server", &obj);
INSIST(result == ISC_R_SUCCESS);
obj2 = cfg_tuple_get(obj, "fetches");
fps = cfg_obj_asuint32(obj2);
obj2 = cfg_tuple_get(obj, "response");
if (!cfg_obj_isvoid(obj2)) {
const char *resp = cfg_obj_asstring(obj2);
isc_result_t r;
if (strcasecmp(resp, "drop") == 0)
r = DNS_R_DROP;
else if (strcasecmp(resp, "fail") == 0)
r = DNS_R_SERVFAIL;
else
INSIST(0);
dns_resolver_setquotaresponse(view->resolver,
dns_quotatype_server, r);
}
obj = NULL;
result = ns_config_get(maps, "fetch-quota-params", &obj);
INSIST(result == ISC_R_SUCCESS);
obj2 = cfg_tuple_get(obj, "frequency");
freq = cfg_obj_asuint32(obj2);
obj2 = cfg_tuple_get(obj, "low");
low = (double) cfg_obj_asfixedpoint(obj2) / 100.0;
obj2 = cfg_tuple_get(obj, "high");
high = (double) cfg_obj_asfixedpoint(obj2) / 100.0;
obj2 = cfg_tuple_get(obj, "discount");
discount = (double) cfg_obj_asfixedpoint(obj2) / 100.0;
dns_adb_setquota(view->adb, fps, freq, low, high, discount);
}
/*
* Set resolver's lame-ttl.
*/
@ -3503,6 +3553,27 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
INSIST(result == ISC_R_SUCCESS);
dns_resolver_setmaxqueries(view->resolver, cfg_obj_asuint32(obj));
obj = NULL;
result = ns_config_get(maps, "fetches-per-zone", &obj);
INSIST(result == ISC_R_SUCCESS);
obj2 = cfg_tuple_get(obj, "fetches");
dns_resolver_setfetchesperzone(view->resolver, cfg_obj_asuint32(obj2));
obj2 = cfg_tuple_get(obj, "response");
if (!cfg_obj_isvoid(obj2)) {
const char *resp = cfg_obj_asstring(obj2);
isc_result_t r;
if (strcasecmp(resp, "drop") == 0)
r = DNS_R_DROP;
else if (strcasecmp(resp, "fail") == 0)
r = DNS_R_SERVFAIL;
else
INSIST(0);
dns_resolver_setquotaresponse(view->resolver,
dns_quotatype_zone, r);
}
#ifdef ALLOW_FILTER_AAAA
obj = NULL;
result = ns_config_get(maps, "filter-aaaa-on-v4", &obj);
@ -5507,6 +5578,7 @@ load_configuration(const char *filename, ns_server_t *server,
ns_cachelist_t cachelist, tmpcachelist;
ns_cfgctx_t *nzctx;
unsigned int maxsocks;
isc_uint32_t softquota = 0;
ISC_LIST_INIT(viewlist);
ISC_LIST_INIT(builtin_viewlist);
@ -5701,11 +5773,22 @@ 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)
isc_quota_soft(&server->recursionquota,
server->recursionquota.max - 100);
else
isc_quota_soft(&server->recursionquota, 0);
if (server->recursionquota.max > 1000) {
int margin = ISC_MAX(100, ns_g_cpus + 1);
if (margin > server->recursionquota.max - 100) {
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
"'recursive-clients %d' too low when "
"running with %d worker threads",
server->recursionquota.max, ns_g_cpus);
CHECK(ISC_R_RANGE);
}
softquota = server->recursionquota.max - margin;
} else
softquota = (server->recursionquota.max * 90) / 100;
isc_quota_soft(&server->recursionquota, softquota);
CHECK(configure_view_acl(NULL, config, "blackhole", NULL,
ns_g_aclconfctx, ns_g_mctx,
@ -6854,7 +6937,6 @@ ns_server_create(isc_mem_t *mctx, ns_server_t **serverp) {
result = isc_quota_init(&server->recursionquota, 100);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
result = dns_aclenv_init(mctx, &server->aclenv);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
@ -7865,12 +7947,20 @@ dumpdone(void *arg, isc_result_t result) {
goto cleanup;
}
}
if ((dctx->dumpadb || dctx->dumpbad || dctx->dumpfail) &&
dctx->cache == NULL && dctx->view->view->cachedb != NULL)
dns_db_attach(dctx->view->view->cachedb, &dctx->cache);
if (dctx->cache != NULL) {
dns_adb_dump(dctx->view->view->adb, dctx->fp);
dns_resolver_printbadcache(dctx->view->view->resolver,
dctx->fp);
dns_badcache_print(dctx->view->view->failcache,
"SERVFAIL cache", dctx->fp);
if (dctx->dumpadb)
dns_adb_dump(dctx->view->view->adb, dctx->fp);
if (dctx->dumpbad)
dns_resolver_printbadcache(dctx->view->view->resolver,
dctx->fp);
if (dctx->dumpfail)
dns_badcache_print(dctx->view->view->failcache,
"SERVFAIL cache", dctx->fp);
dns_db_detach(&dctx->cache);
}
if (dctx->dumpzones) {
@ -7954,6 +8044,9 @@ ns_server_dumpdb(ns_server_t *server, char *args) {
dctx->mctx = server->mctx;
dctx->dumpcache = ISC_TRUE;
dctx->dumpadb = ISC_TRUE;
dctx->dumpbad = ISC_TRUE;
dctx->dumpfail = ISC_TRUE;
dctx->dumpzones = ISC_FALSE;
dctx->fp = NULL;
ISC_LIST_INIT(dctx->viewlist);
@ -7977,15 +8070,36 @@ ns_server_dumpdb(ns_server_t *server, char *args) {
ptr = next_token(&args, " \t");
if (ptr != NULL && strcmp(ptr, "-all") == 0) {
/* also dump zones */
dctx->dumpzones = ISC_TRUE;
dctx->dumpcache = ISC_TRUE;
ptr = next_token(&args, " \t");
} else if (ptr != NULL && strcmp(ptr, "-cache") == 0) {
dctx->dumpzones = ISC_FALSE;
dctx->dumpcache = ISC_TRUE;
/* this is the default */
ptr = next_token(&args, " \t");
} else if (ptr != NULL && strcmp(ptr, "-zones") == 0) {
/* only dump zones, suppress caches */
dctx->dumpadb = ISC_FALSE;
dctx->dumpbad = ISC_FALSE;
dctx->dumpcache = ISC_FALSE;
dctx->dumpfail = ISC_FALSE;
dctx->dumpzones = ISC_TRUE;
ptr = next_token(&args, " \t");
} else if (ptr != NULL && strcmp(ptr, "-adb") == 0) {
/* only dump adb, suppress other caches */
dctx->dumpbad = ISC_FALSE;
dctx->dumpcache = ISC_FALSE;
dctx->dumpfail = ISC_FALSE;
ptr = next_token(&args, " \t");
} else if (ptr != NULL && strcmp(ptr, "-bad") == 0) {
/* only dump badcache, suppress other caches */
dctx->dumpadb = ISC_FALSE;
dctx->dumpcache = ISC_FALSE;
dctx->dumpfail = ISC_FALSE;
ptr = next_token(&args, " \t");
} else if (ptr != NULL && strcmp(ptr, "-fail") == 0) {
/* only dump servfail cache, suppress other caches */
dctx->dumpadb = ISC_FALSE;
dctx->dumpbad = ISC_FALSE;
dctx->dumpcache = ISC_FALSE;
ptr = next_token(&args, " \t");
}
@ -8114,12 +8228,24 @@ ns_server_dumpsecroots(ns_server_t *server, char *args, isc_buffer_t **text) {
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:

View file

@ -338,6 +338,9 @@ init_desc(void) {
SET_RESSTATDESC(cookieok, "COOKIE client ok", "CookieClientOk");
SET_RESSTATDESC(badvers, "bad EDNS version", "BadEDNSVersion");
SET_RESSTATDESC(badcookie, "bad cookie rcode", "BadCookieRcode");
SET_RESSTATDESC(zonequota, "spilled due to zone quota", "ZoneQuota");
SET_RESSTATDESC(serverquota, "spilled due to server quota",
"ServerQuota");
INSIST(i == dns_resstatscounter_max);

View file

@ -107,7 +107,7 @@ command is one of the following:\n\
Add zone to given view. Requires new-zone-file option.\n\
delzone [-clean] zone [class [view]]\n\
Removes zone from given view. Requires new-zone-file option.\n\
dumpdb [-all|-cache|-zones] [view ...]\n\
dumpdb [-all|-cache|-zones|-adb|-bad|-fail] [view ...]\n\
Dump cache(s) to the dump file (named_dump.db).\n\
flush Flushes all of the server's caches.\n\
flush [view] Flushes the server's cache for a view.\n\

View file

@ -312,7 +312,7 @@
</varlistentry>
<varlistentry>
<term><userinput>dumpdb <optional>-all|-cache|-zone</optional> <optional><replaceable>view ...</replaceable></optional></userinput></term>
<term><userinput>dumpdb <optional>-all|-cache|-zone|-adb|-bad|-fail</optional> <optional><replaceable>view ...</replaceable></optional></userinput></term>
<listitem>
<para>
Dump the server's caches (default) and/or zones to
@ -601,13 +601,17 @@
</varlistentry>
<varlistentry>
<term><userinput>recursing</userinput></term>
<listitem>
<para>
Dump the list of queries <command>named</command> is currently recursing
on.
</para>
</listitem>
<term><userinput>recursing</userinput></term>
<listitem>
<para>
Dump the list of queries <command>named</command> is currently
recursing on, and the list of domains to which iterative
queries are currently being sent. (The second list includes
the number of fetches currently active for the given domain,
and how many have been passed or dropped because of the
<option>fetches-per-zone</option> option.)
</para>
</listitem>
</varlistentry>
<varlistentry>

View file

@ -68,7 +68,7 @@ SUBDIRS="acl additional allow_query addzone autosign builtin
cacheclean case checkconf @CHECKDS@ checknames checkzone
cookie @COVERAGE@ database digdelv dlv dlvauto dlz dlzexternal
dname dns64 dnssec dsdigest dscp ecdsa ednscompliance
emptyzones filter-aaaa formerr forward geoip glue gost
emptyzones fetchlimit filter-aaaa formerr forward geoip glue gost
ixfr inline legacy limits logfileconfig lwresd masterfile
masterformat metadata mkeys notify nslookup nsupdate pending
pipelined @PKCS11_TEST@ reclimit redirect resolver rndc

90
bin/tests/system/ditch.pl Normal file
View file

@ -0,0 +1,90 @@
#!/usr/bin/perl
#
# Copyright (C) 2011, 2012 Internet Systems Consortium, Inc. ("ISC")
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
# This is a tool for sending queries via UDP to specified address and
# port, then exiting without waiting for a response.
#
# Usage: ditch.pl [-s <address>] [-p <port>] [filename]
#
# Input (in filename, if specified, otherwise stdin) is a series of one
# or more DNS names and types to send as queries, e.g.:
#
# www.example.com A
# www.example.org MX
#
# If not specified, address defaults to 127.0.0.1, port to 53.
require 5.006.001;
use strict;
use Getopt::Std;
use Net::DNS;
use Net::DNS::Packet;
use IO::File;
use IO::Socket;
sub usage {
print ("Usage: ditch.pl [-s address] [-p port] [file]\n");
exit 1;
}
my %options={};
getopts("s:p:t:", \%options);
my $addr = "127.0.0.1";
$addr = $options{s} if defined $options{s};
my $port = 53;
$port = $options{p} if defined $options{p};
my $file = "STDIN";
if (@ARGV >= 1) {
my $filename = shift @ARGV;
open FH, "<$filename" or die "$filename: $!";
$file = "FH";
}
my $input = "";
while (defined(my $line = <$file>) ) {
chomp $line;
next if ($line =~ m/^ *#/);
my @tokens = split (' ', $line);
my $packet;
if ($Net::DNS::VERSION > 0.68) {
$packet = new Net::DNS::Packet();
$@ and die $@;
} else {
my $err;
($packet, $err) = new Net::DNS::Packet();
$err and die $err;
}
my $q = new Net::DNS::Question($tokens[0], $tokens[1], "IN");
$packet->header->rd(1);
$packet->push(question => $q);
my $sock = IO::Socket::INET->new(PeerAddr => $addr, PeerPort => $port,
Proto => "udp",) or die "$!";
my $bytes = $sock->send($packet->data);
#print ("sent $bytes bytes to $addr:$port:\n");
#print (" ", unpack("H* ", $packet->data), "\n");
$sock->close;
}
close $file;

View file

@ -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";
}
}

View file

@ -0,0 +1,21 @@
#!/bin/sh
#
# Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
rm -f */named.memstats */ans.run */named.recursing
rm -f dig.out*
rm -f ans4/norespond
rm -f ns3/named.conf ns3/named.stats ns3/named_dump.db
rm -f burst.input.*

View file

@ -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";
};

View file

@ -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

View file

@ -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

View file

@ -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; };
};

View file

@ -0,0 +1,2 @@
# Don't specify '-T clienttest' as it consumes lots of memory with this test
-D ns3 -X named.lock -m record,size,mctx -c named.conf -d 99 -g -U 4

View file

@ -0,0 +1,45 @@
/*
* Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
controls { /* empty */ };
options {
query-source address 10.53.0.3;
notify-source 10.53.0.3;
transfer-source 10.53.0.3;
port 5300;
directory ".";
pid-file "named.pid";
listen-on { 10.53.0.3; };
listen-on-v6 { none; };
recursion yes;
notify yes;
fetches-per-server 400;
};
key rndc_key {
secret "1234abcd8765";
algorithm hmac-sha256;
};
controls {
inet 10.53.0.3 port 9953 allow { any; } keys { rndc_key; };
};
zone "." {
type hint;
file "root.hint";
};

View file

@ -0,0 +1,45 @@
/*
* Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
controls { /* empty */ };
options {
query-source address 10.53.0.3;
notify-source 10.53.0.3;
transfer-source 10.53.0.3;
port 5300;
directory ".";
pid-file "named.pid";
listen-on { 10.53.0.3; };
listen-on-v6 { none; };
recursion yes;
notify yes;
fetches-per-zone 40;
};
key rndc_key {
secret "1234abcd8765";
algorithm hmac-sha256;
};
controls {
inet 10.53.0.3 port 9953 allow { any; } keys { rndc_key; };
};
zone "." {
type hint;
file "root.hint";
};

View file

@ -0,0 +1,45 @@
/*
* Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
controls { /* empty */ };
options {
query-source address 10.53.0.3;
notify-source 10.53.0.3;
transfer-source 10.53.0.3;
port 5300;
directory ".";
pid-file "named.pid";
listen-on { 10.53.0.3; };
listen-on-v6 { none; };
recursion yes;
notify yes;
recursive-clients 400;
};
key rndc_key {
secret "1234abcd8765";
algorithm hmac-sha256;
};
controls {
inet 10.53.0.3 port 9953 allow { any; } keys { rndc_key; };
};
zone "." {
type hint;
file "root.hint";
};

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,186 @@
#!/bin/sh
#
# Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
SYSTEMTESTTOP=..
. $SYSTEMTESTTOP/conf.sh
DIGCMD="$DIG @10.53.0.3 -p 5300 +tries=1 +time=1"
RNDCCMD="$RNDC -p 9953 -s 10.53.0.3 -c ../common/rndc.conf"
burst() {
num=${3:-20}
rm -f burst.input.$$
while [ $num -gt 0 ]; do
num=`expr $num - 1`
echo "${num}${1}${2}.lamesub.example A" >> burst.input.$$
done
$PERL ../ditch.pl -p 5300 -s 10.53.0.3 burst.input.$$
rm -f burst.input.$$
}
stat() {
clients=`$RNDCCMD status | grep "recursive clients" |
sed 's;.*: \([^/][^/]*\)/.*;\1;'`
echo "I: clients: $clients"
[ "$clients" = "" ] && return 1
[ "$clients" -le $1 ]
}
status=0
echo "I: checking recursing clients are dropped at the per-server limit"
ret=0
# make the server lame and restart
$RNDCCMD flush
touch ans4/norespond
for try in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20; do
burst a $try
# fetches-per-server is at 400, but at 20qps against a lame server,
# we'll reach 200 at the tenth second, and the quota should have been
# tuned to less than that by then
stat 200 || ret=1
[ $ret -eq 1 ] && break
sleep 1
done
if [ $ret != 0 ]; then echo "I: failed"; fi
status=`expr $status + $ret`
echo "I: dumping ADB data"
$RNDCCMD dumpdb -adb
info=`grep '10.53.0.4' ns3/named_dump.db | sed 's/.*\(atr [.0-9]*\).*\(quota [0-9]*\).*/I: \1 \2/'`
echo $info
set -- $info
quota=$5
[ ${5:-200} -lt 200 ] || ret=1
echo "I: checking servfail statistics"
rm -f ns3/named.stats
$RNDCCMD stats
for try in 1 2 3 4 5; do
[ -f ns3/named.stats ] && break
sleep 1
done
sspill=`grep 'spilled due to server' ns3/named.stats | sed 's/\([0-9][0-9]*\) spilled.*/\1/'`
[ -z "$sspill" ] && sspill=0
fails=`grep 'queries resulted in SERVFAIL' ns3/named.stats | sed 's/\([0-9][0-9]*\) queries.*/\1/'`
[ -z "$fails" ] && fails=0
[ "$fails" -ge "$sspill" ] || ret=1
if [ $ret != 0 ]; then echo "I: failed"; fi
status=`expr $status + $ret`
echo "I: checking lame server recovery"
ret=0
rm -f ans4/norespond
for try in 1 2 3 4 5; do
burst b $try
stat 200 || ret=1
[ $ret -eq 1 ] && break
sleep 1
done
echo "I: dumping ADB data"
$RNDCCMD dumpdb -adb
info=`grep '10.53.0.4' ns3/named_dump.db | sed 's/.*\(atr [.0-9]*\).*\(quota [0-9]*\).*/I: \1 \2/'`
echo $info
set -- $info
[ ${5:-${quota}} -lt $quota ] || ret=1
quota=$5
for try in 1 2 3 4 5 6 7 8 9 10; do
burst c $try
stat 20 || ret=1
[ $ret -eq 1 ] && break
sleep 1
done
echo "I: dumping ADB data"
$RNDCCMD dumpdb -adb
info=`grep '10.53.0.4' ns3/named_dump.db | sed 's/.*\(atr [.0-9]*\).*\(quota [0-9]*\).*/I: \1 \2/'`
echo $info
set -- $info
[ ${5:-${quota}} -gt $quota ] || ret=1
quota=$5
if [ $ret != 0 ]; then echo "I: failed"; fi
status=`expr $status + $ret`
if [ $ret != 0 ]; then echo "I: failed"; fi
status=`expr $status + $ret`
cp -f ns3/named2.conf ns3/named.conf
$RNDCCMD reconfig 2>&1 | sed 's/^/I:ns3 /'
echo "I: checking lame server clients are dropped at the per-domain limit"
ret=0
fail=0
success=0
touch ans4/norespond
for try in 1 2 3 4 5; do
burst b $try 300
$DIGCMD a ${try}.example > dig.out.ns3.$try
grep "status: NOERROR" dig.out.ns3.$try > /dev/null 2>&1 && \
success=`expr $success + 1`
grep "status: SERVFAIL" dig.out.ns3.$try > /dev/null 2>&1 && \
fail=`expr $fail + 1`
stat 50 || ret=1
[ $ret -eq 1 ] && break
$RNDCCMD recursing 2>&1 | sed 's/^/I:ns3 /'
sleep 1
done
echo "I: $success successful valid queries, $fail SERVFAIL"
if [ $ret != 0 ]; then echo "I: failed"; fi
status=`expr $status + $ret`
echo "I: checking drop statistics"
rm -f ns3/named.stats
$RNDCCMD stats
for try in 1 2 3 4 5; do
[ -f ns3/named.stats ] && break
sleep 1
done
zspill=`grep 'spilled due to zone' ns3/named.stats | sed 's/\([0-9][0-9]*\) spilled.*/\1/'`
[ -z "$zspill" ] && zspill=0
drops=`grep 'queries dropped' ns3/named.stats | sed 's/\([0-9][0-9]*\) queries.*/\1/'`
[ -z "$drops" ] && drops=0
[ "$drops" -ge "$zspill" ] || ret=1
if [ $ret != 0 ]; then echo "I: failed"; fi
status=`expr $status + $ret`
cp -f ns3/named3.conf ns3/named.conf
$RNDCCMD reconfig 2>&1 | sed 's/^/I:ns3 /'
echo "I: checking lame server clients are dropped at the soft limit"
ret=0
fail=0
success=0
touch ans4/norespond
for try in 1 2 3 4 5; do
burst b $try 400
$DIG @10.53.0.3 -p 5300 a ${try}.example > dig.out.ns3.$try
stat 360 || ret=1
grep "status: NOERROR" dig.out.ns3.$try > /dev/null 2>&1 && \
success=`expr $success + 1`
grep "status: SERVFAIL" dig.out.ns3.$try > /dev/null 2>&1 && \
fail=`expr $fail + 1`
[ $ret -eq 1 ] && break
sleep 1
done
echo "I: $success successful valid queries, $fail SERVFAIL"
[ "$success" -eq 5 ] || ret=1
if [ $ret != 0 ]; then echo "I: failed"; fi
status=`expr $status + $ret`
echo "I:exit status: $status"
exit $status

View file

@ -15,8 +15,6 @@
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
# $Id: tests.sh,v 1.22 2012/02/09 23:47:18 tbox Exp $
SYSTEMTESTTOP=..
. $SYSTEMTESTTOP/conf.sh
@ -43,7 +41,7 @@ if [ -x ${RESOLVE} ] ; then
echo "I:checking that local bound address can be set (Can't query from a denied address)"
ret=0
${RESOLVE} -b 10.53.0.8 -p 5300 -t a -s 10.53.0.1 www.example.org 2> resolve.out || ret=1
grep "resolution failed: failure" resolve.out > /dev/null || ret=1
grep "resolution failed: SERVFAIL" resolve.out > /dev/null || ret=1
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
@ -79,7 +77,7 @@ if [ -x ${RESOLVE} ] ; then
echo "I:checking handling of bogus referrals using dns_client"
ret=0
${RESOLVE} -p 5300 -t a -s 10.53.0.1 www.example.com 2> resolve.out || ret=1
grep "resolution failed: failure" resolve.out > /dev/null || ret=1
grep "resolution failed: SERVFAIL" resolve.out > /dev/null || ret=1
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
fi

View file

@ -4886,9 +4886,14 @@ badresp:1,adberr:0,findfail:0,valfail:0]
<optional> max-transfer-time-out <replaceable>number</replaceable>; </optional>
<optional> max-transfer-idle-in <replaceable>number</replaceable>; </optional>
<optional> max-transfer-idle-out <replaceable>number</replaceable>; </optional>
<optional> tcp-clients <replaceable>number</replaceable>; </optional>
<optional> reserved-sockets <replaceable>number</replaceable>; </optional>
<optional> recursive-clients <replaceable>number</replaceable>; </optional>
<optional> tcp-clients <replaceable>number</replaceable>; </optional>
<optional> clients-per-query <replaceable>number</replaceable> ; </optional>
<optional> max-clients-per-query <replaceable>number</replaceable> ; </optional>
<optional> fetches-per-server <replaceable>number</replaceable> <optional><replaceable>(drop | fail)</replaceable></optional>; </optional>
<optional> fetch-quota-params <replaceable>number fixedpoint fixedpoint fixedpoint</replaceable> ; </optional>
<optional> fetches-per-zone<replaceable>number</replaceable> <optional><replaceable>(drop | fail)</replaceable></optional>; </optional>
<optional> notify-rate <replaceable>number</replaceable>; </optional>
<optional> startup-notify-rate <replaceable>number</replaceable>; </optional>
<optional> serial-query-rate <replaceable>number</replaceable>; </optional>
@ -8382,17 +8387,33 @@ avoid-v6-udp-ports { 40000; range 50000 60000; };
<term><command>recursive-clients</command></term>
<listitem>
<para>
The maximum number of simultaneous recursive lookups
the server will perform on behalf of clients. The default
is
The maximum number ("hard quota") of simultaneous
recursive lookups the server will perform on behalf
of clients. The default is
<literal>1000</literal>. Because each recursing
client uses a fair
bit of memory, on the order of 20 kilobytes, the value of
the
bit of memory (on the order of 20 kilobytes), the
value of the
<command>recursive-clients</command> option may
have to be decreased
on hosts with limited memory.
have to be decreased on hosts with limited memory.
</para>
<para>
<option>recursive-clients</option> defines a "hard
quota" limit for pending recursive clients: when more
clients than this are pending, new incoming requests
will not be accepted, and for each incoming request
a previous pending request will also be dropped.
</para>
<para>
A "soft quota" is also set. When this lower
quota is exceeded, incoming requests are accepted, but
for each one, a pending request will be dropped.
If <option>recursive-clients</option> is greater than
1000, the soft quota is set to
<option>recursive-clients</option> minus 100;
otherwise it is set to 90% of
<option>recursive-clients</option>.
</para>
</listitem>
</varlistentry>
@ -8407,6 +8428,175 @@ avoid-v6-udp-ports { 40000; range 50000 60000; };
</listitem>
</varlistentry>
<varlistentry id="clients-per-query">
<term><command>clients-per-query</command></term>
<term><command>max-clients-per-query</command></term>
<listitem>
<para>These set the
initial value (minimum) and maximum number of recursive
simultaneous clients for any given query
(&lt;qname,qtype,qclass&gt;) that the server will accept
before dropping additional clients. <command>named</command> will attempt to
self tune this value and changes will be logged. The
default values are 10 and 100.
</para>
<para>
This value should reflect how many queries come in for
a given name in the time it takes to resolve that name.
If the number of queries exceed this value, <command>named</command> will
assume that it is dealing with a non-responsive zone
and will drop additional queries. If it gets a response
after dropping queries, it will raise the estimate. The
estimate will then be lowered in 20 minutes if it has
remained unchanged.
</para>
<para>
If <command>clients-per-query</command> is set to zero,
then there is no limit on the number of clients per query
and no queries will be dropped.
</para>
<para>
If <command>max-clients-per-query</command> is set to zero,
then there is no upper bound other than imposed by
<command>recursive-clients</command>.
</para>
</listitem>
</varlistentry>
<varlistentry id="fetches-per-zone">
<term><command>fetches-per-zone</command></term>
<listitem>
<para>
The maximum number of simultaneous iterative
queries to any one domain that the server will
permit before blocking new queries for data
in or beneath that zone.
This value should reflect how many fetches would
normally be sent to any one zone in the time it
would take to resolve them. It should be smaller
than <option>recursive-clients</option>.
</para>
<para>
When many clients simultaneously query for the
same name and type, the clients will all be attached
to the same fetch, up to the
<option>max-clients-per-query</option> limit,
and only one iterative query will be sent.
However, when clients are simultaneously
querying for <emphasis>different</emphasis> names
or types, multiple queries will be sent and
<option>max-clients-per-query</option> is not
effective as a limit.
</para>
<para>
Optionally, this value may be followed by the keyword
<literal>drop</literal> or <literal>fail</literal>,
indicating whether queries which exceed the fetch
quota for a zone will be dropped with no response,
or answered with SERVFAIL. The default is
<literal>drop</literal>.
</para>
<para>
If <command>fetches-per-zone</command> is set to zero,
then there is no limit on the number of fetches per query
and no queries will be dropped. The default is zero.
</para>
<para>
The current list of active fetches can be dumped by
running <command>rndc recursing</command>. The list
includes the number of active fetches for each
domain and the number of queries that have been
passed or dropped as a result of the
<option>fetches-per-zone</option> limit. (Note:
these counters are not cumulative over time; whenever
the number of active fetches for a domain drops to
zero, the counter for that domain is deleted, and the
next time a fetch is sent to that domain, it is
recreated with the counters set to zero.)
</para>
</listitem>
</varlistentry>
<varlistentry id="fetches-per-server">
<term><command>fetches-per-server</command></term>
<listitem>
<para>
The maximum number of simultaneous iterative
queries that the server will allow to be sent to
a single upstream name server before blocking
additional queries.
This value should reflect how many fetches would
normally be sent to any one server in the time it
would take to resolve them. It should be smaller
than <option>recursive-clients</option>.
</para>
<para>
Optionally, this value may be followed by the keyword
<literal>drop</literal> or <literal>fail</literal>,
indicating whether queries will be dropped with no
response, or answered with SERVFAIL, when all of the
servers authoritative for a zone are found to have
exceeded the per-server quota. The default is
<literal>fail</literal>.
</para>
<para>
If <command>fetches-per-server</command> is set to zero,
then there is no limit on the number of fetches per query
and no queries will be dropped. The default is zero.
</para>
<para>
The <command>fetches-per-server</command> quota is
dynamically adjusted in response to detected
congestion. As queries are sent to a server
and are either answered or time out, an
exponentially weighted moving average is calculated
of the ratio of timeouts to responses. If the
current average timeout ratio rises above a "high"
threshold, then <command>fetches-per-server</command>
is reduced for that server. If the timeout ratio
drops below a "low" threshold, then
<command>fetches-per-server</command> is increased.
The <command>fetch-quota-params</command> options
can be used to adjust the parameters for this
calculation.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>fetch-quota-params</command></term>
<listitem>
<para>
Sets the parameters to use for dynamic resizing of
the <option>fetches-per-server</option> quota in
response to detected congestion.
</para>
<para>
The first argument is an integer value indicating
how frequently to recalculate the moving average
of the ratio of timeouts to responses for each
server. The default is 100, meaning we recalculate
the average ratio after every 100 queries have either
been answered or timed out.
</para>
<para>
The remaining three arguments represent the "low"
threshold (defaulting to a timeout ratio of 0.1),
the "high" threshold (defaulting to a timeout
ratio of 0.3), and the discount rate for
the moving average (defaulting to 0.7).
A higher discount rate causes recent events to
weigh more heavily when calculating the moving
average; a lower discount rate causes past
events to weigh more heavily, smoothing out
short-term blips in the timeout ratio.
These arguments are all fixed-point numbers with
precision of 1/100: at most two places after
the decimal point are significant.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>reserved-sockets</command></term>
<listitem>

View file

@ -111,6 +111,42 @@
<sect2 id="relnotes_features">
<title>New Features</title>
<itemizedlist>
<listitem>
<para>
New quotas have been added to limit the queries that are
sent by recursive resolvers to authoritative servers
experiencing denial-of-service attacks. When configured,
these options can both reduce the harm done to authoritative
servers and also avoid the resource exhaustion that can be
experienced by recursives when they are being used as a
vehicle for such an attack.
</para>
<itemizedlist>
<listitem>
<para>
<option>fetches-per-server</option> limits the number of
simultaneous queries that can be sent to any single
authoritative server. The configured value is a starting
point; it is automatically adjusted downward if the server is
partially or completely non-responsive. The algorithm used to
adjust the quota can be configured via the
<option>fetch-quota-params</option> option.
</para>
</listitem>
<listitem>
<para>
<option>fetches-per-zone</option> limits the number of
simultaneous queries that can be sent for names within a
single domain. (Note: Unlike "fetches-per-server", this
value is not self-tuning.)
</para>
</listitem>
</itemizedlist>
<para>
Statistics counters have also been added to track the number
of queries affected by these quotas.
</para>
</listitem>
<listitem>
<para>
New statistics counters have been added to track traffic

View file

@ -163,6 +163,12 @@ struct dns_adb {
isc_boolean_t growentries_sent;
isc_event_t grownames;
isc_boolean_t grownames_sent;
isc_uint32_t quota;
isc_uint32_t atr_freq;
double atr_low;
double atr_high;
double atr_discount;
};
/*
@ -244,10 +250,18 @@ struct dns_adbentry {
unsigned int flags;
unsigned int srtt;
isc_uint16_t udpsize;
unsigned int completed;
unsigned int timeouts;
unsigned char plain;
unsigned char plainto;
unsigned char edns;
unsigned char to4096; /* Our max. */
isc_uint8_t mode;
isc_uint32_t quota;
isc_uint32_t active;
double atr;
/*
* Allow for encapsulated IPv4/IPv6 UDP packet over ethernet.
* Ethernet 1500 - IP(20) - IP6(40) - UDP(8) = 1432.
@ -300,6 +314,7 @@ static inline dns_adbentry_t *find_entry_and_lock(dns_adb_t *,
static void dump_adb(dns_adb_t *, FILE *, isc_boolean_t debug, isc_stdtime_t);
static void print_dns_name(FILE *, dns_name_t *);
static void print_namehook_list(FILE *, const char *legend,
dns_adb_t *adb,
dns_adbnamehooklist_t *list,
isc_boolean_t debug,
isc_stdtime_t now);
@ -335,10 +350,13 @@ static inline void link_entry(dns_adb_t *, int, dns_adbentry_t *);
static inline isc_boolean_t unlink_entry(dns_adb_t *, dns_adbentry_t *);
static isc_boolean_t kill_name(dns_adbname_t **, isc_eventtype_t);
static void water(void *, int);
static void dump_entry(FILE *, dns_adbentry_t *, isc_boolean_t, isc_stdtime_t);
static void dump_entry(FILE *, dns_adb_t *, dns_adbentry_t *,
isc_boolean_t, isc_stdtime_t);
static void adjustsrtt(dns_adbaddrinfo_t *addr, unsigned int rtt,
unsigned int factor, isc_stdtime_t now);
static void shutdown_task(isc_task_t *task, isc_event_t *ev);
static void log_quota(dns_adbentry_t *entry, const char *fmt, ...)
ISC_FORMAT_PRINTF(2, 3);
/*
* MUST NOT overlap DNS_ADBFIND_* flags!
@ -1811,6 +1829,8 @@ new_adbentry(dns_adb_t *adb) {
e->flags = 0;
e->udpsize = 0;
e->edns = 0;
e->completed = 0;
e->timeouts = 0;
e->plain = 0;
e->plainto = 0;
e->to4096 = 0;
@ -1823,6 +1843,10 @@ new_adbentry(dns_adb_t *adb) {
e->srtt = (r & 0x1f) + 1;
e->lastage = 0;
e->expires = 0;
e->active = 0;
e->mode = 0;
e->quota = adb->quota;
e->atr = 0.0;
ISC_LIST_INIT(e->lameinfo);
ISC_LINK_INIT(e, plink);
LOCK(&adb->entriescntlock);
@ -2138,6 +2162,25 @@ entry_is_lame(dns_adb_t *adb, dns_adbentry_t *entry, dns_name_t *qname,
return (is_bad);
}
static void
log_quota(dns_adbentry_t *entry, const char *fmt, ...) {
va_list ap;
char msgbuf[2048];
char addrbuf[ISC_NETADDR_FORMATSIZE];
isc_netaddr_t netaddr;
va_start(ap, fmt);
vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
va_end(ap);
isc_netaddr_fromsockaddr(&netaddr, &entry->sockaddr);
isc_netaddr_format(&netaddr, addrbuf, sizeof(addrbuf));
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ADB,
ISC_LOG_INFO, "adb: quota %s (%d/%d): %s",
addrbuf, entry->active, entry->quota, msgbuf);
}
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,
@ -2158,6 +2201,15 @@ 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->quota != 0 &&
entry->active >= entry->quota)
{
find->options |=
(DNS_ADBFIND_LAMEPRUNED|
DNS_ADBFIND_OVERQUOTA);
goto nextv4;
}
if (!FIND_RETURNLAME(find)
&& entry_is_lame(adb, entry, qname, qtype, now)) {
find->options |= DNS_ADBFIND_LAMEPRUNED;
@ -2189,6 +2241,15 @@ 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->quota != 0 &&
entry->active >= entry->quota)
{
find->options |=
(DNS_ADBFIND_LAMEPRUNED|
DNS_ADBFIND_OVERQUOTA);
goto nextv6;
}
if (!FIND_RETURNLAME(find)
&& entry_is_lame(adb, entry, qname, qtype, now)) {
find->options |= DNS_ADBFIND_LAMEPRUNED;
@ -2521,6 +2582,12 @@ dns_adb_create(isc_mem_t *mem, dns_view_t *view, isc_timermgr_t *timermgr,
adb, NULL, NULL);
adb->growentries_sent = ISC_FALSE;
adb->quota = 0;
adb->atr_freq = 0;
adb->atr_low = 0.0;
adb->atr_high = 0.0;
adb->atr_discount = 0.0;
adb->nnames = nbuckets[0];
adb->namescnt = 0;
adb->names = NULL;
@ -3431,8 +3498,10 @@ dump_adb(dns_adb_t *adb, FILE *f, isc_boolean_t debug, isc_stdtime_t now) {
fprintf(f, "\n");
print_namehook_list(f, "v4", &name->v4, debug, now);
print_namehook_list(f, "v6", &name->v6, debug, now);
print_namehook_list(f, "v4", adb,
&name->v4, debug, now);
print_namehook_list(f, "v6", adb,
&name->v6, debug, now);
if (debug)
print_fetch_list(f, name);
@ -3447,7 +3516,7 @@ dump_adb(dns_adb_t *adb, FILE *f, isc_boolean_t debug, isc_stdtime_t now) {
entry = ISC_LIST_HEAD(adb->entries[i]);
while (entry != NULL) {
if (entry->nh == 0)
dump_entry(f, entry, debug, now);
dump_entry(f, adb, entry, debug, now);
entry = ISC_LIST_NEXT(entry, plink);
}
}
@ -3462,8 +3531,8 @@ dump_adb(dns_adb_t *adb, FILE *f, isc_boolean_t debug, isc_stdtime_t now) {
}
static void
dump_entry(FILE *f, dns_adbentry_t *entry, isc_boolean_t debug,
isc_stdtime_t now)
dump_entry(FILE *f, dns_adb_t *adb, dns_adbentry_t *entry,
isc_boolean_t debug, isc_stdtime_t now)
{
char addrbuf[ISC_NETADDR_FORMATSIZE];
char typebuf[DNS_RDATATYPE_FORMATSIZE];
@ -3491,10 +3560,17 @@ dump_entry(FILE *f, dns_adbentry_t *entry, isc_boolean_t debug,
}
if (entry->expires != 0)
fprintf(f, " [ttl %d]", entry->expires - now);
if (adb != NULL && adb->quota != 0 && adb->atr_freq != 0) {
fprintf(f, " [atr %0.2f] [quota %d]",
entry->atr, entry->quota);
}
fprintf(f, "\n");
for (li = ISC_LIST_HEAD(entry->lameinfo);
li != NULL;
li = ISC_LIST_NEXT(li, plink)) {
li = ISC_LIST_NEXT(li, plink))
{
fprintf(f, ";\t\t");
print_dns_name(f, &li->qname);
dns_rdatatype_format(li->qtype, typebuf, sizeof(typebuf));
@ -3566,7 +3642,8 @@ print_dns_name(FILE *f, dns_name_t *name) {
}
static void
print_namehook_list(FILE *f, const char *legend, dns_adbnamehooklist_t *list,
print_namehook_list(FILE *f, const char *legend,
dns_adb_t *adb, dns_adbnamehooklist_t *list,
isc_boolean_t debug, isc_stdtime_t now)
{
dns_adbnamehook_t *nh;
@ -3577,7 +3654,7 @@ print_namehook_list(FILE *f, const char *legend, dns_adbnamehooklist_t *list,
{
if (debug)
fprintf(f, ";\tHook(%s) %p\n", legend, nh);
dump_entry(f, nh->entry, debug, now);
dump_entry(f, adb, nh->entry, debug, now);
}
}
@ -4142,6 +4219,75 @@ dns_adb_changeflags(dns_adb_t *adb, dns_adbaddrinfo_t *addr,
UNLOCK(&adb->entrylocks[bucket]);
}
/*
* (10000 / ((10 + n) / 10)^(3/2)) for n in 0..99.
* These will be used to make quota adjustments.
*/
static int quota_adj[] = {
10000, 8668, 7607, 6747, 6037, 5443, 4941, 4512, 4141,
3818, 3536, 3286, 3065, 2867, 2690, 2530, 2385, 2254,
2134, 2025, 1925, 1832, 1747, 1668, 1595, 1527, 1464,
1405, 1350, 1298, 1250, 1205, 1162, 1121, 1083, 1048,
1014, 981, 922, 894, 868, 843, 820, 797, 775, 755,
735, 716, 698, 680, 664, 648, 632, 618, 603, 590, 577,
564, 552, 540, 529, 518, 507, 497, 487, 477, 468, 459,
450, 442, 434, 426, 418, 411, 404, 397, 390, 383, 377,
370, 364, 358, 353, 347, 342, 336, 331, 326, 321, 316,
312, 307, 303, 298, 294, 290, 286, 282, 278
};
/*
* Caller must hold adbentry lock
*/
static void
maybe_adjust_quota(dns_adb_t *adb, dns_adbaddrinfo_t *addr,
isc_boolean_t timeout)
{
double tr;
UNUSED(adb);
if (adb->quota == 0 || adb->atr_freq == 0)
return;
if (timeout)
addr->entry->timeouts++;
if (addr->entry->completed++ <= adb->atr_freq)
return;
/*
* Calculate an exponential rolling average of the timeout ratio
*
* XXX: Integer arithmetic might be better than floating point
*/
tr = (double) addr->entry->timeouts / addr->entry->completed;
addr->entry->timeouts = addr->entry->completed = 0;
INSIST(addr->entry->atr >= 0.0);
INSIST(addr->entry->atr <= 1.0);
INSIST(adb->atr_discount >= 0.0);
INSIST(adb->atr_discount <= 1.0);
addr->entry->atr *= 1.0 - adb->atr_discount;
addr->entry->atr += tr * adb->atr_discount;
addr->entry->atr = ISC_CLAMP(addr->entry->atr, 0.0, 1.0);
if (addr->entry->atr < adb->atr_low && addr->entry->mode > 0) {
addr->entry->quota = adb->quota *
quota_adj[--addr->entry->mode] / 10000;
log_quota(addr->entry, "atr %0.2f, quota increased to %d",
addr->entry->atr, addr->entry->quota);
} else if (addr->entry->atr > adb->atr_high && addr->entry->mode < 99) {
addr->entry->quota = adb->quota *
quota_adj[++addr->entry->mode] / 10000;
log_quota(addr->entry, "atr %0.2f, quota decreased to %d",
addr->entry->atr, addr->entry->quota);
}
/* Ensure we don't drop to zero */
if (addr->entry->quota == 0)
addr->entry->quota = 1;
}
#define EDNSTOS 3U
isc_boolean_t
dns_adb_noedns(dns_adb_t *adb, dns_adbaddrinfo_t *addr) {
@ -4153,6 +4299,7 @@ dns_adb_noedns(dns_adb_t *adb, dns_adbaddrinfo_t *addr) {
bucket = addr->entry->lock_bucket;
LOCK(&adb->entrylocks[bucket]);
if (addr->entry->edns == 0U &&
(addr->entry->plain > EDNSTOS || addr->entry->to4096 > EDNSTOS)) {
if (((addr->entry->plain + addr->entry->to4096) & 0x3f) != 0) {
@ -4187,6 +4334,8 @@ dns_adb_plainresponse(dns_adb_t *adb, dns_adbaddrinfo_t *addr) {
bucket = addr->entry->lock_bucket;
LOCK(&adb->entrylocks[bucket]);
maybe_adjust_quota(adb, addr, ISC_FALSE);
addr->entry->plain++;
if (addr->entry->plain == 0xff) {
addr->entry->edns >>= 1;
@ -4209,6 +4358,9 @@ dns_adb_timeout(dns_adb_t *adb, dns_adbaddrinfo_t *addr) {
bucket = addr->entry->lock_bucket;
LOCK(&adb->entrylocks[bucket]);
maybe_adjust_quota(adb, addr, ISC_TRUE);
/*
* If we have not had a successful query then clear all
* edns timeout information.
@ -4224,6 +4376,7 @@ dns_adb_timeout(dns_adb_t *adb, dns_adbaddrinfo_t *addr) {
addr->entry->to1432 >>= 1;
addr->entry->to4096 >>= 1;
}
addr->entry->plainto++;
if (addr->entry->plainto == 0xff) {
addr->entry->edns >>= 1;
@ -4243,6 +4396,8 @@ dns_adb_ednsto(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int size) {
bucket = addr->entry->lock_bucket;
LOCK(&adb->entrylocks[bucket]);
maybe_adjust_quota(adb, addr, ISC_TRUE);
if (size <= 512U) {
if (addr->entry->to512 <= EDNSTOS) {
addr->entry->to512++;
@ -4291,6 +4446,9 @@ dns_adb_setudpsize(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int size) {
size = 512U;
if (size > addr->entry->udpsize)
addr->entry->udpsize = size;
maybe_adjust_quota(adb, addr, ISC_FALSE);
addr->entry->edns++;
if (addr->entry->edns == 0xff) {
addr->entry->edns >>= 1;
@ -4618,3 +4776,53 @@ dns_adb_setadbsize(dns_adb_t *adb, size_t size) {
else
isc_mem_setwater(adb->mctx, water, adb, hiwater, lowater);
}
void
dns_adb_setquota(dns_adb_t *adb, isc_uint32_t quota, isc_uint32_t freq,
double low, double high, double discount)
{
REQUIRE(DNS_ADB_VALID(adb));
adb->quota = quota;
adb->atr_freq = freq;
adb->atr_low = low;
adb->atr_high = high;
adb->atr_discount = discount;
}
isc_boolean_t
dns_adbentry_overquota(dns_adbentry_t *entry) {
isc_boolean_t block;
REQUIRE(DNS_ADBENTRY_VALID(entry));
block = ISC_TF(entry->quota != 0 && entry->active >= entry->quota);
return (block);
}
void
dns_adb_beginudpfetch(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]);
addr->entry->active++;
UNLOCK(&adb->entrylocks[bucket]);
}
void
dns_adb_endudpfetch(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]);
if (addr->entry->active > 0)
addr->entry->active--;
UNLOCK(&adb->entrylocks[bucket]);
}

View file

@ -206,6 +206,11 @@ struct dns_adbfind {
* Must set _WANTEVENT for this to be meaningful.
*/
#define DNS_ADBFIND_LAMEPRUNED 0x00000200
/*%
* The server's fetch quota is exceeded; it will be treated as
* lame for this query.
*/
#define DNS_ADBFIND_OVERQUOTA 0x00000400
/*%
* The answers to queries come back as a list of these.
@ -777,6 +782,64 @@ dns_adb_getcookie(dns_adb_t *adb, dns_adbaddrinfo_t *addr,
* or it doesn't exist.
*/
void
dns_adb_setquota(dns_adb_t *adb, isc_uint32_t quota, isc_uint32_t freq,
double low, double high, double discount);
/*%<
* Set the baseline ADB quota, and configure parameters for the
* quota adjustment algorithm.
*
* If the number of fetches currently waiting for responses from this
* address exceeds the current quota, then additional fetches are spilled.
*
* 'quota' is the highest permissible quota; it will adjust itself
* downward in response to detected congestion.
*
* After every 'freq' fetches have either completed or timed out, an
* exponentially weighted moving average of the ratio of timeouts
* to responses is calculated. If the EWMA goes above a 'high'
* threshold, then the quota is adjusted down one step; if it drops
* below a 'low' threshold, then the quota is adjusted back up one
* step.
*
* The quota adjustment is based on the function (1 / 1 + (n/10)^(3/2)),
* for values of n from 0 to 99. It starts at 100% of the baseline
* quota, and descends after 100 steps to 2%.
*
* 'discount' represents the discount rate of the moving average. Higher
* values cause older values to be discounted sooner, providing a faster
* response to changes in the timeout ratio.
*
* Requires:
*\li 'adb' is valid.
*/
isc_boolean_t
dns_adbentry_overquota(dns_adbentry_t *entry);
/*%<
* Returns true if the specified ADB has too many active fetches.
*
* Requires:
*\li 'entry' is valid.
*/
void
dns_adb_beginudpfetch(dns_adb_t *adb, dns_adbaddrinfo_t *addr);
void
dns_adb_endudpfetch(dns_adb_t *adb, dns_adbaddrinfo_t *addr);
/*%
* Begin/end a UDP fetch on a particular address.
*
* These functions increment or decrement the fetch counter for
* the ADB entry so that the fetch quota can be enforced.
*
* Requires:
*
*\li adb be valid.
*
*\li addr be valid.
*/
ISC_LANG_ENDDECLS
#endif /* DNS_ADB_H */

View file

@ -15,8 +15,6 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: log.h,v 1.47 2011/10/13 22:48:24 tbox Exp $ */
/*! \file dns/log.h
* \author Principal Authors: DCL */
@ -45,6 +43,7 @@ LIBDNS_EXTERNAL_DATA extern isc_logmodule_t dns_modules[];
#define DNS_LOGCATEGORY_RPZ (&dns_categories[12])
#define DNS_LOGCATEGORY_RRL (&dns_categories[13])
#define DNS_LOGCATEGORY_CNAME (&dns_categories[14])
#define DNS_LOGCATEGORY_SPILL (&dns_categories[15])
/* Backwards compatibility. */
#define DNS_LOGCATEGORY_GENERAL ISC_LOGCATEGORY_GENERAL

View file

@ -15,8 +15,6 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: resolver.h,v 1.72 2011/12/05 17:10:51 each Exp $ */
#ifndef DNS_RESOLVER_H
#define DNS_RESOLVER_H 1
@ -54,6 +52,7 @@
#include <isc/lang.h>
#include <isc/socket.h>
#include <isc/stats.h>
#include <dns/types.h>
#include <dns/fixedname.h>
@ -84,6 +83,14 @@ typedef struct dns_fetchevent {
isc_result_t vresult;
} dns_fetchevent_t;
/*%
* The two quota types (fetches-per-zone and fetches-per-server)
*/
typedef enum {
dns_quotatype_zone = 0,
dns_quotatype_server
} dns_quotatype_t;
/*
* Options that modify how a 'fetch' is done.
*/
@ -558,6 +565,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,
@ -665,6 +674,31 @@ dns_resolver_getmaxqueries(dns_resolver_t *resolver);
* \li resolver to be valid.
*/
void
dns_resolver_setquotaresponse(dns_resolver_t *resolver,
dns_quotatype_t which, isc_result_t resp);
isc_result_t
dns_resolver_getquotaresponse(dns_resolver_t *resolver, dns_quotatype_t which);
/*%
* Get and set the result code that will be used when quotas
* are exceeded. If 'which' is set to quotatype "zone", then the
* result specified in 'resp' will be used when the fetches-per-zone
* quota is exceeded by a fetch. If 'which' is set to quotatype "server",
* then the reuslt specified in 'resp' will be used when the
* fetches-per-server quota has been exceeded for all the
* authoritative servers for a zone. Valid choices are
* DNS_R_DROP or DNS_R_SERVFAIL.
*
* Requires:
* \li 'resolver' to be valid.
* \li 'which' to be dns_quotatype_zone or dns_quotatype_server
* \li 'resp' to be DNS_R_DROP or DNS_R_SERVFAIL.
*/
void
dns_resolver_dumpfetches(dns_resolver_t *resolver,
isc_statsformat_t format, FILE *fp);
ISC_LANG_ENDDECLS
#endif /* DNS_RESOLVER_H */

View file

@ -72,7 +72,9 @@ enum {
dns_resstatscounter_cookieok = 38,
dns_resstatscounter_badvers = 39,
dns_resstatscounter_badcookie = 40,
dns_resstatscounter_max = 41,
dns_resstatscounter_zonequota = 41,
dns_resstatscounter_serverquota = 42,
dns_resstatscounter_max = 43,
/*
* DNSSEC stats.

View file

@ -15,8 +15,6 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: log.c,v 1.49 2011/10/13 22:48:24 tbox Exp $ */
/*! \file */
/* Principal Authors: DCL */
@ -47,6 +45,7 @@ LIBDNS_EXTERNAL_DATA isc_logcategory_t dns_categories[] = {
{ "rpz", 0 },
{ "rate-limit", 0 },
{ "cname", 0 },
{ "spill", 0 },
{ NULL, 0 }
};

View file

@ -155,8 +155,9 @@
#define DEFAULT_QUERY_TIMEOUT MINIMUM_QUERY_TIMEOUT
#endif
/* The maximum time in seconds for the whole query to live. */
#ifndef MAXIMUM_QUERY_TIMEOUT
#define MAXIMUM_QUERY_TIMEOUT 30 /* The maximum time in seconds for the whole query to live. */
#define MAXIMUM_QUERY_TIMEOUT 30
#endif
/* The default maximum number of recursions to follow before giving up. */
@ -169,6 +170,12 @@
#define DEFAULT_MAX_QUERIES 75
#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.
*/
@ -250,6 +257,7 @@ struct fetchctx {
dns_rdatatype_t type;
unsigned int options;
unsigned int bucketnum;
unsigned int dbucketnum;
char * info;
isc_mem_t * mctx;
@ -350,6 +358,7 @@ struct fetchctx {
unsigned int querysent;
unsigned int referrals;
unsigned int lamecount;
unsigned int quotacount;
unsigned int neterr;
unsigned int badresp;
unsigned int adberr;
@ -410,6 +419,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 {
@ -445,6 +471,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;
@ -464,6 +491,7 @@ struct dns_resolver {
unsigned int query_timeout;
unsigned int maxdepth;
unsigned int maxqueries;
isc_result_t quotaresp[2];
/* Locked by lock. */
unsigned int references;
@ -472,6 +500,7 @@ struct dns_resolver {
unsigned int activebuckets;
isc_boolean_t priming;
unsigned int spillat; /* clients-per-query */
unsigned int zspill; /* fetches-per-zone */
dns_badcache_t * badcache; /* Bad cache. */
@ -490,7 +519,6 @@ struct dns_resolver {
*/
#define FCTX_ADDRINFO_MARK 0x00001
#define FCTX_ADDRINFO_FORWARDER 0x01000
#define FCTX_ADDRINFO_TRIED 0x02000
#define FCTX_ADDRINFO_EDNSOK 0x04000
#define FCTX_ADDRINFO_NOCOOKIE 0x08000
#define FCTX_ADDRINFO_BADCOOKIE 0x10000
@ -499,8 +527,6 @@ struct dns_resolver {
== 0)
#define ISFORWARDER(a) (((a)->flags & \
FCTX_ADDRINFO_FORWARDER) != 0)
#define TRIED(a) (((a)->flags & \
FCTX_ADDRINFO_TRIED) != 0)
#define NOCOOKIE(a) (((a)->flags & \
FCTX_ADDRINFO_NOCOOKIE) != 0)
#define EDNSOK(a) (((a)->flags & \
@ -910,6 +936,13 @@ fctx_cancelquery(resquery_t **queryp, dns_dispatchevent_t **deventp,
} else {
isc_uint32_t value;
isc_uint32_t mask;
if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0)
dns_adb_ednsto(fctx->adb, query->addrinfo,
query->udpsize);
else
dns_adb_timeout(fctx->adb, query->addrinfo);
/*
* We don't have an RTT for this query. Maybe the
* packet was lost, or maybe this server is very
@ -931,11 +964,6 @@ fctx_cancelquery(resquery_t **queryp, dns_dispatchevent_t **deventp,
mask = 0x7ffff;
else
mask = 0xfffff;
if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0)
dns_adb_ednsto(fctx->adb, query->addrinfo,
query->udpsize);
else
dns_adb_timeout(fctx->adb, query->addrinfo);
/*
* Don't adjust timeout on EDNS queries unless we have
@ -945,22 +973,20 @@ fctx_cancelquery(resquery_t **queryp, dns_dispatchevent_t **deventp,
!EDNSOK(query->addrinfo)) {
mask >>= 2;
}
rtt = query->addrinfo->srtt + (value & mask);
if (rtt > MAX_SINGLE_QUERY_TIMEOUT_US)
rtt = MAX_SINGLE_QUERY_TIMEOUT_US;
/*
* Replace the current RTT with our value.
*/
factor = DNS_ADB_RTTADJREPLACE;
}
dns_adb_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);
}
dns_adb_endudpfetch(fctx->adb, query->addrinfo);
/*
* Age RTTs of servers not tried.
@ -1141,6 +1167,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;
@ -1663,6 +1809,12 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
query->connects++;
QTRACE("connecting via TCP");
} else {
if (dns_adbentry_overquota(addrinfo->entry))
goto cleanup_dispatch;
/* Inform the ADB that we're starting a fetch */
dns_adb_beginudpfetch(fctx->adb, addrinfo);
result = resquery_send(query);
if (result != ISC_R_SUCCESS)
goto cleanup_dispatch;
@ -2841,7 +2993,7 @@ sort_finds(dns_adbfindlist_t *findlist) {
static void
findname(fetchctx_t *fctx, dns_name_t *name, in_port_t port,
unsigned int options, unsigned int flags, isc_stdtime_t now,
isc_boolean_t *need_alternate)
isc_boolean_t *overquota, isc_boolean_t *need_alternate)
{
dns_adbaddrinfo_t *ai;
dns_adbfind_t *find;
@ -2932,7 +3084,12 @@ 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_OVERQUOTA) != 0) {
if (overquota != NULL)
*overquota = ISC_TRUE;
fctx->quotacount++; /* quota exceeded */
}
else if ((find->options & DNS_ADBFIND_LAMEPRUNED) != 0)
fctx->lamecount++; /* cached lame server */
else
fctx->adberr++; /* unreachable server, etc. */
@ -2975,6 +3132,7 @@ fctx_getaddresses(fetchctx_t *fctx, isc_boolean_t badcache) {
isc_boolean_t all_bad;
dns_rdata_ns_t ns;
isc_boolean_t need_alternate = ISC_FALSE;
isc_boolean_t all_spilled = ISC_TRUE;
FCTXTRACE("getaddresses");
@ -3032,28 +3190,33 @@ fctx_getaddresses(fetchctx_t *fctx, isc_boolean_t badcache) {
dns_fixedname_init(&fixed);
domain = dns_fixedname_name(&fixed);
result = dns_fwdtable_find2(fctx->res->view->fwdtable, name,
result = dns_fwdtable_find2(res->view->fwdtable, name,
domain, &forwarders);
if (result == ISC_R_SUCCESS) {
fwd = ISC_LIST_HEAD(forwarders->fwdrs);
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);
}
}
}
while (fwd != NULL) {
if ((isc_sockaddr_pf(&fwd->addr) == AF_INET &&
fctx->res->dispatches4 == NULL) ||
res->dispatches4 == NULL) ||
(isc_sockaddr_pf(&fwd->addr) == AF_INET6 &&
fctx->res->dispatches6 == NULL)) {
res->dispatches6 == NULL))
{
fwd = ISC_LIST_NEXT(fwd, link);
continue;
}
@ -3118,6 +3281,8 @@ fctx_getaddresses(fetchctx_t *fctx, isc_boolean_t badcache) {
result == ISC_R_SUCCESS;
result = dns_rdataset_next(&fctx->nameservers))
{
isc_boolean_t overquota = ISC_FALSE;
dns_rdataset_current(&fctx->nameservers, &rdata);
/*
* Extract the name from the NS record.
@ -3127,7 +3292,11 @@ fctx_getaddresses(fetchctx_t *fctx, isc_boolean_t badcache) {
continue;
findname(fctx, &ns.name, 0, stdoptions, 0, now,
&need_alternate);
&overquota, &need_alternate);
if (!overquota)
all_spilled = ISC_FALSE;
dns_rdata_reset(&rdata);
dns_rdata_freestruct(&ns);
}
@ -3141,13 +3310,13 @@ fctx_getaddresses(fetchctx_t *fctx, isc_boolean_t badcache) {
int family;
alternate_t *a;
family = (res->dispatches6 != NULL) ? AF_INET6 : AF_INET;
for (a = ISC_LIST_HEAD(fctx->res->alternates);
for (a = ISC_LIST_HEAD(res->alternates);
a != NULL;
a = ISC_LIST_NEXT(a, link)) {
if (!a->isaddress) {
findname(fctx, &a->_u._n.name, a->_u._n.port,
stdoptions, FCTX_ADDRINFO_FORWARDER,
now, NULL);
now, NULL, NULL);
continue;
}
if (isc_sockaddr_pf(&a->_u.addr) != family)
@ -3206,10 +3375,19 @@ fctx_getaddresses(fetchctx_t *fctx, isc_boolean_t badcache) {
fctx->type == dns_rdatatype_dlv ||
fctx->type == dns_rdatatype_ds) &&
result == ISC_R_SUCCESS)
dns_resolver_addbadcache(fctx->res,
&fctx->name,
dns_resolver_addbadcache(res, &fctx->name,
fctx->type, &expire);
result = ISC_R_FAILURE;
/*
* If all of the addresses found were over the
* fetches-per-server quota, return the configured
* response.
*/
if (all_spilled) {
result = res->quotaresp[dns_quotatype_server];
inc_stats(res, dns_resstatscounter_serverquota);
} else
result = ISC_R_FAILURE;
}
} else {
/*
@ -3432,14 +3610,17 @@ fctx_nextaddress(fetchctx_t *fctx) {
static void
fctx_try(fetchctx_t *fctx, isc_boolean_t retrying, isc_boolean_t badcache) {
isc_result_t result;
dns_adbaddrinfo_t *addrinfo;
dns_adbaddrinfo_t *addrinfo = NULL;
dns_resolver_t *res;
FCTXTRACE("try");
REQUIRE(!ADDRWAIT(fctx));
res = fctx->res;
/* We've already exceeded maximum query count */
if (isc_counter_used(fctx->qc) > fctx->res->maxqueries) {
if (isc_counter_used(fctx->qc) > res->maxqueries) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(3),
"exceeded max queries resolving '%s'",
@ -3448,11 +3629,13 @@ fctx_try(fetchctx_t *fctx, isc_boolean_t retrying, isc_boolean_t badcache) {
return;
}
addrinfo = fctx_nextaddress(fctx);
/* Try to find an address that isn't over quota */
while ((addrinfo = fctx_nextaddress(fctx)) != NULL)
if (! dns_adbentry_overquota(addrinfo->entry))
break;
if (addrinfo == NULL) {
/*
* We have no more addresses. Start over.
*/
/* We have no more addresses. Start over. */
fctx_cancelqueries(fctx, ISC_TRUE);
fctx_cleanupfinds(fctx);
fctx_cleanupaltfinds(fctx);
@ -3474,7 +3657,11 @@ 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_overquota(addrinfo->entry))
break;
}
/*
* While we may have addresses from the ADB, they
* might be bad ones. In this case, return SERVFAIL.
@ -3501,7 +3688,7 @@ fctx_try(fetchctx_t *fctx, isc_boolean_t retrying, isc_boolean_t badcache) {
if (result != ISC_R_SUCCESS)
fctx_done(fctx, result, __LINE__);
else if (retrying)
inc_stats(fctx->res, dns_resstatscounter_retry);
inc_stats(res, dns_resstatscounter_retry);
}
static isc_boolean_t
@ -3596,6 +3783,7 @@ fctx_destroy(fetchctx_t *fctx) {
}
isc_counter_detach(&fctx->qc);
fcount_decr(fctx);
isc_timer_detach(&fctx->timer);
dns_message_destroy(&fctx->rmessage);
dns_message_destroy(&fctx->qmessage);
@ -3993,6 +4181,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;
@ -4019,6 +4208,7 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
TIME_NOW(&fctx->start);
fctx->timeouts = 0;
fctx->lamecount = 0;
fctx->quotacount = 0;
fctx->adberr = 0;
fctx->neterr = 0;
fctx->badresp = 0;
@ -4111,6 +4301,16 @@ 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 = fctx->res->quotaresp[dns_quotatype_zone];
inc_stats(res, dns_resstatscounter_zonequota);
goto cleanup_domain;
}
log_ns_ttl(fctx, "fctx_create");
INSIST(dns_name_issubdomain(&fctx->name, &fctx->domain));
@ -4120,7 +4320,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,
@ -4197,6 +4397,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);
@ -6496,11 +6699,15 @@ 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);
dns_name_init(&fctx->domain, NULL);
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;
@ -7069,6 +7276,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);
@ -7076,6 +7285,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.
*/
@ -8044,6 +8258,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
fctx->referrals++;
fctx->querysent = 0;
fctx->lamecount = 0;
fctx->quotacount = 0;
fctx->neterr = 0;
fctx->badresp = 0;
fctx->adberr = 0;
@ -8175,6 +8390,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);
@ -8182,6 +8399,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);
@ -8281,6 +8503,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)
@ -8387,7 +8616,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;
@ -8427,10 +8656,13 @@ dns_resolver_create(dns_view_t *view,
res->spillatmin = res->spillat = 10;
res->spillatmax = 100;
res->spillattimer = NULL;
res->zspill = 0;
res->zero_no_soa_ttl = ISC_FALSE;
res->query_timeout = DEFAULT_QUERY_TIMEOUT;
res->maxdepth = DEFAULT_RECURSION_DEPTH;
res->maxqueries = DEFAULT_MAX_QUERIES;
res->quotaresp[dns_quotatype_zone] = DNS_R_DROP;
res->quotaresp[dns_quotatype_server] = DNS_R_SERVFAIL;
res->nbuckets = ntasks;
if (view->resstats != NULL)
isc_stats_set(view->resstats, ntasks,
@ -8476,6 +8708,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,
@ -8572,6 +8822,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);
@ -9164,15 +9422,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,quota:%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->quotacount,
fctx->neterr, fctx->badresp, fctx->adberr,
fctx->findfail, fctx->valfail);
fctx->logged = ISC_TRUE;
@ -9663,6 +9922,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));
@ -9755,3 +10025,47 @@ dns_resolver_getmaxqueries(dns_resolver_t *resolver) {
return (resolver->maxqueries);
}
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 spilled, %d allowed)\n",
fc->count, fc->dropped, fc->allowed);
}
UNLOCK(&resolver->dbuckets[i].lock);
}
}
void
dns_resolver_setquotaresponse(dns_resolver_t *resolver,
dns_quotatype_t which, isc_result_t resp)
{
REQUIRE(VALID_RESOLVER(resolver));
REQUIRE(which == dns_quotatype_zone || which == dns_quotatype_server);
REQUIRE(resp == DNS_R_DROP || resp == DNS_R_SERVFAIL);
resolver->quotaresp[which] = resp;
}
isc_result_t
dns_resolver_getquotaresponse(dns_resolver_t *resolver, dns_quotatype_t which)
{
REQUIRE(VALID_RESOLVER(resolver));
REQUIRE(which == dns_quotatype_zone || which == dns_quotatype_server);
return (resolver->quotaresp[which]);
}

View file

@ -731,7 +731,8 @@ dns_view_createzonetable(dns_view_t *view) {
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,

View file

@ -37,6 +37,7 @@ dns_aclenv_init
dns_adb_agesrtt
dns_adb_adjustsrtt
dns_adb_attach
dns_adb_beginudpfetch
dns_adb_cancelfind
dns_adb_changeflags
dns_adb_create
@ -47,6 +48,7 @@ dns_adb_detach
dns_adb_dump
dns_adb_dumpfind
dns_adb_ednsto
dns_adb_endudpfetch
dns_adb_findaddrinfo
dns_adb_flush
dns_adb_flushname
@ -61,10 +63,12 @@ dns_adb_probesize
dns_adb_probesize2
dns_adb_setadbsize
dns_adb_setcookie
dns_adb_setquota
dns_adb_setudpsize
dns_adb_shutdown
dns_adb_timeout
dns_adb_whenshutdown
dns_adbentry_overquota
dns_badcache_add
dns_badcache_destroy
dns_badcache_find
@ -820,6 +824,7 @@ dns_resolver_dispatchmgr
dns_resolver_dispatchv4
dns_resolver_dispatchv6
dns_resolver_ds_digest_supported
dns_resolver_dumpfetches
dns_resolver_flushbadcache
dns_resolver_flushbadnames
dns_resolver_freeze
@ -832,6 +837,7 @@ dns_resolver_getmustbesecure
dns_resolver_getoptions
dns_resolver_getquerydscp4
dns_resolver_getquerydscp6
dns_resolver_getquotaresponse
dns_resolver_gettimeout
dns_resolver_getudpsize
dns_resolver_getzeronosoattl
@ -843,12 +849,14 @@ dns_resolver_reset_algorithms
dns_resolver_reset_ds_digests
dns_resolver_resetmustbesecure
dns_resolver_setclientsperquery
dns_resolver_setfetchesperzone
dns_resolver_setlamettl
dns_resolver_setmaxdepth
dns_resolver_setmaxqueries
dns_resolver_setmustbesecure
dns_resolver_setquerydscp4
dns_resolver_setquerydscp6
dns_resolver_setquotaresponse
dns_resolver_settimeout
dns_resolver_setudpsize
dns_resolver_setzeronosoattl

View file

@ -56,6 +56,8 @@
#define ISC_MAX(a, b) ((a) > (b) ? (a) : (b))
#define ISC_MIN(a, b) ((a) < (b) ? (a) : (b))
#define ISC_CLAMP(v, x, y) ((v) < (x) ? (x) : ((v) > (y) ? (y) : (v)))
/*%
* Use this to remove the const qualifier of a variable to assign it to
* a non-const variable or pass it as a non-const function argument ...

View file

@ -285,6 +285,18 @@ cfg_obj_asuint64(const cfg_obj_t *obj);
* \li A 64-bit unsigned integer.
*/
isc_uint32_t
cfg_obj_asfixedpoint(const cfg_obj_t *obj);
/*%<
* Returns the value of a configuration object of fixed point number.
*
* Requires:
* \li 'obj' points to a valid configuration object of fixed point type.
*
* Returns:
* \li A 32-bit unsigned integer.
*/
isc_boolean_t
cfg_obj_isstring(const cfg_obj_t *obj);
/*%<

View file

@ -266,6 +266,7 @@ LIBISCCFG_EXTERNAL_DATA extern cfg_rep_t cfg_rep_tuple;
LIBISCCFG_EXTERNAL_DATA extern cfg_rep_t cfg_rep_sockaddr;
LIBISCCFG_EXTERNAL_DATA extern cfg_rep_t cfg_rep_netprefix;
LIBISCCFG_EXTERNAL_DATA extern cfg_rep_t cfg_rep_void;
LIBISCCFG_EXTERNAL_DATA extern cfg_rep_t cfg_rep_fixedpoint;
/*@}*/
/*@{*/
@ -290,6 +291,7 @@ LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_netprefix;
LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_void;
LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_token;
LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_unsupported;
LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_fixedpoint;
/*@}*/
isc_result_t
@ -452,6 +454,12 @@ cfg_print_void(cfg_printer_t *pctx, const cfg_obj_t *obj);
void
cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type);
isc_result_t
cfg_parse_fixedpoint(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
void
cfg_print_fixedpoint(cfg_printer_t *pctx, const cfg_obj_t *obj);
isc_result_t
cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);

View file

@ -122,6 +122,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;
@ -889,6 +890,58 @@ static cfg_type_t cfg_type_cookiealg = {
"cookiealg", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
&cfg_rep_string, &cookiealg_enums };
/*%
* fetch-quota-params
*/
static cfg_tuplefielddef_t fetchquota_fields[] = {
{ "frequency", &cfg_type_uint32, 0 },
{ "low", &cfg_type_fixedpoint, 0 },
{ "high", &cfg_type_fixedpoint, 0 },
{ "discount", &cfg_type_fixedpoint, 0 },
{ NULL, NULL, 0 }
};
static cfg_type_t cfg_type_fetchquota = {
"fetchquota", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
&cfg_rep_tuple, fetchquota_fields
};
/*%
* fetches-per-server or fetches-per-zone
*/
static const char *response_enums[] = { "drop", "fail", NULL };
static isc_result_t
parse_optional_response(cfg_parser_t *pctx, const cfg_type_t *type,
cfg_obj_t **ret)
{
return (parse_enum_or_other(pctx, type, &cfg_type_void, ret));
}
static void
doc_optional_response(cfg_printer_t *pctx, const cfg_type_t *type) {
UNUSED(type);
cfg_print_cstr(pctx, "[ ( drop | fail ) ]");
}
static cfg_type_t cfg_type_responsetype = {
"responsetype", parse_optional_response, cfg_print_ustring,
doc_optional_response, &cfg_rep_string, response_enums
};
static cfg_tuplefielddef_t fetchesper_fields[] = {
{ "fetches", &cfg_type_uint32, 0 },
{ "response", &cfg_type_responsetype, 0 },
{ NULL, NULL, 0 }
};
static cfg_type_t cfg_type_fetchesper = {
"fetchesper", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
&cfg_rep_tuple, fetchesper_fields
};
/*%
* Clauses that can be found within the top level of the named.conf
* file only.
@ -1175,7 +1228,7 @@ cleanup:
}
/*
* Parse a tuple consisting of any kind of required field followed
* Parse a tuple consisting of any kind of required field followed
* by 2 or more optional keyvalues that can be in any order.
*/
static isc_result_t
@ -1366,15 +1419,12 @@ static cfg_type_t cfg_type_rrl = {
&cfg_rep_map, rrl_clausesets
};
/*%
* dnssec-lookaside
*/
static void
print_lookaside(cfg_printer_t *pctx, const cfg_obj_t *obj)
{
print_lookaside(cfg_printer_t *pctx, const cfg_obj_t *obj) {
const cfg_obj_t *domain = obj->value.tuple[0];
if (domain->value.string.length == 4 &&
@ -1516,6 +1566,9 @@ view_clauses[] = {
{ "empty-server", &cfg_type_astring, 0 },
{ "empty-zones-enable", &cfg_type_boolean, 0 },
{ "fetch-glue", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
{ "fetch-quota-params", &cfg_type_fetchquota, 0 },
{ "fetches-per-server", &cfg_type_fetchesper, 0 },
{ "fetches-per-zone", &cfg_type_fetchesper, 0 },
{ "ixfr-from-differences", &cfg_type_ixfrdifftype, 0 },
{ "lame-ttl", &cfg_type_ttlval, 0 },
{ "nocookie-udp-size", &cfg_type_uint32, 0 },

View file

@ -19,6 +19,8 @@
#include <config.h>
#include <stdlib.h>
#include <isc/buffer.h>
#include <isc/dir.h>
#include <isc/formatcheck.h>
@ -120,6 +122,7 @@ cfg_rep_t cfg_rep_tuple = { "tuple", free_tuple };
cfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_noop };
cfg_rep_t cfg_rep_netprefix = { "netprefix", free_noop };
cfg_rep_t cfg_rep_void = { "void", free_noop };
cfg_rep_t cfg_rep_fixedpoint = { "fixedpoint", free_noop };
/*
* Configuration type definitions.
@ -646,6 +649,80 @@ cfg_type_t cfg_type_void = {
"void", cfg_parse_void, cfg_print_void, cfg_doc_void, &cfg_rep_void,
NULL };
/*
* Fixed point
*/
isc_result_t
cfg_parse_fixedpoint(cfg_parser_t *pctx, const cfg_type_t *type,
cfg_obj_t **ret)
{
isc_result_t result;
cfg_obj_t *obj = NULL;
UNUSED(type);
size_t n1, n2, n3, l;
const char *p;
UNUSED(type);
CHECK(cfg_gettoken(pctx, 0));
if (pctx->token.type != isc_tokentype_string) {
cfg_parser_error(pctx, CFG_LOG_NEAR,
"expected fixed point number");
return (ISC_R_UNEXPECTEDTOKEN);
}
p = TOKEN_STRING(pctx);
l = strlen(p);
n1 = strspn(p, "0123456789");
n2 = strspn(p + n1, ".");
n3 = strspn(p + n1 + n2, "0123456789");
if ((n1 + n2 + n3 != l) || (n1 + n3 == 0) ||
n1 > 5 || n2 > 1 || n3 > 2) {
cfg_parser_error(pctx, CFG_LOG_NEAR,
"expected fixed point number");
return (ISC_R_UNEXPECTEDTOKEN);
}
CHECK(cfg_create_obj(pctx, &cfg_type_fixedpoint, &obj));
obj->value.uint32 = strtoul(p, NULL, 10) * 100;
switch (n3) {
case 2:
obj->value.uint32 += strtoul(p + n1 + n2, NULL, 10);
break;
case 1:
obj->value.uint32 += strtoul(p + n1 + n2, NULL, 10) * 10;
break;
}
*ret = obj;
cleanup:
return (result);
}
void
cfg_print_fixedpoint(cfg_printer_t *pctx, const cfg_obj_t *obj) {
char buf[64];
int n;
n = snprintf(buf, sizeof(buf), "%u.%02u",
obj->value.uint32/100, obj->value.uint32%100);
INSIST(n > 0 && (size_t)n < sizeof(buf));
cfg_print_chars(pctx, buf, strlen(buf));
}
isc_uint32_t
cfg_obj_asfixedpoint(const cfg_obj_t *obj) {
REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_fixedpoint);
return (obj->value.uint32);
}
cfg_type_t cfg_type_fixedpoint = {
"fixedpoint", cfg_parse_fixedpoint, cfg_print_fixedpoint,
cfg_doc_terminal, &cfg_rep_fixedpoint, NULL
};
/*
* uint32

View file

@ -32,6 +32,7 @@ cfg_map_count
cfg_map_get
cfg_map_getname
cfg_obj_asboolean
cfg_obj_asfixedpoint
cfg_obj_asnetprefix
cfg_obj_assockaddr
cfg_obj_asstring
@ -63,6 +64,7 @@ cfg_parse_buffer2
cfg_parse_dscp
cfg_parse_enum
cfg_parse_file
cfg_parse_fixedpoint
cfg_parse_listelt
cfg_parse_map
cfg_parse_mapbody
@ -94,6 +96,7 @@ cfg_print_boolean
cfg_print_bracketed_list
cfg_print_chars
cfg_print_cstr
cfg_print_fixedpoint
cfg_print_grammar
cfg_print_map
cfg_print_mapbody