]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
[v9_9_6_patch] refactor max-recursion-queries
authorEvan Hunt <each@isc.org>
Thu, 20 Nov 2014 02:39:06 +0000 (18:39 -0800)
committerEvan Hunt <each@isc.org>
Thu, 20 Nov 2014 02:39:06 +0000 (18:39 -0800)
- the counters weren't set correctly when fetches timed out.
  instead we now pass down a counter object.

(cherry picked from commit 05e448935cb2d6ab08c24257f6536362d3496512)
(cherry picked from commit 6c049c57d94ba245089be71fbda612a053eca5ce)
(cherry picked from commit 1d47cb124d4e32b0d40b95ba907590ae4e9ccba1)

24 files changed:
bin/named/query.c
bin/tests/system/reclimit/README
bin/tests/system/reclimit/ans2/ans.pl
bin/tests/system/reclimit/ans4/ans.pl [new file with mode: 0644]
bin/tests/system/reclimit/ans7/ans.pl [new file with mode: 0644]
bin/tests/system/reclimit/ns1/root.db
bin/tests/system/reclimit/tests.sh
lib/dns/adb.c
lib/dns/include/dns/adb.h
lib/dns/include/dns/resolver.h
lib/dns/resolver.c
lib/export/isc/Makefile.in
lib/isc/Makefile.in
lib/isc/counter.c [new file with mode: 0644]
lib/isc/include/isc/Makefile.in
lib/isc/include/isc/counter.h [new file with mode: 0644]
lib/isc/include/isc/types.h
lib/isc/tests/Makefile.in
lib/isc/tests/counter_test.c [new file with mode: 0644]
lib/isc/win32/libisc.def.in
lib/isc/win32/libisc.dsp.in
lib/isc/win32/libisc.mak.in
lib/isc/win32/libisc.vcxproj.filters.in
lib/isc/win32/libisc.vcxproj.in

index 44196f70b25e4ef8744ed1cdbcd828b24b2342b4..af8e5da8204d3374f35d2dfa5c9f7e6ba563ddfd 100644 (file)
@@ -3880,7 +3880,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);
index b802a057ef48f8bba38623fc364adfecfd7c6c26..9bac5e6e925e90451b49cd98ef3ad859e685b21b 100644 (file)
@@ -2,5 +2,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
index a6e39a6a949dff98a7764e280d21e00559ff4da8..ce2615204629f271282155460bce1ded0bfa438e 100644 (file)
@@ -20,13 +20,13 @@ my $send_response = 0;
 
 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;
@@ -47,53 +47,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 (file)
index 0000000..044385a
--- /dev/null
@@ -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 (file)
index 0000000..0d2dc4b
--- /dev/null
@@ -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;
index 8e706b2bde9c9f25bcfe322ac4727d08368ac6ac..a77eba7101947de5c4b97b2340a6c8a5e2021d07 100644 (file)
@@ -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
index f67da3e3dc9b376cce212c2c5a55f121982da5a5..158ef5948e48b31b317dc6c9671514b0930a3b20 100644 (file)
@@ -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`
 
index fe9b3f7d4b6435bcac2a56aeb98e73888efa7ba8..c75ea59f751f8cc5a01e2df3e236b1573f8f87d9 100644 (file)
@@ -300,8 +300,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);
@@ -309,7 +308,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 *);
@@ -985,7 +985,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);
@@ -1410,7 +1410,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;
@@ -1470,7 +1470,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;
@@ -1829,7 +1828,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);
@@ -2804,7 +2802,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
@@ -2812,7 +2810,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;
@@ -3045,7 +3043,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 %p",
@@ -3056,7 +3054,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: "
@@ -3672,7 +3670,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);
 
@@ -3683,8 +3680,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]);
 
@@ -3839,14 +3834,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;
@@ -3895,8 +3890,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)
index 7501f0136ebcf7ff7050d9f6a79de74a284c8a40..8222bd211006347369fb353c9f8fbddf90125292 100644 (file)
@@ -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;
@@ -341,7 +339,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
index 8a8200a7ecf9a89996272b1cb88161edb14a7e98..a533f4ed2b24f8be6b6579cb88fd331bb846a16f 100644 (file)
@@ -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;
 
 /*
@@ -283,7 +282,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,
index fd7396ace55f950616003e7484dd2d982345b9fc..befe3cafe0f2ee852567e3582b5126951fc75a8d 100644 (file)
@@ -21,6 +21,7 @@
 
 #include <config.h>
 
+#include <isc/counter.h>
 #include <isc/log.h>
 #include <isc/platform.h>
 #include <isc/print.h>
@@ -244,12 +245,13 @@ struct fetchctx {
        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.
@@ -307,7 +309,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;
@@ -1111,7 +1112,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++;
        }
@@ -1554,7 +1554,6 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
        }
 
        fctx->querysent++;
-       fctx->totalqueries++;
 
        ISC_LIST_APPEND(fctx->queries, query, link);
        query->fctx->nqueries++;
@@ -2211,7 +2210,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 {
@@ -2503,7 +2501,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) {
                        /*
@@ -3057,15 +3055,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) {
                /*
@@ -3103,6 +3092,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__);
@@ -3201,6 +3200,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);
@@ -3424,7 +3424,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.
@@ -3494,7 +3493,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);
 
        /*
@@ -3532,7 +3530,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;
@@ -3554,6 +3552,21 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
        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 */
@@ -3561,7 +3574,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");
        dns_name_init(&fctx->name, NULL);
@@ -3603,7 +3616,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;
@@ -3793,6 +3805,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));
 
@@ -8255,7 +8270,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));
 }
 
@@ -8273,7 +8288,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));
 }
 
@@ -8284,7 +8299,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,
@@ -8378,7 +8393,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;
index a92f66f30a98bff43dc36a7f88a37b539b00098d..9b0249acf66ecdfd633a6824a385ea28766c87c6 100644 (file)
@@ -63,7 +63,7 @@ WIN32OBJS =   win32/condition.@O@ win32/dir.@O@ win32/file.@O@ \
 # 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@ \
@@ -86,7 +86,7 @@ ISCDRIVERSRCS =       mem.c task.c lib.c timer.c heap.c
 
 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 \
index e4eb83785d9a020e39ac3764a71d5defffa592de..3176a267988d63cfbbd6a85d322071d9906e8cdf 100644 (file)
@@ -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@
@@ -53,7 +51,7 @@ WIN32OBJS =   win32/condition.@O@ win32/dir.@O@ win32/file.@O@ \
 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@ \
@@ -71,8 +69,8 @@ SYMTBLOBJS =  backtrace-emptytbl.@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 \
diff --git a/lib/isc/counter.c b/lib/isc/counter.c
new file mode 100644 (file)
index 0000000..d7d187b
--- /dev/null
@@ -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 <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);
+}
index 46738c464fb0f190ea0f7d9a49a24383a8b62ed5..572bb3e1046b839d570a31ec9a69830c11a5670a 100644 (file)
@@ -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 =      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 \
diff --git a/lib/isc/include/isc/counter.h b/lib/isc/include/isc/counter.h
new file mode 100644 (file)
index 0000000..6a29319
--- /dev/null
@@ -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 <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 */
index a1edadd10db7e6a6fceb32bc2cc0893e131596d9..6f6e64ea1e6771090425512e3242cfbfedc3233e 100644 (file)
@@ -50,6 +50,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 struct isc_entropy             isc_entropy_t;          /*%< Entropy */
 typedef struct isc_entropysource       isc_entropysource_t;    /*%< Entropy Source */
 typedef struct isc_event               isc_event_t;            /*%< Event */
index 5fc6180183b33c88e44d99dc6ab2e31dce29fe07..3552ad0eef3394db6558d4202882eb3789716a11 100644 (file)
@@ -39,7 +39,7 @@ SRCS =                isctest.c taskpool_test.c socket_test.c hash_test.c \
                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@ \
@@ -47,7 +47,7 @@ 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@
 
@@ -107,6 +107,10 @@ time_test@EXEEXT@: time_test.@O@ ${ISCDEPLIBS}
        ${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
 
diff --git a/lib/isc/tests/counter_test.c b/lib/isc/tests/counter_test.c
new file mode 100644 (file)
index 0000000..a7a1997
--- /dev/null
@@ -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 <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());
+}
+
index 624ed432df8ce15807dd96c1d761e5f144260074..2b2e5320dbe682ff278df2a57fcbdae95e685fcc 100644 (file)
@@ -194,6 +194,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_dir_chdir
 isc_dir_chroot
 isc_dir_close
index 2c06974aa897d82463fbee773bc95455a0976630..c7f466e143849ad2d24d8e0c8c3bbe5ad8ed3205 100644 (file)
@@ -249,6 +249,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
@@ -629,6 +633,10 @@ SOURCE=..\commandline.c
 # End Source File
 # Begin Source File
 
+SOURCE=..\counter.c
+# End Source File
+# Begin Source File
+
 SOURCE=..\error.c
 # End Source File
 # Begin Source File
index 5cca6d967b45b298e2abb24e659178842d41948e..45a264a8d34c4b62d75cc53d26fff710341f6c20 100644 (file)
@@ -124,6 +124,7 @@ CLEAN :
        -@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"
@@ -250,6 +251,7 @@ LINK32_OBJS= \
        "$(INTDIR)\buffer.obj" \
        "$(INTDIR)\bufferlist.obj" \
        "$(INTDIR)\commandline.obj" \
+       "$(INTDIR)\counter.obj" \
        "$(INTDIR)\error.obj" \
        "$(INTDIR)\event.obj" \
        "$(INTDIR)\hash.obj" \
@@ -335,6 +337,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)\dir.obj"
@@ -531,6 +535,7 @@ BSC32_SBRS= \
        "$(INTDIR)\buffer.sbr" \
        "$(INTDIR)\bufferlist.sbr" \
        "$(INTDIR)\commandline.sbr" \
+       "$(INTDIR)\counter.sbr" \
        "$(INTDIR)\error.sbr" \
        "$(INTDIR)\event.sbr" \
        "$(INTDIR)\hash.sbr" \
@@ -623,6 +628,7 @@ LINK32_OBJS= \
        "$(INTDIR)\buffer.obj" \
        "$(INTDIR)\bufferlist.obj" \
        "$(INTDIR)\commandline.obj" \
+       "$(INTDIR)\counter.obj" \
        "$(INTDIR)\error.obj" \
        "$(INTDIR)\event.obj" \
        "$(INTDIR)\hash.obj" \
@@ -1280,6 +1286,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=..\error.c
index 8cc60a08a247d577d35fb799bf43f3802a9d7437..37380cba44604de3c1c0071785b124a52651c0ed 100644 (file)
@@ -59,6 +59,9 @@
     <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
index bcdf45c3b430626011e766ccb61420ee0e01ea47..2cd6c6a2a39cd999c2ec3495fcd981b545cadf38 100644 (file)
@@ -260,6 +260,7 @@ copy /Y @VCREDIST_PATH@ ..\Build\Release\
     <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
@@ -370,6 +371,7 @@ copy /Y @VCREDIST_PATH@ ..\Build\Release\
     <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