From 05e448935cb2d6ab08c24257f6536362d3496512 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Wed, 19 Nov 2014 18:21:02 -0800 Subject: [PATCH] [master] refactor max-recursion-queries - the counters weren't set correctly when fetches timed out. instead we now pass down a counter object. --- bin/named/query.c | 4 +- bin/tests/system/reclimit/README | 4 +- bin/tests/system/reclimit/ans2/ans.pl | 98 ++++++++--------- bin/tests/system/reclimit/ans4/ans.pl | 83 ++++++++++++++ bin/tests/system/reclimit/ans7/ans.pl | 64 +++++++++++ bin/tests/system/reclimit/ns1/root.db | 2 + bin/tests/system/reclimit/tests.sh | 33 ++++-- lib/dns/adb.c | 31 +++--- lib/dns/include/dns/adb.h | 5 +- lib/dns/include/dns/resolver.h | 3 +- lib/dns/resolver.c | 58 +++++----- lib/isc/Makefile.in | 6 +- lib/isc/counter.c | 138 ++++++++++++++++++++++++ lib/isc/include/isc/Makefile.in | 4 +- lib/isc/include/isc/counter.h | 130 ++++++++++++++++++++++ lib/isc/include/isc/types.h | 1 + lib/isc/tests/Makefile.in | 8 +- lib/isc/tests/counter_test.c | 69 ++++++++++++ lib/isc/win32/libisc.def.in | 6 ++ lib/isc/win32/libisc.dsp.in | 8 ++ lib/isc/win32/libisc.mak.in | 24 +++++ lib/isc/win32/libisc.vcxproj.filters.in | 6 ++ lib/isc/win32/libisc.vcxproj.in | 2 + 23 files changed, 669 insertions(+), 118 deletions(-) create mode 100644 bin/tests/system/reclimit/ans4/ans.pl create mode 100644 bin/tests/system/reclimit/ans7/ans.pl create mode 100644 lib/isc/counter.c create mode 100644 lib/isc/include/isc/counter.h create mode 100644 lib/isc/tests/counter_test.c diff --git a/bin/named/query.c b/bin/named/query.c index a73c14421c..4222b1721f 100644 --- a/bin/named/query.c +++ b/bin/named/query.c @@ -3908,7 +3908,7 @@ query_prefetch(ns_client_t *client, dns_name_t *qname, result = dns_resolver_createfetch3(client->view->resolver, qname, rdataset->type, NULL, NULL, NULL, peeraddr, client->message->id, - options, 0, client->task, + options, 0, NULL, client->task, prefetch_done, client, tmprdataset, NULL, &client->query.prefetch); @@ -4029,7 +4029,7 @@ query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname, 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); diff --git a/bin/tests/system/reclimit/README b/bin/tests/system/reclimit/README index 069f105a11..223a313055 100644 --- a/bin/tests/system/reclimit/README +++ b/bin/tests/system/reclimit/README @@ -5,5 +5,7 @@ system test for recursion limits 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 diff --git a/bin/tests/system/reclimit/ans2/ans.pl b/bin/tests/system/reclimit/ans2/ans.pl index b9663a074c..82bb8749f5 100644 --- a/bin/tests/system/reclimit/ans2/ans.pl +++ b/bin/tests/system/reclimit/ans2/ans.pl @@ -34,13 +34,13 @@ my $send_response = 0; sub getlimit { if ( -e "ans.limit") { - open(FH, "<", "ans.limit"); - my $line = ; - chomp $line; - close FH; - if ($line =~ /^\d+$/) { - return $line; - } + open(FH, "<", "ans.limit"); + my $line = ; + chomp $line; + close FH; + if ($line =~ /^\d+$/) { + return $line; + } } return 0; @@ -61,53 +61,53 @@ sub reply_handler { $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 diff --git a/bin/tests/system/reclimit/ans4/ans.pl b/bin/tests/system/reclimit/ans4/ans.pl new file mode 100644 index 0000000000..044385a8b6 --- /dev/null +++ b/bin/tests/system/reclimit/ans4/ans.pl @@ -0,0 +1,83 @@ +#!/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; diff --git a/bin/tests/system/reclimit/ans7/ans.pl b/bin/tests/system/reclimit/ans7/ans.pl new file mode 100644 index 0000000000..0d2dc4bcfb --- /dev/null +++ b/bin/tests/system/reclimit/ans7/ans.pl @@ -0,0 +1,64 @@ +#!/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; diff --git a/bin/tests/system/reclimit/ns1/root.db b/bin/tests/system/reclimit/ns1/root.db index 8e706b2bde..a77eba7101 100644 --- a/bin/tests/system/reclimit/ns1/root.db +++ b/bin/tests/system/reclimit/ns1/root.db @@ -18,3 +18,5 @@ ns.nil. 60 IN A 10.53.0.1 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 diff --git a/bin/tests/system/reclimit/tests.sh b/bin/tests/system/reclimit/tests.sh index f67da3e3dc..158ef5948e 100644 --- a/bin/tests/system/reclimit/tests.sh +++ b/bin/tests/system/reclimit/tests.sh @@ -90,29 +90,28 @@ sleep 2 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` @@ -124,29 +123,43 @@ sleep 2 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` diff --git a/lib/dns/adb.c b/lib/dns/adb.c index 3b8deaff7f..2a45dadae4 100644 --- a/lib/dns/adb.c +++ b/lib/dns/adb.c @@ -313,8 +313,7 @@ static inline isc_boolean_t dec_entry_refcnt(dns_adb_t *, isc_boolean_t, 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); @@ -322,7 +321,8 @@ static void cancel_fetches_at_name(dns_adbname_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 *); @@ -1025,7 +1025,7 @@ kill_name(dns_adbname_t **n, isc_eventtype_t ev) { * 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); @@ -1450,7 +1450,7 @@ event_free(isc_event_t *event) { */ 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; @@ -1510,7 +1510,6 @@ clean_finds_at_name(dns_adbname_t *name, isc_eventtype_t evtype, 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; @@ -1886,7 +1885,6 @@ new_adbfind(dns_adb_t *adb) { 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); @@ -2870,7 +2868,7 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, { 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 @@ -2878,7 +2876,7 @@ dns_adb_createfind2(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, 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; @@ -3117,7 +3115,7 @@ dns_adb_createfind2(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, * 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 %s (%p)", @@ -3128,7 +3126,7 @@ dns_adb_createfind2(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, * 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: " "started AAAA fetch for name %s (%p)", @@ -3760,7 +3758,6 @@ fetch_callback(isc_task_t *task, isc_event_t *ev) { isc_result_t result; unsigned int address_type; isc_boolean_t want_check_exit = ISC_FALSE; - isc_uint32_t qtotal = 0; UNUSED(task); @@ -3771,8 +3768,6 @@ fetch_callback(isc_task_t *task, isc_event_t *ev) { adb = name->adb; INSIST(DNS_ADB_VALID(adb)); - qtotal = dev->qtotal; - bucket = name->lock_bucket; LOCK(&adb->namelocks[bucket]); @@ -3927,14 +3922,14 @@ fetch_callback(isc_task_t *task, isc_event_t *ev) { 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; @@ -3983,8 +3978,8 @@ fetch_name(dns_adbname_t *adbname, isc_boolean_t start_at_zone, 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) diff --git a/lib/dns/include/dns/adb.h b/lib/dns/include/dns/adb.h index 063602f84f..b7e4fafa06 100644 --- a/lib/dns/include/dns/adb.h +++ b/lib/dns/include/dns/adb.h @@ -118,8 +118,6 @@ struct dns_adbfind { 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; @@ -343,7 +341,8 @@ dns_adb_createfind2(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, 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 diff --git a/lib/dns/include/dns/resolver.h b/lib/dns/include/dns/resolver.h index bc1560f9b7..64e5f6f080 100644 --- a/lib/dns/include/dns/resolver.h +++ b/lib/dns/include/dns/resolver.h @@ -82,7 +82,6 @@ typedef struct dns_fetchevent { isc_sockaddr_t * client; dns_messageid_t id; isc_result_t vresult; - isc_uint32_t qtotal; } dns_fetchevent_t; /* @@ -286,7 +285,7 @@ dns_resolver_createfetch3(dns_resolver_t *res, dns_name_t *name, 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, diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index a480b2c813..06ab05c7ad 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -281,12 +282,13 @@ struct fetchctx { ISC_LIST(struct tried) edns; ISC_LIST(struct tried) 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. @@ -344,7 +346,6 @@ struct fetchctx { isc_uint64_t duration; isc_boolean_t logged; unsigned int querysent; - unsigned int totalqueries; unsigned int referrals; unsigned int lamecount; unsigned int neterr; @@ -1176,7 +1177,6 @@ fctx_sendevents(fetchctx_t *fctx, isc_result_t result, int line) { event->result == DNS_R_NCACHENXRRSET); } - event->qtotal = fctx->totalqueries; isc_task_sendanddetach(&task, ISC_EVENT_PTR(&event)); count++; } @@ -1643,7 +1643,6 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, } fctx->querysent++; - fctx->totalqueries++; ISC_LIST_APPEND(fctx->queries, query, link); query->fctx->nqueries++; @@ -2540,7 +2539,6 @@ fctx_finddone(isc_task_t *task, isc_event_t *event) { */ INSIST(!SHUTTINGDOWN(fctx)); fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; - fctx->totalqueries += find->qtotal; if (event->ev_type == DNS_EVENT_ADBMOREADDRESSES) { want_try = ISC_TRUE; } else { @@ -2833,7 +2831,7 @@ findname(fetchctx_t *fctx, dns_name_t *name, in_port_t port, &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) { /* @@ -3386,15 +3384,6 @@ fctx_try(fetchctx_t *fctx, isc_boolean_t retrying, isc_boolean_t badcache) { 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) { /* @@ -3432,6 +3421,16 @@ fctx_try(fetchctx_t *fctx, isc_boolean_t retrying, isc_boolean_t badcache) { } } + 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__); @@ -3530,6 +3529,7 @@ fctx_destroy(fetchctx_t *fctx) { 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); @@ -3757,7 +3757,6 @@ fctx_start(isc_task_t *task, isc_event_t *event) { * Normal fctx startup. */ fctx->state = fetchstate_active; - fctx->totalqueries = 0; /* * Reset the control event for later use in shutting down * the fctx. @@ -3827,7 +3826,6 @@ fctx_join(fetchctx_t *fctx, isc_task_t *task, isc_sockaddr_t *client, event->fetch = fetch; event->client = client; event->id = id; - event->qtotal = 0; dns_fixedname_init(&event->foundname); /* @@ -3865,7 +3863,7 @@ static isc_result_t fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, dns_name_t *domain, dns_rdataset_t *nameservers, unsigned int options, unsigned int bucketnum, unsigned int depth, - fetchctx_t **fctxp) + isc_counter_t *qc, fetchctx_t **fctxp) { fetchctx_t *fctx; isc_result_t result; @@ -3887,6 +3885,16 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, 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". @@ -3898,7 +3906,7 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, fctx->info = isc_mem_strdup(mctx, buf); if (fctx->info == NULL) { result = ISC_R_NOMEMORY; - goto cleanup_fetch; + goto cleanup_counter; } FCTXTRACE("create"); @@ -3941,7 +3949,6 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, fctx->pending = 0; fctx->restarts = 0; fctx->querysent = 0; - fctx->totalqueries = 0; fctx->referrals = 0; TIME_NOW(&fctx->start); fctx->timeouts = 0; @@ -4136,6 +4143,9 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, cleanup_info: isc_mem_free(mctx, fctx->info); + cleanup_counter: + isc_counter_detach(&fctx->qc); + cleanup_fetch: isc_mem_put(mctx, fctx, sizeof(*fctx)); @@ -8793,7 +8803,7 @@ dns_resolver_createfetch(dns_resolver_t *res, dns_name_t *name, { 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)); } @@ -8811,7 +8821,7 @@ dns_resolver_createfetch2(dns_resolver_t *res, dns_name_t *name, { 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)); } @@ -8822,7 +8832,7 @@ dns_resolver_createfetch3(dns_resolver_t *res, dns_name_t *name, 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, @@ -8916,7 +8926,7 @@ dns_resolver_createfetch3(dns_resolver_t *res, dns_name_t *name, 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; diff --git a/lib/isc/Makefile.in b/lib/isc/Makefile.in index 077e3237fa..5d088987e5 100644 --- a/lib/isc/Makefile.in +++ b/lib/isc/Makefile.in @@ -13,8 +13,6 @@ # 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@ @@ -57,7 +55,7 @@ WIN32OBJS = win32/condition.@O@ win32/dir.@O@ win32/file.@O@ \ OBJS = @ISC_EXTRA_OBJS@ @ISC_PK11_O@ @ISC_PK11_RESULT_O@ \ aes.@O@ assertions.@O@ backtrace.@O@ base32.@O@ base64.@O@ \ bind9.@O@ buffer.@O@ bufferlist.@O@ \ - commandline.@O@ crc64.@O@ error.@O@ event.@O@ \ + commandline.@O@ counter.@O@ crc64.@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@ \ @@ -77,7 +75,7 @@ CHACHASRCS = chacha_private.h # Alphabetically SRCS = @ISC_EXTRA_SRCS@ @ISC_PK11_C@ @ISC_PK11_RESULT_C@ \ aes.c assertions.c backtrace.c base32.c base64.c bind9.c \ - buffer.c bufferlist.c commandline.c crc64.c \ + buffer.c bufferlist.c commandline.c counter.c crc64.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 \ diff --git a/lib/isc/counter.c b/lib/isc/counter.c new file mode 100644 index 0000000000..d7d187bbee --- /dev/null +++ b/lib/isc/counter.c @@ -0,0 +1,138 @@ +/* + * 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 + +#include + +#include +#include +#include +#include + +#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); +} diff --git a/lib/isc/include/isc/Makefile.in b/lib/isc/include/isc/Makefile.in index 9ab9318765..0a0b0773ae 100644 --- a/lib/isc/include/isc/Makefile.in +++ b/lib/isc/include/isc/Makefile.in @@ -13,8 +13,6 @@ # 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@ @@ -28,7 +26,7 @@ top_srcdir = @top_srcdir@ # HEADERS = aes.h app.h assertions.h backtrace.h base32.h base64.h \ bind9.h boolean.h buffer.h bufferlist.h \ - commandline.h crc64.h entropy.h error.h event.h \ + commandline.h counter.h crc64.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 json.h \ diff --git a/lib/isc/include/isc/counter.h b/lib/isc/include/isc/counter.h new file mode 100644 index 0000000000..6a29319f5f --- /dev/null +++ b/lib/isc/include/isc/counter.h @@ -0,0 +1,130 @@ +/* + * 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 +#include +#include + +/***** + ***** 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 */ diff --git a/lib/isc/include/isc/types.h b/lib/isc/include/isc/types.h index a32d56130a..e0e6ddd3b0 100644 --- a/lib/isc/include/isc/types.h +++ b/lib/isc/include/isc/types.h @@ -48,6 +48,7 @@ typedef struct isc_buffer isc_buffer_t; /*%< Buffer */ 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 isc_int16_t isc_dscp_t; /*%< Diffserv code point */ typedef struct isc_entropy isc_entropy_t; /*%< Entropy */ typedef struct isc_entropysource isc_entropysource_t; /*%< Entropy Source */ diff --git a/lib/isc/tests/Makefile.in b/lib/isc/tests/Makefile.in index 8160f8a9a0..eacb7e0713 100644 --- a/lib/isc/tests/Makefile.in +++ b/lib/isc/tests/Makefile.in @@ -40,7 +40,7 @@ SRCS = isctest.c taskpool_test.c socket_test.c hash_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 \ socket_test.c safe_test.c time_test.c aes_test.c \ - file_test.c buffer_test.c + file_test.c buffer_test.c counter_test.c SUBDIRS = TARGETS = taskpool_test@EXEEXT@ socket_test@EXEEXT@ hash_test@EXEEXT@ \ @@ -49,7 +49,7 @@ TARGETS = taskpool_test@EXEEXT@ socket_test@EXEEXT@ hash_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@ aes_test@EXEEXT@ \ - file_test@EXEEXT@ buffer_test@EXEEXT@ + file_test@EXEEXT@ buffer_test@EXEEXT@ counter_test@EXEEXT@ @BIND9_MAKE_RULES@ @@ -129,6 +129,10 @@ buffer_test@EXEEXT@: buffer_test.@O@ ${ISCDEPLIBS} ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ buffer_test.@O@ isctest.@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 diff --git a/lib/isc/tests/counter_test.c b/lib/isc/tests/counter_test.c new file mode 100644 index 0000000000..a7a19972aa --- /dev/null +++ b/lib/isc/tests/counter_test.c @@ -0,0 +1,69 @@ +/* + * 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 +#include + +#include + +#include +#include + +#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()); +} + diff --git a/lib/isc/win32/libisc.def.in b/lib/isc/win32/libisc.def.in index 2dbbfaf026..3ed0a9aca3 100644 --- a/lib/isc/win32/libisc.def.in +++ b/lib/isc/win32/libisc.def.in @@ -170,6 +170,12 @@ isc_condition_init 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_crc64_final isc_crc64_init isc_crc64_update diff --git a/lib/isc/win32/libisc.dsp.in b/lib/isc/win32/libisc.dsp.in index b5de4c3b39..6d32a7fb12 100644 --- a/lib/isc/win32/libisc.dsp.in +++ b/lib/isc/win32/libisc.dsp.in @@ -265,6 +265,10 @@ SOURCE=..\include\isc\commandline.h # 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 @@ -693,6 +697,10 @@ SOURCE=..\commandline.c # End Source File # Begin Source File +SOURCE=..\counter.c +# End Source File +# Begin Source File + SOURCE=..\crc64.c # End Source File # Begin Source File diff --git a/lib/isc/win32/libisc.mak.in b/lib/isc/win32/libisc.mak.in index 9ea19a3870..9e49e6577a 100644 --- a/lib/isc/win32/libisc.mak.in +++ b/lib/isc/win32/libisc.mak.in @@ -127,6 +127,7 @@ CLEAN : -@erase "$(INTDIR)\buffer.obj" -@erase "$(INTDIR)\bufferlist.obj" -@erase "$(INTDIR)\commandline.obj" + -@erase "$(INTDIR)\counter.obj" -@erase "$(INTDIR)\condition.obj" -@erase "$(INTDIR)\crc64.obj" -@erase "$(INTDIR)\dir.obj" @@ -269,6 +270,7 @@ LINK32_OBJS= \ "$(INTDIR)\buffer.obj" \ "$(INTDIR)\bufferlist.obj" \ "$(INTDIR)\commandline.obj" \ + "$(INTDIR)\counter.obj" \ "$(INTDIR)\crc64.obj" \ "$(INTDIR)\error.obj" \ "$(INTDIR)\event.obj" \ @@ -363,6 +365,8 @@ CLEAN : -@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)\crc64.obj" @@ -576,6 +580,7 @@ BSC32_SBRS= \ "$(INTDIR)\buffer.sbr" \ "$(INTDIR)\bufferlist.sbr" \ "$(INTDIR)\commandline.sbr" \ + "$(INTDIR)\counter.sbr" \ "$(INTDIR)\crc64.sbr" \ "$(INTDIR)\error.sbr" \ "$(INTDIR)\event.sbr" \ @@ -679,6 +684,7 @@ LINK32_OBJS= \ "$(INTDIR)\buffer.obj" \ "$(INTDIR)\bufferlist.obj" \ "$(INTDIR)\commandline.obj" \ + "$(INTDIR)\counter.obj" \ "$(INTDIR)\crc64.obj" \ "$(INTDIR)\error.obj" \ "$(INTDIR)\event.obj" \ @@ -1377,6 +1383,24 @@ SOURCE=..\commandline.c $(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=..\crc64.c diff --git a/lib/isc/win32/libisc.vcxproj.filters.in b/lib/isc/win32/libisc.vcxproj.filters.in index 9d91678719..af0b311050 100644 --- a/lib/isc/win32/libisc.vcxproj.filters.in +++ b/lib/isc/win32/libisc.vcxproj.filters.in @@ -61,6 +61,9 @@ Library Header Files + + Library Header Files + Library Header Files @@ -492,6 +495,9 @@ Library Source Files + + Library Source Files + Library Source Files diff --git a/lib/isc/win32/libisc.vcxproj.in b/lib/isc/win32/libisc.vcxproj.in index ca7d3606de..d071eb9768 100644 --- a/lib/isc/win32/libisc.vcxproj.in +++ b/lib/isc/win32/libisc.vcxproj.in @@ -276,6 +276,7 @@ copy /Y @VCREDIST_PATH@ ..\Build\Release\ + @@ -400,6 +401,7 @@ copy /Y @VCREDIST_PATH@ ..\Build\Release\ +