result = dns_resolver_createfetch3(client->view->resolver,
qname, qtype, qdomain, nameservers,
NULL, peeraddr, client->message->id,
- client->query.fetchoptions, 0,
+ client->query.fetchoptions, 0, NULL,
client->task, query_resume, client,
rdataset, sigrdataset,
&client->query.fetch);
ns1 -- root server
ans2 -- delegate to ns1.(n+1).example.com for all n, up to
- the value specified in ans.limit
+ the value specified in ans.limit (or forever if limit is 0)
ns3 -- resolver under test
+ans4 -- delegates every query to 16 more name servers, with "victim" address
+ans7 -- "victim" server
sub getlimit {
if ( -e "ans.limit") {
- open(FH, "<", "ans.limit");
- my $line = <FH>;
- chomp $line;
- close FH;
- if ($line =~ /^\d+$/) {
- return $line;
- }
+ open(FH, "<", "ans.limit");
+ my $line = <FH>;
+ chomp $line;
+ close FH;
+ if ($line =~ /^\d+$/) {
+ return $line;
+ }
}
return 0;
$count += 1;
if ($qname eq "count" ) {
- if ($qtype eq "TXT") {
- my ($ttl, $rdata) = (0, "$count");
- my $rr = new Net::DNS::RR("$qname $ttl $qclass $qtype $rdata");
- push @ans, $rr;
- print ("\tcount: $count\n");
- }
- $rcode = "NOERROR";
+ if ($qtype eq "TXT") {
+ my ($ttl, $rdata) = (0, "$count");
+ my $rr = new Net::DNS::RR("$qname $ttl $qclass $qtype $rdata");
+ push @ans, $rr;
+ print ("\tcount: $count\n");
+ }
+ $rcode = "NOERROR";
} elsif ($qname eq "reset" ) {
- $count = 0;
- $send_response = 0;
- $limit = getlimit();
- $rcode = "NOERROR";
- print ("\tlimit: $limit\n");
+ $count = 0;
+ $send_response = 0;
+ $limit = getlimit();
+ $rcode = "NOERROR";
+ print ("\tlimit: $limit\n");
} elsif ($qname eq "direct.example.org" ) {
- if ($qtype eq "A") {
- my ($ttl, $rdata) = (3600, $localaddr);
- my $rr = new Net::DNS::RR("$qname $ttl $qclass $qtype $rdata");
- push @ans, $rr;
- }
- $rcode = "NOERROR";
+ if ($qtype eq "A") {
+ my ($ttl, $rdata) = (3600, $localaddr);
+ my $rr = new Net::DNS::RR("$qname $ttl $qclass $qtype $rdata");
+ push @ans, $rr;
+ }
+ $rcode = "NOERROR";
} elsif ($qname eq "indirect.example.org") {
- if (! $send_response) {
- my $rr = new Net::DNS::RR("indirect.example.org 86400 $qclass NS ns1.1.example.org");
- push @auth, $rr;
- } elsif ($qtype eq "A") {
- my ($ttl, $rdata) = (3600, $localaddr);
- my $rr = new Net::DNS::RR("$qname $ttl $qclass $qtype $rdata");
- push @ans, $rr;
- }
- $rcode = "NOERROR";
+ if (! $send_response) {
+ my $rr = new Net::DNS::RR("indirect.example.org 86400 $qclass NS ns1.1.example.org");
+ push @auth, $rr;
+ } elsif ($qtype eq "A") {
+ my ($ttl, $rdata) = (3600, $localaddr);
+ my $rr = new Net::DNS::RR("$qname $ttl $qclass $qtype $rdata");
+ push @ans, $rr;
+ }
+ $rcode = "NOERROR";
} elsif ($qname =~ /^ns1\.(\d+)\.example\.org$/) {
- my $next = $1 + 1;
- if ($limit == 0 || (! $send_response && $next <= $limit)) {
- my $rr = new Net::DNS::RR("$1.example.org 86400 $qclass NS ns1.$next.example.org");
- push @auth, $rr;
- } else {
- $send_response = 1;
- if ($qtype eq "A") {
- my ($ttl, $rdata) = (3600, $localaddr);
- my $rr = new Net::DNS::RR("$qname $ttl $qclass $qtype $rdata");
-print("\tresponse: $qname $ttl $qclass $qtype $rdata\n");
- push @ans, $rr;
- }
- }
- $rcode = "NOERROR";
+ my $next = $1 + 1;
+ if ($limit == 0 || (! $send_response && $next <= $limit)) {
+ my $rr = new Net::DNS::RR("$1.example.org 86400 $qclass NS ns1.$next.example.org");
+ push @auth, $rr;
+ } else {
+ $send_response = 1;
+ if ($qtype eq "A") {
+ my ($ttl, $rdata) = (3600, $localaddr);
+ my $rr = new Net::DNS::RR("$qname $ttl $qclass $qtype $rdata");
+ print("\tresponse: $qname $ttl $qclass $qtype $rdata\n");
+ push @ans, $rr;
+ }
+ }
+ $rcode = "NOERROR";
} else {
- $rcode = "NXDOMAIN";
+ $rcode = "NXDOMAIN";
}
# mark the answer as authoritive (by setting the 'aa' flag
--- /dev/null
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use IO::File;
+use Getopt::Long;
+use Net::DNS::Nameserver;
+
+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;
+
+my $count = 0;
+my $send_response = 0;
+
+my $localaddr = "10.53.0.4";
+my $localport = 5300;
+my $verbose = 0;
+
+sub reply_handler {
+ my ($qname, $qclass, $qtype, $peerhost, $query, $conn) = @_;
+ my ($rcode, @ans, @auth, @add);
+
+ print ("request: $qname/$qtype\n");
+ STDOUT->flush();
+
+ $count += 1;
+
+ if ($qname eq "count" ) {
+ if ($qtype eq "TXT") {
+ my ($ttl, $rdata) = (0, "$count");
+ my $rr = new Net::DNS::RR("$qname $ttl $qclass $qtype $rdata");
+ push @ans, $rr;
+ print ("\tcount: $count\n");
+ }
+ $rcode = "NOERROR";
+ } elsif ($qname eq "reset" ) {
+ $count = 0;
+ $send_response = 0;
+ $rcode = "NOERROR";
+ } elsif ($qname eq "direct.example.net" ) {
+ if ($qtype eq "A") {
+ my ($ttl, $rdata) = (3600, $localaddr);
+ my $rr = new Net::DNS::RR("$qname $ttl $qclass $qtype $rdata");
+ push @ans, $rr;
+ }
+ $rcode = "NOERROR";
+ } elsif( $qname =~ /^ns1\.(\d+)\.example\.net$/ ) {
+ my $next = ($1 + 1) * 16;
+ for (my $i = 1; $i < 16; $i++) {
+ my $s = $next + $i;
+ my $rr = new Net::DNS::RR("$1.example.net 86400 $qclass NS ns1.$s.example.net");
+ push @auth, $rr;
+ $rr = new Net::DNS::RR("ns1.$s.example.net 86400 $qclass A 10.53.0.7");
+ push @add, $rr;
+ }
+ $rcode = "NOERROR";
+ } else {
+ $rcode = "NXDOMAIN";
+ }
+
+ # mark the answer as authoritive (by setting the 'aa' flag
+ return ($rcode, \@ans, \@auth, \@add, { aa => 1 });
+}
+
+GetOptions(
+ 'port=i' => \$localport,
+ 'verbose!' => \$verbose,
+);
+
+my $ns = Net::DNS::Nameserver->new(
+ LocalAddr => $localaddr,
+ LocalPort => $localport,
+ ReplyHandler => \&reply_handler,
+ Verbose => $verbose,
+);
+
+$ns->main_loop;
--- /dev/null
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use IO::File;
+use Getopt::Long;
+use Net::DNS::Nameserver;
+
+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;
+
+my $count = 0;
+
+my $localaddr = "10.53.0.7";
+my $localport = 5300;
+my $verbose = 0;
+
+sub reply_handler {
+ my ($qname, $qclass, $qtype, $peerhost, $query, $conn) = @_;
+ my ($rcode, @ans, @auth, @add);
+
+ print ("request: $qname/$qtype\n");
+ STDOUT->flush();
+
+ $count += 1;
+
+ if ($qname eq "count" ) {
+ if ($qtype eq "TXT") {
+ my ($ttl, $rdata) = (0, "$count");
+ my $rr = new Net::DNS::RR("$qname $ttl $qclass $qtype $rdata");
+ push @ans, $rr;
+ print ("\tcount: $count\n");
+ }
+ $rcode = "NOERROR";
+ } elsif ($qname eq "reset") {
+ $count = 0;
+ $rcode = "NOERROR";
+ } else {
+ $rcode = "REFUSED";
+ }
+
+ # mark the answer as authoritive (by setting the 'aa' flag
+ return ($rcode, \@ans, \@auth, \@add, { aa => 1 });
+}
+
+GetOptions(
+ 'port=i' => \$localport,
+ 'verbose!' => \$verbose,
+);
+
+my $ns = Net::DNS::Nameserver->new(
+ LocalAddr => $localaddr,
+ LocalPort => $localport,
+ ReplyHandler => \&reply_handler,
+ Verbose => $verbose,
+);
+
+$ns->main_loop;
ns.tld1. 60 IN A 10.53.0.1
example.org. 60 IN NS direct.example.org.
direct.example.org. 60 IN A 10.53.0.2
+example.net. 60 IN NS direct.example.net.
+direct.example.net. 60 IN A 10.53.0.4
n=`expr $n + 1`
echo "I: attempt excessive-queries lookup ($n)"
ret=0
-echo "25" > ans2/ans.limit
+echo "13" > ans2/ans.limit
$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 flush 2>&1 | sed 's/^/I:ns1 /'
$DIG $DIGOPTS @10.53.0.2 reset > /dev/null || ret=1
$DIG $DIGOPTS @10.53.0.3 indirect.example.org > dig.out.1.test$n || ret=1
grep "status: SERVFAIL" dig.out.1.test$n > /dev/null || ret=1
-grep "exceeded max queries resolving 'indirect.example.org/A'" ns3/named.run > /dev/null || ret=1
$DIG $DIGOPTS +short @10.53.0.2 count txt > dig.out.2.test$n || ret=1
eval count=`cat dig.out.2.test$n`
-[ $count -eq 100 ] || ret=1
+[ $count -le 50 ] || ret=1
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
n=`expr $n + 1`
echo "I: attempt permissible lookup ($n)"
ret=0
-echo "24" > ans2/ans.limit
+echo "12" > ans2/ans.limit
$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 flush 2>&1 | sed 's/^/I:ns1 /'
$DIG $DIGOPTS @10.53.0.2 reset > /dev/null || ret=1
$DIG $DIGOPTS @10.53.0.3 indirect.example.org > dig.out.1.test$n || ret=1
grep "status: NOERROR" dig.out.1.test$n > /dev/null || ret=1
$DIG $DIGOPTS +short @10.53.0.2 count txt > dig.out.2.test$n || ret=1
eval count=`cat dig.out.2.test$n`
-[ $count -eq 97 ] || ret=1
+[ $count -le 50 ] || ret=1
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
n=`expr $n + 1`
echo "I: attempt excessive-queries lookup ($n)"
ret=0
-echo "21" > ans2/ans.limit
+echo "10" > ans2/ans.limit
$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 flush 2>&1 | sed 's/^/I:ns1 /'
$DIG $DIGOPTS @10.53.0.2 reset > /dev/null || ret=1
$DIG $DIGOPTS @10.53.0.3 indirect.example.org > dig.out.1.test$n || ret=1
grep "status: SERVFAIL" dig.out.1.test$n > /dev/null || ret=1
-grep "exceeded max queries resolving 'indirect.example.org/A'" ns3/named.run > /dev/null || ret=1
$DIG $DIGOPTS +short @10.53.0.2 count txt > dig.out.2.test$n || ret=1
eval count=`cat dig.out.2.test$n`
-[ $count -eq 84 ] || ret=1
+[ $count -le 40 ] || ret=1
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
n=`expr $n + 1`
echo "I: attempt permissible lookup ($n)"
ret=0
-echo "19" > ans2/ans.limit
+echo "9" > ans2/ans.limit
$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 flush 2>&1 | sed 's/^/I:ns1 /'
$DIG $DIGOPTS @10.53.0.2 reset > /dev/null || ret=1
$DIG $DIGOPTS @10.53.0.3 indirect.example.org > dig.out.1.test$n || ret=1
grep "status: NOERROR" dig.out.1.test$n > /dev/null || ret=1
$DIG $DIGOPTS +short @10.53.0.2 count txt > dig.out.2.test$n || ret=1
eval count=`cat dig.out.2.test$n`
-[ $count -eq 77 ] || ret=1
+[ $count -le 40 ] || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I: attempting NS explosion ($n)"
+ret=0
+$RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 flush 2>&1 | sed 's/^/I:ns1 /'
+$DIG $DIGOPTS +short @10.53.0.3 ns1.1.example.net > dig.out.1.test$n || ret=1
+sleep 2
+$DIG $DIGOPTS +short @10.53.0.4 count txt > dig.out.2.test$n || ret=1
+eval count=`cat dig.out.2.test$n`
+[ $count -lt 50 ] || ret=1
+$DIG $DIGOPTS +short @10.53.0.7 count txt > dig.out.3.test$n || ret=1
+eval count=`cat dig.out.3.test$n`
+[ $count -lt 50 ] || ret=1
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
static inline void violate_locking_hierarchy(isc_mutex_t *, isc_mutex_t *);
static isc_boolean_t clean_namehooks(dns_adb_t *, dns_adbnamehooklist_t *);
static void clean_target(dns_adb_t *, dns_name_t *);
-static void clean_finds_at_name(dns_adbname_t *, isc_eventtype_t,
- isc_uint32_t, unsigned int);
+static void clean_finds_at_name(dns_adbname_t *, isc_eventtype_t, unsigned int);
static isc_boolean_t check_expire_namehooks(dns_adbname_t *, isc_stdtime_t);
static isc_boolean_t check_expire_entry(dns_adb_t *, dns_adbentry_t **,
isc_stdtime_t);
static isc_result_t dbfind_name(dns_adbname_t *, isc_stdtime_t,
dns_rdatatype_t);
static isc_result_t fetch_name(dns_adbname_t *, isc_boolean_t,
- unsigned int, dns_rdatatype_t);
+ unsigned int, isc_counter_t *qc,
+ dns_rdatatype_t);
static inline void check_exit(dns_adb_t *);
static void destroy(dns_adb_t *);
static isc_boolean_t shutdown_names(dns_adb_t *);
* Clean up the name's various lists. These two are destructive
* in that they will always empty the list.
*/
- clean_finds_at_name(name, ev, 0, DNS_ADBFIND_ADDRESSMASK);
+ clean_finds_at_name(name, ev, DNS_ADBFIND_ADDRESSMASK);
result4 = clean_namehooks(adb, &name->v4);
result6 = clean_namehooks(adb, &name->v6);
clean_target(adb, &name->target);
*/
static void
clean_finds_at_name(dns_adbname_t *name, isc_eventtype_t evtype,
- isc_uint32_t qtotal, unsigned int addrs)
+ unsigned int addrs)
{
isc_event_t *ev;
isc_task_t *task;
ev->ev_sender = find;
find->result_v4 = find_err_map[name->fetch_err];
find->result_v6 = find_err_map[name->fetch6_err];
- find->qtotal += qtotal;
ev->ev_type = evtype;
ev->ev_destroy = event_free;
ev->ev_destroy_arg = find;
h->flags = 0;
h->result_v4 = ISC_R_UNEXPECTED;
h->result_v6 = ISC_R_UNEXPECTED;
- h->qtotal = 0;
ISC_LINK_INIT(h, publink);
ISC_LINK_INIT(h, plink);
ISC_LIST_INIT(h->list);
{
return (dns_adb_createfind2(adb, task, action, arg, name,
qname, qtype, options, now,
- target, port, 0, findp));
+ target, port, 0, NULL, findp));
}
isc_result_t
void *arg, dns_name_t *name, dns_name_t *qname,
dns_rdatatype_t qtype, unsigned int options,
isc_stdtime_t now, dns_name_t *target,
- in_port_t port, unsigned int depth,
+ in_port_t port, unsigned int depth, isc_counter_t *qc,
dns_adbfind_t **findp)
{
dns_adbfind_t *find;
* Start V4.
*/
if (WANT_INET(wanted_fetches) &&
- fetch_name(adbname, start_at_zone, depth,
+ fetch_name(adbname, start_at_zone, depth, qc,
dns_rdatatype_a) == ISC_R_SUCCESS) {
DP(DEF_LEVEL,
"dns_adb_createfind: started A fetch for name %p",
* Start V6.
*/
if (WANT_INET6(wanted_fetches) &&
- fetch_name(adbname, start_at_zone, depth,
+ fetch_name(adbname, start_at_zone, depth, qc,
dns_rdatatype_aaaa) == ISC_R_SUCCESS) {
DP(DEF_LEVEL,
"dns_adb_createfind: "
isc_result_t result;
unsigned int address_type;
isc_boolean_t want_check_exit = ISC_FALSE;
- isc_uint32_t qtotal = 0;
UNUSED(task);
adb = name->adb;
INSIST(DNS_ADB_VALID(adb));
- qtotal = dev->qtotal;
-
bucket = name->lock_bucket;
LOCK(&adb->namelocks[bucket]);
free_adbfetch(adb, &fetch);
isc_event_free(&ev);
- clean_finds_at_name(name, ev_status, qtotal, address_type);
+ clean_finds_at_name(name, ev_status, address_type);
UNLOCK(&adb->namelocks[bucket]);
}
static isc_result_t
fetch_name(dns_adbname_t *adbname, isc_boolean_t start_at_zone,
- unsigned int depth, dns_rdatatype_t type)
+ unsigned int depth, isc_counter_t *qc, dns_rdatatype_t type)
{
isc_result_t result;
dns_adbfetch_t *fetch = NULL;
result = dns_resolver_createfetch3(adb->view->resolver, &adbname->name,
type, name, nameservers, NULL,
- NULL, 0, options, depth, adb->task,
- fetch_callback, adbname,
+ NULL, 0, options, depth, qc,
+ adb->task, fetch_callback, adbname,
&fetch->rdataset, NULL,
&fetch->fetch);
if (result != ISC_R_SUCCESS)
isc_result_t result_v6; /*%< RO: v6 result */
ISC_LINK(dns_adbfind_t) publink; /*%< RW: client use */
- isc_uint32_t qtotal;
-
/* Private */
isc_mutex_t lock; /* locks all below */
in_port_t port;
void *arg, dns_name_t *name, dns_name_t *qname,
dns_rdatatype_t qtype, unsigned int options,
isc_stdtime_t now, dns_name_t *target, in_port_t port,
- unsigned int depth, dns_adbfind_t **find);
+ unsigned int depth, isc_counter_t *qc,
+ dns_adbfind_t **find);
/*%<
* Main interface for clients. The adb will look up the name given in
* "name" and will build up a list of found addresses, and perhaps start
isc_sockaddr_t * client;
dns_messageid_t id;
isc_result_t vresult;
- isc_uint32_t qtotal;
} dns_fetchevent_t;
/*
dns_forwarders_t *forwarders,
isc_sockaddr_t *client, isc_uint16_t id,
unsigned int options, unsigned int depth,
- isc_task_t *task,
+ isc_counter_t *qc, isc_task_t *task,
isc_taskaction_t action, void *arg,
dns_rdataset_t *rdataset,
dns_rdataset_t *sigrdataset,
#include <config.h>
+#include <isc/counter.h>
#include <isc/log.h>
#include <isc/platform.h>
#include <isc/print.h>
isc_sockaddrlist_t edns;
isc_sockaddrlist_t edns512;
isc_sockaddrlist_t bad_edns;
- dns_validator_t *validator;
+ dns_validator_t * validator;
ISC_LIST(dns_validator_t) validators;
dns_db_t * cache;
dns_adb_t * adb;
isc_boolean_t ns_ttl_ok;
isc_uint32_t ns_ttl;
+ isc_counter_t * qc;
/*%
* The number of events we're waiting for.
isc_uint64_t duration;
isc_boolean_t logged;
unsigned int querysent;
- unsigned int totalqueries;
unsigned int referrals;
unsigned int lamecount;
unsigned int neterr;
event->result == DNS_R_NCACHENXRRSET);
}
- event->qtotal = fctx->totalqueries;
isc_task_sendanddetach(&task, ISC_EVENT_PTR(&event));
count++;
}
}
fctx->querysent++;
- fctx->totalqueries++;
ISC_LIST_APPEND(fctx->queries, query, link);
query->fctx->nqueries++;
*/
INSIST(!SHUTTINGDOWN(fctx));
fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
- fctx->totalqueries += find->qtotal;
if (event->ev_type == DNS_EVENT_ADBMOREADDRESSES) {
want_try = ISC_TRUE;
} else {
&fctx->name, fctx->type,
options, now, NULL,
res->view->dstport,
- fctx->depth + 1, &find);
+ fctx->depth + 1, fctx->qc, &find);
if (result != ISC_R_SUCCESS) {
if (result == DNS_R_ALIAS) {
/*
REQUIRE(!ADDRWAIT(fctx));
- if (fctx->totalqueries > fctx->res->maxqueries) {
- isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
- DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(3),
- "exceeded max queries resolving '%s'",
- fctx->info);
- fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
- return;
- }
-
addrinfo = fctx_nextaddress(fctx);
if (addrinfo == NULL) {
/*
}
}
+ result = isc_counter_increment(fctx->qc);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+ DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(3),
+ "exceeded max queries resolving '%s'",
+ fctx->info);
+ fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
+ return;
+ }
+
result = fctx_query(fctx, addrinfo, fctx->options);
if (result != ISC_R_SUCCESS)
fctx_done(fctx, result, __LINE__);
isc_mem_put(fctx->mctx, sa, sizeof(*sa));
}
+ isc_counter_detach(&fctx->qc);
isc_timer_detach(&fctx->timer);
dns_message_destroy(&fctx->rmessage);
dns_message_destroy(&fctx->qmessage);
* Normal fctx startup.
*/
fctx->state = fetchstate_active;
- fctx->totalqueries = 0;
/*
* Reset the control event for later use in shutting down
* the fctx.
event->fetch = fetch;
event->client = client;
event->id = id;
- event->qtotal = 0;
dns_fixedname_init(&event->foundname);
/*
fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
dns_name_t *domain, dns_rdataset_t *nameservers,
unsigned int options, unsigned int bucketnum, unsigned int depth,
- fetchctx_t **fctxp)
+ isc_counter_t *qc, fetchctx_t **fctxp)
{
fetchctx_t *fctx;
isc_result_t result;
fctx = isc_mem_get(mctx, sizeof(*fctx));
if (fctx == NULL)
return (ISC_R_NOMEMORY);
+
+ fctx->qc = NULL;
+ if (qc != NULL) {
+ isc_counter_attach(qc, &fctx->qc);
+ } else {
+ result = isc_counter_create(res->mctx,
+ res->maxqueries, &fctx->qc);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_fetch;
+ }
+
+ /*
+ * Make fctx->info point to a copy of a formatted string
+ * "name/type".
+ */
dns_name_format(name, buf, sizeof(buf));
dns_rdatatype_format(type, typebuf, sizeof(typebuf));
strcat(buf, "/"); /* checked */
fctx->info = isc_mem_strdup(mctx, buf);
if (fctx->info == NULL) {
result = ISC_R_NOMEMORY;
- goto cleanup_fetch;
+ goto cleanup_counter;
}
FCTXTRACE("create");
dns_name_init(&fctx->name, NULL);
fctx->pending = 0;
fctx->restarts = 0;
fctx->querysent = 0;
- fctx->totalqueries = 0;
fctx->referrals = 0;
TIME_NOW(&fctx->start);
fctx->timeouts = 0;
cleanup_info:
isc_mem_free(mctx, fctx->info);
+ cleanup_counter:
+ isc_counter_detach(&fctx->qc);
+
cleanup_fetch:
isc_mem_put(mctx, fctx, sizeof(*fctx));
{
return (dns_resolver_createfetch3(res, name, type, domain,
nameservers, forwarders, NULL, 0,
- options, 0, task, action, arg,
+ options, 0, NULL, task, action, arg,
rdataset, sigrdataset, fetchp));
}
{
return (dns_resolver_createfetch3(res, name, type, domain,
nameservers, forwarders, client, id,
- options, 0, task, action, arg,
+ options, 0, NULL, task, action, arg,
rdataset, sigrdataset, fetchp));
}
dns_forwarders_t *forwarders,
isc_sockaddr_t *client, dns_messageid_t id,
unsigned int options, unsigned int depth,
- isc_task_t *task,
+ isc_counter_t *qc, isc_task_t *task,
isc_taskaction_t action, void *arg,
dns_rdataset_t *rdataset,
dns_rdataset_t *sigrdataset,
if (fctx == NULL) {
result = fctx_create(res, name, type, domain, nameservers,
- options, bucketnum, depth, &fctx);
+ options, bucketnum, depth, qc, &fctx);
if (result != ISC_R_SUCCESS)
goto unlock;
new_fctx = ISC_TRUE;
# Alphabetically
OBJS = @ISC_EXTRA_OBJS@ \
assertions.@O@ backtrace.@O@ backtrace-emptytbl.@O@ base32.@O@ \
- base64.@O@ buffer.@O@ bufferlist.@O@ \
+ base64.@O@ buffer.@O@ bufferlist.@O@ counter.@O@ \
error.@O@ event.@O@ \
hash.@O@ hex.@O@ hmacmd5.@O@ hmacsha.@O@ \
inet_aton.@O@ iterated_hash.@O@ lex.@O@ lfsr.@O@ log.@O@ \
SRCS = @ISC_EXTRA_SRCS@ \
assertions.c backtrace.c backtrace-emptytbl.c base32.c \
- base64.c buffer.c bufferlist.c \
+ base64.c buffer.c bufferlist.c counter.c \
error.c event.c \
hash.c hex.c hmacmd5.c hmacsha.c \
inet_aton.c iterated_hash.c lex.c log.c lfsr.c \
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
-# $Id$
-
srcdir = @srcdir@
VPATH = @srcdir@
top_srcdir = @top_srcdir@
OBJS = @ISC_EXTRA_OBJS@ \
assertions.@O@ backtrace.@O@ base32.@O@ base64.@O@ \
bitstring.@O@ buffer.@O@ bufferlist.@O@ commandline.@O@ \
- error.@O@ event.@O@ \
+ counter.@O@ error.@O@ event.@O@ \
hash.@O@ heap.@O@ hex.@O@ hmacmd5.@O@ hmacsha.@O@ \
httpd.@O@ inet_aton.@O@ iterated_hash.@O@ \
lex.@O@ lfsr.@O@ lib.@O@ log.@O@ \
# Alphabetically
SRCS = @ISC_EXTRA_SRCS@ \
assertions.c backtrace.c base32.c base64.c bitstring.c \
- buffer.c bufferlist.c commandline.c error.c event.c \
- heap.c hex.c hmacmd5.c hmacsha.c \
+ buffer.c bufferlist.c commandline.c counter.c \
+ error.c event.c heap.c hex.c hmacmd5.c hmacsha.c \
httpd.c inet_aton.c iterated_hash.c \
lex.c lfsr.c lib.c log.c \
md5.c mem.c mutexblock.c \
--- /dev/null
+/*
+ * 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.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stddef.h>
+
+#include <isc/counter.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/util.h>
+
+#define COUNTER_MAGIC ISC_MAGIC('C', 'n', 't', 'r')
+#define VALID_COUNTER(r) ISC_MAGIC_VALID(r, COUNTER_MAGIC)
+
+struct isc_counter {
+ unsigned int magic;
+ isc_mem_t *mctx;
+ isc_mutex_t lock;
+ unsigned int references;
+ unsigned int limit;
+ unsigned int used;
+};
+
+isc_result_t
+isc_counter_create(isc_mem_t *mctx, int limit, isc_counter_t **counterp) {
+ isc_result_t result;
+ isc_counter_t *counter;
+
+ REQUIRE(counterp != NULL && *counterp == NULL);
+
+ counter = isc_mem_get(mctx, sizeof(*counter));
+ if (counter == NULL)
+ return (ISC_R_NOMEMORY);
+
+ result = isc_mutex_init(&counter->lock);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, counter, sizeof(*counter));
+ return (result);
+ }
+
+ counter->mctx = NULL;
+ isc_mem_attach(mctx, &counter->mctx);
+
+ counter->references = 1;
+ counter->limit = limit;
+ counter->used = 0;
+
+ counter->magic = COUNTER_MAGIC;
+ *counterp = counter;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_counter_increment(isc_counter_t *counter) {
+ isc_result_t result = ISC_R_SUCCESS;
+
+ LOCK(&counter->lock);
+ counter->used++;
+ if (counter->limit != 0 && counter->used >= counter->limit)
+ result = ISC_R_QUOTA;
+ UNLOCK(&counter->lock);
+
+ return (result);
+}
+
+unsigned int
+isc_counter_used(isc_counter_t *counter) {
+ REQUIRE(VALID_COUNTER(counter));
+
+ return (counter->used);
+}
+
+void
+isc_counter_setlimit(isc_counter_t *counter, int limit) {
+ REQUIRE(VALID_COUNTER(counter));
+
+ LOCK(&counter->lock);
+ counter->limit = limit;
+ UNLOCK(&counter->lock);
+}
+
+void
+isc_counter_attach(isc_counter_t *source, isc_counter_t **targetp) {
+ REQUIRE(VALID_COUNTER(source));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ LOCK(&source->lock);
+ source->references++;
+ INSIST(source->references > 0);
+ UNLOCK(&source->lock);
+
+ *targetp = source;
+}
+
+static void
+destroy(isc_counter_t *counter) {
+ counter->magic = 0;
+ isc_mutex_destroy(&counter->lock);
+ isc_mem_putanddetach(&counter->mctx, counter, sizeof(*counter));
+}
+
+void
+isc_counter_detach(isc_counter_t **counterp) {
+ isc_counter_t *counter;
+ isc_boolean_t want_destroy = ISC_FALSE;
+
+ REQUIRE(counterp != NULL && *counterp != NULL);
+ counter = *counterp;
+ REQUIRE(VALID_COUNTER(counter));
+
+ *counterp = NULL;
+
+ LOCK(&counter->lock);
+ INSIST(counter->references > 0);
+ counter->references--;
+ if (counter->references == 0)
+ want_destroy = ISC_TRUE;
+ UNLOCK(&counter->lock);
+
+ if (want_destroy)
+ destroy(counter);
+}
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
-# $Id$
-
srcdir = @srcdir@
VPATH = @srcdir@
top_srcdir = @top_srcdir@
#
HEADERS = app.h assertions.h backtrace.h base32.h base64.h \
bind9.h bitstring.h boolean.h buffer.h bufferlist.h \
- commandline.h entropy.h error.h event.h \
+ commandline.h counter.h entropy.h error.h event.h \
eventclass.h file.h formatcheck.h fsaccess.h \
hash.h heap.h hex.h hmacmd5.h hmacsha.h httpd.h \
interfaceiter.h @ISC_IPV6_H@ iterated_hash.h \
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef ISC_COUNTER_H
+#define ISC_COUNTER_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file isc/quota.h
+ *
+ * \brief The isc_quota_t object is a simple helper object for implementing
+ * quotas on things like the number of simultaneous connections to
+ * a server. It keeps track of the amount of quota in use, and
+ * encapsulates the locking necessary to allow multiple tasks to
+ * share a quota.
+ */
+
+/***
+ *** Imports.
+ ***/
+
+#include <isc/lang.h>
+#include <isc/mutex.h>
+#include <isc/types.h>
+
+/*****
+ ***** Types.
+ *****/
+
+ISC_LANG_BEGINDECLS
+
+void
+isc_quota_soft(isc_quota_t *quota, int soft);
+/*%<
+ * Set a soft quota.
+ */
+
+void
+isc_quota_max(isc_quota_t *quota, int max);
+/*%<
+ * Re-set a maximum quota.
+ */
+
+isc_result_t
+isc_quota_reserve(isc_quota_t *quota);
+/*%<
+ * Attempt to reserve one unit of 'quota'.
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS Success
+ * \li #ISC_R_SOFTQUOTA Success soft quota reached
+ * \li #ISC_R_QUOTA Quota is full
+ */
+
+void
+isc_quota_release(isc_quota_t *quota);
+
+isc_result_t
+isc_quota_attach(isc_quota_t *quota, isc_quota_t **p);
+/*%<
+ * Like isc_quota_reserve, and also attaches '*p' to the
+ * quota if successful (ISC_R_SUCCESS or ISC_R_SOFTQUOTA).
+ */
+
+void
+isc_quota_detach(isc_quota_t **p);
+/*%<
+ * Like isc_quota_release, and also detaches '*p' from the
+ * quota.
+ */
+
+isc_result_t
+isc_counter_create(isc_mem_t *mctx, int limit, isc_counter_t **counterp);
+/*%<
+ * Allocate and initialize a counter object.
+ */
+
+isc_result_t
+isc_counter_increment(isc_counter_t *counter);
+/*%<
+ * Increment the counter.
+ *
+ * If the counter limit is nonzero and has been reached, then
+ * return ISC_R_QUOTA, otherwise ISC_R_SUCCESS. (The counter is
+ * incremented regardless of return value.)
+ */
+
+unsigned int
+isc_counter_used(isc_counter_t *counter);
+/*%<
+ * Return the current counter value.
+ */
+
+void
+isc_counter_setlimit(isc_counter_t *counter, int limit);
+/*%<
+ * Set the counter limit.
+ */
+
+void
+isc_counter_attach(isc_counter_t *source, isc_counter_t **targetp);
+/*%<
+ * Attach to a counter object, increasing its reference counter.
+ */
+
+void
+isc_counter_detach(isc_counter_t **counterp);
+/*%<
+ * Detach (and destroy if reference counter has dropped to zero)
+ * a counter object.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* ISC_COUNTER_H */
typedef ISC_LIST(isc_buffer_t) isc_bufferlist_t; /*%< Buffer List */
typedef struct isc_constregion isc_constregion_t; /*%< Const region */
typedef struct isc_consttextregion isc_consttextregion_t; /*%< Const Text Region */
+typedef struct isc_counter isc_counter_t; /*%< Counter */
typedef struct isc_entropy isc_entropy_t; /*%< Entropy */
typedef struct isc_entropysource isc_entropysource_t; /*%< Entropy Source */
typedef struct isc_event isc_event_t; /*%< Event */
lex_test.c \
sockaddr_test.c symtab_test.c task_test.c queue_test.c \
parse_test.c pool_test.c print_test.c regex_test.c \
- safe_test.c time_test.c
+ safe_test.c time_test.c counter_test.c
SUBDIRS =
TARGETS = taskpool_test@EXEEXT@ socket_test@EXEEXT@ hash_test@EXEEXT@ \
sockaddr_test@EXEEXT@ symtab_test@EXEEXT@ task_test@EXEEXT@ \
queue_test@EXEEXT@ parse_test@EXEEXT@ pool_test@EXEEXT@ \
print_test@EXEEXT@ regex_test@EXEEXT@ socket_test@EXEEXT@ \
- safe_test@EXEEXT@ time_test@EXEEXT@
+ safe_test@EXEEXT@ time_test@EXEEXT@ counter_test@EXEEXT@
@BIND9_MAKE_RULES@
${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
time_test.@O@ ${ISCLIBS} ${LIBS}
+counter_test@EXEEXT@: counter_test.@O@ ${ISCDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ counter_test.@O@ isctest.@O@ ${ISCLIBS} ${LIBS}
+
unit::
sh ${top_srcdir}/unit/unittest.sh
--- /dev/null
+/*
+ * 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.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+
+#include <atf-c.h>
+
+#include <isc/counter.h>
+#include <isc/result.h>
+
+#include "isctest.h"
+
+ATF_TC(isc_counter);
+ATF_TC_HEAD(isc_counter, tc) {
+ atf_tc_set_md_var(tc, "descr", "isc counter object");
+}
+ATF_TC_BODY(isc_counter, tc) {
+ isc_result_t result;
+ isc_counter_t *counter = NULL;
+ int i;
+
+ result = isc_test_begin(NULL, ISC_TRUE);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = isc_counter_create(mctx, 0, &counter);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ for (i = 0; i < 10; i++) {
+ result = isc_counter_increment(counter);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ }
+
+ ATF_CHECK_EQ(isc_counter_used(counter), 10);
+
+ isc_counter_setlimit(counter, 15);
+ for (i = 0; i < 10; i++) {
+ result = isc_counter_increment(counter);
+ if (result != ISC_R_SUCCESS)
+ break;
+ }
+
+ ATF_CHECK_EQ(isc_counter_used(counter), 15);
+
+ isc_counter_detach(&counter);
+ isc_test_end();
+}
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+ ATF_TP_ADD_TC(tp, isc_counter);
+ return (atf_no_error());
+}
+
isc_condition_signal
isc_condition_wait
isc_condition_waituntil
+isc_counter_attach
+isc_counter_create
+isc_counter_detach
+isc_counter_increment
+isc_counter_setlimit
+isc_counter_used
isc_dir_chdir
isc_dir_chroot
isc_dir_close
# End Source File
# Begin Source File
+SOURCE=..\include\isc\counter.h
+# End Source File
+# Begin Source File
+
SOURCE=.\include\isc\condition.h
# End Source File
# Begin Source File
# End Source File
# Begin Source File
+SOURCE=..\counter.c
+# End Source File
+# Begin Source File
+
SOURCE=..\error.c
# End Source File
# Begin Source File
-@erase "$(INTDIR)\buffer.obj"
-@erase "$(INTDIR)\bufferlist.obj"
-@erase "$(INTDIR)\commandline.obj"
+ -@erase "$(INTDIR)\counter.obj"
-@erase "$(INTDIR)\condition.obj"
-@erase "$(INTDIR)\dir.obj"
-@erase "$(INTDIR)\DLLMain.obj"
"$(INTDIR)\buffer.obj" \
"$(INTDIR)\bufferlist.obj" \
"$(INTDIR)\commandline.obj" \
+ "$(INTDIR)\counter.obj" \
"$(INTDIR)\error.obj" \
"$(INTDIR)\event.obj" \
"$(INTDIR)\hash.obj" \
-@erase "$(INTDIR)\bufferlist.sbr"
-@erase "$(INTDIR)\commandline.obj"
-@erase "$(INTDIR)\commandline.sbr"
+ -@erase "$(INTDIR)\counter.obj"
+ -@erase "$(INTDIR)\counter.sbr"
-@erase "$(INTDIR)\condition.obj"
-@erase "$(INTDIR)\condition.sbr"
-@erase "$(INTDIR)\dir.obj"
"$(INTDIR)\buffer.sbr" \
"$(INTDIR)\bufferlist.sbr" \
"$(INTDIR)\commandline.sbr" \
+ "$(INTDIR)\counter.sbr" \
"$(INTDIR)\error.sbr" \
"$(INTDIR)\event.sbr" \
"$(INTDIR)\hash.sbr" \
"$(INTDIR)\buffer.obj" \
"$(INTDIR)\bufferlist.obj" \
"$(INTDIR)\commandline.obj" \
+ "$(INTDIR)\counter.obj" \
"$(INTDIR)\error.obj" \
"$(INTDIR)\event.obj" \
"$(INTDIR)\hash.obj" \
$(CPP) $(CPP_PROJ) $(SOURCE)
+!ENDIF
+
+SOURCE=..\counter.c
+
+!IF "$(CFG)" == "libisc - @PLATFORM@ Release"
+
+
+"$(INTDIR)\counter.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\counter.obj" "$(INTDIR)\counter.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
!ENDIF
SOURCE=..\error.c
<ClInclude Include="..\include\isc\commandline.h">\r
<Filter>Library Header Files</Filter>\r
</ClInclude>\r
+ <ClInclude Include="..\include\isc\counter.h">\r
+ <Filter>Library Header Files</Filter>\r
+ </ClInclude>\r
<ClInclude Include="..\include\isc\entropy.h">\r
<Filter>Library Header Files</Filter>\r
</ClInclude>\r
<ClCompile Include="..\commandline.c">\r
<Filter>Library Source Files</Filter>\r
</ClCompile>\r
+ <ClCompile Include="..\counter.c">\r
+ <Filter>Library Source Files</Filter>\r
+ </ClCompile>\r
<ClCompile Include="..\error.c">\r
<Filter>Library Source Files</Filter>\r
</ClCompile>\r
<ClInclude Include="..\include\isc\buffer.h" />\r
<ClInclude Include="..\include\isc\bufferlist.h" />\r
<ClInclude Include="..\include\isc\commandline.h" />\r
+ <ClInclude Include="..\include\isc\counter.h" />\r
<ClInclude Include="..\include\isc\entropy.h" />\r
<ClInclude Include="..\include\isc\error.h" />\r
<ClInclude Include="..\include\isc\event.h" />\r
<ClCompile Include="..\buffer.c" />\r
<ClCompile Include="..\bufferlist.c" />\r
<ClCompile Include="..\commandline.c" />\r
+ <ClCompile Include="..\counter.c" />\r
<ClCompile Include="..\error.c" />\r
<ClCompile Include="..\event.c" />\r
<ClCompile Include="..\hash.c" />\r