Browse Source

[2689] Merge branch 'master' into trac2689

fixed Conflicts:
	src/bin/stats/stats.py.in
(some tests current fail; will fix them)
JINMEI Tatuya 12 years ago
parent
commit
808dcdccc7
64 changed files with 3379 additions and 2222 deletions
  1. 8 0
      ChangeLog
  2. 4 1
      README
  3. 6 3
      configure.ac
  4. 7 0
      doc/guide/bind10-guide.xml
  5. 24 4
      src/bin/auth/Makefile.am
  6. 1 217
      src/bin/auth/auth.spec.pre.in
  7. 82 93
      src/bin/auth/auth_srv.cc
  8. 1 4
      src/bin/auth/auth_srv.h
  9. 31 31
      src/bin/auth/b10-auth.xml
  10. 385 0
      src/bin/auth/gen-statisticsitems.py.pre.in
  11. 0 284
      src/bin/auth/statistics.cc
  12. 277 0
      src/bin/auth/statistics.cc.pre
  13. 223 142
      src/bin/auth/statistics.h
  14. 0 609
      src/bin/auth/statistics_items.h
  15. 53 0
      src/bin/auth/statistics_items.h.pre
  16. 48 0
      src/bin/auth/statistics_msg_items.def
  17. 9 2
      src/bin/auth/tests/Makefile.am
  18. 310 166
      src/bin/auth/tests/auth_srv_unittest.cc
  19. 90 0
      src/bin/auth/tests/gen-statisticsitems_test.py
  20. 0 123
      src/bin/auth/tests/statistics_unittest.cc
  21. 714 0
      src/bin/auth/tests/statistics_unittest.cc.pre
  22. 75 0
      src/bin/auth/tests/statistics_util.cc
  23. 38 0
      src/bin/auth/tests/statistics_util.h
  24. 0 1
      src/bin/auth/tests/testdata/Makefile.am
  25. 6 1
      src/bin/cmdctl/cmdctl.py.in
  26. 24 37
      src/bin/ddns/ddns.py.in
  27. 17 27
      src/bin/ddns/ddns_messages.mes
  28. 3 2
      src/bin/ddns/tests/ddns_test.py
  29. 0 3
      src/bin/loadzone/tests/correct/correct_test.sh.in
  30. 45 46
      src/bin/stats/stats.py.in
  31. 8 20
      src/bin/stats/stats_httpd.py.in
  32. 1 1
      src/bin/xfrin/tests/xfrin_test.py
  33. 17 5
      src/bin/xfrin/xfrin.py.in
  34. 8 16
      src/bin/zonemgr/tests/zonemgr_test.py
  35. 9 10
      src/bin/zonemgr/zonemgr.py.in
  36. 1 1
      src/lib/asiodns/sync_udp_server.cc
  37. 0 2
      src/lib/asiodns/sync_udp_server.h
  38. 3 4
      src/lib/asiolink/tests/tcp_socket_unittest.cc
  39. 6 3
      src/lib/cache/local_zone_data.h
  40. 1 0
      src/lib/cc/proto_defs.cc
  41. 2 2
      src/lib/dhcpsrv/dhcpsrv_messages.mes
  42. 3 2
      src/lib/dhcpsrv/pool.cc
  43. 66 3
      src/lib/python/isc/config/ccsession.py
  44. 61 0
      src/lib/python/isc/config/tests/ccsession_test.py
  45. 6 4
      src/lib/python/isc/config/tests/unittest_fakesession.py
  46. 9 5
      src/lib/python/isc/server_common/auth_command.py
  47. 71 68
      src/lib/server_common/tests/socket_requestor_test.cc
  48. 6 15
      src/lib/statistics/counter.h
  49. 56 31
      src/lib/statistics/counter_dict.h
  50. 8 0
      src/lib/testutils/testdata/example.com
  51. BIN
      src/lib/testutils/testdata/example.sqlite3
  52. 41 37
      src/lib/util/tests/fd_share_tests.cc
  53. 20 14
      src/lib/util/tests/fd_tests.cc
  54. 77 68
      src/lib/util/tests/interprocess_sync_file_unittest.cc
  55. 17 12
      src/lib/util/threads/tests/condvar_unittest.cc
  56. 29 27
      src/lib/util/threads/tests/lock_unittest.cc
  57. 29 20
      src/lib/util/threads/tests/thread_unittest.cc
  58. 5 0
      src/lib/util/unittests/check_valgrind.h
  59. 2 0
      src/lib/util/unittests/fork.cc
  60. 4 0
      tests/lettuce/configurations/example.org.inmem.config
  61. 189 3
      tests/lettuce/features/queries.feature
  62. 58 6
      tests/lettuce/features/terrain/bind10_control.py
  63. 73 36
      tests/system/bindctl/tests.sh
  64. 12 11
      tests/tools/perfdhcp/test_control.cc

+ 8 - 0
ChangeLog

@@ -1,3 +1,11 @@
+581.	[func]*		y-aharen
+	Added statistics items in b10-auth based on
+	http://bind10.isc.org/wiki/StatisticsItems. Qtype counters are
+	dropped as it requires further spec design discussion.
+	(Trac #2154, Trac #2155,
+	             git 61d7c3959eb991b22bc1c0ef8f4ecb96b65d9325)
+	(Trac #2157, git e653adac032f871cbd66cd500c37407a56d14589)
+
 bind10-1.0.0-rc released on February 14, 2013
 
 580.	[func]*		muks

+ 4 - 1
README

@@ -20,7 +20,10 @@ of concept recursive or forwarding DNS server, b10-resolver.)
 
 BIND 10 also provides experimental DHCPv4 and DHCPv6 servers,
 b10-dhcp4 and b10-dhcp6, a portable DHCP library, libdhcp++, and
-a DHCP benchmarking tool, perfdhcp.
+a DHCP benchmarking tool, perfdhcp.  In this release of BIND 10,
+the DHCPv4 and DHCPv6 servers must be considered experimental.
+Limitations and known issues with this DHCP release can be found
+at http://bind10.isc.org/wiki/KeaKnownIssues
 
 Documentation is included with the source. See doc/guide/bind10-guide.txt
 (or bind10-guide.html) for installation instructions.  The

+ 6 - 3
configure.ac

@@ -2,7 +2,7 @@
 # Process this file with autoconf to produce a configure script.
 
 AC_PREREQ([2.59])
-AC_INIT(bind10, 20130213, bind10-dev@isc.org)
+AC_INIT(bind10, 20130221, bind10-dev@isc.org)
 AC_CONFIG_SRCDIR(README)
 # serial-tests is not available in automake version before 1.13. In
 # automake 1.13 and higher, AM_PROG_INSTALL is undefined, so we'll check
@@ -161,8 +161,10 @@ if test $with_werror = 1; then
    CXXFLAGS_SAVED="$CXXFLAGS"
    CXXFLAGS="$CXXFLAGS $B10_CXXFLAGS -Werror"
    AC_MSG_CHECKING(for in-TU anonymous namespace breakage)
-   AC_TRY_COMPILE([namespace { class Foo {}; }
-   namespace isc {class Bar {Foo foo_;};} ],,
+   # We use struct, not class, here, because some compilers complain about
+   # "unused private members", causing a false positive.
+   AC_TRY_COMPILE([namespace { struct Foo {}; }
+   namespace isc {struct Bar {Foo foo_;};} ],,
 	[AC_MSG_RESULT(no)
 	 werror_ok=1
 	 B10_CXXFLAGS="$B10_CXXFLAGS -Werror"],
@@ -1359,6 +1361,7 @@ AC_OUTPUT([doc/version.ent
            src/bin/auth/tests/testdata/example.zone
            src/bin/auth/tests/testdata/example-base.zone
            src/bin/auth/tests/testdata/example-nsec3.zone
+           src/bin/auth/gen-statisticsitems.py.pre
            src/bin/dhcp4/spec_config.h.pre
            src/bin/dhcp6/spec_config.h.pre
            src/bin/tests/process_rename_test.py

+ 7 - 0
doc/guide/bind10-guide.xml

@@ -3372,6 +3372,13 @@ then change those defaults with config set Resolver/forward_addresses[0]/address
     while those details specific to DHCPv6 are described in <xref linkend="dhcp6"/>
     </para>
 
+    <note>
+      <simpara>
+	In this release of BIND 10, the DHCPv4 and DHCPv6 servers
+	must be considered experimental.
+      </simpara>
+    </note>
+
     <section id="dhcp-install-configure">
       <title>DHCP Database Installation and Configuration</title>
       <para>

+ 24 - 4
src/bin/auth/Makefile.am

@@ -18,6 +18,9 @@ pkglibexecdir = $(libexecdir)/@PACKAGE@
 
 CLEANFILES  = *.gcno *.gcda auth.spec spec_config.h
 CLEANFILES += auth_messages.h auth_messages.cc
+CLEANFILES += gen-statisticsitems.py
+# auto-generated by gen-statisticsitems.py
+CLEANFILES += statistics.cc statistics_items.h b10-auth.xml tests/statistics_unittest.cc
 
 man_MANS = b10-auth.8
 DISTCLEANFILES = $(man_MANS)
@@ -26,7 +29,7 @@ EXTRA_DIST = $(man_MANS) b10-auth.xml
 if GENERATE_DOCS
 
 b10-auth.8: b10-auth.xml
-	@XSLTPROC@ --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/b10-auth.xml
+	@XSLTPROC@ --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(builddir)/b10-auth.xml
 
 else
 
@@ -36,8 +39,18 @@ $(man_MANS):
 
 endif
 
-auth.spec: auth.spec.pre
-	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" auth.spec.pre >$@
+auth.spec: auth.spec.pre statistics_msg_items.def
+b10-auth.xml: b10-auth.xml.pre statistics_msg_items.def
+statistics_items.h: statistics_items.h.pre statistics_msg_items.def
+statistics.cc: statistics.cc.pre statistics_msg_items.def
+tests/statistics_unittest.cc: tests/statistics_unittest.cc.pre statistics_msg_items.def
+
+gen-statisticsitems.py: gen-statisticsitems.py.pre
+	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" gen-statisticsitems.py.pre >$@
+	chmod +x $@
+
+auth.spec b10-auth.xml statistics_items.h statistics.cc tests/statistics_unittest.cc: Makefile gen-statisticsitems.py
+	./gen-statisticsitems.py
 
 spec_config.h: spec_config.h.pre
 	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" spec_config.h.pre >$@
@@ -46,6 +59,8 @@ auth_messages.h auth_messages.cc: auth_messages.mes
 	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/bin/auth/auth_messages.mes
 
 BUILT_SOURCES = spec_config.h auth_messages.h auth_messages.cc
+# auto-generated by gen-statisticsitems.py
+BUILT_SOURCES += statistics_items.h statistics.cc
 
 pkglibexec_PROGRAMS = b10-auth
 b10_auth_SOURCES = query.cc query.h
@@ -54,13 +69,18 @@ b10_auth_SOURCES += auth_log.cc auth_log.h
 b10_auth_SOURCES += auth_config.cc auth_config.h
 b10_auth_SOURCES += command.cc command.h
 b10_auth_SOURCES += common.h common.cc
-b10_auth_SOURCES += statistics.cc statistics.h statistics_items.h
+b10_auth_SOURCES += statistics.h
 b10_auth_SOURCES += datasrc_clients_mgr.h
 b10_auth_SOURCES += datasrc_config.h datasrc_config.cc
 b10_auth_SOURCES += main.cc
 
 nodist_b10_auth_SOURCES = auth_messages.h auth_messages.cc
+nodist_b10_auth_SOURCES += statistics.cc statistics_items.h
 EXTRA_DIST += auth_messages.mes
+EXTRA_DIST += statistics_msg_items.def
+EXTRA_DIST += b10-auth.xml.pre
+EXTRA_DIST += statistics_items.h.pre statistics.cc.pre
+EXTRA_DIST += tests/statistics_unittest.cc.pre
 
 b10_auth_LDADD =  $(top_builddir)/src/lib/datasrc/libb10-datasrc.la
 b10_auth_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la

+ 1 - 217
src/bin/auth/auth.spec.pre.in

@@ -122,7 +122,7 @@
             "item_name": "class", "item_type": "string",
             "item_optional": true, "item_default": "IN"
           },
-	  {
+          {
             "item_name": "origin", "item_type": "string",
             "item_optional": false, "item_default": ""
           }
@@ -140,222 +140,6 @@
       }
     ],
     "statistics": [
-      {
-        "item_name": "queries.tcp",
-        "item_type": "integer",
-        "item_optional": false,
-        "item_default": 0,
-        "item_title": "Queries TCP",
-        "item_description": "A number of total query counts which all auth servers receive over TCP since they started initially"
-      },
-      {
-        "item_name": "queries.udp",
-        "item_type": "integer",
-        "item_optional": false,
-        "item_default": 0,
-        "item_title": "Queries UDP",
-        "item_description": "A number of total query counts which all auth servers receive over UDP since they started initially"
-      },
-      {
-        "item_name": "opcode.query",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Received query requests",
-        "item_description": "The number of total request counts whose opcode is query"
-      },
-      {
-        "item_name": "opcode.iquery",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Received inverse query requests",
-        "item_description": "The number of total request counts whose opcode is inverse query"
-      },
-      {
-        "item_name": "opcode.status",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Received status requests",
-        "item_description": "The number of total request counts whose opcode is status"
-      },
-      {
-        "item_name": "opcode.notify",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Received notify requests",
-        "item_description": "The number of total request counts whose opcode is notify"
-      },
-      {
-        "item_name": "opcode.update",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Received update requests",
-        "item_description": "The number of total request counts whose opcode is update"
-      },
-      {
-        "item_name": "opcode.other",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Received requests opcode other",
-        "item_description": "The number of total request counts whose opcode is other (not well-known)"
-      },
-      {
-        "item_name": "rcode.noerror",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Sent success response",
-        "item_description": "The number of total responses with rcode 0 (NOERROR)"
-      },
-      {
-        "item_name": "rcode.formerr",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Sent 'format error' response",
-        "item_description": "The number of total responses with rcode 1 (FORMERR)"
-      },
-      {
-        "item_name": "rcode.servfail",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Sent 'server failure' response",
-        "item_description": "The number of total responses with rcode 2 (SERVFAIL)"
-      },
-      {
-        "item_name": "rcode.nxdomain",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Sent 'name error' response",
-        "item_description": "The number of total responses with rcode 3 (NXDOMAIN)"
-      },
-      {
-        "item_name": "rcode.notimp",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Sent 'not implemented' response",
-        "item_description": "The number of total responses with rcode 4 (NOTIMP)"
-      },
-      {
-        "item_name": "rcode.refused",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Sent 'refused' response",
-        "item_description": "The number of total responses with rcode 5 (REFUSED)"
-      },
-      {
-        "item_name": "rcode.yxdomain",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Sent 'name unexpectedly exists' response",
-        "item_description": "The number of total responses with rcode 6 (YXDOMAIN)"
-      },
-      {
-        "item_name": "rcode.yxrrset",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Sent 'rrset unexpectedly exists' response",
-        "item_description": "The number of total responses with rcode 7 (YXRRSET)"
-      },
-      {
-        "item_name": "rcode.nxrrset",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Sent 'no such rrset' response",
-        "item_description": "The number of total responses with rcode 8 (NXRRSET)"
-      },
-      {
-        "item_name": "rcode.notauth",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Sent 'not authoritative' response",
-        "item_description": "The number of total responses with rcode 9 (NOTAUTH)"
-      },
-      {
-        "item_name": "rcode.notzone",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Sent 'name not in zone' response",
-        "item_description": "The number of total responses with rcode 10 (NOTZONE)"
-      },
-      {
-        "item_name": "rcode.badsigvers",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Sent 'EDNS version not implemented' response",
-        "item_description": "The number of total responses with rcode 16 (BADVERS)"
-      },
-      {
-        "item_name": "rcode.badkey",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Sent 'Key not recognized' response",
-        "item_description": "The number of total responses with rcode 17 (BADKEY)"
-      },
-      {
-        "item_name": "rcode.badtime",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Sent 'Signature out of time window' response",
-        "item_description": "The number of total responses with rcode 18 (BADTIME)"
-      },
-      {
-        "item_name": "rcode.badmode",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Sent 'Bad TKEY Mode' response",
-        "item_description": "The number of total responses with rcode 19 (BADMODE)"
-      },
-      {
-        "item_name": "rcode.badname",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Sent 'Duplicate key name' response",
-        "item_description": "The number of total responses with rcode 20 (BADNAME)"
-      },
-      {
-        "item_name": "rcode.badalg",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Sent 'Algorithm not supported' response",
-        "item_description": "The number of total responses with rcode 21 (BADALG)"
-      },
-      {
-        "item_name": "rcode.badtrunc",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Sent 'Bad Truncation' response",
-        "item_description": "The number of total responses with rcode 22 (BADTRUNC)"
-      },
-      {
-        "item_name": "rcode.other",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Sent responses with rcode other",
-        "item_description": "The number of total responses with rcode other (not well-known)"
-      }
     ]
   }
 }

+ 82 - 93
src/bin/auth/auth_srv.cc

@@ -86,6 +86,7 @@ using namespace isc::asiolink;
 using namespace isc::asiodns;
 using namespace isc::server_common::portconfig;
 using isc::auth::statistics::Counters;
+using isc::auth::statistics::MessageAttributes;
 
 namespace {
 // A helper class for cleaning up message renderer.
@@ -102,17 +103,21 @@ namespace {
 // user of this class, so we hide it within the implementation.
 class RendererHolder {
 public:
-    RendererHolder(MessageRenderer& renderer, OutputBuffer* buffer) :
-        renderer_(renderer)
+    RendererHolder(MessageRenderer& renderer, OutputBuffer* buffer,
+                   MessageAttributes& stats_attrs) :
+        renderer_(renderer),
+        stats_attrs_(stats_attrs)
     {
         renderer.setBuffer(buffer);
     }
     ~RendererHolder() {
+        stats_attrs_.setResponseTruncated(renderer_.isTruncated());
         renderer_.setBuffer(NULL);
         renderer_.clear();
     }
 private:
     MessageRenderer& renderer_;
+    MessageAttributes& stats_attrs_;
 };
 
 // Similar to Renderer holder, this is a very basic RAII-style class
@@ -239,15 +244,19 @@ public:
                 BaseSocketSessionForwarder& ddns_forwarder);
     ~AuthSrvImpl();
 
-    bool processNormalQuery(const IOMessage& io_message, Message& message,
+    bool processNormalQuery(const IOMessage& io_message,
+                            ConstEDNSPtr remote_edns, Message& message,
                             OutputBuffer& buffer,
-                            auto_ptr<TSIGContext> tsig_context);
+                            auto_ptr<TSIGContext> tsig_context,
+                            MessageAttributes& stats_attrs);
     bool processXfrQuery(const IOMessage& io_message, Message& message,
                          OutputBuffer& buffer,
-                         auto_ptr<TSIGContext> tsig_context);
+                         auto_ptr<TSIGContext> tsig_context,
+                         MessageAttributes& stats_attrs);
     bool processNotify(const IOMessage& io_message, Message& message,
                        OutputBuffer& buffer,
-                       auto_ptr<TSIGContext> tsig_context);
+                       auto_ptr<TSIGContext> tsig_context,
+                       MessageAttributes& stats_attrs);
     bool processUpdate(const IOMessage& io_message);
 
     IOService io_service_;
@@ -272,10 +281,6 @@ public:
     /// The data source client list manager
     auth::DataSrcClientsMgr datasrc_clients_mgr_;
 
-    /// Bind the ModuleSpec object in config_session_ with
-    /// isc:config::ModuleSpec::validateStatistics.
-    void registerStatisticsValidator();
-
     /// Socket session forwarder for dynamic update requests
     BaseSocketSessionForwarder& ddns_base_forwarder_;
 
@@ -293,25 +298,17 @@ public:
     ///
     /// \param server The DNSServer as passed to processMessage()
     /// \param message The response as constructed by processMessage()
-    /// \param stats_attrs Query/response attributes for statistics which is
-    ///                    not in \p messsage.
-    ///                    Note: This parameter is modified inside this method
-    ///                          to store whether the answer has been sent and
-    ///                          the response is truncated.
     /// \param done If true, it indicates there is a response.
     ///             this value will be passed to server->resume(bool)
     void resumeServer(isc::asiodns::DNSServer* server,
                       isc::dns::Message& message,
-                      statistics::QRAttributes& stats_attrs,
+                      MessageAttributes& stats_attrs,
                       const bool done);
 
 private:
     bool xfrout_connected_;
     AbstractXfroutClient& xfrout_client_;
 
-    // validateStatistics
-    bool validateStatistics(isc::data::ConstElementPtr data) const;
-
     auth::Query query_;
 };
 
@@ -423,6 +420,7 @@ public:
 void
 makeErrorMessage(MessageRenderer& renderer, Message& message,
                  OutputBuffer& buffer, const Rcode& rcode,
+                 MessageAttributes& stats_attrs,
                  std::auto_ptr<TSIGContext> tsig_context =
                  std::auto_ptr<TSIGContext>())
 {
@@ -455,9 +453,10 @@ makeErrorMessage(MessageRenderer& renderer, Message& message,
 
     message.setRcode(rcode);
 
-    RendererHolder holder(renderer, &buffer);
+    RendererHolder holder(renderer, &buffer, stats_attrs);
     if (tsig_context.get() != NULL) {
         message.toWire(renderer, *tsig_context);
+        stats_attrs.setResponseTSIG(true);
     } else {
         message.toWire(renderer);
     }
@@ -484,7 +483,6 @@ AuthSrv::setXfrinSession(AbstractSession* xfrin_session) {
 void
 AuthSrv::setConfigSession(ModuleCCSession* config_session) {
     impl_->config_session_ = config_session;
-    impl_->registerStatisticsValidator();
 }
 
 ModuleCCSession*
@@ -497,11 +495,11 @@ AuthSrv::processMessage(const IOMessage& io_message, Message& message,
                         OutputBuffer& buffer, DNSServer* server)
 {
     InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());
-    statistics::QRAttributes stats_attrs;
+    MessageAttributes stats_attrs;
 
-    // statistics: check transport carrying the message (IP, transport)
-    stats_attrs.setQueryIPVersion(io_message.getRemoteEndpoint().getFamily());
-    stats_attrs.setQueryTransportProtocol(
+    stats_attrs.setRequestIPVersion(
+        io_message.getRemoteEndpoint().getFamily());
+    stats_attrs.setRequestTransportProtocol(
         io_message.getRemoteEndpoint().getProtocol());
 
     // First, check the header part.  If we fail even for the base header,
@@ -522,19 +520,26 @@ AuthSrv::processMessage(const IOMessage& io_message, Message& message,
         return;
     }
 
+    const Opcode& opcode = message.getOpcode();
+    // Get opcode at this point; for all requests regardless of message body
+    // sanity check.
+    stats_attrs.setRequestOpCode(opcode);
+
     try {
         // Parse the message.
         message.fromWire(request_buffer);
     } catch (const DNSProtocolError& error) {
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PROTOCOL_FAILURE)
                   .arg(error.getRcode().toText()).arg(error.what());
-        makeErrorMessage(impl_->renderer_, message, buffer, error.getRcode());
+        makeErrorMessage(impl_->renderer_, message, buffer, error.getRcode(),
+                         stats_attrs);
         impl_->resumeServer(server, message, stats_attrs, true);
         return;
     } catch (const Exception& ex) {
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PARSE_FAILED)
                   .arg(ex.what());
-        makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL());
+        makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL(),
+                         stats_attrs);
         impl_->resumeServer(server, message, stats_attrs, true);
         return;
     } // other exceptions will be handled at a higher layer.
@@ -558,85 +563,82 @@ AuthSrv::processMessage(const IOMessage& io_message, Message& message,
                                            **impl_->keyring_));
         tsig_error = tsig_context->verify(tsig_record, io_message.getData(),
                                           io_message.getDataSize());
-        // statistics: check TSIG attributes
-        // SIG(0) is currently not implemented in Auth, but it is implemented
-        // in BIND 9. At the point we support it, the code to check if the
-        // signature is valid would be around here.
-        stats_attrs.setQuerySig(true, false,
-                                tsig_error == TSIGError::NOERROR());
+        stats_attrs.setRequestTSIG(true, tsig_error != TSIGError::NOERROR());
     }
 
     if (tsig_error != TSIGError::NOERROR()) {
         makeErrorMessage(impl_->renderer_, message, buffer,
-                         tsig_error.toRcode(), tsig_context);
+                         tsig_error.toRcode(), stats_attrs, tsig_context);
         impl_->resumeServer(server, message, stats_attrs, true);
         return;
     }
 
-    const Opcode opcode = message.getOpcode();
     bool send_answer = true;
     try {
-        // statistics: check EDNS
-        //     note: This can only be reliable after TSIG check succeeds.
+        // note: This can only be reliable after TSIG check succeeds.
         ConstEDNSPtr edns = message.getEDNS();
-        if (edns != NULL) {
-            stats_attrs.setQueryEDNS(true, edns->getVersion() == 0);
-            stats_attrs.setQueryDO(edns->getDNSSECAwareness());
+        if (edns) {
+            stats_attrs.setRequestEDNS0(true);
+            stats_attrs.setRequestDO(edns->getDNSSECAwareness());
         }
 
-        // statistics: check OpCode
-        //     note: This can only be reliable after TSIG check succeeds.
-        stats_attrs.setQueryOpCode(opcode.getCode());
-
+        // note: This can only be reliable after TSIG check succeeds.
         if (opcode == Opcode::NOTIFY()) {
             send_answer = impl_->processNotify(io_message, message, buffer,
-                                               tsig_context);
+                                               tsig_context, stats_attrs);
         } else if (opcode == Opcode::UPDATE()) {
             if (impl_->ddns_forwarder_) {
                 send_answer = impl_->processUpdate(io_message);
             } else {
                 makeErrorMessage(impl_->renderer_, message, buffer,
-                                 Rcode::NOTIMP(), tsig_context);
+                                 Rcode::NOTIMP(), stats_attrs, tsig_context);
             }
         } else if (opcode != Opcode::QUERY()) {
             LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_UNSUPPORTED_OPCODE)
                       .arg(message.getOpcode().toText());
             makeErrorMessage(impl_->renderer_, message, buffer,
-                             Rcode::NOTIMP(), tsig_context);
+                             Rcode::NOTIMP(), stats_attrs, tsig_context);
         } else if (message.getRRCount(Message::SECTION_QUESTION) != 1) {
             makeErrorMessage(impl_->renderer_, message, buffer,
-                             Rcode::FORMERR(), tsig_context);
+                             Rcode::FORMERR(), stats_attrs, tsig_context);
         } else {
             ConstQuestionPtr question = *message.beginQuestion();
             const RRType& qtype = question->getType();
             if (qtype == RRType::AXFR()) {
                 send_answer = impl_->processXfrQuery(io_message, message,
-                                                     buffer, tsig_context);
+                                                     buffer, tsig_context,
+                                                     stats_attrs);
             } else if (qtype == RRType::IXFR()) {
                 send_answer = impl_->processXfrQuery(io_message, message,
-                                                     buffer, tsig_context);
+                                                     buffer, tsig_context,
+                                                     stats_attrs);
             } else {
-                send_answer = impl_->processNormalQuery(io_message, message,
-                                                        buffer, tsig_context);
+                send_answer = impl_->processNormalQuery(io_message, edns,
+                                                        message, buffer,
+                                                        tsig_context,
+                                                        stats_attrs);
             }
         }
     } catch (const std::exception& ex) {
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_FAILURE)
                   .arg(ex.what());
-        makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL());
+        makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL(),
+                         stats_attrs);
     } catch (...) {
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_FAILURE_UNKNOWN);
-        makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL());
+        makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL(),
+                         stats_attrs);
     }
     impl_->resumeServer(server, message, stats_attrs, send_answer);
 }
 
 bool
-AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
+AuthSrvImpl::processNormalQuery(const IOMessage& io_message,
+                                ConstEDNSPtr remote_edns, Message& message,
                                 OutputBuffer& buffer,
-                                auto_ptr<TSIGContext> tsig_context)
+                                auto_ptr<TSIGContext> tsig_context,
+                                MessageAttributes& stats_attrs)
 {
-    ConstEDNSPtr remote_edns = message.getEDNS();
     const bool dnssec_ok = remote_edns && remote_edns->getDNSSECAwareness();
     const uint16_t remote_bufsize = remote_edns ? remote_edns->getUDPSize() :
         Message::DEFAULT_MAX_UDPSIZE;
@@ -666,21 +668,24 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
             const Name& qname = question->getName();
             query_.process(*list, qname, qtype, message, dnssec_ok);
         } else {
-            makeErrorMessage(renderer_, message, buffer, Rcode::REFUSED());
+            makeErrorMessage(renderer_, message, buffer, Rcode::REFUSED(),
+                             stats_attrs);
             return (true);
         }
     } catch (const Exception& ex) {
         LOG_ERROR(auth_logger, AUTH_PROCESS_FAIL).arg(ex.what());
-        makeErrorMessage(renderer_, message, buffer, Rcode::SERVFAIL());
+        makeErrorMessage(renderer_, message, buffer, Rcode::SERVFAIL(),
+                         stats_attrs);
         return (true);
     }
 
-    RendererHolder holder(renderer_, &buffer);
+    RendererHolder holder(renderer_, &buffer, stats_attrs);
     const bool udp_buffer =
         (io_message.getSocket().getProtocol() == IPPROTO_UDP);
     renderer_.setLengthLimit(udp_buffer ? remote_bufsize : 65535);
     if (tsig_context.get() != NULL) {
         message.toWire(renderer_, *tsig_context);
+        stats_attrs.setResponseTSIG(true);
     } else {
         message.toWire(renderer_);
     }
@@ -697,12 +702,13 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
 bool
 AuthSrvImpl::processXfrQuery(const IOMessage& io_message, Message& message,
                              OutputBuffer& buffer,
-                             auto_ptr<TSIGContext> tsig_context)
+                             auto_ptr<TSIGContext> tsig_context,
+                             MessageAttributes& stats_attrs)
 {
     if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_AXFR_UDP);
         makeErrorMessage(renderer_, message, buffer, Rcode::FORMERR(),
-                         tsig_context);
+                         stats_attrs, tsig_context);
         return (true);
     }
 
@@ -728,7 +734,7 @@ AuthSrvImpl::processXfrQuery(const IOMessage& io_message, Message& message,
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_AXFR_PROBLEM)
                   .arg(err.what());
         makeErrorMessage(renderer_, message, buffer, Rcode::SERVFAIL(),
-                         tsig_context);
+                         stats_attrs, tsig_context);
         return (true);
     }
 
@@ -738,7 +744,8 @@ AuthSrvImpl::processXfrQuery(const IOMessage& io_message, Message& message,
 bool
 AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
                            OutputBuffer& buffer,
-                           std::auto_ptr<TSIGContext> tsig_context)
+                           std::auto_ptr<TSIGContext> tsig_context,
+                           MessageAttributes& stats_attrs)
 {
     // The incoming notify must contain exactly one question for SOA of the
     // zone name.
@@ -746,7 +753,7 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NOTIFY_QUESTIONS)
                   .arg(message.getRRCount(Message::SECTION_QUESTION));
         makeErrorMessage(renderer_, message, buffer, Rcode::FORMERR(),
-                         tsig_context);
+                         stats_attrs, tsig_context);
         return (true);
     }
     ConstQuestionPtr question = *message.beginQuestion();
@@ -754,7 +761,7 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NOTIFY_RRTYPE)
                   .arg(question->getType().toText());
         makeErrorMessage(renderer_, message, buffer, Rcode::FORMERR(),
-                         tsig_context);
+                         stats_attrs, tsig_context);
         return (true);
     }
 
@@ -812,9 +819,10 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
     message.setHeaderFlag(Message::HEADERFLAG_AA);
     message.setRcode(Rcode::NOERROR());
 
-    RendererHolder holder(renderer_, &buffer);
+    RendererHolder holder(renderer_, &buffer, stats_attrs);
     if (tsig_context.get() != NULL) {
         message.toWire(renderer_, *tsig_context);
+        stats_attrs.setResponseTSIG(true);
     } else {
         message.toWire(renderer_);
     }
@@ -822,7 +830,8 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
 }
 
 bool
-AuthSrvImpl::processUpdate(const IOMessage& io_message) {
+AuthSrvImpl::processUpdate(const IOMessage& io_message)
+{
     // Push the update request to a separate process via the forwarder.
     // On successful push, the request shouldn't be responded from b10-auth,
     // so we return false.
@@ -831,31 +840,10 @@ AuthSrvImpl::processUpdate(const IOMessage& io_message) {
 }
 
 void
-AuthSrvImpl::registerStatisticsValidator() {
-    counters_.registerStatisticsValidator(
-        boost::bind(&AuthSrvImpl::validateStatistics, this, _1));
-}
-
-bool
-AuthSrvImpl::validateStatistics(isc::data::ConstElementPtr data) const {
-    if (config_session_ == NULL) {
-        return (false);
-    }
-    return (
-        config_session_->getModuleSpec().validateStatistics(
-            data, true));
-}
-
-void
 AuthSrvImpl::resumeServer(DNSServer* server, Message& message,
-                          statistics::QRAttributes& stats_attrs,
+                          MessageAttributes& stats_attrs,
                           const bool done) {
-    if (done) {
-        stats_attrs.answerWasSent();
-        // isTruncated from MessageRenderer
-        stats_attrs.setResponseTruncated(renderer_.isTruncated());
-    }
-    counters_.inc(stats_attrs, message);
+    counters_.inc(stats_attrs, message, done);
     server->resume(done);
 }
 
@@ -875,7 +863,7 @@ AuthSrv::updateConfig(ConstElementPtr new_config) {
 }
 
 ConstElementPtr AuthSrv::getStatistics() const {
-    return (impl_->counters_.getStatistics());
+    return (impl_->counters_.get());
 }
 
 const AddressList&
@@ -905,7 +893,8 @@ void
 AuthSrv::createDDNSForwarder() {
     LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_START_DDNS_FORWARDER);
     impl_->ddns_forwarder_.reset(
-        new SocketSessionForwarderHolder("update", impl_->ddns_base_forwarder_));
+        new SocketSessionForwarderHolder("update",
+                                         impl_->ddns_base_forwarder_));
 }
 
 void

+ 1 - 4
src/bin/auth/auth_srv.h

@@ -41,9 +41,6 @@
 
 #include <boost/shared_ptr.hpp>
 
-#include <map>
-#include <string>
-
 namespace isc {
 namespace util {
 namespace io {
@@ -222,7 +219,7 @@ public:
     /// \brief Returns statistics data
     ///
     /// This function can throw an exception from
-    /// Counters::getStatistics().
+    /// Counters::get().
     ///
     /// \return JSON format statistics data.
     isc::data::ConstElementPtr getStatistics() const;

+ 31 - 31
src/bin/auth/b10-auth.xml

@@ -20,7 +20,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>December 18, 2012</date>
+    <date>February 5, 2013</date>
   </refentryinfo>
 
   <refmeta>
@@ -100,7 +100,7 @@
       <varname>database_file</varname> defines the path to the
       SQLite3 zone file when using the sqlite datasource.
       The default is
-      <filename>/usr/local/var/bind10/zone.sqlite3</filename>.
+      <filename>@@LOCALSTATEDIR@@/bind10/zone.sqlite3</filename>.
     </para>
 
     <para>
@@ -144,15 +144,6 @@
     </para>
 
     <para>
-      <varname>statistics-interval</varname> is the timer interval
-      in seconds for <command>b10-auth</command> to share its
-      statistics information to
-      <citerefentry><refentrytitle>b10-stats</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
-      Statistics updates can be disabled by setting this to 0.
-      The default is 60.
-    </para>
-
-    <para>
       <varname>tcp_recv_timeout</varname> is the timeout used on
       incoming TCP connections, in milliseconds. If the query
       is not sent within this time, the connection is closed.
@@ -191,6 +182,11 @@
     </para>
 
     <para>
+      <command>getstats</command> tells <command>b10-auth</command>
+      to send its statistics data.
+    </para>
+
+    <para>
       <command>shutdown</command> exits <command>b10-auth</command>.
       This has an optional <varname>pid</varname> argument to
       select the process ID to stop.
@@ -230,32 +226,36 @@
       daemon for <quote>Auth</quote> include:
     </para>
 
-    <variablelist>
-
-      <varlistentry>
-        <term>queries.tcp</term>
-        <listitem><simpara>Total count of queries received by the
-          <command>b10-auth</command> server over TCP since startup.
-        </simpara></listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term>queries.udp</term>
-        <listitem><simpara>Total count of queries received by the
-          <command>b10-auth</command> server over UDP since startup.
-        </simpara></listitem>
-      </varlistentry>
-
-    </variablelist>
-
-<!-- TODO: missing stats docs. See ticket #1721 -->
+<!-- ### STATISTICS DATA PLACEHOLDER ### -->
+
+    <note>
+      <para>
+        Opcode of a request message will not be counted if:
+        <itemizedlist>
+          <listitem><para>
+            The request message is too short to parse the message header
+          </para></listitem>
+          <listitem><para>
+            The request message is a response (i.e. QR bit is set)
+          </para></listitem>
+        </itemizedlist>
+      </para>
+
+      <para>
+        Request attributes except for opcode will not be counted if TSIG
+        validation failed as they are not reliable.
+        We always count opcode mainly for compatibility with BIND 9,
+        but remember that if there's any error related to TSIG, some
+        of the counted opcode may not be trustworthy.
+      </para>
+    </note>
 
   </refsect1>
 
   <refsect1>
     <title>FILES</title>
     <para>
-      <filename>/usr/local/var/bind10/zone.sqlite3</filename>
+      <filename>@@LOCALSTATEDIR@@/bind10/zone.sqlite3</filename>
       &mdash; Location for the SQLite3 zone database
       when <emphasis>database_file</emphasis> configuration is not
       defined.

+ 385 - 0
src/bin/auth/gen-statisticsitems.py.pre.in

@@ -0,0 +1,385 @@
+#!@PYTHON@
+
+# Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+"""\
+This script generates spec file, docbook XML and some part of statistics code
+from statistics_msg_items.def.
+"""
+
+import os
+import re
+import sys
+import json
+from xml.etree import ElementTree
+
+item_list = []
+localstatedir = '@@LOCALSTATEDIR@@'
+builddir = '@builddir@'
+srcdir = '@srcdir@'
+pre_suffix = '.pre'
+
+xmldocument_command_name = 'b10-auth'
+
+def need_generate(filepath, prepath, mtime):
+    '''Check if we need to generate the specified file.
+
+    To avoid unnecessary compilation, we skip (re)generating the file when
+    the file already exists and newer than the base file, and definition file
+    specified with mtime.
+    '''
+    if os.path.exists(filepath) and\
+        (os.path.getmtime(filepath) > mtime and
+         os.path.getmtime(filepath) > os.path.getmtime(prepath)):
+        return False
+    return True
+
+def import_definitions():
+    '''Load statsitics items definitions from statistics_msg_items.def.
+
+    statistics_msg_items.def defines a tree of message statistics items.
+    Syntax:
+        Each line describes a node; branch node for subset of counters,
+        leaf node for a counter item.
+        Each fields are separated with one or more field separator (Tab).
+        Field separator in the head of a line is ignored.
+
+        branch node:
+        (item name)\t+(internal branch name)\t+(description of the item)\t+'='
+        leaf node:
+        (item name)\t+(internal item counter name)\t+(description of the item)
+
+        Branch nodes contain leaf nodes and/or branch nodes. The end of
+        a branch node is indicated with ';' as item name (first column).
+
+        Internal branch name and internal item counter name must be unique.
+
+    Returns mtime of statistics_msg_items.def. It will be used to check
+    auto-generated files need to be regenerated.
+    '''
+    global item_list
+
+    items_definition_file = srcdir + os.sep + 'statistics_msg_items.def'
+    with open(items_definition_file, 'r') as item_definition:
+        re_splitter = re.compile('\t+')
+        l = item_list
+        lp = None
+        for line in item_definition.readlines():
+            element = re_splitter.split(line.rstrip())
+            # pop first element if it is empty to skip indent
+            if element[0] == '':
+                element.pop(0)
+
+            # last element is '=': a branch node definition.
+            if element[-1] == '=':
+                l.append({'name': element[0], 'child': [], 'index': element[1],
+                          'description': element[2], 'parent': lp})
+                lp = l
+                l = l[-1]['child']
+            # last element is ';': end of a branch node.
+            elif element[-1] == ';':
+                l = lp
+                lp = l[-1]['parent']
+            # otherwise, a leaf node definition.
+            else:
+                l.append({'name': element[0], 'child': None,
+                          'index': element[1], 'description': element[2],
+                          'parent': lp})
+    return os.path.getmtime(items_definition_file)
+
+def generate_specfile(specfile, def_mtime):
+    '''Generate spec in specfile from skeleton (specfille+'.pre').
+    If the specfile is newer than both skeleton and def_mtime, file generation
+    will be skipped.
+
+    This method reads the content of skeleton and appends statistics items
+    definition into { "module_spec": { "statistics": } }.
+    LOCALSTATEDIR is also expanded.
+
+    Returns nothing.
+    '''
+
+    def convert_list(items, prefix=''):
+        spec_list = []
+        default_map = {}
+        for item in items:
+            full_item_name = prefix + item['name']
+            if item['child'] is None:
+                default_map[item['name']] = 0
+                spec_list.append({
+                        'item_name': item['name'],
+                        'item_optional': False,
+                        'item_type': 'integer',
+                        'item_default': 0,
+                        'item_title': full_item_name,
+                        'item_description': item['description'],
+                    })
+            else:
+                child_spec_list, child_default_map = \
+                    convert_list(item['child'], full_item_name + '.')
+                spec_list.append({
+                        'item_name': item['name'],
+                        'item_type': 'map',
+                        'item_optional': False,
+                        'item_title': full_item_name,
+                        'item_description': item['description'],
+                        'item_default': child_default_map,
+                        'map_item_spec': child_spec_list,
+                    })
+                default_map[item['name']] = child_default_map
+        return spec_list, default_map
+
+    item_spec_list, item_default_map = convert_list(item_list)
+
+    statistics_spec_list = [{
+        'item_name': 'zones',
+        'item_type': 'named_set',
+        'item_optional': False,
+        'item_title': 'Zone statistics',
+        'item_description':
+                'Zone statistics items. ' +
+                "Items for all zones are stored in '_SERVER_'.",
+        'item_default': { '_SERVER_': item_default_map },
+        'named_set_item_spec': {
+            'item_name': 'zone',
+            'item_type': 'map',
+            'item_optional': False,
+            'item_default': {},
+            'map_item_spec': item_spec_list,
+            },
+        }]
+
+    if need_generate(builddir+os.sep+specfile,
+                     builddir+os.sep+specfile+pre_suffix, def_mtime):
+        with open(builddir+os.sep+specfile+pre_suffix, 'r') as stats_pre:
+            # split LOCALSTATEDIR to avoid substitution
+            stats_pre_json = \
+                json.loads(stats_pre.read().replace('@@LOCAL'+'STATEDIR@@',
+                                                    localstatedir))
+        stats_pre_json['module_spec']['statistics'] = statistics_spec_list
+        statistics_spec_json = json.dumps(stats_pre_json, sort_keys=True,
+                                          indent=2)
+        with open(builddir+os.sep+specfile, 'w') as stats_spec:
+            stats_spec.write(statistics_spec_json)
+    else:
+        print('skip generating ' + specfile)
+    return
+
+def generate_docfile(docfile, def_mtime):
+    '''Generate docbook XML in docfile from skeleton (docfile+'.pre').
+    If the docfile is newer than both skeleton and def_mtime, file generation
+    will be skipped.
+
+    This method reads the content of skeleton and replaces
+    <!-- ### STATISTICS DATA PLACEHOLDER ### --> with statistics items
+    definition. LOCALSTATEDIR is also expanded.
+
+    Returns nothing.
+    '''
+    def convert_list(items, tree, prefix=''):
+        '''
+        Build XML tree from items.
+            <varlistentry>
+              <term>##item_full_name##</term>
+              <listitem><simpara>##item_description##</simpara></listitem>
+            </varlistentry>
+        xmldocument_command_name in item_description is put inside <command>
+        element.
+        '''
+        for item in items:
+            full_item_name = prefix + item['name']
+            if item['child'] is None:
+                # the item is a leaf node: build varlistentry
+                child_element = ElementTree.SubElement(tree, 'varlistentry')
+                term = ElementTree.SubElement(child_element, 'term')
+                term.text = full_item_name
+                list_item = ElementTree.SubElement(child_element, 'listitem')
+                sim_para = ElementTree.SubElement(list_item, 'simpara')
+                sim_para.text = ''
+                prev = None
+                # put xmldocument_command_name in <command> node
+                for word in item['description'].split():
+                    if word == xmldocument_command_name:
+                        command = ElementTree.SubElement(sim_para, 'command')
+                        command.text = word
+                        # at this point command.tail is None
+                        # append a space as trailing text for the next word
+                        # so it can be concatenated with trailing words
+                        command.tail = ' '
+                        prev = command
+                    else:
+                        if prev is None:
+                            sim_para.text += word + ' '
+                        else:
+                            prev.tail += word + ' '
+                # remove trailing whitespaces at the end of the description
+                if prev is None:
+                    sim_para.text = sim_para.text.rstrip()
+                else:
+                    prev.tail = prev.tail.rstrip()
+            else:
+                # the item is a branch node: call myself for child nodes
+                convert_list(item['child'], tree, full_item_name + '.')
+        return
+
+    if need_generate(builddir+os.sep+docfile,
+                     srcdir+os.sep+docfile+pre_suffix, def_mtime):
+        with open(srcdir+os.sep+docfile+pre_suffix, 'r') as doc_pre:
+            # split LOCALSTATEDIR to avoid substitution
+            doc_pre_xml = doc_pre.read().replace('@@LOCAL'+'STATEDIR@@',
+                                                 localstatedir)
+
+        variable_tree = ElementTree.Element('variablelist')
+        convert_list(item_list, variable_tree)
+        pretty_xml = ElementTree.tostring(variable_tree)
+        if not isinstance(pretty_xml, str):
+            pretty_xml = pretty_xml.decode('utf-8')
+        # put newline around <variablelist> and <varlistentry> element
+        pretty_xml = \
+            re.sub(r'(</?var(?:iablelist|listentry)>)', r'\1\n', pretty_xml)
+        # indent <term> and <listitem>
+        pretty_xml = \
+            re.sub(r'(<(?:term|listitem)>)', r'  \1', pretty_xml)
+        # put newline after </term> and </listitem>
+        pretty_xml = \
+            re.sub(r'(</(?:term|listitem)>)', r'\1\n', pretty_xml)
+
+        with open(builddir+os.sep+docfile, 'w') as doc:
+            doc.write(doc_pre_xml.replace(
+                '<!-- ### STATISTICS DATA PLACEHOLDER ### -->',
+                pretty_xml))
+    else:
+        print('skip generating ' + docfile)
+    return
+
+def generate_cxx(itemsfile, ccfile, utfile, def_mtime):
+    '''Generate some part of statistics code in itemsfile, ccfile, utfile from
+    skeleton (itemsfile+'.pre', ccfile+'.pre', utfile+'.pre').
+    If the file is newer than both skeleton and def_mtime, file generation
+    will be skipped.
+
+    This method reads the content of skeleton and replaces
+    // ### STATISTICS ITEMS DEFINITION ### with statistics items definition in
+    ccfile and utfile,
+    // ### STATISTICS ITEMS DECLARATION ### with statistics items declaration
+    in itemsfile.
+
+    Returns nothing.
+    '''
+    msg_counter_types = ['enum MSGCounterType {']
+    item_names =  ['// using -1 as counter_id to state it is not a '
+                   + 'counter item']
+    item_names += ['const int NOT_ITEM = -1;', '']
+
+    def convert_list(items, item_head, msg_counter_types, item_names):
+        '''Convert item tree to a set of C++ code fragment in given lists.
+
+        This method recursively builds two lists:
+        - msg_counter_types consists of strings for all leaf items, each
+          defines one enum element with a comment, e.g.
+          COUNTER_ITEM, ///< item's descriptin
+        - item_names consists of tuples of three elements, depending on
+          whether it's a leaf element (no child from it) or not:
+          (leaf)   ( "item_name", NULL, COUNTER_ITEM )
+          (branch) ( "item_name", CHILD_NAME, NOT_ITEM )
+
+        Each single call to this function builds a C++ structure beginning
+        with the given item_head, which is a string that reads like
+        'const struct CounterSpec some_counters[] = {'
+        followed by the item_names tuples corresponding to that level.
+        If some of the items of this level have a child, recursive calls
+        to this function extends msg_counter_types and item_names.
+        item_names for this level will be concatenated at the end end of
+        the given item_names.
+
+        '''
+        item_names_current = [item_head]
+        for item in items:
+            item_spec = '    { "' + item['name'] + '", '
+            if item['child'] is None:
+                item_spec += 'NULL, ' + item['index']
+                msg_counter_types.append('    ' + item['index'] + ',    ' +
+                                         '///< ' + item['description'])
+            else:
+                item_spec += item['index'] + ', NOT_ITEM'
+                child_head = 'const struct CounterSpec ' + \
+                    item['index'] + '[] = {'
+                convert_list(item['child'], child_head,
+                             msg_counter_types, item_names)
+            item_names_current.append(item_spec + ' },')
+
+        item_names_current.append('    { NULL, NULL, NOT_ITEM }\n};')
+        item_names.extend(item_names_current)
+
+    convert_list(item_list, 'const struct CounterSpec msg_counter_tree[] = {',
+                 msg_counter_types, item_names)
+    msg_counter_types.extend([
+            '    // End of counter types',
+            '    MSG_COUNTER_TYPES  ///< The number of defined counters',
+            '};'])
+
+    item_decls = '\n'.join(msg_counter_types)
+    item_defs = '\n'.join(item_names)
+
+    if need_generate(builddir+os.sep+itemsfile,
+                     srcdir+os.sep+itemsfile+pre_suffix, def_mtime):
+        with open(srcdir+os.sep+itemsfile+pre_suffix, 'r') \
+            as statistics_items_h_pre:
+            items_pre = statistics_items_h_pre.read()
+
+        with open(builddir+os.sep+itemsfile, 'w') as statistics_items_h:
+            statistics_items_h.write(items_pre.replace(
+                '// ### STATISTICS ITEMS DECLARATION ###', item_decls))
+    else:
+        print('skip generating ' + itemsfile)
+
+    if need_generate(builddir+os.sep+ccfile,
+                     srcdir+os.sep+ccfile+pre_suffix, def_mtime):
+        with open(srcdir+os.sep+ccfile+pre_suffix, 'r') as statistics_cc_pre:
+            items_pre = statistics_cc_pre.read()
+
+        with open(builddir+os.sep+ccfile, 'w') as statistics_cc:
+            statistics_cc.write(items_pre.replace(
+                '// ### STATISTICS ITEMS DEFINITION ###', item_defs))
+    else:
+        print('skip generating ' + ccfile)
+
+    if need_generate(builddir+os.sep+utfile,
+                     srcdir+os.sep+utfile+pre_suffix, def_mtime):
+        with open(srcdir+os.sep+utfile+pre_suffix, 'r') \
+            as statistics_ut_cc_pre:
+            items_pre = statistics_ut_cc_pre.read()
+
+        with open(builddir+os.sep+utfile, 'w') as statistics_ut_cc:
+            statistics_ut_cc.write(items_pre.replace(
+                '// ### STATISTICS ITEMS DEFINITION ###', item_defs))
+    else:
+        print('skip generating ' + utfile)
+
+    return
+
+if __name__ == "__main__":
+    try:
+        def_mtime = import_definitions()
+        generate_specfile('auth.spec', def_mtime)
+        generate_docfile('b10-auth.xml', def_mtime)
+        generate_cxx('statistics_items.h',
+                     'statistics.cc',
+                     'tests'+os.sep+'statistics_unittest.cc',
+                     def_mtime)
+    except:
+        sys.stderr.write('File generation failed due to exception: %s\n' %
+                         sys.exc_info()[1])
+        exit(1)

+ 0 - 284
src/bin/auth/statistics.cc

@@ -1,284 +0,0 @@
-// Copyright (C) 2010  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 <auth/statistics.h>
-#include <auth/statistics_items.h>
-#include <auth/auth_log.h>
-
-#include <dns/opcode.h>
-#include <dns/rcode.h>
-
-#include <cc/data.h>
-#include <cc/session.h>
-
-#include <statistics/counter.h>
-#include <statistics/counter_dict.h>
-
-#include <algorithm>
-#include <cctype>
-#include <cassert>
-#include <string>
-#include <sstream>
-#include <iostream>
-
-#include <boost/noncopyable.hpp>
-
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netdb.h>
-
-using namespace isc::dns;
-using namespace isc::auth;
-using namespace isc::statistics;
-
-namespace isc {
-namespace auth {
-namespace statistics {
-
-// TODO: Make use of wrappers like isc::dns::Opcode
-// for counter item type.
-
-class CountersImpl : boost::noncopyable {
-public:
-    CountersImpl();
-    ~CountersImpl();
-    void inc(const QRAttributes& qrattrs, const Message& response);
-    isc::data::ConstElementPtr getStatistics() const;
-    void registerStatisticsValidator(Counters::validator_type validator);
-private:
-    // counter for query/response
-    Counter server_qr_counter_;
-    // set of counters for zones
-    CounterDictionary zone_qr_counters_;
-    // validator
-    Counters::validator_type validator_;
-};
-
-CountersImpl::CountersImpl() :
-    // size of server_qr_counter_, zone_qr_counters_: QR_COUNTER_TYPES
-    server_qr_counter_(QR_COUNTER_TYPES),
-    zone_qr_counters_(QR_COUNTER_TYPES),
-    validator_()
-{}
-
-CountersImpl::~CountersImpl()
-{}
-
-void
-CountersImpl::inc(const QRAttributes& qrattrs, const Message& response) {
-    // protocols carrying request
-    if (qrattrs.req_ip_version_ == AF_INET) {
-        server_qr_counter_.inc(QR_REQUEST_IPV4);
-    } else if (qrattrs.req_ip_version_ == AF_INET6) {
-        server_qr_counter_.inc(QR_REQUEST_IPV6);
-    }
-    if (qrattrs.req_transport_protocol_ == IPPROTO_UDP) {
-        server_qr_counter_.inc(QR_REQUEST_UDP);
-    } else if (qrattrs.req_transport_protocol_ == IPPROTO_TCP) {
-        server_qr_counter_.inc(QR_REQUEST_TCP);
-    }
-
-    // query TSIG
-    if (qrattrs.req_is_tsig_) {
-        server_qr_counter_.inc(QR_REQUEST_TSIG);
-    }
-    if (qrattrs.req_is_sig0_) {
-        server_qr_counter_.inc(QR_REQUEST_SIG0);
-    }
-    if (qrattrs.req_is_badsig_) {
-        server_qr_counter_.inc(QR_REQUEST_BADSIG);
-        // If signature validation is failed, no other attributes are reliable
-        return;
-    }
-
-    // query EDNS
-    if (qrattrs.req_is_edns_0_) {
-        server_qr_counter_.inc(QR_REQUEST_EDNS0);
-    }
-    if (qrattrs.req_is_edns_badver_) {
-        server_qr_counter_.inc(QR_REQUEST_BADEDNSVER);
-    }
-
-    // query DNSSEC
-    if (qrattrs.req_is_dnssec_ok_) {
-        server_qr_counter_.inc(QR_REQUEST_DNSSEC_OK);
-    }
-
-    // QTYPE
-    unsigned int qtype_type = QR_QTYPE_OTHER;
-    const QuestionIterator qiter = response.beginQuestion();
-    if (qiter != response.endQuestion()) {
-        // get the first and only question section and
-        // get the qtype code
-        const unsigned int qtype = (*qiter)->getType().getCode();
-        if (qtype < 258) {
-            // qtype 0..257: lookup qtype-countertype table
-            qtype_type = QRQTypeToQRCounterType[qtype];
-        } else if (qtype < 32768) {
-            // qtype 258..32767: (Unassigned)
-            qtype_type = QR_QTYPE_OTHER;
-        } else if (qtype < 32770) {
-            // qtype 32768..32769: TA and DLV
-            qtype_type = QR_QTYPE_TA + (qtype - 32768);
-        } else {
-            // qtype 32770..65535: (Unassigned, Private use, Reserved)
-            qtype_type = QR_QTYPE_OTHER;
-        }
-    }
-    server_qr_counter_.inc(qtype_type);
-    // OPCODE
-    server_qr_counter_.inc(QROpCodeToQRCounterType[qrattrs.req_opcode_]);
-
-    // response
-    if (qrattrs.answer_sent_) {
-        // responded
-        server_qr_counter_.inc(QR_RESPONSE);
-
-        // response truncated
-        if (qrattrs.res_is_truncated_) {
-            server_qr_counter_.inc(QR_RESPONSE_TRUNCATED);
-        }
-
-        // response EDNS
-        ConstEDNSPtr response_edns = response.getEDNS();
-        if (response_edns != NULL && response_edns->getVersion() == 0) {
-            server_qr_counter_.inc(QR_RESPONSE_EDNS0);
-        }
-
-        // response TSIG
-        if (qrattrs.req_is_tsig_) {
-            // assume response is TSIG signed if request is TSIG signed
-            server_qr_counter_.inc(QR_RESPONSE_TSIG);
-        }
-
-        // response SIG(0) is currently not implemented
-
-        // RCODE
-        const unsigned int rcode = response.getRcode().getCode();
-        unsigned int rcode_type = QR_RCODE_OTHER;
-        if (rcode < 23) {
-            // rcode 0..22: lookup rcode-countertype table
-            rcode_type = QRRCodeToQRCounterType[rcode];
-        } else {
-            // opcode larger than 22 is reserved or unassigned
-            rcode_type = QR_RCODE_OTHER;
-        }
-        server_qr_counter_.inc(rcode_type);
-
-        // compound attributes
-        const unsigned int answer_rrs =
-            response.getRRCount(Message::SECTION_ANSWER);
-        const bool is_aa_set = response.getHeaderFlag(Message::HEADERFLAG_AA);
-
-        if (is_aa_set) {
-            // QryAuthAns
-            server_qr_counter_.inc(QR_QRYAUTHANS);
-        } else {
-            // QryNoAuthAns
-            server_qr_counter_.inc(QR_QRYNOAUTHANS);
-        }
-
-        if (rcode == Rcode::NOERROR_CODE) {
-            if (answer_rrs > 0) {
-                // QrySuccess
-                server_qr_counter_.inc(QR_QRYSUCCESS);
-            } else {
-                if (is_aa_set) {
-                    // QryNxrrset
-                    server_qr_counter_.inc(QR_QRYNXRRSET);
-                } else {
-                    // QryReferral
-                    server_qr_counter_.inc(QR_QRYREFERRAL);
-                }
-            }
-        } else if (rcode == Rcode::REFUSED_CODE) {
-            // AuthRej
-            server_qr_counter_.inc(QR_QRYREJECT);
-        }
-    }
-}
-
-isc::data::ConstElementPtr
-CountersImpl::getStatistics() const {
-    std::stringstream statistics_string;
-    statistics_string << "{ \"queries.udp\": "
-                      << server_qr_counter_.get(QR_REQUEST_UDP)
-                      << ", \"queries.tcp\": "
-                      << server_qr_counter_.get(QR_REQUEST_TCP);
-    // Insert non 0 Opcode counters.
-    for (int i = QR_OPCODE_QUERY; i <= QR_OPCODE_OTHER; ++i) {
-        const Counter::Type counter = server_qr_counter_.get(i);
-        if (counter != 0) {
-            statistics_string << ", \"" << "opcode." <<
-                                 QRCounterOpcode[i - QR_OPCODE_QUERY].name <<
-                                 "\": " << counter;
-        }
-    }
-    // Insert non 0 Rcode counters.
-    for (int i = QR_RCODE_NOERROR; i <= QR_RCODE_OTHER; ++i) {
-        const Counter::Type counter = server_qr_counter_.get(i);
-        if (counter != 0) {
-            statistics_string << ", \"" << "rcode." <<
-                                 QRCounterRcode[i - QR_RCODE_NOERROR].name <<
-                                 "\": " << counter;
-        }
-    }
-    statistics_string << "}";
-
-    isc::data::ConstElementPtr statistics_element =
-        isc::data::Element::fromJSON(statistics_string);
-    // validate the statistics data before send
-    if (validator_) {
-        if (!validator_(statistics_element)) {
-            LOG_ERROR(auth_logger, AUTH_INVALID_STATISTICS_DATA);
-            return (isc::data::ElementPtr());
-        }
-    }
-    return (statistics_element);
-}
-
-void
-CountersImpl::registerStatisticsValidator
-    (Counters::validator_type validator)
-{
-    validator_ = validator;
-}
-
-Counters::Counters() : impl_(new CountersImpl())
-{}
-
-Counters::~Counters() {}
-
-void
-Counters::inc(const QRAttributes& qrattrs, const Message& response) {
-    impl_->inc(qrattrs, response);
-}
-
-isc::data::ConstElementPtr
-Counters::getStatistics() const {
-    return (impl_->getStatistics());
-}
-
-void
-Counters::registerStatisticsValidator
-    (Counters::validator_type validator) const
-{
-    return (impl_->registerStatisticsValidator(validator));
-}
-
-} // namespace statistics
-} // namespace auth
-} // namespace isc

+ 277 - 0
src/bin/auth/statistics.cc.pre

@@ -0,0 +1,277 @@
+// Copyright (C) 2010  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 <auth/statistics.h>
+#include <auth/statistics_items.h>
+#include <auth/auth_log.h>
+
+#include <cc/data.h>
+
+#include <dns/message.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
+
+#include <statistics/counter.h>
+
+#include <boost/optional.hpp>
+
+using namespace isc::dns;
+using namespace isc::auth;
+using namespace isc::statistics;
+using namespace isc::auth::statistics;
+
+namespace {
+
+/// \brief Fill isc::data::ElementPtr with given counter.
+/// \param counter Counter which stores values to fill
+/// \param type_tree CounterSpec corresponding to counter for building item
+///                  name
+/// \param trees isc::data::ElementPtr to be filled in; caller has ownership of
+///              isc::data::ElementPtr
+void
+fillNodes(const Counter& counter,
+          const struct isc::auth::statistics::CounterSpec type_tree[],
+          isc::data::ElementPtr& trees)
+{
+    using namespace isc::data;
+
+    for (int i = 0; type_tree[i].name != NULL; ++i) {
+        if (type_tree[i].sub_counters != NULL) {
+            isc::data::ElementPtr sub_counters = Element::createMap();
+            trees->set(type_tree[i].name, sub_counters);
+            fillNodes(counter, type_tree[i].sub_counters, sub_counters);
+        } else {
+            trees->set(type_tree[i].name,
+                       Element::create(static_cast<long int>(
+                           counter.get(type_tree[i].counter_id)))
+                       );
+        }
+    }
+}
+
+// ### STATISTICS ITEMS DEFINITION ###
+
+} // anonymous namespace
+
+namespace isc {
+namespace auth {
+namespace statistics {
+
+// Note: opcode in this array must be start with 0 and be sequential
+const int opcode_to_msgcounter[] = {
+    MSG_OPCODE_QUERY,    // Opcode =  0: Query
+    MSG_OPCODE_IQUERY,   // Opcode =  1: IQuery
+    MSG_OPCODE_STATUS,   // Opcode =  2: Status
+    MSG_OPCODE_OTHER,    // Opcode =  3: (Unassigned)
+    MSG_OPCODE_NOTIFY,   // Opcode =  4: Notify
+    MSG_OPCODE_UPDATE,   // Opcode =  5: Update
+    MSG_OPCODE_OTHER,    // Opcode =  6: (Unassigned)
+    MSG_OPCODE_OTHER,    // Opcode =  7: (Unassigned)
+    MSG_OPCODE_OTHER,    // Opcode =  8: (Unassigned)
+    MSG_OPCODE_OTHER,    // Opcode =  9: (Unassigned)
+    MSG_OPCODE_OTHER,    // Opcode = 10: (Unassigned)
+    MSG_OPCODE_OTHER,    // Opcode = 11: (Unassigned)
+    MSG_OPCODE_OTHER,    // Opcode = 12: (Unassigned)
+    MSG_OPCODE_OTHER,    // Opcode = 13: (Unassigned)
+    MSG_OPCODE_OTHER,    // Opcode = 14: (Unassigned)
+    MSG_OPCODE_OTHER     // Opcode = 15: (Unassigned)
+};
+const size_t num_opcode_to_msgcounter =
+    sizeof(opcode_to_msgcounter) / sizeof(opcode_to_msgcounter[0]);
+
+// Note: rcode in this array must be start with 0 and be sequential
+const int rcode_to_msgcounter[] = {
+    MSG_RCODE_NOERROR,       // Rcode =  0: NoError
+    MSG_RCODE_FORMERR,       // Rcode =  1: FormErr
+    MSG_RCODE_SERVFAIL,      // Rcode =  2: ServFail
+    MSG_RCODE_NXDOMAIN,      // Rcode =  3: NXDomain
+    MSG_RCODE_NOTIMP,        // Rcode =  4: NotImp
+    MSG_RCODE_REFUSED,       // Rcode =  5: Refused
+    MSG_RCODE_YXDOMAIN,      // Rcode =  6: YXDomain
+    MSG_RCODE_YXRRSET,       // Rcode =  7: YXRRSet
+    MSG_RCODE_NXRRSET,       // Rcode =  8: NXRRSet
+    MSG_RCODE_NOTAUTH,       // Rcode =  9: NotAuth
+    MSG_RCODE_NOTZONE,       // Rcode = 10: NotZone
+    MSG_RCODE_OTHER,         // Rcode = 11: (Unassigned)
+    MSG_RCODE_OTHER,         // Rcode = 12: (Unassigned)
+    MSG_RCODE_OTHER,         // Rcode = 13: (Unassigned)
+    MSG_RCODE_OTHER,         // Rcode = 14: (Unassigned)
+    MSG_RCODE_OTHER,         // Rcode = 15: (Unassigned)
+    MSG_RCODE_BADVERS        // Rcode = 16: BADVERS
+};
+const size_t num_rcode_to_msgcounter =
+    sizeof(rcode_to_msgcounter) / sizeof(rcode_to_msgcounter[0]);
+
+Counters::Counters() :
+    server_msg_counter_(MSG_COUNTER_TYPES)
+{}
+
+void
+Counters::incRequest(const MessageAttributes& msgattrs) {
+    // protocols carrying request
+    if (msgattrs.getRequestIPVersion() == AF_INET) {
+        server_msg_counter_.inc(MSG_REQUEST_IPV4);
+    } else if (msgattrs.getRequestIPVersion() == AF_INET6) {
+        server_msg_counter_.inc(MSG_REQUEST_IPV6);
+    }
+    if (msgattrs.getRequestTransportProtocol() == IPPROTO_UDP) {
+        server_msg_counter_.inc(MSG_REQUEST_UDP);
+    } else if (msgattrs.getRequestTransportProtocol() == IPPROTO_TCP) {
+        server_msg_counter_.inc(MSG_REQUEST_TCP);
+    }
+
+    // Opcode
+    const boost::optional<isc::dns::Opcode>& opcode =
+        msgattrs.getRequestOpCode();
+    // Increment opcode counter only if the opcode exists; opcode can be empty
+    // if a short message which does not contain DNS header is received, or
+    // a response message (i.e. QR bit is set) is received.
+    if (opcode) {
+        server_msg_counter_.inc(opcode_to_msgcounter[opcode.get().getCode()]);
+    }
+
+    // TSIG
+    if (msgattrs.requestHasTSIG()) {
+        server_msg_counter_.inc(MSG_REQUEST_TSIG);
+    }
+    if (msgattrs.requestHasBadSig()) {
+        server_msg_counter_.inc(MSG_REQUEST_BADSIG);
+        // If signature validation failed, no other request attributes (except
+        // for opcode) are reliable. Skip processing of the rest of request
+        // counters.
+        return;
+    }
+
+    // EDNS0
+    if (msgattrs.requestHasEDNS0()) {
+        server_msg_counter_.inc(MSG_REQUEST_EDNS0);
+    }
+
+    // DNSSEC OK bit
+    if (msgattrs.requestHasDO()) {
+        server_msg_counter_.inc(MSG_REQUEST_DNSSEC_OK);
+    }
+}
+
+void
+Counters::incResponse(const MessageAttributes& msgattrs,
+                      const Message& response)
+{
+    // responded
+    server_msg_counter_.inc(MSG_RESPONSE);
+
+    // response truncated
+    if (msgattrs.responseIsTruncated()) {
+        server_msg_counter_.inc(MSG_RESPONSE_TRUNCATED);
+    }
+
+    // response EDNS
+    ConstEDNSPtr response_edns = response.getEDNS();
+    if (response_edns && response_edns->getVersion() == 0) {
+        server_msg_counter_.inc(MSG_RESPONSE_EDNS0);
+    }
+
+    // response TSIG
+    if (msgattrs.responseHasTSIG()) {
+        server_msg_counter_.inc(MSG_RESPONSE_TSIG);
+    }
+
+    // response SIG(0) is currently not implemented
+
+    // RCODE
+    const unsigned int rcode = response.getRcode().getCode();
+    const unsigned int rcode_type =
+        rcode < num_rcode_to_msgcounter ?
+        rcode_to_msgcounter[rcode] : MSG_RCODE_OTHER;
+    server_msg_counter_.inc(rcode_type);
+    // Unsupported EDNS version
+    if (rcode == Rcode::BADVERS().getCode()) {
+        server_msg_counter_.inc(MSG_REQUEST_BADEDNSVER);
+    }
+
+    const boost::optional<isc::dns::Opcode>& opcode =
+        msgattrs.getRequestOpCode();
+    if (!opcode) {
+        isc_throw(isc::Unexpected, "Opcode of the request is empty while it is"
+                                   " responded");
+    }
+    if (!msgattrs.requestHasBadSig() && opcode.get() == Opcode::QUERY()) {
+        // compound attributes
+        const unsigned int answer_rrs =
+            response.getRRCount(Message::SECTION_ANSWER);
+        const bool is_aa_set =
+            response.getHeaderFlag(Message::HEADERFLAG_AA);
+
+        if (is_aa_set) {
+            // QryAuthAns
+            server_msg_counter_.inc(MSG_QRYAUTHANS);
+        } else {
+            // QryNoAuthAns
+            server_msg_counter_.inc(MSG_QRYNOAUTHANS);
+        }
+
+        if (rcode == Rcode::NOERROR_CODE) {
+            if (answer_rrs > 0) {
+                // QrySuccess
+                server_msg_counter_.inc(MSG_QRYSUCCESS);
+            } else {
+                if (is_aa_set) {
+                    // QryNxrrset
+                    server_msg_counter_.inc(MSG_QRYNXRRSET);
+                } else {
+                    // QryReferral
+                    server_msg_counter_.inc(MSG_QRYREFERRAL);
+                }
+            }
+        } else if (rcode == Rcode::REFUSED_CODE) {
+            if (!response.getHeaderFlag(Message::HEADERFLAG_RD)) {
+                // AuthRej
+                server_msg_counter_.inc(MSG_QRYREJECT);
+            }
+        }
+    }
+}
+
+void
+Counters::inc(const MessageAttributes& msgattrs, const Message& response,
+              const bool done)
+{
+    // increment request counters
+    incRequest(msgattrs);
+
+    if (done) {
+        // increment response counters if answer was sent
+        incResponse(msgattrs, response);
+    }
+}
+
+Counters::ConstItemTreePtr
+Counters::get() const {
+    using namespace isc::data;
+
+    isc::data::ElementPtr item_tree = Element::createMap();
+
+    isc::data::ElementPtr zones = Element::createMap();
+    item_tree->set("zones", zones);
+
+    isc::data::ElementPtr server = Element::createMap();
+    fillNodes(server_msg_counter_, msg_counter_tree, server);
+    zones->set("_SERVER_", server);
+
+    return (item_tree);
+}
+
+} // namespace statistics
+} // namespace auth
+} // namespace isc

+ 223 - 142
src/bin/auth/statistics.h

@@ -15,208 +15,289 @@
 #ifndef STATISTICS_H
 #define STATISTICS_H 1
 
-#include <cc/session.h>
 #include <cc/data.h>
 
 #include <dns/message.h>
+#include <dns/opcode.h>
 
-#include <string>
+#include <statistics/counter.h>
+
+#include <boost/noncopyable.hpp>
+#include <boost/optional.hpp>
+
+#include <bitset>
 
 #include <stdint.h>
-#include <boost/scoped_ptr.hpp>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
 
 namespace isc {
 namespace auth {
 namespace statistics {
 
-class CountersImpl;
-
-class QRAttributes {
-/// \brief Query/Response attributes for statistics.
+/// \brief DNS Message attributes for statistics.
 ///
-/// This class holds some attributes related to a query/response
+/// This class holds some attributes related to a DNS message
 /// for statistics data collection.
-///
-/// This class does not have getter methods since it exposes private members
-/// to \c CountersImpl directly.
-friend class CountersImpl;
+class MessageAttributes {
+public:
+    /// \brief IP version of DNS message.
+    enum IPVersionType {
+        IP_VERSION_UNSPEC,          // (initial value; internal use only)
+        IP_VERSION_IPV4,            ///< IPv4 message
+        IP_VERSION_IPV6             ///< IPv6 message
+    };
+
+    /// \brief Transport protocol of DNS message.
+    enum TransportProtocolType {
+        TRANSPORT_UNSPEC,           // (initial value; internal use only)
+        TRANSPORT_UDP,              ///< UDP message
+        TRANSPORT_TCP               ///< TCP message
+    };
 private:
     // request attributes
-    int req_ip_version_;            // IP version
+    int req_address_family_;        // IP version
     int req_transport_protocol_;    // Transport layer protocol
-    int req_opcode_;                // OpCode
-    bool req_is_edns_0_;            // EDNS ver.0
-    bool req_is_edns_badver_;       // other EDNS version
-    bool req_is_dnssec_ok_;         // DO bit
-    bool req_is_tsig_;              // signed with valid TSIG
-    bool req_is_sig0_;              // signed with valid SIG(0)
-    bool req_is_badsig_;            // signed but bad signature
-    // zone origin
-    std::string zone_origin_;       // zone origin
-    // response attributes
-    bool answer_sent_;              // DNS message has sent
-    bool res_is_truncated_;         // DNS message is truncated
+    boost::optional<isc::dns::Opcode> req_opcode_;  // OpCode
+    enum BitAttributes {
+        REQ_WITH_EDNS_0,            // request with EDNS ver.0
+        REQ_WITH_DNSSEC_OK,         // DNSSEC OK (DO) bit is set in request
+        REQ_TSIG_SIGNED,            // request is signed with valid TSIG
+        REQ_BADSIG,                 // request is signed but bad signature
+        RES_IS_TRUNCATED,           // response is truncated
+        RES_TSIG_SIGNED,            // response is signed with TSIG
+        BIT_ATTRIBUTES_TYPES
+    };
+    std::bitset<BIT_ATTRIBUTES_TYPES> bit_attributes_;
 public:
-    /// The constructor.
+    /// \brief The constructor.
     ///
-    /// This constructor is mostly exception free. But it may still throw
-    /// a standard exception if memory allocation fails inside the method.
-    ///
-    QRAttributes() {
-        reset();
-    };
+    /// \throw None
+    MessageAttributes() : req_address_family_(0), req_transport_protocol_(0)
+    {}
 
-    /// The destructor.
+    /// \brief Return opcode of the request.
     ///
-    /// This method never throws an exception.
+    /// \return opcode of the request wrapped with boost::optional; it's
+    ///         converted to false if Opcode hasn't been set.
+    /// \throw None
+    const boost::optional<isc::dns::Opcode>& getRequestOpCode() const {
+        return (req_opcode_);
+    }
+
+    /// \brief Set opcode of the request.
     ///
-    ~QRAttributes() {};
-    /// \brief Set query opcode.
+    /// \param opcode Opcode of the request
     /// \throw None
-    void setQueryOpCode(const int opcode) {
+    void setRequestOpCode(const isc::dns::Opcode& opcode) {
         req_opcode_ = opcode;
-    };
-    /// \brief Set IP version carrying a query.
+    }
+
+    /// \brief Get IP version carrying a request.
+    ///
+    /// \return IP address family carrying a request (AF_INET or AF_INET6)
     /// \throw None
-    void setQueryIPVersion(const int ip_version) {
-        req_ip_version_ = ip_version;
-    };
-    /// \brief Set transport protocol carrying a query.
+    int getRequestIPVersion() const {
+        return (req_address_family_);
+    }
+
+    /// \brief Set IP address family carrying a request.
+    ///
+    /// \param address_family AF_INET or AF_INET6
+    /// \throw None
+    void setRequestIPVersion(const int address_family) {
+        if (address_family != AF_INET && address_family != AF_INET6) {
+            isc_throw(isc::InvalidParameter, "Unknown address family");
+        }
+        req_address_family_ = address_family;
+    }
+
+    /// \brief Get transport protocol carrying a request.
+    ///
+    /// \return Transport protocol carrying a request
+    ///         (IPPROTO_UDP or IPPROTO_TCP)
     /// \throw None
-    void setQueryTransportProtocol(const int transport_protocol) {
+    int getRequestTransportProtocol() const {
+        return (req_transport_protocol_);
+    }
+
+    /// \brief Set transport protocol carrying a request.
+    ///
+    /// \param transport_protocol IPPROTO_UDP or IPPROTO_TCP
+    /// \throw None
+    void setRequestTransportProtocol(const int transport_protocol) {
+        if (transport_protocol != IPPROTO_UDP &&
+            transport_protocol != IPPROTO_TCP)
+        {
+            isc_throw(isc::InvalidParameter, "Unknown transport protocol");
+        }
         req_transport_protocol_ = transport_protocol;
-    };
-    /// \brief Set query EDNS attributes.
+    }
+
+    /// \brief Return whether EDNS version of the request is 0 or not.
+    ///
+    /// \return true if EDNS version of the request is 0
     /// \throw None
-    void setQueryEDNS(const bool is_edns_0, const bool is_edns_badver) {
-        req_is_edns_0_ = is_edns_0;
-        req_is_edns_badver_ = is_edns_badver;
-    };
-    /// \brief Set query DO bit.
+    bool requestHasEDNS0() const {
+        return (bit_attributes_[REQ_WITH_EDNS_0]);
+    }
+
+    /// \brief Set whether EDNS version of the request is 0 or not.
+    ///
+    /// \param with_edns_0 true if EDNS version of the request is 0
     /// \throw None
-    void setQueryDO(const bool is_dnssec_ok) {
-        req_is_dnssec_ok_ = is_dnssec_ok;
-    };
-    /// \brief Set query TSIG attributes.
-    /// \throw None
-    void setQuerySig(const bool is_tsig, const bool is_sig0,
-                            const bool is_badsig)
-    {
-        req_is_tsig_ = is_tsig;
-        req_is_sig0_ = is_sig0;
-        req_is_badsig_ = is_badsig;
-    };
-    /// \brief Set zone origin.
+    void setRequestEDNS0(const bool with_edns_0) {
+        bit_attributes_[REQ_WITH_EDNS_0] = with_edns_0;
+    }
+
+    /// \brief Return DNSSEC OK (DO) bit of the request.
+    ///
+    /// \return true if DNSSEC OK (DO) bit of the request is set
     /// \throw None
-    void setOrigin(const std::string& origin) {
-        zone_origin_ = origin;
-    };
-    /// \brief Set if the answer was sent.
+    bool requestHasDO() const {
+        return (bit_attributes_[REQ_WITH_DNSSEC_OK]);
+    }
+
+    /// \brief Set DNSSEC OK (DO) bit of the request.
+    ///
+    /// \param with_dnssec_ok true if DNSSEC OK (DO) bit of the request is set
     /// \throw None
-    void answerWasSent() {
-        answer_sent_ = true;
-    };
-    /// \brief Set if the response is truncated.
+    void setRequestDO(const bool with_dnssec_ok) {
+        bit_attributes_[REQ_WITH_DNSSEC_OK] = with_dnssec_ok;
+    }
+
+    /// \brief Return whether the request is TSIG signed or not.
+    ///
+    /// \return true if the request is TSIG signed
+    /// \throw None
+    bool requestHasTSIG() const {
+        return (bit_attributes_[REQ_TSIG_SIGNED]);
+    }
+
+    /// \brief Return whether the signature of the request is bad or not.
+    ///
+    /// \return true if the signature of the request is bad
+    /// \throw None
+    bool requestHasBadSig() const {
+        return (bit_attributes_[REQ_BADSIG]);
+    }
+
+    /// \brief Set TSIG attributes of the request.
+    ///
+    /// \param signed_tsig true if the request is signed with TSIG
+    /// \param badsig true if the signature of the request is bad; it must not
+    //                be true unless signed_tsig is true
+    /// \throw isc::InvalidParameter if badsig is true though the request is
+    ///                              not signed
+    void setRequestTSIG(const bool signed_tsig, const bool badsig) {
+        if (!signed_tsig && badsig) {
+            isc_throw(isc::InvalidParameter, "Message is not signed but badsig"
+                                             " is true");
+        }
+        bit_attributes_[REQ_TSIG_SIGNED] = signed_tsig;
+        bit_attributes_[REQ_BADSIG] = badsig;
+    }
+
+    /// \brief Return TC (truncated) bit of the response.
+    ///
+    /// \return true if the response is truncated
+    /// \throw None
+    bool responseIsTruncated() const {
+        return (bit_attributes_[RES_IS_TRUNCATED]);
+    }
+
+    /// \brief Set TC (truncated) bit of the response.
+    ///
+    /// \param is_truncated true if the response is truncated
     /// \throw None
     void setResponseTruncated(const bool is_truncated) {
-        res_is_truncated_ = is_truncated;
-    };
-    /// \brief Reset attributes.
-    /// \throw None
-    void reset() {
-        req_ip_version_ = 0;
-        req_transport_protocol_ = 0;
-        req_opcode_ = 0;
-        req_is_edns_0_ = false;
-        req_is_edns_badver_ = false;
-        req_is_dnssec_ok_ = false;
-        req_is_tsig_ = false;
-        req_is_sig0_ = false;
-        req_is_badsig_ = false;
-        zone_origin_.clear();
-        answer_sent_ = false;
-        res_is_truncated_ = false;
-    };
+        bit_attributes_[RES_IS_TRUNCATED] = is_truncated;
+    }
+
+    /// \brief Return whether the response is TSIG signed or not.
+    ///
+    /// \return true if the response is signed with TSIG
+    /// \throw None
+    bool responseHasTSIG() const {
+        return (bit_attributes_[RES_TSIG_SIGNED]);
+    }
+
+    /// \brief Set whether the response is TSIG signed or not.
+    ///
+    /// \param signed_tsig true if the response is signed with TSIG
+    /// \throw None
+    void setResponseTSIG(const bool signed_tsig) {
+        bit_attributes_[RES_TSIG_SIGNED] = signed_tsig;
+    }
 };
 
-/// \brief Set of query counters.
+/// \brief Set of DNS message counters.
 ///
-/// \c Counters is set of query counters class. It holds query counters
-/// and provides an interface to increment the counter of specified type
-/// (e.g. UDP query, TCP query).
-///
-/// This class also provides a function to send statistics information to
-/// statistics module.
+/// \c Counters is a set of DNS message counters class. It holds DNS message
+/// counters and provides an interface to increment the counter of specified
+/// type (e.g. UDP message, TCP message).
 ///
 /// This class is designed to be a part of \c AuthSrv.
-/// Call \c inc() to increment a counter for the query.
-/// Call \c getStatistics() to answer statistics information to statistics
-/// module with statistics_session, when the command \c getstats is received.
+/// Call \c inc() to increment a counter for the message.
+/// Call \c get() to get a set of DNS message counters.
 ///
 /// We may eventually want to change the structure to hold values that are
 /// not counters (such as concurrent TCP connections), or seperate generic
 /// part to src/lib to share with the other modules.
 ///
-/// This class uses pimpl idiom and hides detailed implementation.
 /// This class is constructed on startup of the server, so
 /// construction overhead of this approach should be acceptable.
 ///
-/// \todo Hold counters for each query types (Notify, Axfr, Ixfr, Normal)
 /// \todo Consider overhead of \c Counters::inc()
-class Counters {
+class Counters : boost::noncopyable {
 private:
-    boost::scoped_ptr<CountersImpl> impl_;
+    // counter for DNS message attributes
+    isc::statistics::Counter server_msg_counter_;
+    void incRequest(const MessageAttributes& msgattrs);
+    void incResponse(const MessageAttributes& msgattrs,
+                     const isc::dns::Message& response);
 public:
-    /// The constructor.
+    /// \brief A type of statistics item tree in isc::data::MapElement.
+    /// \verbatim
+    ///        {
+    ///          zone_name => {
+    ///                         item_name => item_value,
+    ///                         item_name => item_value, ...
+    ///                       },
+    ///          ...
+    ///        }
+    ///        item_name is a string seperated by '.'.
+    ///        item_value is an integer.
+    /// \endverbatim
+    typedef isc::data::ConstElementPtr ConstItemTreePtr;
+
+    /// \brief The constructor.
     ///
     /// This constructor is mostly exception free. But it may still throw
     /// a standard exception if memory allocation fails inside the method.
-    ///
     Counters();
-    /// The destructor.
-    ///
-    /// This method never throws an exception.
-    ///
-    ~Counters();
 
     /// \brief Increment counters according to the parameters.
     ///
-    /// \param qrattrs Query/Response attributes.
+    /// \param msgattrs DNS message attributes.
     /// \param response DNS response message.
-    ///
-    /// \throw None
-    ///
-    void inc(const QRAttributes& qrattrs, const isc::dns::Message& response);
+    /// \param done DNS response was sent to the client.
+    /// \throw isc::Unexpected Internal condition check failed.
+    void inc(const MessageAttributes& msgattrs,
+             const isc::dns::Message& response, const bool done);
 
-    /// \brief Answers statistics counters to statistics module.
+    /// \brief Get statistics counters.
     ///
-    /// This method is mostly exception free (error conditions are
-    /// represented via the return value). But it may still throw
-    /// a standard exception if memory allocation fails inside the method.
+    /// This method is mostly exception free. But it may still throw a
+    /// standard exception if memory allocation fails inside the method.
     ///
     /// \return statistics data
-    ///
-    isc::data::ConstElementPtr getStatistics() const;
-
-    /// \brief A type of validation function for the specification in
-    /// isc::config::ModuleSpec.
-    ///
-    /// This type might be useful for not only statistics
-    /// specificatoin but also for config_data specification and for
-    /// commnad.
-    ///
-    typedef boost::function<bool(const isc::data::ConstElementPtr&)>
-    validator_type;
-
-    /// \brief Register a function type of the statistics validation
-    /// function for Counters.
-    ///
-    /// This method never throws an exception.
-    ///
-    /// \param validator A function type of the validation of
-    /// statistics specification.
-    ///
-    void registerStatisticsValidator(Counters::validator_type validator) const;
+    /// \throw std::bad_alloc Internal resource allocation fails
+    ConstItemTreePtr get() const;
 };
 
 } // namespace statistics

+ 0 - 609
src/bin/auth/statistics_items.h

@@ -1,609 +0,0 @@
-// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __STATISTICS_ITEMS_H
-#define __STATISTICS_ITEMS_H 1
-
-/// This file defines a set of statistics items in Auth module for internal
-/// use. This file is intended to be included in statistics.cc.
-
-namespace {
-
-struct CounterTypeTree {
-    const char* const name;
-    const struct CounterTypeTree* const sub_tree;
-    const int counter_id;
-};
-
-// enum for query/response counters
-enum QRCounterType {
-    // Request Attributes
-    QR_REQUEST_IPV4,        ///< Number of IPv4 requests received
-    QR_REQUEST_IPV6,        ///< Number of IPv6 requests received
-    QR_REQUEST_EDNS0,       ///< Number of requests with EDNS(0) received
-    QR_REQUEST_BADEDNSVER,  ///< Number of requests with unsupported EDNS version received
-    QR_REQUEST_TSIG,        ///< Number of requests with TSIG received
-    QR_REQUEST_SIG0,        ///< Number of requests with SIG(0) received; not implemented in BIND 10
-    QR_REQUEST_BADSIG,      ///< Number of requests with invalid TSIG or SIG(0) signature received
-    QR_REQUEST_UDP,         ///< Number of UDP requests received
-    QR_REQUEST_TCP,         ///< Number of TCP requests received
-    QR_REQUEST_DNSSEC_OK,   ///< Number of requests with DO bit
-    // Request Opcodes
-    QR_OPCODE_QUERY,        ///< Number of Opcode=QUERY requests received
-    QR_OPCODE_IQUERY,       ///< Number of Opcode=IQUERY requests received
-    QR_OPCODE_STATUS,       ///< Number of Opcode=STATUS requests received
-    QR_OPCODE_NOTIFY,       ///< Number of Opcode=NOTIFY requests received
-    QR_OPCODE_UPDATE,       ///< Number of Opcode=UPDATE requests received
-    QR_OPCODE_OTHER,        ///< Number of requests in other OpCode received
-    // Query Types
-    QR_QTYPE_A,             ///< Number of QTYPE = A queries received
-    QR_QTYPE_NS,            ///< Number of QTYPE = NS queries received
-    QR_QTYPE_MD,            ///< Number of QTYPE = MD queries received
-    QR_QTYPE_MF,            ///< Number of QTYPE = MF queries received
-    QR_QTYPE_CNAME,         ///< Number of QTYPE = CNAME queries received
-    QR_QTYPE_SOA,           ///< Number of QTYPE = SOA queries received
-    QR_QTYPE_MB,            ///< Number of QTYPE = MB queries received
-    QR_QTYPE_MG,            ///< Number of QTYPE = MG queries received
-    QR_QTYPE_MR,            ///< Number of QTYPE = MR queries received
-    QR_QTYPE_NULL,          ///< Number of QTYPE = NULL queries received
-    QR_QTYPE_WKS,           ///< Number of QTYPE = WKS queries received
-    QR_QTYPE_PTR,           ///< Number of QTYPE = PTR queries received
-    QR_QTYPE_HINFO,         ///< Number of QTYPE = HINFO queries received
-    QR_QTYPE_MINFO,         ///< Number of QTYPE = MINFO queries received
-    QR_QTYPE_MX,            ///< Number of QTYPE = MX queries received
-    QR_QTYPE_TXT,           ///< Number of QTYPE = TXT queries received
-    QR_QTYPE_RP,            ///< Number of QTYPE = RP queries received
-    QR_QTYPE_AFSDB,         ///< Number of QTYPE = AFSDB queries received
-    QR_QTYPE_X25,           ///< Number of QTYPE = X25 queries received
-    QR_QTYPE_ISDN,          ///< Number of QTYPE = ISDN queries received
-    QR_QTYPE_RT,            ///< Number of QTYPE = RT queries received
-    QR_QTYPE_NSAP,          ///< Number of QTYPE = NSAP queries received
-    QR_QTYPE_NSAP_PTR,      ///< Number of QTYPE = NSAP-PTR queries received
-    QR_QTYPE_SIG,           ///< Number of QTYPE = SIG queries received
-    QR_QTYPE_KEY,           ///< Number of QTYPE = KEY queries received
-    QR_QTYPE_PX,            ///< Number of QTYPE = PX queries received
-    QR_QTYPE_GPOS,          ///< Number of QTYPE = GPOS queries received
-    QR_QTYPE_AAAA,          ///< Number of QTYPE = AAAA queries received
-    QR_QTYPE_LOC,           ///< Number of QTYPE = LOC queries received
-    QR_QTYPE_NXT,           ///< Number of QTYPE = NXT queries received
-    QR_QTYPE_EID,           ///< Number of QTYPE = EID queries received
-    QR_QTYPE_NIMLOC,        ///< Number of QTYPE = NIMLOC queries received
-    QR_QTYPE_SRV,           ///< Number of QTYPE = SRV queries received
-    QR_QTYPE_ATMA,          ///< Number of QTYPE = ATMA queries received
-    QR_QTYPE_NAPTR,         ///< Number of QTYPE = NAPTR queries received
-    QR_QTYPE_KX,            ///< Number of QTYPE = KX queries received
-    QR_QTYPE_CERT,          ///< Number of QTYPE = CERT queries received
-    QR_QTYPE_A6,            ///< Number of QTYPE = A6 queries received
-    QR_QTYPE_DNAME,         ///< Number of QTYPE = DNAME queries received
-    QR_QTYPE_SINK,          ///< Number of QTYPE = SINK queries received
-    QR_QTYPE_OPT,           ///< Number of QTYPE = OPT queries received
-    QR_QTYPE_APL,           ///< Number of QTYPE = APL queries received
-    QR_QTYPE_DS,            ///< Number of QTYPE = DS queries received
-    QR_QTYPE_SSHFP,         ///< Number of QTYPE = SSHFP queries received
-    QR_QTYPE_IPSECKEY,      ///< Number of QTYPE = IPSECKEY queries received
-    QR_QTYPE_RRSIG,         ///< Number of QTYPE = RRSIG queries received
-    QR_QTYPE_NSEC,          ///< Number of QTYPE = NSEC queries received
-    QR_QTYPE_DNSKEY,        ///< Number of QTYPE = DNSKEY queries received
-    QR_QTYPE_DHCID,         ///< Number of QTYPE = DHCID queries received
-    QR_QTYPE_NSEC3,         ///< Number of QTYPE = NSEC3 queries received
-    QR_QTYPE_NSEC3PARAM,    ///< Number of QTYPE = NSEC3PARAM queries received
-    QR_QTYPE_HIP,           ///< Number of QTYPE = HIP queries received
-    QR_QTYPE_NINFO,         ///< Number of QTYPE = NINFO queries received
-    QR_QTYPE_RKEY,          ///< Number of QTYPE = RKEY queries received
-    QR_QTYPE_TALINK,        ///< Number of QTYPE = TALINK queries received
-    QR_QTYPE_SPF,           ///< Number of QTYPE = SPF queries received
-    QR_QTYPE_UINFO,         ///< Number of QTYPE = UINFO queries received
-    QR_QTYPE_UID,           ///< Number of QTYPE = UID queries received
-    QR_QTYPE_GID,           ///< Number of QTYPE = GID queries received
-    QR_QTYPE_UNSPEC,        ///< Number of QTYPE = UNSPEC queries received
-    QR_QTYPE_TKEY,          ///< Number of QTYPE = TKEY queries received
-    QR_QTYPE_TSIG,          ///< Number of QTYPE = TSIG queries received
-    QR_QTYPE_IXFR,          ///< Number of QTYPE = IXFR queries received
-    QR_QTYPE_AXFR,          ///< Number of QTYPE = AXFR queries received
-    QR_QTYPE_MAILB,         ///< Number of QTYPE = MAILB queries received
-    QR_QTYPE_MAILA,         ///< Number of QTYPE = MAILA queries received
-    QR_QTYPE_URI,           ///< Number of QTYPE = URI queries received
-    QR_QTYPE_CAA,           ///< Number of QTYPE = CAA queries received
-    QR_QTYPE_TA,            ///< Number of QTYPE = TA queries received
-    QR_QTYPE_DLV,           ///< Number of QTYPE = DLV queries received
-    QR_QTYPE_OTHER,         ///< Number of queries in other QTYPE received
-    // Respose Attributes
-    QR_RESPONSE,            ///< Number of responses sent
-    QR_RESPONSE_TRUNCATED,  ///< Number of truncated responses sent
-    QR_RESPONSE_EDNS0,      ///< Number of responses with EDNS0; not implemented in BIND 10
-    QR_RESPONSE_TSIG,       ///< Number of responses with TSIG
-    QR_RESPONSE_SIG0,       ///< Number of responses with SIG(0); not implemented in BIND 10
-    QR_QRYSUCCESS,          ///< Number of queries resulted in rcode = NOERROR and answer RR >= 1
-    QR_QRYAUTHANS,          ///< Number of queries resulted in authoritative answer
-    QR_QRYNOAUTHANS,        ///< Number of queries resulted in non-authoritative answer
-    QR_QRYREFERRAL,         ///< Number of queries resulted in referral answer
-    QR_QRYNXRRSET,          ///< Number of queries resulted in NOERROR but answer RR == 0
-    QR_QRYREJECT,           ///< Number of queries rejected
-    // Response Rcodes
-    QR_RCODE_NOERROR,       ///< Number of queries resulted in RCODE = 0 (NoError)
-    QR_RCODE_FORMERR,       ///< Number of queries resulted in RCODE = 1 (FormErr)
-    QR_RCODE_SERVFAIL,      ///< Number of queries resulted in RCODE = 2 (ServFail)
-    QR_RCODE_NXDOMAIN,      ///< Number of queries resulted in RCODE = 3 (NXDomain)
-    QR_RCODE_NOTIMP,        ///< Number of queries resulted in RCODE = 4 (NotImp)
-    QR_RCODE_REFUSED,       ///< Number of queries resulted in RCODE = 5 (Refused)
-    QR_RCODE_YXDOMAIN,      ///< Number of queries resulted in RCODE = 6 (YXDomain)
-    QR_RCODE_YXRRSET,       ///< Number of queries resulted in RCODE = 7 (YXRRSet)
-    QR_RCODE_NXRRSET,       ///< Number of queries resulted in RCODE = 8 (NXRRSet)
-    QR_RCODE_NOTAUTH,       ///< Number of queries resulted in RCODE = 9 (NotAuth)
-    QR_RCODE_NOTZONE,       ///< Number of queries resulted in RCODE = 10 (NotZone)
-    QR_RCODE_BADSIGVERS,    ///< Number of queries resulted in RCODE = 16 (BADVERS, BADSIG)
-    QR_RCODE_BADKEY,        ///< Number of queries resulted in RCODE = 17 (BADKEY)
-    QR_RCODE_BADTIME,       ///< Number of queries resulted in RCODE = 18 (BADTIME)
-    QR_RCODE_BADMODE,       ///< Number of queries resulted in RCODE = 19 (BADMODE)
-    QR_RCODE_BADNAME,       ///< Number of queries resulted in RCODE = 20 (BADNAME)
-    QR_RCODE_BADALG,        ///< Number of queries resulted in RCODE = 21 (BADALG)
-    QR_RCODE_BADTRUNC,      ///< Number of queries resulted in RCODE = 22 (BADTRUNC)
-    QR_RCODE_OTHER,         ///< Number of queries resulted in other RCODEs
-    // End of counter types
-    QR_COUNTER_TYPES  ///< The number of defined counters
-};
-
-// item names for query/response counters
-const struct CounterTypeTree QRCounterRequest[] = {
-    { "v4",         NULL,   QR_REQUEST_IPV4       },
-    { "v6",         NULL,   QR_REQUEST_IPV6       },
-    { "edns0",      NULL,   QR_REQUEST_EDNS0      },
-    { "badednsver", NULL,   QR_REQUEST_BADEDNSVER },
-    { "tsig",       NULL,   QR_REQUEST_TSIG       },
-    { "sig0",       NULL,   QR_REQUEST_SIG0       },
-    { "badsig",     NULL,   QR_REQUEST_BADSIG     },
-    { "udp",        NULL,   QR_REQUEST_UDP        },
-    { "tcp",        NULL,   QR_REQUEST_TCP        },
-    { "dnssec_ok",  NULL,   QR_REQUEST_DNSSEC_OK  },
-    { NULL,         NULL,   -1                    }
-};
-const struct CounterTypeTree QRCounterOpcode[] = {
-    { "query",  NULL,   QR_OPCODE_QUERY  },
-    { "iquery", NULL,   QR_OPCODE_IQUERY },
-    { "status", NULL,   QR_OPCODE_STATUS },
-    { "notify", NULL,   QR_OPCODE_NOTIFY },
-    { "update", NULL,   QR_OPCODE_UPDATE },
-    { "other",  NULL,   QR_OPCODE_OTHER  },
-    { NULL,     NULL,   -1               }
-};
-const struct CounterTypeTree QRCounterQtype[] = {
-    { "a",          NULL,   QR_QTYPE_A,         },
-    { "ns",         NULL,   QR_QTYPE_NS         },
-    { "md",         NULL,   QR_QTYPE_MD         },
-    { "mf",         NULL,   QR_QTYPE_MF         },
-    { "cname",      NULL,   QR_QTYPE_CNAME      },
-    { "soa",        NULL,   QR_QTYPE_SOA        },
-    { "mb",         NULL,   QR_QTYPE_MB         },
-    { "mg",         NULL,   QR_QTYPE_MG         },
-    { "mr",         NULL,   QR_QTYPE_MR         },
-    { "null",       NULL,   QR_QTYPE_NULL       },
-    { "wks",        NULL,   QR_QTYPE_WKS        },
-    { "ptr",        NULL,   QR_QTYPE_PTR        },
-    { "hinfo",      NULL,   QR_QTYPE_HINFO      },
-    { "minfo",      NULL,   QR_QTYPE_MINFO      },
-    { "mx",         NULL,   QR_QTYPE_MX         },
-    { "txt",        NULL,   QR_QTYPE_TXT        },
-    { "rp",         NULL,   QR_QTYPE_RP         },
-    { "afsdb",      NULL,   QR_QTYPE_AFSDB      },
-    { "x25",        NULL,   QR_QTYPE_X25        },
-    { "isdn",       NULL,   QR_QTYPE_ISDN       },
-    { "rt",         NULL,   QR_QTYPE_RT         },
-    { "nsap",       NULL,   QR_QTYPE_NSAP       },
-    { "nsap-ptr",   NULL,   QR_QTYPE_NSAP_PTR   },
-    { "sig",        NULL,   QR_QTYPE_SIG        },
-    { "key",        NULL,   QR_QTYPE_KEY        },
-    { "px",         NULL,   QR_QTYPE_PX         },
-    { "gpos",       NULL,   QR_QTYPE_GPOS       },
-    { "aaaa",       NULL,   QR_QTYPE_AAAA       },
-    { "loc",        NULL,   QR_QTYPE_LOC        },
-    { "nxt",        NULL,   QR_QTYPE_NXT        },
-    { "eid",        NULL,   QR_QTYPE_EID        },
-    { "nimloc",     NULL,   QR_QTYPE_NIMLOC     },
-    { "srv",        NULL,   QR_QTYPE_SRV        },
-    { "atma",       NULL,   QR_QTYPE_ATMA       },
-    { "naptr",      NULL,   QR_QTYPE_NAPTR      },
-    { "kx",         NULL,   QR_QTYPE_KX         },
-    { "cert",       NULL,   QR_QTYPE_CERT       },
-    { "a6",         NULL,   QR_QTYPE_A6         },
-    { "dname",      NULL,   QR_QTYPE_DNAME      },
-    { "sink",       NULL,   QR_QTYPE_SINK       },
-    { "opt",        NULL,   QR_QTYPE_OPT        },
-    { "apl",        NULL,   QR_QTYPE_APL        },
-    { "ds",         NULL,   QR_QTYPE_DS         },
-    { "sshfp",      NULL,   QR_QTYPE_SSHFP      },
-    { "ipseckey",   NULL,   QR_QTYPE_IPSECKEY   },
-    { "rrsig",      NULL,   QR_QTYPE_RRSIG      },
-    { "nsec",       NULL,   QR_QTYPE_NSEC       },
-    { "dnskey",     NULL,   QR_QTYPE_DNSKEY     },
-    { "dhcid",      NULL,   QR_QTYPE_DHCID      },
-    { "nsec3",      NULL,   QR_QTYPE_NSEC3      },
-    { "nsec3param", NULL,   QR_QTYPE_NSEC3PARAM },
-    { "hip",        NULL,   QR_QTYPE_HIP        },
-    { "ninfo",      NULL,   QR_QTYPE_NINFO      },
-    { "rkey",       NULL,   QR_QTYPE_RKEY       },
-    { "talink",     NULL,   QR_QTYPE_TALINK     },
-    { "spf",        NULL,   QR_QTYPE_SPF        },
-    { "uinfo",      NULL,   QR_QTYPE_UINFO      },
-    { "uid",        NULL,   QR_QTYPE_UID        },
-    { "gid",        NULL,   QR_QTYPE_GID        },
-    { "unspec",     NULL,   QR_QTYPE_UNSPEC     },
-    { "tkey",       NULL,   QR_QTYPE_TKEY       },
-    { "tsig",       NULL,   QR_QTYPE_TSIG       },
-    { "ixfr",       NULL,   QR_QTYPE_IXFR       },
-    { "axfr",       NULL,   QR_QTYPE_AXFR       },
-    { "mailb",      NULL,   QR_QTYPE_MAILB      },
-    { "maila",      NULL,   QR_QTYPE_MAILA      },
-    { "uri",        NULL,   QR_QTYPE_URI        },
-    { "caa",        NULL,   QR_QTYPE_CAA        },
-    { "ta",         NULL,   QR_QTYPE_TA         },
-    { "dlv",        NULL,   QR_QTYPE_DLV        },
-    { "other",      NULL,   QR_QTYPE_OTHER      },
-    { NULL,         NULL,   -1                  }
-};
-const struct CounterTypeTree QRCounterResponse[] = {
-    { "truncated",  NULL,   QR_RESPONSE_TRUNCATED },
-    { "edns0",      NULL,   QR_RESPONSE_EDNS0     },
-    { "tsig",       NULL,   QR_RESPONSE_TSIG      },
-    { "sig0",       NULL,   QR_RESPONSE_SIG0      },
-    { NULL,         NULL,   -1                    }
-};
-const struct CounterTypeTree QRCounterRcode[] = {
-    { "noerror",    NULL,   QR_RCODE_NOERROR    },
-    { "formerr",    NULL,   QR_RCODE_FORMERR    },
-    { "servfail",   NULL,   QR_RCODE_SERVFAIL   },
-    { "nxdomain",   NULL,   QR_RCODE_NXDOMAIN   },
-    { "notimp",     NULL,   QR_RCODE_NOTIMP     },
-    { "refused",    NULL,   QR_RCODE_REFUSED    },
-    { "yxdomain",   NULL,   QR_RCODE_YXDOMAIN   },
-    { "yxrrset",    NULL,   QR_RCODE_YXRRSET    },
-    { "nxrrset",    NULL,   QR_RCODE_NXRRSET    },
-    { "notauth",    NULL,   QR_RCODE_NOTAUTH    },
-    { "notzone",    NULL,   QR_RCODE_NOTZONE    },
-    { "badsigvers", NULL,   QR_RCODE_BADSIGVERS },
-    { "badkey",     NULL,   QR_RCODE_BADKEY     },
-    { "badtime",    NULL,   QR_RCODE_BADTIME    },
-    { "badmode",    NULL,   QR_RCODE_BADMODE    },
-    { "badname",    NULL,   QR_RCODE_BADNAME    },
-    { "badalg",     NULL,   QR_RCODE_BADALG     },
-    { "badtrunc",   NULL,   QR_RCODE_BADTRUNC   },
-    { "other",      NULL,   QR_RCODE_OTHER      },
-    { NULL,         NULL,   -1 }
-};
-const struct CounterTypeTree QRCounterTree[] = {
-    { "request",        QRCounterRequest,   -1              },
-    { "opcode",         QRCounterOpcode,    -1              },
-    { "qtype",          QRCounterQtype,     -1              },
-    { "responses",      NULL,               QR_RESPONSE     },
-    { "response",       QRCounterResponse,  -1              },
-    { "qrysuccess",     NULL,               QR_QRYSUCCESS   },
-    { "qryauthans",     NULL,               QR_QRYAUTHANS   },
-    { "qrynoauthans",   NULL,               QR_QRYNOAUTHANS },
-    { "qryreferral",    NULL,               QR_QRYREFERRAL  },
-    { "qrynxrrset",     NULL,               QR_QRYNXRRSET   },
-    { "authqryrej",     NULL,               QR_QRYREJECT    },
-    { "rcode",          QRCounterRcode,     -1              },
-    { NULL,             NULL,               -1              }
-};
-
-const int QROpCodeToQRCounterType[16] = {
-    QR_OPCODE_QUERY,    // Opcode =  0: Query
-    QR_OPCODE_IQUERY,   // Opcode =  1: Iquery
-    QR_OPCODE_STATUS,   // Opcode =  2: STATUS
-    QR_OPCODE_OTHER,    // Opcode =  3: (Unassigned)
-    QR_OPCODE_NOTIFY,   // Opcode =  4: Notify
-    QR_OPCODE_UPDATE,   // Opcode =  5: Update
-    QR_OPCODE_OTHER,    // Opcode =  6: (Unassigned)
-    QR_OPCODE_OTHER,    // Opcode =  7: (Unassigned)
-    QR_OPCODE_OTHER,    // Opcode =  8: (Unassigned)
-    QR_OPCODE_OTHER,    // Opcode =  9: (Unassigned)
-    QR_OPCODE_OTHER,    // Opcode = 10: (Unassigned)
-    QR_OPCODE_OTHER,    // Opcode = 11: (Unassigned)
-    QR_OPCODE_OTHER,    // Opcode = 12: (Unassigned)
-    QR_OPCODE_OTHER,    // Opcode = 13: (Unassigned)
-    QR_OPCODE_OTHER,    // Opcode = 14: (Unassigned)
-    QR_OPCODE_OTHER     // Opcode = 15: (Unassigned)
-};
-const int QRQTypeToQRCounterType[258] = {
-    QR_QTYPE_OTHER,         // RRtype =   0: special use
-    QR_QTYPE_A,             // RRtype =   1: A
-    QR_QTYPE_NS,            // RRtype =   2: NS
-    QR_QTYPE_MD,            // RRtype =   3: MD
-    QR_QTYPE_MF,            // RRtype =   4: MF
-    QR_QTYPE_CNAME,         // RRtype =   5: CNAME
-    QR_QTYPE_SOA,           // RRtype =   6: SOA
-    QR_QTYPE_MB,            // RRtype =   7: MB
-    QR_QTYPE_MG,            // RRtype =   8: MG
-    QR_QTYPE_MR,            // RRtype =   9: MR
-    QR_QTYPE_NULL,          // RRtype =  10: NULL
-    QR_QTYPE_WKS,           // RRtype =  11: WKS
-    QR_QTYPE_PTR,           // RRtype =  12: PTR
-    QR_QTYPE_HINFO,         // RRtype =  13: HINFO
-    QR_QTYPE_MINFO,         // RRtype =  14: MINFO
-    QR_QTYPE_MX,            // RRtype =  15: MX
-    QR_QTYPE_TXT,           // RRtype =  16: TXT
-    QR_QTYPE_RP,            // RRtype =  17: RP
-    QR_QTYPE_AFSDB,         // RRtype =  18: AFSDB
-    QR_QTYPE_X25,           // RRtype =  19: X25
-    QR_QTYPE_ISDN,          // RRtype =  20: ISDN
-    QR_QTYPE_RT,            // RRtype =  21: RT
-    QR_QTYPE_NSAP,          // RRtype =  22: NSAP
-    QR_QTYPE_NSAP_PTR,      // RRtype =  23: NSAP-PTR
-    QR_QTYPE_SIG,           // RRtype =  24: SIG
-    QR_QTYPE_KEY,           // RRtype =  25: KEY
-    QR_QTYPE_PX,            // RRtype =  26: PX
-    QR_QTYPE_GPOS,          // RRtype =  27: GPOS
-    QR_QTYPE_AAAA,          // RRtype =  28: AAAA
-    QR_QTYPE_LOC,           // RRtype =  29: LOC
-    QR_QTYPE_NXT,           // RRtype =  30: NXT
-    QR_QTYPE_EID,           // RRtype =  31: EID        
-    QR_QTYPE_NIMLOC,        // RRtype =  32: NIMLOC     
-    QR_QTYPE_SRV,           // RRtype =  33: SRV        
-    QR_QTYPE_ATMA,          // RRtype =  34: ATMA       
-    QR_QTYPE_NAPTR,         // RRtype =  35: NAPTR      
-    QR_QTYPE_KX,            // RRtype =  36: KX         
-    QR_QTYPE_CERT,          // RRtype =  37: CERT       
-    QR_QTYPE_A6,            // RRtype =  38: A6         
-    QR_QTYPE_DNAME,         // RRtype =  39: DNAME      
-    QR_QTYPE_SINK,          // RRtype =  40: SINK       
-    QR_QTYPE_OPT,           // RRtype =  41: OPT        
-    QR_QTYPE_APL,           // RRtype =  42: APL        
-    QR_QTYPE_DS,            // RRtype =  43: DS         
-    QR_QTYPE_SSHFP,         // RRtype =  44: SSHFP      
-    QR_QTYPE_IPSECKEY,      // RRtype =  45: IPSECKEY   
-    QR_QTYPE_RRSIG,         // RRtype =  46: RRSIG      
-    QR_QTYPE_NSEC,          // RRtype =  47: NSEC       
-    QR_QTYPE_DNSKEY,        // RRtype =  48: DNSKEY     
-    QR_QTYPE_DHCID,         // RRtype =  49: DHCID      
-    QR_QTYPE_NSEC3,         // RRtype =  50: NSEC3      
-    QR_QTYPE_NSEC3PARAM,    // RRtype =  51: NSEC3PARAM 
-    QR_QTYPE_OTHER,         // RRtype =  52: TLSA
-    QR_QTYPE_OTHER,         // RRtype =  53: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  54: (Unassigned)
-    QR_QTYPE_HIP,           // RRtype =  55: HIP
-    QR_QTYPE_NINFO,         // RRtype =  56: NINFO
-    QR_QTYPE_RKEY,          // RRtype =  57: RKEY
-    QR_QTYPE_TALINK,        // RRtype =  58: TALINK
-    QR_QTYPE_OTHER,         // RRtype =  59: CDS
-    QR_QTYPE_OTHER,         // RRtype =  60: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  61: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  62: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  63: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  64: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  65: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  66: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  67: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  68: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  69: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  70: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  71: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  72: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  73: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  74: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  75: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  76: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  77: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  78: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  79: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  80: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  81: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  82: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  83: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  84: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  85: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  86: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  87: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  88: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  89: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  90: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  91: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  92: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  93: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  94: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  95: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  96: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  97: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype =  98: (Unassigned)
-    QR_QTYPE_SPF,           // RRtype =  99: SPF
-    QR_QTYPE_UINFO,         // RRtype = 100: UINFO
-    QR_QTYPE_UID,           // RRtype = 101: UID
-    QR_QTYPE_GID,           // RRtype = 102: GID
-    QR_QTYPE_UNSPEC,        // RRtype = 103: UNSPEC
-    QR_QTYPE_OTHER,         // RRtype = 104: NID
-    QR_QTYPE_OTHER,         // RRtype = 105: L32
-    QR_QTYPE_OTHER,         // RRtype = 106: L64
-    QR_QTYPE_OTHER,         // RRtype = 107: LP 
-    QR_QTYPE_OTHER,         // RRtype = 108: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 109: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 110: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 111: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 112: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 113: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 114: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 115: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 116: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 117: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 118: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 119: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 120: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 121: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 122: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 123: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 124: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 125: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 126: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 127: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 128: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 129: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 130: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 131: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 132: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 133: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 134: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 135: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 136: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 137: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 138: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 139: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 140: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 141: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 142: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 143: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 144: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 145: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 146: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 147: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 148: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 149: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 150: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 151: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 152: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 153: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 154: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 155: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 156: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 157: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 158: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 159: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 160: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 161: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 162: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 163: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 164: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 165: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 166: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 167: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 168: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 169: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 170: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 171: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 172: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 173: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 174: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 175: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 176: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 177: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 178: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 179: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 180: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 181: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 182: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 183: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 184: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 185: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 186: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 187: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 188: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 189: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 190: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 191: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 192: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 193: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 194: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 195: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 196: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 197: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 198: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 199: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 200: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 201: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 202: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 203: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 204: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 205: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 206: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 207: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 208: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 209: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 210: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 211: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 212: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 213: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 214: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 215: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 216: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 217: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 218: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 219: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 220: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 221: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 222: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 223: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 224: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 225: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 226: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 227: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 228: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 229: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 230: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 231: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 232: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 233: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 234: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 235: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 236: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 237: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 238: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 239: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 240: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 241: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 242: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 243: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 244: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 245: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 246: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 247: (Unassigned)
-    QR_QTYPE_OTHER,         // RRtype = 248: (Unassigned)
-    QR_QTYPE_TKEY,          // RRtype = 249: TKEY
-    QR_QTYPE_TSIG,          // RRtype = 250: TSIG
-    QR_QTYPE_IXFR,          // RRtype = 251: IXFR
-    QR_QTYPE_AXFR,          // RRtype = 252: AXFR
-    QR_QTYPE_MAILB,         // RRtype = 253: MAILB
-    QR_QTYPE_MAILA,         // RRtype = 254: MAILA
-    QR_QTYPE_OTHER,         // RRtype = 255: for All records
-    QR_QTYPE_URI,           // RRtype = 256: URI
-    QR_QTYPE_CAA            // RRtype = 257: CAA
-};
-const int QRRCodeToQRCounterType[23] = {
-    QR_RCODE_NOERROR,       // Rcode =  0: NoError
-    QR_RCODE_FORMERR,       // Rcode =  1: FormErr
-    QR_RCODE_SERVFAIL,      // Rcode =  2: ServFail
-    QR_RCODE_NXDOMAIN,      // Rcode =  3: NXDomain
-    QR_RCODE_NOTIMP,        // Rcode =  4: NotImp
-    QR_RCODE_REFUSED,       // Rcode =  5: Refused
-    QR_RCODE_YXDOMAIN,      // Rcode =  6: YXDomain
-    QR_RCODE_YXRRSET,       // Rcode =  7: YXRRSet
-    QR_RCODE_NXRRSET,       // Rcode =  8: NXRRSet
-    QR_RCODE_NOTAUTH,       // Rcode =  9: NotAuth
-    QR_RCODE_NOTZONE,       // Rcode = 10: NotZone
-    QR_RCODE_OTHER,         // Rcode = 11: (Unassigned)
-    QR_RCODE_OTHER,         // Rcode = 12: (Unassigned)
-    QR_RCODE_OTHER,         // Rcode = 13: (Unassigned)
-    QR_RCODE_OTHER,         // Rcode = 14: (Unassigned)
-    QR_RCODE_OTHER,         // Rcode = 15: (Unassigned)
-    QR_RCODE_BADSIGVERS,    // Rcode = 16: BADVERS, BADSIG
-    QR_RCODE_BADKEY,        // Rcode = 17: BADKEY
-    QR_RCODE_BADTIME,       // Rcode = 18: BADTIME
-    QR_RCODE_BADMODE,       // Rcode = 19: BADMODE
-    QR_RCODE_BADNAME,       // Rcode = 20: BADNAME
-    QR_RCODE_BADALG,        // Rcode = 21: BADALG
-    QR_RCODE_BADTRUNC       // Rcode = 22: BADTRUNC
-};
-
-} // anonymous namespace
-
-#endif // __STATISTICS_ITEMS_H
-
-// Local Variables:
-// mode: c++
-// End:

+ 53 - 0
src/bin/auth/statistics_items.h.pre

@@ -0,0 +1,53 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __STATISTICS_ITEMS_H
+#define __STATISTICS_ITEMS_H 1
+
+/// This file declares a set of statistics items in Auth module for internal
+/// use. This file is intended to be included in statistics.cc and unittests.
+
+namespace isc {
+namespace auth {
+namespace statistics {
+
+struct CounterSpec {
+    /// \brief name Name of this node. This appears in the spec file.
+    const char* const name;
+    /// \brief sub_counters If this is a branch node, sub_counters points to
+    ///                     CounterSpec which contains child nodes. Otherwise,
+    ///                     for leaf nodes, sub_counters is NULL.
+    const struct CounterSpec* const sub_counters;
+    /// \brief counter_id If this is a leaf node, counter_id is an enumerator
+    ///                   of this item. Otherwise, for branch nodes, counter_id
+    ///                   is NOT_ITEM.
+    const int counter_id;
+};
+
+// ### STATISTICS ITEMS DECLARATION ###
+
+extern const int opcode_to_msgcounter[];
+extern const size_t num_opcode_to_msgcounter;
+extern const int rcode_to_msgcounter[];
+extern const size_t num_rcode_to_msgcounter;
+
+} // namespace statistics
+} // namespace auth
+} // namespace isc
+
+#endif // __STATISTICS_ITEMS_H
+
+// Local Variables:
+// mode: c++
+// End:

+ 48 - 0
src/bin/auth/statistics_msg_items.def

@@ -0,0 +1,48 @@
+request	msg_counter_request		Request statistics	=
+	v4		MSG_REQUEST_IPV4	Number of IPv4 requests received by the b10-auth server.
+	v6		MSG_REQUEST_IPV6	Number of IPv6 requests received by the b10-auth server.
+	edns0		MSG_REQUEST_EDNS0	Number of requests with EDNS0 received by the b10-auth server.
+	badednsver	MSG_REQUEST_BADEDNSVER	Number of requests with unsupported EDNS version received by the b10-auth server.
+	tsig		MSG_REQUEST_TSIG	Number of requests with TSIG received by the b10-auth server.
+	sig0		MSG_REQUEST_SIG0	Number of requests with SIG(0) received by the b10-auth server; currently not implemented in BIND 10.
+	badsig		MSG_REQUEST_BADSIG	Number of requests with invalid TSIG or SIG(0) signature received by the b10-auth server.
+	udp		MSG_REQUEST_UDP		Number of UDP requests received by the b10-auth server.
+	tcp		MSG_REQUEST_TCP		Number of TCP requests received by the b10-auth server.
+	dnssec_ok	MSG_REQUEST_DNSSEC_OK	Number of requests with "DNSSEC OK" (DO) bit was set received by the b10-auth server.
+	;
+opcode	msg_counter_opcode		OpCode statistics	=
+	query		MSG_OPCODE_QUERY	Number of OpCode=Query requests received by the b10-auth server.
+	iquery		MSG_OPCODE_IQUERY	Number of OpCode=IQuery requests received by the b10-auth server.
+	status		MSG_OPCODE_STATUS	Number of OpCode=Status requests received by the b10-auth server.
+	notify		MSG_OPCODE_NOTIFY	Number of OpCode=Notify requests received by the b10-auth server.
+	update		MSG_OPCODE_UPDATE	Number of OpCode=Update requests received by the b10-auth server.
+	other		MSG_OPCODE_OTHER	Number of requests in other OpCode received by the b10-auth server.
+	;
+responses	MSG_RESPONSE			Number of responses sent by the b10-auth server.
+response	msg_counter_response	Response statistics	=
+	truncated	MSG_RESPONSE_TRUNCATED	Number of truncated responses sent by the b10-auth server.
+	edns0		MSG_RESPONSE_EDNS0	Number of responses with EDNS0 sent by the b10-auth server.
+	tsig		MSG_RESPONSE_TSIG	Number of responses with TSIG sent by the b10-auth server.
+	sig0		MSG_RESPONSE_SIG0	Number of responses with SIG(0) sent by the b10-auth server; currently not implemented in BIND 10.
+	;
+qrysuccess	MSG_QRYSUCCESS			Number of queries received by the b10-auth server resulted in rcode = NoError and the number of answer RR >= 1.
+qryauthans	MSG_QRYAUTHANS			Number of queries received by the b10-auth server resulted in authoritative answer.
+qrynoauthans	MSG_QRYNOAUTHANS		Number of queries received by the b10-auth server resulted in non-authoritative answer.
+qryreferral	MSG_QRYREFERRAL			Number of queries received by the b10-auth server resulted in referral answer.
+qrynxrrset	MSG_QRYNXRRSET			Number of queries received by the b10-auth server resulted in NoError and AA bit is set in the response, but the number of answer RR == 0.
+authqryrej	MSG_QRYREJECT			Number of authoritative queries rejected by the b10-auth server.
+rcode		msg_counter_rcode	Rcode statistics	=
+	noerror		MSG_RCODE_NOERROR	Number of requests received by the b10-auth server resulted in RCODE = 0 (NoError).
+	formerr		MSG_RCODE_FORMERR	Number of requests received by the b10-auth server resulted in RCODE = 1 (FormErr).
+	servfail	MSG_RCODE_SERVFAIL	Number of requests received by the b10-auth server resulted in RCODE = 2 (ServFail).
+	nxdomain	MSG_RCODE_NXDOMAIN	Number of requests received by the b10-auth server resulted in RCODE = 3 (NXDomain).
+	notimp		MSG_RCODE_NOTIMP	Number of requests received by the b10-auth server resulted in RCODE = 4 (NotImp).
+	refused		MSG_RCODE_REFUSED	Number of requests received by the b10-auth server resulted in RCODE = 5 (Refused).
+	yxdomain	MSG_RCODE_YXDOMAIN	Number of requests received by the b10-auth server resulted in RCODE = 6 (YXDomain).
+	yxrrset		MSG_RCODE_YXRRSET	Number of requests received by the b10-auth server resulted in RCODE = 7 (YXRRSet).
+	nxrrset		MSG_RCODE_NXRRSET	Number of requests received by the b10-auth server resulted in RCODE = 8 (NXRRSet).
+	notauth		MSG_RCODE_NOTAUTH	Number of requests received by the b10-auth server resulted in RCODE = 9 (NotAuth).
+	notzone		MSG_RCODE_NOTZONE	Number of requests received by the b10-auth server resulted in RCODE = 10 (NotZone).
+	badvers		MSG_RCODE_BADVERS	Number of requests received by the b10-auth server resulted in RCODE = 16 (BADVERS).
+	other		MSG_RCODE_OTHER		Number of requests received by the b10-auth server resulted in other RCODEs.
+	;

+ 9 - 2
src/bin/auth/tests/Makefile.am

@@ -34,6 +34,9 @@ TESTS_ENVIRONMENT = \
 TESTS =
 if HAVE_GTEST
 
+# auto-generated by statistics_items.py
+BUILT_SOURCES = statistics_unittest.cc
+
 run_unittests_SOURCES = $(top_srcdir)/src/lib/dns/tests/unittest_util.h
 run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
 run_unittests_SOURCES += ../auth_srv.h ../auth_srv.cc
@@ -45,13 +48,13 @@ run_unittests_SOURCES += ../common.h ../common.cc
 run_unittests_SOURCES += ../statistics.h ../statistics.cc ../statistics_items.h
 run_unittests_SOURCES += ../datasrc_config.h ../datasrc_config.cc
 run_unittests_SOURCES += datasrc_util.h datasrc_util.cc
+run_unittests_SOURCES += statistics_util.h statistics_util.cc
 run_unittests_SOURCES += auth_srv_unittest.cc
 run_unittests_SOURCES += config_unittest.cc
 run_unittests_SOURCES += config_syntax_unittest.cc
 run_unittests_SOURCES += command_unittest.cc
 run_unittests_SOURCES += common_unittest.cc
 run_unittests_SOURCES += query_unittest.cc
-run_unittests_SOURCES += statistics_unittest.cc
 run_unittests_SOURCES += test_datasrc_clients_mgr.h test_datasrc_clients_mgr.cc
 run_unittests_SOURCES += datasrc_clients_builder_unittest.cc
 run_unittests_SOURCES += datasrc_clients_mgr_unittest.cc
@@ -59,6 +62,7 @@ run_unittests_SOURCES += datasrc_config_unittest.cc
 run_unittests_SOURCES += run_unittests.cc
 
 nodist_run_unittests_SOURCES = ../auth_messages.h ../auth_messages.cc
+nodist_run_unittests_SOURCES += statistics_unittest.cc
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
@@ -83,7 +87,7 @@ run_unittests_LDADD += $(SQLITE_LIBS)
 
 # The following are definitions for auto-generating test data for query
 # tests.
-BUILT_SOURCES = example_base_inc.cc example_nsec3_inc.cc
+BUILT_SOURCES += example_base_inc.cc example_nsec3_inc.cc
 BUILT_SOURCES += testdata/example-base.sqlite3
 BUILT_SOURCES += testdata/example-nsec3.sqlite3
 
@@ -114,8 +118,11 @@ testdata/example-nsec3.sqlite3: testdata/example-nsec3.zone testdata/example-com
 		-c "{\"database_file\": \"$(builddir)/testdata/example-nsec3.sqlite3\"}" \
 		example.com testdata/example-nsec3.zone
 
+EXTRA_DIST += gen-statisticsitems_test.py
+
 check-local:
 	B10_FROM_BUILD=${abs_top_builddir} ./run_unittests
+	$(PYTHON) $(srcdir)/gen-statisticsitems_test.py $(top_builddir)/src/bin/auth/b10-auth.xml
 
 noinst_PROGRAMS = run_unittests
 

+ 310 - 166
src/bin/auth/tests/auth_srv_unittest.cc

@@ -45,6 +45,8 @@
 #include <testutils/portconfig.h>
 #include <testutils/socket_request.h>
 
+#include "statistics_util.h"
+
 #include <gtest/gtest.h>
 
 #include <boost/lexical_cast.hpp>
@@ -73,6 +75,7 @@ using namespace isc::asiodns;
 using namespace isc::asiolink;
 using namespace isc::testutils;
 using namespace isc::server_common::portconfig;
+using namespace isc::auth::unittest;
 using isc::datasrc::memory::ZoneTableSegment;
 using isc::UnitTestUtil;
 using boost::scoped_ptr;
@@ -90,7 +93,8 @@ const char* const STATIC_DSRC_FILE = DSRC_DIR "/static.zone";
 
 // This is a configuration that uses the in-memory data source containing
 // a signed example zone.
-const char* const CONFIG_INMEMORY_EXAMPLE = TEST_DATA_DIR "/rfc5155-example.zone.signed";
+const char* const CONFIG_INMEMORY_EXAMPLE =
+    TEST_DATA_DIR "/rfc5155-example.zone.signed";
 
 // shortcut commonly used in tests
 typedef boost::shared_ptr<ConfigurableClientList> ListPtr;
@@ -108,6 +112,7 @@ protected:
         server.setDNSService(dnss_);
         server.setXfrinSession(&notify_session);
         server.createDDNSForwarder();
+        checkCountersAreInitialized();
     }
 
     ~AuthSrvTest() {
@@ -124,7 +129,8 @@ protected:
 
     // Helper for checking Rcode statistic counters;
     // Checks for one specific Rcode statistics counter value
-    void checkRcodeCounter(const std::string& rcode_name, const int rcode_value,
+    void checkRcodeCounter(const std::string& rcode_name,
+                           const int rcode_value,
                            const int expected_value) const
     {
             EXPECT_EQ(expected_value, rcode_value) <<
@@ -133,38 +139,26 @@ protected:
                       rcode_value;
     }
 
-    // Checks whether all Rcode counters are set to zero
-    void checkAllRcodeCountersZero() const {
-        // with checking NOERROR == 0 and the others are 0
-        checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 0);
-    }
-
     // Checks whether all Rcode counters are set to zero except the given
     // rcode (it is checked to be set to 'value')
     void checkAllRcodeCountersZeroExcept(const Rcode& rcode, int value) const {
         std::string target_rcode_name = rcode.toText();
         std::transform(target_rcode_name.begin(), target_rcode_name.end(),
                        target_rcode_name.begin(), ::tolower);
-        // rcode 16 is registered as both BADVERS and BADSIG
-        if (target_rcode_name == "badvers") {
-            target_rcode_name = "badsigvers";
-        }
 
         const std::map<std::string, ConstElementPtr>
-            stats_map(server.getStatistics()->mapValue());
+            stats_map(server.getStatistics()->get("zones")->get("_SERVER_")->
+                      get("rcode")->mapValue());
 
-        const std::string rcode_prefix("rcode.");
         for (std::map<std::string, ConstElementPtr>::const_iterator
                  i = stats_map.begin(), e = stats_map.end();
              i != e;
              ++i)
         {
-            if (i->first.compare(0, rcode_prefix.size(), rcode_prefix) == 0) {
-                if (i->first.compare(rcode_prefix + target_rcode_name) == 0) {
-                    checkRcodeCounter(i->first, i->second->intValue(), value);
-                } else {
-                    checkRcodeCounter(i->first, i->second->intValue(), 0);
-                }
+            if (i->first.compare(target_rcode_name) == 0) {
+                checkRcodeCounter(i->first, i->second->intValue(), value);
+            } else {
+                checkRcodeCounter(i->first, i->second->intValue(), 0);
             }
         }
     }
@@ -199,6 +193,15 @@ protected:
                               &dnsserv);
     }
 
+    // Check if the counters exist and are initialized to 0.
+    void
+    checkCountersAreInitialized() {
+        const std::map<std::string, int> expect;
+        ConstElementPtr stats = server.getStatistics()->
+            get("zones")->get("_SERVER_");
+        checkStatisticsCounters(stats, expect);
+    }
+
     MockDNSService dnss_;
     MockXfroutClient xfrout;
     MockSocketSessionForwarder ddns_forwarder;
@@ -241,29 +244,6 @@ createBuiltinVersionResponse(const qid_t qid, vector<uint8_t>& data) {
                 renderer.getLength());
 }
 
-// Check if the item has expected value.
-// Before reading the item, check the item exists.
-void
-expectCounterItem(ConstElementPtr stats,
-                  const std::string& item, const int expected) {
-    ConstElementPtr value(Element::create(0));
-    if (item == "queries.udp" || item == "queries.tcp" || expected != 0) {
-        // if the value of the item is not zero, the item exists and has
-        // expected value
-        // item "queries.udp" and "queries.tcp" exists whether the value
-        // is zero or nonzero
-        ASSERT_TRUE(stats->find(item, value)) << "    Item: " << item;
-        // Get the value of the item with another method because of API bug
-        // (ticket #2302)
-        value = stats->find(item);
-        EXPECT_EQ(expected, value->intValue()) << "    Item: " << item;
-    } else {
-        // otherwise the item does not exist
-        ASSERT_FALSE(stats->find(item, value)) << "    Item: " << item <<
-            std::endl << "   Value: " << value->intValue();
-    }
-}
-
 // We did not configure any client lists. Therefore it should be REFUSED
 TEST_F(AuthSrvTest, noClientList) {
     UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
@@ -276,6 +256,18 @@ TEST_F(AuthSrvTest, noClientList) {
     EXPECT_TRUE(dnsserv.hasAnswer());
     headerCheck(*parse_message, default_qid, Rcode::REFUSED(),
                 opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
+
+    ConstElementPtr stats_after = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    std::map<std::string, int> expect;
+    expect["request.v4"] = 1;
+    expect["request.udp"] = 1;
+    expect["opcode.query"] = 1;
+    expect["responses"] = 1;
+    expect["qrynoauthans"] = 1;
+    expect["authqryrej"] = 1;
+    expect["rcode.refused"] = 1;
+    checkStatisticsCounters(stats_after, expect);
 }
 
 // Unsupported requests.  Should result in NOTIMP.
@@ -295,32 +287,74 @@ TEST_F(AuthSrvTest, multiQuestion) {
 // dropped.
 TEST_F(AuthSrvTest, shortMessage) {
     shortMessage();
-    checkAllRcodeCountersZero();
+
+    ConstElementPtr stats_after = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    std::map<std::string, int> expect;
+    expect["request.v4"] = 1;
+    expect["request.udp"] = 1;
+    checkStatisticsCounters(stats_after, expect);
 }
 
 // Response messages.  Must be silently dropped, whether it's a valid response
 // or malformed or could otherwise cause a protocol error.
 TEST_F(AuthSrvTest, response) {
+    // isc::testutils::SrvTestBase::response() processes 3 messages.
     response();
-    checkAllRcodeCountersZero();
+
+    ConstElementPtr stats_after = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    std::map<std::string, int> expect;
+    expect["request.v4"] = 3;
+    expect["request.udp"] = 3;
+    checkStatisticsCounters(stats_after, expect);
 }
 
 // Query with a broken question
 TEST_F(AuthSrvTest, shortQuestion) {
     shortQuestion();
-    checkAllRcodeCountersZeroExcept(Rcode::FORMERR(), 1);
+    ConstElementPtr stats_after = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    std::map<std::string, int> expect;
+    expect["request.v4"] = 1;
+    expect["request.udp"] = 1;
+    expect["opcode.query"] = 1;
+    expect["responses"] = 1;
+    expect["rcode.formerr"] = 1;
+    expect["qrynoauthans"] = 1;
+    checkStatisticsCounters(stats_after, expect);
 }
 
 // Query with a broken answer section
 TEST_F(AuthSrvTest, shortAnswer) {
     shortAnswer();
-    checkAllRcodeCountersZeroExcept(Rcode::FORMERR(), 1);
+    ConstElementPtr stats_after = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    std::map<std::string, int> expect;
+    expect["request.v4"] = 1;
+    expect["request.udp"] = 1;
+    expect["opcode.query"] = 1;
+    expect["responses"] = 1;
+    expect["rcode.formerr"] = 1;
+    expect["qrynoauthans"] = 1;
+    checkStatisticsCounters(stats_after, expect);
 }
 
 // Query with unsupported version of EDNS.
 TEST_F(AuthSrvTest, ednsBadVers) {
     ednsBadVers();
-    checkAllRcodeCountersZeroExcept(Rcode::BADVERS(), 1);
+
+    ConstElementPtr stats_after = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    std::map<std::string, int> expect;
+    expect["request.v4"] = 1;
+    expect["request.badednsver"] = 1;
+    expect["request.udp"] = 1;
+    expect["opcode.query"] = 1;
+    expect["responses"] = 1;
+    expect["rcode.badvers"] = 1;
+    expect["qrynoauthans"] = 1;
+    checkStatisticsCounters(stats_after, expect);
 }
 
 TEST_F(AuthSrvTest, AXFROverUDP) {
@@ -339,7 +373,14 @@ TEST_F(AuthSrvTest, AXFRSuccess) {
                           &dnsserv);
     EXPECT_FALSE(dnsserv.hasAnswer());
     EXPECT_TRUE(xfrout.isConnected());
-    checkAllRcodeCountersZero();
+
+    ConstElementPtr stats_after = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    std::map<std::string, int> expect;
+    expect["request.v4"] = 1;
+    expect["request.tcp"] = 1;
+    expect["opcode.query"] = 1;
+    checkStatisticsCounters(stats_after, expect);
 }
 
 // Give the server a signed request, but don't give it the key. It will
@@ -373,7 +414,20 @@ TEST_F(AuthSrvTest, TSIGSignedBadKey) {
     EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
         "It should be unsigned with this error";
 
+    // check Rcode counters and TSIG counters
     checkAllRcodeCountersZeroExcept(Rcode::NOTAUTH(), 1);
+    ConstElementPtr stats_after = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    std::map<std::string, int> expect;
+    expect["request.v4"] = 1;
+    expect["request.tsig"] = 1;
+    expect["request.badsig"] = 1;
+    expect["request.udp"] = 1;
+    expect["opcode.query"] = 1;
+    expect["responses"] = 1;
+    expect["response.tsig"] = 1;
+    expect["rcode.notauth"] = 1;
+    checkStatisticsCounters(stats_after, expect);
 }
 
 // Give the server a signed request, but signed by a different key
@@ -408,7 +462,18 @@ TEST_F(AuthSrvTest, TSIGBadSig) {
     EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
         "It should be unsigned with this error";
 
-    checkAllRcodeCountersZeroExcept(Rcode::NOTAUTH(), 1);
+    ConstElementPtr stats_after = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    std::map<std::string, int> expect;
+    expect["request.v4"] = 1;
+    expect["request.tsig"] = 1;
+    expect["request.badsig"] = 1;
+    expect["request.udp"] = 1;
+    expect["opcode.query"] = 1;
+    expect["responses"] = 1;
+    expect["response.tsig"] = 1;
+    expect["rcode.notauth"] = 1;
+    checkStatisticsCounters(stats_after, expect);
 }
 
 // Give the server a signed unsupported request with a bad signature.
@@ -445,13 +510,19 @@ TEST_F(AuthSrvTest, TSIGCheckFirst) {
     EXPECT_EQ(TSIGError::BAD_SIG_CODE, tsig->getRdata().getError());
     EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
         "It should be unsigned with this error";
-    // TSIG should have failed, and so the per opcode counter shouldn't be
-    // incremented.
-    ConstElementPtr stats = server.getStatistics();
-    expectCounterItem(stats, "opcode.normal", 0);
-    expectCounterItem(stats, "opcode.other", 0);
 
-    checkAllRcodeCountersZeroExcept(Rcode::NOTAUTH(), 1);
+    ConstElementPtr stats_after = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    std::map<std::string, int> expect;
+    expect["request.v4"] = 1;
+    expect["request.tsig"] = 1;
+    expect["request.badsig"] = 1;
+    expect["request.udp"] = 1;
+    expect["opcode.other"] = 1;
+    expect["responses"] = 1;
+    expect["response.tsig"] = 1;
+    expect["rcode.notauth"] = 1;
+    checkStatisticsCounters(stats_after, expect);
 }
 
 TEST_F(AuthSrvTest, AXFRConnectFail) {
@@ -589,7 +660,8 @@ TEST_F(AuthSrvTest, notify) {
     // external module.  Check them.
     EXPECT_EQ("Zonemgr", notify_session.getMessageDest());
     EXPECT_EQ("notify",
-              notify_session.getSentMessage()->get("command")->get(0)->stringValue());
+              notify_session.getSentMessage()->get("command")->get(0)->
+                  stringValue());
     ConstElementPtr notify_args =
         notify_session.getSentMessage()->get("command")->get(1);
     EXPECT_EQ("example.com.", notify_args->get("zone_name")->stringValue());
@@ -607,7 +679,15 @@ TEST_F(AuthSrvTest, notify) {
     EXPECT_EQ(RRClass::IN(), question->getClass());
     EXPECT_EQ(RRType::SOA(), question->getType());
 
-    checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
+    ConstElementPtr stats_after = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    std::map<std::string, int> expect;
+    expect["request.v4"] = 1;
+    expect["request.udp"] = 1;
+    expect["opcode.notify"] = 1;
+    expect["responses"] = 1;
+    expect["rcode.noerror"] = 1;
+    checkStatisticsCounters(stats_after, expect);
 }
 
 TEST_F(AuthSrvTest, notifyForCHClass) {
@@ -626,6 +706,16 @@ TEST_F(AuthSrvTest, notifyForCHClass) {
     ConstElementPtr notify_args =
         notify_session.getSentMessage()->get("command")->get(1);
     EXPECT_EQ("CH", notify_args->get("zone_class")->stringValue());
+
+    ConstElementPtr stats_after = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    std::map<std::string, int> expect;
+    expect["request.v4"] = 1;
+    expect["request.udp"] = 1;
+    expect["opcode.notify"] = 1;
+    expect["responses"] = 1;
+    expect["rcode.noerror"] = 1;
+    checkStatisticsCounters(stats_after, expect);
 }
 
 TEST_F(AuthSrvTest, notifyEmptyQuestion) {
@@ -641,6 +731,16 @@ TEST_F(AuthSrvTest, notifyEmptyQuestion) {
     EXPECT_TRUE(dnsserv.hasAnswer());
     headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
                 Opcode::NOTIFY().getCode(), QR_FLAG, 0, 0, 0, 0);
+
+    ConstElementPtr stats_after = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    std::map<std::string, int> expect;
+    expect["request.v4"] = 1;
+    expect["request.udp"] = 1;
+    expect["opcode.notify"] = 1;
+    expect["responses"] = 1;
+    expect["rcode.formerr"] = 1;
+    checkStatisticsCounters(stats_after, expect);
 }
 
 TEST_F(AuthSrvTest, notifyMultiQuestions) {
@@ -854,6 +954,19 @@ TEST_F(AuthSrvTest, TSIGSigned) {
         "The server signed the response, but it doesn't seem to be valid";
 
     checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
+    ConstElementPtr stats_after = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    std::map<std::string, int> expect;
+    expect["request.v4"] = 1;
+    expect["request.tsig"] = 1;
+    expect["request.udp"] = 1;
+    expect["opcode.query"] = 1;
+    expect["responses"] = 1;
+    expect["response.tsig"] = 1;
+    expect["qrysuccess"] = 1;
+    expect["qryauthans"] = 1;
+    expect["rcode.noerror"] = 1;
+    checkStatisticsCounters(stats_after, expect);
 }
 
 // Same test emulating the UDPServer class behavior (defined in libasiolink).
@@ -954,8 +1067,8 @@ TEST_F(AuthSrvTest, updateConfig) {
     server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
     EXPECT_TRUE(dnsserv.hasAnswer());
-    headerCheck(*parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
-                QR_FLAG | AA_FLAG, 1, 1, 1, 0);
+    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
+                opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 1, 0);
 }
 
 #ifdef USE_STATIC_LINK
@@ -975,6 +1088,18 @@ TEST_F(AuthSrvTest, datasourceFail) {
     EXPECT_TRUE(dnsserv.hasAnswer());
     headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
                 opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
+
+    checkAllRcodeCountersZeroExcept(Rcode::SERVFAIL(), 1);
+    ConstElementPtr stats = server.getStatistics()->get("zones")->
+        get("_SERVER_");
+    std::map<std::string, int> expect;
+    expect["request.v4"] = 1;
+    expect["request.udp"] = 1;
+    expect["opcode.query"] = 1;
+    expect["responses"] = 1;
+    expect["qrynoauthans"] = 1;
+    expect["rcode.servfail"] = 1;
+    checkStatisticsCounters(stats, expect);
 }
 
 #ifdef USE_STATIC_LINK
@@ -1083,14 +1208,39 @@ TEST_F(AuthSrvTest,
                 opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 1, 0);
 }
 
+#ifdef USE_STATIC_LINK
+TEST_F(AuthSrvTest, DISABLED_queryCounterTruncTest) {
+#else
+TEST_F(AuthSrvTest, queryCounterTruncTest) {
+#endif
+    // use CONFIG_TESTDB for large-rdata.example.com.
+    updateDatabase(server, CONFIG_TESTDB);
+
+    // Create UDP message and process.
+    // large-rdata.example.com. TXT; expect it exceeds 512 octet
+    UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
+                                       default_qid,
+                                       Name("large-rdata.example.com."),
+                                       RRClass::IN(), RRType::TXT());
+    createRequestPacket(request_message, IPPROTO_UDP);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
+
+    ConstElementPtr stats_after = server.getStatistics()->
+        get("zones")->get("_SERVER_");
+    std::map<std::string, int> expect;
+    expect["request.v4"] = 1;
+    expect["request.udp"] = 1;
+    expect["opcode.query"] = 1;
+    expect["responses"] = 1;
+    expect["response.truncated"] = 1;
+    expect["qrysuccess"] = 1;
+    expect["qryauthans"] = 1;
+    expect["rcode.noerror"] = 1;
+    checkStatisticsCounters(stats_after, expect);
+}
 // Submit UDP normal query and check query counter
 TEST_F(AuthSrvTest, queryCounterUDPNormal) {
-    // The counters should be initialized to 0.
-    ConstElementPtr stats_init = server.getStatistics();
-    expectCounterItem(stats_init, "queries.udp", 0);
-    expectCounterItem(stats_init, "queries.tcp", 0);
-    expectCounterItem(stats_init, "opcode.query", 0);
-    expectCounterItem(stats_init, "rcode.refused", 0);
     // Create UDP message and process.
     UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
                                        default_qid, Name("example.com"),
@@ -1098,25 +1248,50 @@ TEST_F(AuthSrvTest, queryCounterUDPNormal) {
     createRequestPacket(request_message, IPPROTO_UDP);
     server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
-    // After processing the UDP query, these counters should be incremented:
-    //   queries.udp, opcode.query, rcode.refused
-    // and these counters should not be incremented:
-    //   queries.tcp
-    ConstElementPtr stats_after = server.getStatistics();
-    expectCounterItem(stats_after, "queries.udp", 1);
-    expectCounterItem(stats_after, "queries.tcp", 0);
-    expectCounterItem(stats_after, "opcode.query", 1);
-    expectCounterItem(stats_after, "rcode.refused", 1);
+
+    ConstElementPtr stats_after = server.getStatistics()->
+        get("zones")->get("_SERVER_");
+    std::map<std::string, int> expect;
+    expect["request.v4"] = 1;
+    expect["request.udp"] = 1;
+    expect["opcode.query"] = 1;
+    expect["responses"] = 1;
+    expect["qrynoauthans"] = 1;
+    expect["authqryrej"] = 1;
+    expect["rcode.refused"] = 1;
+    checkStatisticsCounters(stats_after, expect);
+}
+
+// Submit UDP normal query with DNSSEC and check query counter
+TEST_F(AuthSrvTest, queryCounterUDPNormalWithDNSSEC) {
+    // Create UDP message and process.
+    UnitTestUtil::createDNSSECRequestMessage(request_message, Opcode::QUERY(),
+                                             default_qid, Name("example.com"),
+                                             RRClass::IN(), RRType::NS());
+    createRequestPacket(request_message, IPPROTO_UDP);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
+
+    ConstElementPtr stats_after = server.getStatistics()->
+        get("zones")->get("_SERVER_");
+    std::map<std::string, int> expect;
+    expect["request.v4"] = 1;
+    expect["request.edns0"] = 1;
+    expect["request.udp"] = 1;
+    expect["request.dnssec_ok"] = 1;
+    expect["opcode.query"] = 1;
+    expect["responses"] = 1;
+    expect["qrynoauthans"] = 1;
+    expect["authqryrej"] = 1;
+    expect["rcode.refused"] = 1;
+    // XXX: with the current implementation, EDNS0 is omitted in
+    // makeErrorMessage.
+    expect["response.edns0"] = 0;
+    checkStatisticsCounters(stats_after, expect);
 }
 
 // Submit TCP normal query and check query counter
 TEST_F(AuthSrvTest, queryCounterTCPNormal) {
-    // The counters should be initialized to 0.
-    ConstElementPtr stats_init = server.getStatistics();
-    expectCounterItem(stats_init, "queries.udp", 0);
-    expectCounterItem(stats_init, "queries.tcp", 0);
-    expectCounterItem(stats_init, "opcode.query", 0);
-    expectCounterItem(stats_init, "rcode.refused", 0);
     // Create TCP message and process.
     UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
                                        default_qid, Name("example.com"),
@@ -1124,24 +1299,22 @@ TEST_F(AuthSrvTest, queryCounterTCPNormal) {
     createRequestPacket(request_message, IPPROTO_TCP);
     server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
-    // After processing the TCP query, these counters should be incremented:
-    //   queries.tcp, opcode.query, rcode.refused
-    // and these counters should not be incremented:
-    //   queries.udp
-    ConstElementPtr stats_after = server.getStatistics();
-    expectCounterItem(stats_after, "queries.udp", 0);
-    expectCounterItem(stats_after, "queries.tcp", 1);
-    expectCounterItem(stats_after, "opcode.query", 1);
-    expectCounterItem(stats_after, "rcode.refused", 1);
+
+    ConstElementPtr stats_after = server.getStatistics()->
+        get("zones")->get("_SERVER_");
+    std::map<std::string, int> expect;
+    expect["request.v4"] = 1;
+    expect["request.tcp"] = 1;
+    expect["opcode.query"] = 1;
+    expect["responses"] = 1;
+    expect["qrynoauthans"] = 1;
+    expect["authqryrej"] = 1;
+    expect["rcode.refused"] = 1;
+    checkStatisticsCounters(stats_after, expect);
 }
 
 // Submit TCP AXFR query and check query counter
 TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
-    // The counters should be initialized to 0.
-    ConstElementPtr stats_init = server.getStatistics();
-    expectCounterItem(stats_init, "queries.udp", 0);
-    expectCounterItem(stats_init, "queries.tcp", 0);
-    expectCounterItem(stats_init, "opcode.query", 0);
     UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
                          Name("example.com"), RRClass::IN(), RRType::AXFR());
     createRequestPacket(request_message, IPPROTO_TCP);
@@ -1150,24 +1323,18 @@ TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
     server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
     EXPECT_FALSE(dnsserv.hasAnswer());
-    // After processing the TCP AXFR query, these counters should be
-    // incremented:
-    //   queries.tcp, opcode.query
-    // and these counters should not be incremented:
-    //   queries.udp
-    ConstElementPtr stats_after = server.getStatistics();
-    expectCounterItem(stats_after, "queries.udp", 0);
-    expectCounterItem(stats_after, "queries.tcp", 1);
-    expectCounterItem(stats_after, "opcode.query", 1);
+
+    ConstElementPtr stats_after = server.getStatistics()->
+        get("zones")->get("_SERVER_");
+    std::map<std::string, int> expect;
+    expect["request.v4"] = 1;
+    expect["request.tcp"] = 1;
+    expect["opcode.query"] = 1;
+    checkStatisticsCounters(stats_after, expect);
 }
 
 // Submit TCP IXFR query and check query counter
 TEST_F(AuthSrvTest, queryCounterTCPIXFR) {
-    // The counters should be initialized to 0.
-    ConstElementPtr stats_init = server.getStatistics();
-    expectCounterItem(stats_init, "queries.udp", 0);
-    expectCounterItem(stats_init, "queries.tcp", 0);
-    expectCounterItem(stats_init, "opcode.query", 0);
     UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
                          Name("example.com"), RRClass::IN(), RRType::IXFR());
     createRequestPacket(request_message, IPPROTO_TCP);
@@ -1176,27 +1343,38 @@ TEST_F(AuthSrvTest, queryCounterTCPIXFR) {
     server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
     EXPECT_FALSE(dnsserv.hasAnswer());
-    // After processing the TCP IXFR query, these counters should be
-    // incremented:
-    //   queries.tcp, opcode.query
-    // and these counters should not be incremented:
-    //   queries.udp
-    ConstElementPtr stats_after = server.getStatistics();
-    expectCounterItem(stats_after, "queries.udp", 0);
-    expectCounterItem(stats_after, "queries.tcp", 1);
-    expectCounterItem(stats_after, "opcode.query", 1);
+
+    ConstElementPtr stats_after = server.getStatistics()->
+        get("zones")->get("_SERVER_");
+    std::map<std::string, int> expect;
+    expect["request.v4"] = 1;
+    expect["request.tcp"] = 1;
+    expect["opcode.query"] = 1;
+    checkStatisticsCounters(stats_after, expect);
 }
 
 TEST_F(AuthSrvTest, queryCounterOpcodes) {
-    // Check for 0..2, 3(=other), 4..5
-    // The counter should be initialized to 0.
-    for (int i = 0; i < 6; ++i) {
-        // The counter should be initialized to 0.
-        expectCounterItem(server.getStatistics(),
-                          std::string("opcode.") +
-                              QRCounterOpcode[QROpCodeToQRCounterType[i] -
-                                                  QR_OPCODE_QUERY].name,
-                          0);
+    int other_expected = 0;
+    for (int i = 0; i < isc::auth::statistics::num_opcode_to_msgcounter; ++i) {
+        std::string item_name;
+        int expected;
+        if (isc::auth::statistics::opcode_to_msgcounter[i] ==
+                isc::auth::statistics::MSG_OPCODE_OTHER)
+        {
+            item_name = "OTHER";
+            other_expected += i + 1;
+            expected = other_expected;
+        } else {
+            item_name = Opcode(i).toText();
+            expected = i + 1;
+        }
+        std::transform(item_name.begin(), item_name.end(), item_name.begin(),
+                       ::tolower);
+
+        // The counter should be initialized to expected value.
+        EXPECT_EQ(expected - (i + 1),
+                  server.getStatistics()->get("zones")->get("_SERVER_")->
+                  get("opcode")->get(item_name)->intValue());
 
         // For each possible opcode, create a request message and send it
         UnitTestUtil::createRequestMessage(request_message, Opcode(i),
@@ -1214,45 +1392,11 @@ TEST_F(AuthSrvTest, queryCounterOpcodes) {
         }
 
         // Confirm the counter.
-        expectCounterItem(server.getStatistics(),
-                          std::string("opcode.") +
-                              QRCounterOpcode[QROpCodeToQRCounterType[i] -
-                                                  QR_OPCODE_QUERY].name,
-                          i + 1);
-    }
-    // Check for 6..15
-    // they are treated as the 'other' opcode
-    // the 'other' opcode counter is 4 at this point
-    int expected = 4;
-    for (int i = 6; i < 16; ++i) {
-        // The counter should be initialized to 0.
-        expectCounterItem(server.getStatistics(),
-                          std::string("opcode.") +
-                              QRCounterOpcode[QROpCodeToQRCounterType[i] -
-                                              QR_OPCODE_QUERY].name,
-                          expected);
-
-        // For each possible opcode, create a request message and send it
-        UnitTestUtil::createRequestMessage(request_message, Opcode(i),
-                                           default_qid, Name("example.com"),
-                                           RRClass::IN(), RRType::NS());
-        createRequestPacket(request_message, IPPROTO_UDP);
-
-        // "send" the request once
-        parse_message->clear(Message::PARSE);
-        server.processMessage(*io_message, *parse_message,
-                              *response_obuffer,
-                              &dnsserv);
-
-        // the 'other' opcode counter should be incremented
-        ++expected;
-
-        // Confirm the counter.
-        expectCounterItem(server.getStatistics(),
-                          std::string("opcode.") +
-                              QRCounterOpcode[QROpCodeToQRCounterType[i] -
-                                              QR_OPCODE_QUERY].name,
-                          expected);
+        // This test only checks for opcodes; some part of the other items
+        // depends on the opcode.
+        EXPECT_EQ(expected,
+                  server.getStatistics()->get("zones")->get("_SERVER_")->
+                  get("opcode")->get(item_name)->intValue());
     }
 }
 

+ 90 - 0
src/bin/auth/tests/gen-statisticsitems_test.py

@@ -0,0 +1,90 @@
+# Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+"""\
+This script checks output of gen-statisticsitems.py.
+
+This script checks XML file. Spec file, C++ code and header files syntax is
+checked in the other unittests or system tests.
+"""
+
+import os
+import sys
+from xml.etree import ElementTree
+
+"""\
+User-defined exception for parse error. It is thrown if a file is not
+formatted as expected.
+"""
+class ParseError(Exception):
+    pass
+
+"""\
+Test XML file.
+
+It should have <refsect1> which has <title>STATISTICS DATA</title>.
+Inside the section, it should have one or more <varlistentry> of each item
+inside <variablelist>.
+Each <varlistentry> should have <term> for item name and <simpara> inside
+<listitem> for item description.
+
+Example:
+    <refsect1>
+        <title>STATISTICS DATA</title>
+        <variablelist>
+        <varlistentry>
+          <term>item1</term>
+          <listitem><simpara>statistics item</simpara></listitem>
+        </varlistentry>
+        <varlistentry>
+          <term>item2</term>
+          <listitem><simpara>another statistics item</simpara></listitem>
+        </varlistentry>
+        </variablelist>
+    </refsect1>
+"""
+def test_xml_file(xmlfilepath):
+    xmltree = ElementTree.parse(xmlfilepath)
+    root = xmltree.getroot()
+    # find <refsect1> which has <title> of 'STATISTICS DATA'
+    stats_node = [t for t in root.findall('./refsect1')
+            if t.find('./title').text == 'STATISTICS DATA']
+    if not stats_node:
+        raise ParseError('Statistics data section does not exist')
+    # find all <varlistentry> inside <variablelist>
+    entries = stats_node[0].find('./variablelist').findall('./varlistentry')
+    if not entries:
+        raise ParseError('<varlistentry> does not exist inside section')
+    for entry in entries:
+        # find <term> for item name
+        name = entry.find('./term')
+        if name is None or name.text == '':
+            raise ParseError('<term> for item name does not exist')
+        # find <simpara> inside <listitem> for item description
+        description = entry.find('./listitem/simpara')
+        if description is None or description.text == '':
+            raise ParseError('<listitem> nor <simpara> for item description'
+                             ' does not exist')
+    return
+
+if __name__ == "__main__":
+    xmlfilepath = sys.argv[1]
+    try:
+        test_xml_file(xmlfilepath)
+    except ImportError:
+        # pyexpat library is required for ElementTree.parse() but it is
+        # missing in some environment. Just skip this test.
+        print ("Required library is missing, skipping this test.")
+        print ("Detailed information:")
+        print (sys.exc_info()[1])

+ 0 - 123
src/bin/auth/tests/statistics_unittest.cc

@@ -1,123 +0,0 @@
-// Copyright (C) 2010  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 <string>
-
-#include <gtest/gtest.h>
-
-#include <boost/bind.hpp>
-
-#include <dns/opcode.h>
-#include <dns/rcode.h>
-
-#include <cc/data.h>
-#include <cc/session.h>
-
-#include <auth/statistics.h>
-#include <auth/statistics_items.h>
-
-#include <dns/tests/unittest_util.h>
-
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netdb.h>
-
-using namespace std;
-using namespace isc::cc;
-using namespace isc::dns;
-using namespace isc::data;
-using isc::auth::statistics::Counters;
-using isc::auth::statistics::QRAttributes;
-
-namespace {
-
-class CountersTest : public ::testing::Test {
-protected:
-    CountersTest() : counters() {}
-    ~CountersTest() {}
-    Counters counters;
-};
-
-// Test if the values of the counters are all zero except for the items
-// specified in except_for.
-void
-checkCountersAllZeroExcept(const isc::data::ConstElementPtr counters,
-                           const std::set<std::string>& except_for) {
-    std::map<std::string, ConstElementPtr> stats_map = counters->mapValue();
-
-    for (std::map<std::string, ConstElementPtr>::const_iterator
-            i = stats_map.begin(), e = stats_map.end();
-            i != e;
-            ++i)
-    {
-        int expect = 0;
-        if (except_for.count(i->first) != 0) {
-            expect = 1;
-        }
-        EXPECT_EQ(expect, i->second->intValue()) << "Expected counter "
-            << i->first << " = " << expect << ", actual: "
-            << i->second->intValue();
-    }
-}
-
-TEST_F(CountersTest, incrementNormalQuery) {
-    Message response(Message::RENDER);
-    QRAttributes qrattrs;
-    std::set<std::string> expect_nonzero;
-
-    expect_nonzero.clear();
-    checkCountersAllZeroExcept(counters.getStatistics(), expect_nonzero);
-
-    qrattrs.setQueryIPVersion(AF_INET6);
-    qrattrs.setQueryTransportProtocol(IPPROTO_UDP);
-    qrattrs.setQueryOpCode(Opcode::QUERY_CODE);
-    qrattrs.setQueryEDNS(true, false);
-    qrattrs.setQueryDO(true);
-    qrattrs.answerWasSent();
-
-    response.setRcode(Rcode::REFUSED());
-    response.addQuestion(Question(Name("example.com"),
-                                  RRClass::IN(), RRType::AAAA()));
-
-    counters.inc(qrattrs, response);
-
-    expect_nonzero.clear();
-    expect_nonzero.insert("opcode.query");
-    expect_nonzero.insert("queries.udp");
-    expect_nonzero.insert("rcode.refused");
-    checkCountersAllZeroExcept(counters.getStatistics(), expect_nonzero);
-}
-
-int
-countTreeElements(const struct CounterTypeTree* tree) {
-    int count = 0;
-    for (int i = 0; tree[i].name != NULL; ++i) {
-        if (tree[i].sub_tree == NULL) {
-            ++count;
-        } else {
-            count += countTreeElements(tree[i].sub_tree);
-        }
-    }
-    return count;
-}
-
-TEST(StatisticsItemsTest, QRItemNamesCheck) {
-    EXPECT_EQ(QR_COUNTER_TYPES, countTreeElements(QRCounterTree));
-}
-
-}

+ 714 - 0
src/bin/auth/tests/statistics_unittest.cc.pre

@@ -0,0 +1,714 @@
+// Copyright (C) 2010  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 <gtest/gtest.h>
+
+#include <boost/bind.hpp>
+
+#include <dns/opcode.h>
+#include <dns/rcode.h>
+#include <dns/rrttl.h>
+
+#include <cc/data.h>
+
+#include <auth/statistics.h>
+#include <auth/statistics_items.h>
+
+#include <dns/tests/unittest_util.h>
+
+#include "statistics_util.h"
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+using namespace std;
+using namespace isc::dns;
+using namespace isc::data;
+using namespace isc::auth::statistics;
+using namespace isc::auth::unittest;
+
+namespace {
+
+// ### STATISTICS ITEMS DEFINITION ###
+
+class CountersTest : public ::testing::Test {
+protected:
+    CountersTest() : counters() {}
+    ~CountersTest() {}
+    Counters counters;
+};
+
+void
+buildSkeletonMessage(MessageAttributes& msgattrs) {
+    msgattrs.setRequestIPVersion(AF_INET);
+    msgattrs.setRequestTransportProtocol(IPPROTO_UDP);
+    msgattrs.setRequestOpCode(Opcode::QUERY());
+    msgattrs.setRequestEDNS0(true);
+    msgattrs.setRequestDO(true);
+}
+
+TEST_F(CountersTest, invalidParameterForSetRequestIPVersion) {
+    MessageAttributes msgattrs;
+
+    // It should not throw if the parameter is AF_INET or AF_INET6.
+    EXPECT_NO_THROW(msgattrs.setRequestIPVersion(AF_INET));
+    EXPECT_NO_THROW(msgattrs.setRequestIPVersion(AF_INET6));
+
+    // It should throw isc::InvalidParameter if the parameter is not AF_INET
+    // nor AF_INET6.
+    EXPECT_THROW(msgattrs.setRequestIPVersion(AF_UNIX), isc::InvalidParameter);
+}
+
+TEST_F(CountersTest, invalidParameterForSetRequestTransportProtocol) {
+    MessageAttributes msgattrs;
+
+    // It should not throw if the parameter is IPPROTO_UDP or IPPROTO_TCP.
+    EXPECT_NO_THROW(msgattrs.setRequestTransportProtocol(IPPROTO_UDP));
+    EXPECT_NO_THROW(msgattrs.setRequestTransportProtocol(IPPROTO_TCP));
+
+    // It should throw isc::InvalidParameter if the parameter is not
+    // IPPROTO_UDP nor IPPROTO_TCP.
+    EXPECT_THROW(msgattrs.setRequestTransportProtocol(IPPROTO_IP),
+                 isc::InvalidParameter);
+}
+
+TEST_F(CountersTest, invalidOperationForGetRequestOpCode) {
+    MessageAttributes msgattrs;
+
+    // getRequestOpCode() should return boost::none when called before
+    // opcode is set with setRequestOpCode().
+    EXPECT_FALSE(msgattrs.getRequestOpCode());
+
+    msgattrs.setRequestOpCode(Opcode::QUERY());
+    // getRequestOpCode() should be Opcode::QUERY.
+    EXPECT_EQ(Opcode::QUERY(), msgattrs.getRequestOpCode().get());
+}
+
+TEST_F(CountersTest, invalidParameterForSetRequestTSIG) {
+    MessageAttributes msgattrs;
+
+    // These patterns should not throw:
+    //      request signature  badsig
+    //     --------------------------
+    //      (none)             false
+    //      TSIG               false
+    //      TSIG               true
+    EXPECT_NO_THROW(msgattrs.setRequestTSIG(false, false));
+    EXPECT_NO_THROW(msgattrs.setRequestTSIG(true, false));
+    EXPECT_NO_THROW(msgattrs.setRequestTSIG(true, true));
+
+    // It should throw isc::InvalidParameter if a message is not signed but
+    // badsig is true
+    EXPECT_THROW(msgattrs.setRequestTSIG(false, true), isc::InvalidParameter);
+}
+
+TEST_F(CountersTest, incrementResponse) {
+    Message response(Message::RENDER);
+    MessageAttributes msgattrs;
+    std::map<std::string, int> expect;
+
+    // Test response counters are incremented only if responded == true.
+    for (int i = 0; i < 2; ++i) {
+        const bool responded = i & 1;
+
+        buildSkeletonMessage(msgattrs);
+
+        response.setRcode(Rcode::REFUSED());
+        response.addQuestion(Question(Name("example.com"),
+                                      RRClass::IN(), RRType::AAAA()));
+        response.setHeaderFlag(Message::HEADERFLAG_QR);
+
+        counters.inc(msgattrs, response, responded);
+
+        expect.clear();
+        expect["opcode.query"] = i+1;
+        expect["request.v4"] = i+1;
+        expect["request.udp"] = i+1;
+        expect["request.edns0"] = i+1;
+        expect["request.badednsver"] = 0;
+        expect["request.dnssec_ok"] = i+1;
+        expect["responses"] = responded ? 1 : 0;
+        expect["qrynoauthans"] = responded ? 1 : 0;
+        expect["rcode.refused"] = responded ? 1 : 0;
+        expect["authqryrej"] = responded ? 1 : 0;
+        checkStatisticsCounters(counters.get()->get("zones")->get("_SERVER_"),
+                                expect);
+    }
+}
+
+TEST_F(CountersTest, incrementProtocolType) {
+    Message response(Message::RENDER);
+    MessageAttributes msgattrs;
+    std::map<std::string, int> expect;
+
+    // Test these patterns:
+    //      af     proto
+    //     -----------------
+    //      ipv6   tcp
+    //      ipv4   tcp
+    //      ipv6   udp
+    //      ipv4   udp
+    int count_v4 = 0, count_v6 = 0, count_udp = 0, count_tcp = 0;
+    for (int i = 0; i < 4; ++i) {
+        const int af = i & 1 ? AF_INET : AF_INET6;
+        const int proto = i & 2 ? IPPROTO_UDP : IPPROTO_TCP;
+
+        buildSkeletonMessage(msgattrs);
+        msgattrs.setRequestIPVersion(af);
+        msgattrs.setRequestTransportProtocol(proto);
+
+        response.setRcode(Rcode::REFUSED());
+        response.addQuestion(Question(Name("example.com"),
+                                      RRClass::IN(), RRType::AAAA()));
+        response.setHeaderFlag(Message::HEADERFLAG_QR);
+
+        counters.inc(msgattrs, response, true);
+
+        if (af == AF_INET) {
+            ++count_v4;
+        } else {
+            ++count_v6;
+        }
+        if (proto == IPPROTO_UDP) {
+            ++count_udp;
+        } else {
+            ++count_tcp;
+        }
+
+        expect.clear();
+        expect["opcode.query"] = i+1;
+        expect["request.v4"] = count_v4;
+        expect["request.v6"] = count_v6;
+        expect["request.udp"] = count_udp;
+        expect["request.tcp"] = count_tcp;
+        expect["request.edns0"] = i+1;
+        expect["request.badednsver"] = 0;
+        expect["request.dnssec_ok"] = i+1;
+        expect["responses"] = i+1;
+        expect["qrynoauthans"] = i+1;
+        expect["rcode.refused"] = i+1;
+        expect["authqryrej"] = i+1;
+        checkStatisticsCounters(counters.get()->get("zones")->get("_SERVER_"),
+                                expect);
+    }
+}
+
+TEST_F(CountersTest, incrementDO) {
+    Message response(Message::RENDER);
+    MessageAttributes msgattrs;
+    std::map<std::string, int> expect;
+
+    // Test these patterns:
+    //     DNSSEC OK
+    //    -----------
+    //     false
+    //     true
+    for (int i = 0; i < 2; ++i) {
+        const bool is_dnssec_ok = i & 1;
+        buildSkeletonMessage(msgattrs);
+        msgattrs.setRequestDO(is_dnssec_ok);
+
+        response.setRcode(Rcode::REFUSED());
+        response.addQuestion(Question(Name("example.com"),
+                                      RRClass::IN(), RRType::AAAA()));
+        response.setHeaderFlag(Message::HEADERFLAG_QR);
+
+        counters.inc(msgattrs, response, true);
+
+        expect.clear();
+        expect["opcode.query"] = i+1;
+        expect["request.v4"] = i+1;
+        expect["request.udp"] = i+1;
+        expect["request.edns0"] = i+1;
+        expect["request.badednsver"] = 0;
+        expect["request.dnssec_ok"] = i & 1;
+        expect["responses"] = i+1;
+        expect["qrynoauthans"] = i+1;
+        expect["rcode.refused"] = i+1;
+        expect["authqryrej"] = i+1;
+        checkStatisticsCounters(counters.get()->get("zones")->get("_SERVER_"),
+                                expect);
+    }
+}
+
+TEST_F(CountersTest, incrementEDNS) {
+    Message response(Message::RENDER);
+    MessageAttributes msgattrs;
+    std::map<std::string, int> expect;
+
+    // Test these patterns:
+    //     request edns0   response edns0
+    //    --------------------------------
+    //     false           true
+    //     true            false
+    //
+    // They can't be both true since edns0 and badednsver are exclusive.
+    int count_req_edns0 = 0, count_res_edns0 = 0;
+    for (int i = 0; i < 2; ++i) {
+        const bool is_edns0 = i & 1;
+        buildSkeletonMessage(msgattrs);
+        msgattrs.setRequestEDNS0(is_edns0);
+
+        if (!is_edns0) {
+            ConstEDNSPtr edns = EDNSPtr(new EDNS(0));
+            response.setEDNS(edns);
+        } else {
+            response.setEDNS(EDNSPtr());
+        }
+        response.setRcode(Rcode::REFUSED());
+        response.addQuestion(Question(Name("example.com"),
+                                      RRClass::IN(), RRType::AAAA()));
+        response.setHeaderFlag(Message::HEADERFLAG_QR);
+
+        counters.inc(msgattrs, response, true);
+
+        if (is_edns0) {
+            ++count_req_edns0;
+        } else {
+            ++count_res_edns0;
+        }
+
+        expect.clear();
+        expect["opcode.query"] = i+1;
+        expect["request.v4"] = i+1;
+        expect["request.udp"] = i+1;
+        expect["request.edns0"] = count_req_edns0;
+        expect["response.edns0"] = count_res_edns0;
+        expect["request.badednsver"] = 0;
+        expect["request.dnssec_ok"] = i+1;
+        expect["responses"] = i+1;
+        expect["qrynoauthans"] = i+1;
+        expect["rcode.refused"] = i+1;
+        expect["authqryrej"] = i+1;
+        checkStatisticsCounters(counters.get()->get("zones")->get("_SERVER_"),
+                                expect);
+    }
+}
+
+TEST_F(CountersTest, incrementTSIG) {
+    Message response(Message::RENDER);
+    MessageAttributes msgattrs;
+    std::map<std::string, int> expect;
+
+    // Test these patterns:
+    //      request signature  badsig   response signature
+    //     -----------------------------------------------
+    //      (none)             false    (none)
+    //      TSIG               false    TSIG
+    //      TSIG               true     (none)
+    //
+    // badsig can't be true if the message does not have signature.
+    int count_req_tsig = 0, count_res_tsig = 0, count_badsig = 0;
+    for (int i = 0; i < 3; ++i) {
+        const bool is_req_tsig = (i == 2) ? true : (i & 1) != 0;
+        const bool is_res_tsig = (i & 1) != 0;
+        const bool is_badsig = (i & 2) != 0;
+        buildSkeletonMessage(msgattrs);
+        msgattrs.setRequestTSIG(is_req_tsig, is_badsig);
+        msgattrs.setResponseTSIG(is_res_tsig);
+
+        response.setRcode(Rcode::REFUSED());
+        response.addQuestion(Question(Name("example.com"),
+                                      RRClass::IN(), RRType::AAAA()));
+        response.setHeaderFlag(Message::HEADERFLAG_QR);
+
+        // don't increment response counters if signature is bad
+        counters.inc(msgattrs, response, !is_badsig);
+
+        if (is_req_tsig) {
+            ++count_req_tsig;
+        }
+        if (is_res_tsig) {
+            ++count_res_tsig;
+        }
+        if (is_badsig) {
+            ++count_badsig;
+        }
+
+        expect.clear();
+        expect["request.v4"] = i+1;
+        expect["request.udp"] = i+1;
+        expect["opcode.query"] = i+1;
+        expect["request.edns0"] = i+1 - count_badsig;
+        expect["request.badednsver"] = 0;
+        expect["request.dnssec_ok"] = i+1 - count_badsig;
+        expect["request.tsig"] = count_req_tsig;
+        expect["response.tsig"] = count_res_tsig;
+        expect["request.sig0"] = 0;
+        expect["request.badsig"] = count_badsig;
+        expect["responses"] = i+1 - count_badsig;
+        expect["qrynoauthans"] = i+1 - count_badsig;
+        expect["rcode.refused"] = i+1 - count_badsig;
+        expect["authqryrej"] = i+1 - count_badsig;
+        checkStatisticsCounters(counters.get()->get("zones")->get("_SERVER_"),
+                                expect);
+    }
+}
+
+TEST_F(CountersTest, incrementOpcode) {
+    Message response(Message::RENDER);
+    MessageAttributes msgattrs;
+    std::map<std::string, int> expect;
+
+    // Test all opcodes (QUERY..RESERVED15)
+    int count_all = 0, count_opcode_other = 0;
+    for (uint8_t i = Opcode::QUERY().getCode(),
+                 e = Opcode::RESERVED15().getCode();
+         i <= e;
+         ++i)
+    {
+        buildSkeletonMessage(msgattrs);
+        msgattrs.setRequestOpCode(Opcode(i));
+        msgattrs.setRequestTSIG(false, false);
+
+        response.setRcode(Rcode::REFUSED());
+        response.addQuestion(Question(Name("example.com"),
+                                      RRClass::IN(), RRType::AAAA()));
+        response.setHeaderFlag(Message::HEADERFLAG_QR);
+
+        for (uint8_t j = 0; j < i; ++j) {
+            // count up i times for i-th opcode to identify counters
+            counters.inc(msgattrs, response, true);
+            ++count_all;
+        }
+
+        expect.clear();
+        expect["request.v4"] = count_all;
+        expect["request.udp"] = count_all;
+        expect["request.edns0"] = count_all;
+        expect["request.badednsver"] = 0;
+        expect["request.dnssec_ok"] = count_all;
+        expect["request.tsig"] = 0;
+        expect["request.sig0"] = 0;
+        expect["request.badsig"] = 0;
+        expect["responses"] = count_all;
+        expect["rcode.refused"] = count_all;
+        if (opcode_to_msgcounter[i] == MSG_OPCODE_OTHER) {
+            count_opcode_other += i;
+        }
+        for (uint8_t j = 0; j <= i; ++j) {
+            if (opcode_to_msgcounter[j] == MSG_OPCODE_OTHER) {
+                expect["opcode.other"] = count_opcode_other;
+            } else {
+                std::string code_text = Opcode(j).toText();
+                std::transform(code_text.begin(), code_text.end(),
+                               code_text.begin(), ::tolower);
+                expect["opcode."+code_text] = j;
+            }
+        }
+        checkStatisticsCounters(counters.get()->get("zones")->get("_SERVER_"),
+                                expect);
+    }
+}
+
+TEST_F(CountersTest, incrementRcode) {
+    Message response(Message::RENDER);
+    MessageAttributes msgattrs;
+    std::map<std::string, int> expect;
+
+    // Test all rcodes (NOERROR..BADVERS)
+    int count_all = 0, count_rcode_other = 0, count_ednsbadver = 0;
+    for (uint16_t i = Rcode::NOERROR().getCode(),
+                  e = Rcode::BADVERS().getCode();
+         i <= e;
+         ++i)
+    {
+        buildSkeletonMessage(msgattrs);
+        msgattrs.setRequestOpCode(Opcode::IQUERY());
+        msgattrs.setRequestTSIG(false, false);
+
+        response.setRcode(Rcode(i));
+        response.addQuestion(Question(Name("example.com"),
+                                      RRClass::IN(), RRType::AAAA()));
+        response.setHeaderFlag(Message::HEADERFLAG_QR);
+
+        for (uint16_t j = 0; j < i; ++j) {
+            // count up i times for i-th rcode to identify counters
+            counters.inc(msgattrs, response, true);
+            ++count_all;
+        }
+
+        expect.clear();
+        expect["opcode.iquery"] = count_all;
+        expect["request.v4"] = count_all;
+        expect["request.udp"] = count_all;
+        expect["request.edns0"] = count_all;
+        expect["request.dnssec_ok"] = count_all;
+        expect["request.tsig"] = 0;
+        expect["request.sig0"] = 0;
+        expect["request.badsig"] = 0;
+        expect["responses"] = count_all;
+        if (rcode_to_msgcounter[i] == MSG_RCODE_OTHER) {
+            count_rcode_other += i;
+        }
+        // "request.badednsver" counts for Rcode == BADVERS
+        if (rcode_to_msgcounter[i] == MSG_RCODE_BADVERS) {
+            count_ednsbadver += i;
+        }
+        expect["request.badednsver"] = count_ednsbadver;
+        for (uint16_t j = 0; j <= i; ++j) {
+            if (rcode_to_msgcounter[j] == MSG_RCODE_OTHER) {
+                expect["rcode.other"] = count_rcode_other;
+            } else {
+                std::string code_text = Rcode(j).toText();
+                std::transform(code_text.begin(), code_text.end(),
+                               code_text.begin(), ::tolower);
+                expect["rcode."+code_text] = j;
+            }
+        }
+        checkStatisticsCounters(counters.get()->get("zones")->get("_SERVER_"),
+                                expect);
+    }
+}
+
+TEST_F(CountersTest, incrementTruncated) {
+    Message response(Message::RENDER);
+    MessageAttributes msgattrs;
+    std::map<std::string, int> expect;
+
+    // Test these patterns:
+    //      truncated
+    //     -----------
+    //      false
+    //      true
+    int count_truncated = 0;
+    for (int i = 0; i < 2; ++i) {
+        const bool is_truncated = i & 1;
+        buildSkeletonMessage(msgattrs);
+        msgattrs.setRequestOpCode(Opcode::IQUERY());
+        msgattrs.setRequestTSIG(false, false);
+        msgattrs.setResponseTruncated(is_truncated);
+
+        response.setRcode(Rcode::SERVFAIL());
+        response.addQuestion(Question(Name("example.com"),
+                                      RRClass::IN(), RRType::TXT()));
+        response.setHeaderFlag(Message::HEADERFLAG_QR);
+
+        counters.inc(msgattrs, response, true);
+
+        if (is_truncated) {
+            ++count_truncated;
+        }
+
+        expect.clear();
+        expect["opcode.iquery"] = i+1;
+        expect["request.v4"] = i+1;
+        expect["request.udp"] = i+1;
+        expect["request.edns0"] = i+1;
+        expect["request.dnssec_ok"] = i+1;
+        expect["responses"] = i+1;
+        expect["rcode.servfail"] = i+1;
+        expect["response.truncated"] = count_truncated;
+        checkStatisticsCounters(counters.get()->get("zones")->get("_SERVER_"),
+                                expect);
+    }
+}
+
+TEST_F(CountersTest, incrementQryAuthAnsAndNoAuthAns) {
+    Message response(Message::RENDER);
+    MessageAttributes msgattrs;
+    std::map<std::string, int> expect;
+
+    // Opcode = QUERY, ANCOUNT = 0 (don't care), Rcode = SERVFAIL (don't care)
+    // Test these patterns:
+    //      AA flag
+    //     -----------------------
+    //      false -> QryNoAuthAns
+    //      true  -> QryAuthAns
+    int count_authans = 0, count_noauthans = 0;
+    for (int i = 0; i < 2; ++i) {
+        const bool is_aa_set = i & 1;
+        buildSkeletonMessage(msgattrs);
+        msgattrs.setRequestTSIG(false, false);
+
+        response.setRcode(Rcode::SERVFAIL());
+        response.addQuestion(Question(Name("example.com"),
+                                      RRClass::IN(), RRType::TXT()));
+        response.setHeaderFlag(Message::HEADERFLAG_QR);
+        if (is_aa_set) {
+            response.setHeaderFlag(Message::HEADERFLAG_AA);
+            ++count_authans;
+        } else {
+            ++count_noauthans;
+        }
+
+        counters.inc(msgattrs, response, true);
+
+        expect.clear();
+        expect["opcode.query"] = i+1;
+        expect["request.v4"] = i+1;
+        expect["request.udp"] = i+1;
+        expect["request.edns0"] = i+1;
+        expect["request.dnssec_ok"] = i+1;
+        expect["responses"] = i+1;
+        expect["rcode.servfail"] = i+1;
+        expect["qryauthans"] = count_authans;
+        expect["qrynoauthans"] = count_noauthans;
+        checkStatisticsCounters(counters.get()->get("zones")->get("_SERVER_"),
+                                expect);
+    }
+}
+
+TEST_F(CountersTest, incrementQrySuccess) {
+    Message response(Message::RENDER);
+    MessageAttributes msgattrs;
+    std::map<std::string, int> expect;
+
+    // Opcode = QUERY, Rcode = NOERROR, ANCOUNT > 0
+    msgattrs.setRequestIPVersion(AF_INET);
+    msgattrs.setRequestTransportProtocol(IPPROTO_UDP);
+    msgattrs.setRequestOpCode(Opcode::QUERY());
+    msgattrs.setRequestEDNS0(true);
+    msgattrs.setRequestDO(true);
+    msgattrs.setRequestTSIG(false, false);
+
+    response.setRcode(Rcode::NOERROR());
+    response.addQuestion(Question(Name("example.com"),
+                                  RRClass::IN(), RRType::TXT()));
+    RRsetPtr answer_rrset(new RRset(Name("example.com"),
+                                    RRClass::IN(), RRType::TXT(),
+                                    RRTTL(3600)));
+    answer_rrset->addRdata(rdata::createRdata(RRType::TXT(),
+                                              RRClass::IN(),
+                                              "Answer"));
+    response.addRRset(Message::SECTION_ANSWER, answer_rrset);
+    response.setHeaderFlag(Message::HEADERFLAG_QR);
+
+    counters.inc(msgattrs, response, true);
+
+    expect.clear();
+    expect["opcode.query"] = 1;
+    expect["request.v4"] = 1;
+    expect["request.udp"] = 1;
+    expect["request.edns0"] = 1;
+    expect["request.dnssec_ok"] = 1;
+    expect["responses"] = 1;
+    expect["rcode.noerror"] = 1;
+    expect["qrysuccess"] = 1;
+    // noauthans is also incremented
+    expect["qrynoauthans"] = 1;
+    checkStatisticsCounters(counters.get()->get("zones")->get("_SERVER_"),
+                            expect);
+}
+
+TEST_F(CountersTest, incrementQryReferralAndNxrrset) {
+    Message response(Message::RENDER);
+    MessageAttributes msgattrs;
+    std::map<std::string, int> expect;
+
+    // Opcode = QUERY, Rcode = NOERROR, ANCOUNT = 0
+    // Test these patterns:
+    //      AA flag
+    //     ----------------------
+    //      false -> QryReferral
+    //      true  -> QryNxrrset
+    int count_referral = 0, count_nxrrset = 0;
+    for (int i = 0; i < 2; ++i) {
+        const bool is_aa_set = i & 1;
+        msgattrs.setRequestIPVersion(AF_INET);
+        msgattrs.setRequestTransportProtocol(IPPROTO_UDP);
+        msgattrs.setRequestOpCode(Opcode::QUERY());
+        msgattrs.setRequestEDNS0(true);
+        msgattrs.setRequestDO(true);
+        msgattrs.setRequestTSIG(false, false);
+
+        response.setRcode(Rcode::NOERROR());
+        response.addQuestion(Question(Name("example.com"),
+                                      RRClass::IN(), RRType::TXT()));
+        response.setHeaderFlag(Message::HEADERFLAG_QR);
+        if (is_aa_set) {
+            response.setHeaderFlag(Message::HEADERFLAG_AA);
+            ++count_nxrrset;
+        } else {
+            ++count_referral;
+        }
+
+        counters.inc(msgattrs, response, true);
+
+        expect.clear();
+        expect["opcode.query"] = i+1;
+        expect["request.v4"] = i+1;
+        expect["request.udp"] = i+1;
+        expect["request.edns0"] = i+1;
+        expect["request.dnssec_ok"] = i+1;
+        expect["responses"] = i+1;
+        expect["rcode.noerror"] = i+1;
+        expect["qrynxrrset"] = count_nxrrset;
+        expect["qryreferral"] = count_referral;
+        // qryauthans or qrynoauthans is also incremented
+        expect["qryauthans"] = count_nxrrset;
+        expect["qrynoauthans"] = count_referral;
+        checkStatisticsCounters(counters.get()->get("zones")->get("_SERVER_"),
+                                expect);
+    }
+}
+
+TEST_F(CountersTest, incrementAuthQryRej) {
+    Message response(Message::RENDER);
+    MessageAttributes msgattrs;
+    std::map<std::string, int> expect;
+
+    // Opcode = QUERY, Rcode = REFUSED, ANCOUNT = 0 (don't care)
+    msgattrs.setRequestIPVersion(AF_INET);
+    msgattrs.setRequestTransportProtocol(IPPROTO_UDP);
+    msgattrs.setRequestOpCode(Opcode::QUERY());
+    msgattrs.setRequestEDNS0(true);
+    msgattrs.setRequestDO(true);
+    msgattrs.setRequestTSIG(false, false);
+
+    response.setRcode(Rcode::REFUSED());
+    response.addQuestion(Question(Name("example.com"),
+                                  RRClass::IN(), RRType::TXT()));
+    response.setHeaderFlag(Message::HEADERFLAG_QR);
+
+    counters.inc(msgattrs, response, true);
+
+    expect.clear();
+    expect["opcode.query"] = 1;
+    expect["request.v4"] = 1;
+    expect["request.udp"] = 1;
+    expect["request.edns0"] = 1;
+    expect["request.dnssec_ok"] = 1;
+    expect["responses"] = 1;
+    expect["rcode.refused"] = 1;
+    expect["authqryrej"] = 1;
+    // noauthans is also incremented since AA bit is not set
+    expect["qrynoauthans"] = 1;
+    checkStatisticsCounters(counters.get()->get("zones")->get("_SERVER_"),
+                            expect);
+}
+
+int
+countTreeElements(const struct CounterSpec* tree) {
+    int count = 0;
+    for (int i = 0; tree[i].name != NULL; ++i) {
+        if (tree[i].sub_counters == NULL) {
+            ++count;
+        } else {
+            count += countTreeElements(tree[i].sub_counters);
+        }
+    }
+    return (count);
+}
+
+TEST(StatisticsItemsTest, MSGItemNamesCheck) {
+    EXPECT_EQ(MSG_COUNTER_TYPES, countTreeElements(msg_counter_tree));
+}
+
+}

+ 75 - 0
src/bin/auth/tests/statistics_util.cc

@@ -0,0 +1,75 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include "statistics_util.h"
+
+#include <gtest/gtest.h>
+
+#include <string>
+#include <map>
+
+namespace {
+void
+flatten(std::map<std::string, int>& flat_map, const std::string& prefix,
+        const isc::data::ConstElementPtr map_element)
+{
+    std::map<std::string, isc::data::ConstElementPtr> map =
+        map_element->mapValue();
+    for (std::map<std::string, isc::data::ConstElementPtr>::const_iterator
+             i = map.begin(), e = map.end();
+         i != e;
+         ++i)
+    {
+        switch (i->second->getType()) {
+            case isc::data::Element::map:
+                flatten(flat_map, i->first + ".", i->second);
+                break;
+            case isc::data::Element::integer:
+                flat_map[prefix + i->first] = i->second->intValue();
+                break;
+            default:
+                FAIL() << "Element Parse Error";
+        }
+    }
+}
+}
+
+namespace isc {
+namespace auth {
+namespace unittest {
+
+void
+checkStatisticsCounters(const isc::data::ConstElementPtr counters,
+                        const std::map<std::string, int>& expect)
+{
+    std::map<std::string, int> stats_map;
+    flatten(stats_map, "", counters);
+
+    for (std::map<std::string, int>::const_iterator
+            i = stats_map.begin(), e = stats_map.end();
+            i != e;
+            ++i)
+    {
+        const int value =
+            expect.find(i->first) == expect.end() ?
+                0 : expect.find(i->first)->second;
+        EXPECT_EQ(value, i->second) << "Expected counter "
+            << i->first << " = " << value << ", actual: "
+            << i->second;
+    }
+}
+
+} // end of unittest
+} // end of auth
+} // end of isc

+ 38 - 0
src/bin/auth/tests/statistics_util.h

@@ -0,0 +1,38 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __AUTH_STATISTICS_UTIL_H
+#define __AUTH_STATISTICS_UTIL_H 1
+
+#include <cc/data.h>
+
+namespace isc {
+namespace auth {
+namespace unittest {
+
+// Test if the counters has expected values specified in expect and the others
+// are zero.
+void
+checkStatisticsCounters(const isc::data::ConstElementPtr counters,
+                        const std::map<std::string, int>& expect);
+
+} // end of unittest
+} // end of auth
+} // end of isc
+
+#endif  // __AUTH_STATISTICS_UTIL_H
+
+// Local Variables:
+// mode: c++
+// End:

+ 0 - 1
src/bin/auth/tests/testdata/Makefile.am

@@ -23,7 +23,6 @@ EXTRA_DIST += simpleresponse_fromWire.spec
 EXTRA_DIST += spec.spec
 
 EXTRA_DIST += example.com
-EXTRA_DIST += example.zone
 EXTRA_DIST += example.sqlite3
 
 EXTRA_DIST += example-base-inc.zone example-nsec3-inc.zone

+ 6 - 1
src/bin/cmdctl/cmdctl.py.in

@@ -429,8 +429,13 @@ class CommandControl():
             # Process the command sent to cmdctl directly.
             answer = self.command_handler(command_name, params)
         else:
+            # FIXME: Due to the fact that we use a separate session
+            # from the module one (due to threads and blocking), and
+            # because the plain cc session does not have the high-level
+            # rpc-call method, we use the low-level way and create the
+            # command ourselves.
             msg = ccsession.create_command(command_name, params)
-            seq = self._cc.group_sendmsg(msg, module_name)
+            seq = self._cc.group_sendmsg(msg, module_name, want_answer=True)
             logger.debug(DBG_CMDCTL_MESSAGING, CMDCTL_COMMAND_SENT,
                          command_name, module_name)
             #TODO, it may be blocked, msqg need to add a new interface waiting in timeout.

+ 24 - 37
src/bin/ddns/ddns.py.in

@@ -32,7 +32,8 @@ import isc.util.cio.socketsession
 import isc.server_common.tsig_keyring
 from isc.server_common.dns_tcp import DNSTCPContext
 from isc.datasrc import DataSourceClient
-from isc.server_common.auth_command import auth_loadzone_command
+from isc.server_common.auth_command import AUTH_LOADZONE_COMMAND, \
+    auth_loadzone_params
 import select
 import time
 import errno
@@ -544,42 +545,38 @@ class DDNSServer:
     def __notify_start_forwarder(self):
         '''Notify auth that DDNS Update messages can now be forwarded'''
         try:
-            seq = self._cc._session.group_sendmsg(create_command(
-                    "start_ddns_forwarder"), AUTH_MODULE_NAME)
-            answer, _ = self._cc._session.group_recvmsg(False, seq)
-            rcode, error_msg = parse_answer(answer)
-            if rcode != 0:
-                logger.error(DDNS_START_FORWARDER_ERROR, error_msg)
-        except (SessionTimeout, SessionError, ProtocolError) as ex:
+            self._cc.rpc_call("start_ddns_forwarder", AUTH_MODULE_NAME)
+        except (SessionTimeout, SessionError, ProtocolError,
+                RPCRecipientMissing) as ex:
             logger.error(DDNS_START_FORWARDER_FAIL, ex)
+        except RPCError as e:
+            logger.error(DDNS_START_FORWARDER_ERROR, e)
 
     def __notify_stop_forwarder(self):
         '''Notify auth that DDNS Update messages should no longer be forwarded.
 
         '''
         try:
-            seq = self._cc._session.group_sendmsg(create_command(
-                    "stop_ddns_forwarder"), AUTH_MODULE_NAME)
-            answer, _ = self._cc._session.group_recvmsg(False, seq)
-            rcode, error_msg = parse_answer(answer)
-            if rcode != 0:
-                logger.error(DDNS_STOP_FORWARDER_ERROR, error_msg)
-        except (SessionTimeout, SessionError, ProtocolError) as ex:
+            self._cc.rpc_call("stop_ddns_forwarder", AUTH_MODULE_NAME)
+        except (SessionTimeout, SessionError, ProtocolError,
+                RPCRecipientMissing) as ex:
             logger.error(DDNS_STOP_FORWARDER_FAIL, ex)
+        except RPCError as e:
+            logger.error(DDNS_STOP_FORWARDER_ERROR, e)
 
     def __notify_auth(self, zname, zclass):
         '''Notify auth of the update, if necessary.'''
-        msg = auth_loadzone_command(self._cc, zname, zclass)
-        if msg is not None:
-            self.__notify_update(AUTH_MODULE_NAME, msg, zname, zclass)
+        self.__notify_update(AUTH_MODULE_NAME, AUTH_LOADZONE_COMMAND,
+                             auth_loadzone_params(zname, zclass), zname,
+                             zclass)
 
     def __notify_xfrout(self, zname, zclass):
         '''Notify xfrout of the update.'''
         param = {'zone_name': zname.to_text(), 'zone_class': zclass.to_text()}
-        msg = create_command('notify', param)
-        self.__notify_update(XFROUT_MODULE_NAME, msg, zname, zclass)
+        self.__notify_update(XFROUT_MODULE_NAME, 'notify', param, zname,
+                             zclass)
 
-    def __notify_update(self, modname, msg, zname, zclass):
+    def __notify_update(self, modname, command, params, zname, zclass):
         '''Notify other module of the update.
 
         Note that we use blocking communication here.  While the internal
@@ -590,27 +587,17 @@ class DDNSServer:
         For a longer term we'll need to switch to asynchronous communication,
         but for now we rely on the blocking operation.
 
-        Note also that we directly refer to the "protected" member of
-        ccsession (_cc._session) rather than creating a separate channel.
-        It's probably not the best practice, but hopefully we can introduce
-        a cleaner way when we support asynchronous communication.
-        At the moment we prefer the brevity with the use of internal channel
-        of the cc session.
-
         '''
         try:
-            seq = self._cc._session.group_sendmsg(msg, modname)
-            answer, _ = self._cc._session.group_recvmsg(False, seq)
-            rcode, error_msg = parse_answer(answer)
-        except (SessionTimeout, SessionError, ProtocolError) as ex:
-            rcode = 1
-            error_msg = str(ex)
-        if rcode == 0:
+            # FIXME? Is really rpc_call the correct one? What if there are more
+            # than one recipient of the given kind? What if none? We need to
+            # think of some kind of notification/broadcast mechanism.
+            self._cc.rpc_call(command, modname, params=params)
             logger.debug(TRACE_BASIC, DDNS_UPDATE_NOTIFY, modname,
                          ZoneFormatter(zname, zclass))
-        else:
+        except (SessionTimeout, SessionError, ProtocolError, RPCError) as ex:
             logger.error(DDNS_UPDATE_NOTIFY_FAIL, modname,
-                         ZoneFormatter(zname, zclass), error_msg)
+                         ZoneFormatter(zname, zclass), ex)
 
     def handle_session(self, fileno):
         """Handle incoming session on the socket with given fileno.

+ 17 - 27
src/bin/ddns/ddns_messages.mes

@@ -69,7 +69,7 @@ it's just a timing issue.  The number of total failed attempts is also
 logged.  If it reaches an internal threshold b10-ddns considers it a
 fatal error and terminates.  Even in that case, if b10-ddns is
 configured as a "dispensable" component (which is the default), the
-parent bind10 process will restart it, and there will be another
+parent ("init") process will restart it, and there will be another
 chance of getting the remote configuration successfully.  These are
 not the optimal behavior, but it's believed to be sufficient in
 practice (there would normally be no failure in the first place).  If
@@ -253,29 +253,19 @@ notify messages to secondary servers.
 b10-ddns has made updates to a zone based on an update request and
 tried to notify an external component of the updates, but the
 notification fails.  One possible cause of this is that the external
-component is not really running and it times out in waiting for the
-response, although it will be less likely to happen in practice
-because these components will normally be configured to run when the
-server provides the authoritative DNS service; ddns is rather optional
-among them.  If this happens, however, it will suspend b10-ddns for a
-few seconds during which it cannot handle new requests (some may be
-delayed, some may be dropped, depending on the volume of the incoming
-requests).  This is obviously bad, and if this error happens due to
-this reason, the administrator should make sure the component in
-question should be configured to run.  For a longer term, b10-ddns
-should be more robust about this case such as by making this
-notification asynchronously and/or detecting the existence of the
-external components to avoid hopeless notification in the first place.
-Severity of this error for the receiving components depends on the
-type of the component.  If it's b10-xfrout, this means DNS notify
-messages won't be sent to secondary servers of the zone.  It's
-suboptimal, but not necessarily critical as the secondary servers will
-try to check the zone's status periodically.  If it's b10-auth and the
-notification was needed to have it reload the corresponding zone, it's
-more serious because b10-auth won't be able to serve the new version
-of the zone unless some explicit recovery action is taken.  So the
-administrator needs to examine this message and takes an appropriate
-action.  In either case, this notification is generally expected to
-succeed; so the fact it fails itself means there's something wrong in
-the BIND 10 system, and it would be advisable to check other log
-messages.
+component is not really running, although it will be less likely to
+happen in practice because these components will normally be
+configured to run when the server provides the authoritative DNS
+service; ddns is rather optional among them. Severity of this error
+for the receiving components depends on the type of the component.  If
+it's b10-xfrout, this means DNS notify messages won't be sent to
+secondary servers of the zone.  It's suboptimal, but not necessarily
+critical as the secondary servers will try to check the zone's status
+periodically.  If it's b10-auth and the notification was needed to
+have it reload the corresponding zone, it's more serious because
+b10-auth won't be able to serve the new version of the zone unless
+some explicit recovery action is taken.  So the administrator needs to
+examine this message and takes an appropriate action.  In either case,
+this notification is generally expected to succeed; so the fact it
+fails itself means there's something wrong in the BIND 10 system, and
+it would be advisable to check other log messages.

+ 3 - 2
src/bin/ddns/tests/ddns_test.py

@@ -191,7 +191,7 @@ class FakeKeyringModule:
         '''Simply return the predefined TSIG keyring unconditionally.'''
         return TEST_TSIG_KEYRING
 
-class MyCCSession(isc.config.ConfigData):
+class MyCCSession(isc.config.ModuleCCSession):
     '''Fake session with minimal interface compliance.'''
 
     # faked CC sequence used in group_send/recvmsg
@@ -276,7 +276,8 @@ class MyCCSession(isc.config.ConfigData):
                     'secondary_zones')
                 return seczone_default, True
 
-    def group_sendmsg(self, msg, group):
+    def group_sendmsg(self, msg, group, instance='*', to='*',
+                      want_answer=False):
         # remember the passed parameter, and return dummy sequence
         self._sent_msg.append((msg, group))
         if self._sendmsg_exception is not None:

+ 0 - 3
src/bin/loadzone/tests/correct/correct_test.sh.in

@@ -18,9 +18,6 @@
 PYTHON_EXEC=${PYTHON_EXEC:-@PYTHON@}
 export PYTHON_EXEC
 
-PYTHONPATH=@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/python:$PYTHONPATH
-export PYTHONPATH
-
 LOADZONE_PATH=@abs_top_builddir@/src/bin/loadzone
 TEST_FILE_PATH=@abs_top_srcdir@/src/bin/loadzone/tests/correct
 TEST_OUTPUT_PATH=@abs_top_builddir@/src/bin/loadzone//tests/correct

+ 45 - 46
src/bin/stats/stats.py.in

@@ -271,36 +271,34 @@ class Stats:
             isc.config.ccsession.create_command("show_processes"), 'Init')
         (answer, env) = self.cc_session.group_recvmsg(False, seq)
         modules = []
-        if answer:
-            (rcode, value) = isc.config.ccsession.parse_answer(answer)
-            if rcode == 0 and type(value) is list:
-                # NOTE: For example, the "show_processes" command
-                # of Init is assumed to return the response in this
-                # format:
-                #  [
-                #  ...
-                #    [
-                #      20061,
-                #      "b10-auth",
-                #      "Auth"
-                #    ],
-                #    [
-                #      20103,
-                #      "b10-auth-2",
-                #      "Auth"
-                #    ]
-                #  ...
-                #  ]
-                # If multiple instances of the same module are
-                # running, the address names of them, which are at the
-                # third element, must be also same. Thus, the value of
-                # the third element of each outer element is read here
-                # for counting multiple instances.  This is a
-                # workaround for counting the instances. This should
-                # be fixed in another proper way in the future
-                # release.
-                modules = [ v[2] if type(v) is list and len(v) > 2 \
-                                else None for v in value ]
+        if type(value) is list:
+            # NOTE: For example, the "show_processes" command
+            # of Init is assumed to return the response in this
+            # format:
+            #  [
+            #  ...
+            #    [
+            #      20061,
+            #      "b10-auth",
+            #      "Auth"
+            #    ],
+            #    [
+            #      20103,
+            #      "b10-auth-2",
+            #      "Auth"
+            #    ]
+            #  ...
+            #  ]
+            # If multiple instances of the same module are
+            # running, the address names of them, which are at the
+            # third element, must be also same. Thus, the value of
+            # the third element of each outer element is read here
+            # for counting multiple instances.  This is a
+            # workaround for counting the instances. This should
+            # be fixed in another proper way in the future
+            # release.
+            modules = [ v[2] if type(v) is list and len(v) > 2 \
+                            else None for v in value ]
         # start requesting each module to collect statistics data
         sequences = []
         for (module_name, data) in self.get_statistics_data().items():
@@ -311,7 +309,12 @@ class Stats:
                          module_name)
             cmd = isc.config.ccsession.create_command(
                 "getstats", None) # no argument
-            seq = self.cc_session.group_sendmsg(cmd, module_name)
+            # Not using rpc_call here. We first send a bunch of commands, then
+            # collect all the answers. This eliminates some of the round-trip
+            # times. Unfortunately, rpc_call is not flexible enough to allow
+            # this, though the future rpc_call_async could.
+            seq = self.cc_session.group_sendmsg(cmd, module_name,
+                                                want_answer=True)
             sequences.append((module_name, seq))
             cnt = modules.count(module_name)
             if cnt > 1:
@@ -438,21 +441,17 @@ class Stats:
         raises StatsError.
         """
         modules = {}
-        seq = self.cc_session.group_sendmsg(
-            isc.config.ccsession.create_command(
-                isc.config.ccsession.COMMAND_GET_STATISTICS_SPEC),
-            'ConfigManager')
-        (answer, env) = self.cc_session.group_recvmsg(False, seq)
-        if answer:
-            (rcode, value) = isc.config.ccsession.parse_answer(answer)
-            if rcode == 0:
-                for mod in value:
-                    spec = { "module_name" : mod }
-                    if value[mod] and type(value[mod]) is list:
-                        spec["statistics"] = value[mod]
-                    modules[mod] = isc.config.module_spec.ModuleSpec(spec)
-            else:
-                raise StatsError("Updating module spec fails: " + str(value))
+        try:
+            value = self.mccs.rpc_call(isc.config.ccsession. \
+                                       COMMAND_GET_STATISTICS_SPEC,
+                                       'ConfigManager')
+        except isc.config.RPCError as e:
+            raise StatsError("Updating module spec fails: " + str(e))
+        for mod in value:
+            spec = { "module_name" : mod }
+            if value[mod] and type(value[mod]) is list:
+                spec["statistics"] = value[mod]
+            modules[mod] = isc.config.module_spec.ModuleSpec(spec)
         modules[self.module_name] = self.mccs.get_module_spec()
         self.modules = modules
 

+ 8 - 20
src/bin/stats/stats_httpd.py.in

@@ -459,20 +459,14 @@ class StatsHttpd:
         if name is not None:
             param['name'] = name
         try:
-            seq = self.cc_session.group_sendmsg(
-                isc.config.ccsession.create_command('show', param), 'Stats')
-            (answer, env) = self.cc_session.group_recvmsg(False, seq)
-            if answer:
-                (rcode, value) = isc.config.ccsession.parse_answer(answer)
+            return self.mccs.rpc_call('show', 'Stats', params=param)
         except (isc.cc.session.SessionTimeout,
-                isc.cc.session.SessionError) as err:
+                isc.cc.session.SessionError,
+                isc.config.RPCRecipientMissing) as err:
             raise StatsHttpdError("%s: %s" %
                                   (err.__class__.__name__, err))
-        else:
-            if rcode == 0:
-                return value
-            else:
-                raise StatsHttpdDataError("Stats module: %s" % str(value))
+        except isc.config.RPCError as e:
+            raise StatsHttpdDataError("Stats module: %s" % str(e))
 
     def get_stats_spec(self, owner=None, name=None):
         """Requests statistics data to the Stats daemon and returns
@@ -493,15 +487,9 @@ class StatsHttpd:
         if name is not None:
             param['name'] = name
         try:
-            seq = self.cc_session.group_sendmsg(
-                isc.config.ccsession.create_command('showschema', param), 'Stats')
-            (answer, env) = self.cc_session.group_recvmsg(False, seq)
-            if answer:
-                (rcode, value) = isc.config.ccsession.parse_answer(answer)
-                if rcode == 0:
-                    return value
-                else:
-                    raise StatsHttpdDataError("Stats module: %s" % str(value))
+            return self.mccs.rpc_call('showschema', 'Stats', params=param)
+        except isc.config.RPCError as e:
+            raise StatsHttpdDataError("Stats module: %s" % str(e))
         except (isc.cc.session.SessionTimeout,
                 isc.cc.session.SessionError) as err:
             raise StatsHttpdError("%s: %s" %

+ 1 - 1
src/bin/xfrin/tests/xfrin_test.py

@@ -2909,7 +2909,7 @@ class TestXfrinProcessMockCCSession:
         self.recv_called = False
         self.recv_called_correctly = False
 
-    def group_sendmsg(self, msg, module):
+    def group_sendmsg(self, msg, module, want_answer=False):
         self.send_called = True
         if module == 'Auth' and msg['command'][0] == 'loadzone':
             self.send_called_correctly = True

+ 17 - 5
src/bin/xfrin/xfrin.py.in

@@ -982,7 +982,6 @@ class XfrinConnection(asyncore.dispatcher):
                          format_addrinfo(self._master_addrinfo))
             ret = XFRIN_FAIL
         except XfrinProtocolError as e:
-            # FIXME: Why is this .info? Even the messageID contains "ERROR".
             logger.info(XFRIN_XFR_TRANSFER_PROTOCOL_VIOLATION, req_str,
                         self.zone_str(),
                         format_addrinfo(self._master_addrinfo), str(e))
@@ -1324,7 +1323,8 @@ def _do_auth_loadzone(server, zone_name, zone_class):
         param = msg['command'][1]
         logger.debug(DBG_XFRIN_TRACE, XFRIN_AUTH_LOADZONE, param["origin"],
                      param["class"])
-        seq = server._send_cc_session.group_sendmsg(msg, AUTH_MODULE_NAME)
+        seq = server._send_cc_session.group_sendmsg(msg, AUTH_MODULE_NAME,
+                                                    want_answer=True)
         answer, env = server._send_cc_session.group_recvmsg(False, seq)
 
 class Xfrin:
@@ -1630,18 +1630,29 @@ class Xfrin:
         param = {'zone_name': zone_name.to_text(),
                  'zone_class': zone_class.to_text()}
         if xfr_result == XFRIN_OK:
+            # FIXME: Due to the hack with two different CC sessions
+            # (see the _cc_setup comment) and the fact the rpc_call
+            # is a high-level call present only at ModuleCCSession,
+            # we are forced to use the primitive way of manually
+            # calling group_sendmsg and the group_recvmsg. Also, why
+            # do we do group_recvmsg when we don't need the answer?
+            # And why is this direct RPC call if a notification would
+            # be more appropriate?
             _do_auth_loadzone(self, zone_name, zone_class)
             msg = create_command(notify_out.ZONE_NEW_DATA_READY_CMD, param)
             # catch the exception, in case msgq has been killed.
             try:
                 seq = self._send_cc_session.group_sendmsg(msg,
-                                                          XFROUT_MODULE_NAME)
+                                                          XFROUT_MODULE_NAME,
+                                                          want_answer=True)
                 try:
                     answer, env = self._send_cc_session.group_recvmsg(False,
                                                                       seq)
                 except isc.cc.session.SessionTimeout:
                     pass        # for now we just ignore the failure
-                seq = self._send_cc_session.group_sendmsg(msg, ZONE_MANAGER_MODULE_NAME)
+                seq = self._send_cc_session.group_sendmsg(msg,
+                                                          ZONE_MANAGER_MODULE_NAME,
+                                                          want_answer=True)
                 try:
                     answer, env = self._send_cc_session.group_recvmsg(False,
                                                                       seq)
@@ -1654,7 +1665,8 @@ class Xfrin:
             msg = create_command(notify_out.ZONE_XFRIN_FAILED, param)
             # catch the exception, in case msgq has been killed.
             try:
-                seq = self._send_cc_session.group_sendmsg(msg, ZONE_MANAGER_MODULE_NAME)
+                seq = self._send_cc_session.group_sendmsg(msg, ZONE_MANAGER_MODULE_NAME,
+                                                          want_answer=True)
                 try:
                     answer, env = self._send_cc_session.group_recvmsg(False,
                                                                       seq)

+ 8 - 16
src/bin/zonemgr/tests/zonemgr_test.py

@@ -41,23 +41,16 @@ TEST_SQLITE3_DBFILE = os.getenv("TESTDATAOBJDIR") + '/initdb.file'
 class ZonemgrTestException(Exception):
     pass
 
-class MySession():
-    def __init__(self):
-        pass
-
-    def group_sendmsg(self, msg, module_name):
-        if module_name not in ("Auth", "Xfrin"):
-            raise ZonemgrTestException("module name not exist")
-
-    def group_recvmsg(self, nonblock, seq):
-        return None, None
-
 class FakeCCSession(isc.config.ConfigData, MockModuleCCSession):
     def __init__(self):
         module_spec = isc.config.module_spec_from_file(SPECFILE_LOCATION)
         ConfigData.__init__(self, module_spec)
         MockModuleCCSession.__init__(self)
 
+    def rpc_call(self, command, module, instance="*", to="*", params=None):
+        if module not in ("Auth", "Xfrin"):
+            raise ZonemgrTestException("module name not exist")
+
     def get_remote_config_value(self, module_name, identifier):
         if module_name == "Auth" and identifier == "database_file":
             return TEST_SQLITE3_DBFILE, False
@@ -84,8 +77,8 @@ class MyZonemgrRefresh(ZonemgrRefresh):
                 return None
         sqlite3_ds.get_zone_soa = get_zone_soa
 
-        ZonemgrRefresh.__init__(self, MySession(), TEST_SQLITE3_DBFILE,
-                                self._slave_socket, FakeCCSession())
+        ZonemgrRefresh.__init__(self, TEST_SQLITE3_DBFILE, self._slave_socket,
+                                FakeCCSession())
         current_time = time.time()
         self._zonemgr_refresh_info = {
          ('example.net.', 'IN'): {
@@ -619,7 +612,6 @@ class MyZonemgr(Zonemgr):
         self._db_file = TEST_SQLITE3_DBFILE
         self._zone_refresh = None
         self._shutdown_event = threading.Event()
-        self._cc = MySession()
         self._module_cc = FakeCCSession()
         self._config_data = {
                     "lowerbound_refresh" : 10,
@@ -664,8 +656,8 @@ class TestZonemgr(unittest.TestCase):
         self.zonemgr.config_handler(config_data3)
         self.assertEqual(0.5, self.zonemgr._config_data.get("refresh_jitter"))
         # The zone doesn't exist in database, simply skip loading soa for it and log an warning
-        self.zonemgr._zone_refresh = ZonemgrRefresh(None, TEST_SQLITE3_DBFILE,
-                                                    None, FakeCCSession())
+        self.zonemgr._zone_refresh = ZonemgrRefresh(TEST_SQLITE3_DBFILE, None,
+                                                    FakeCCSession())
         config_data1["secondary_zones"] = [{"name": "nonexistent.example",
                                             "class": "IN"}]
         self.assertEqual(self.zonemgr.config_handler(config_data1),

+ 9 - 10
src/bin/zonemgr/zonemgr.py.in

@@ -103,8 +103,8 @@ class ZonemgrRefresh:
     can be stopped by calling shutdown() in another thread.
     """
 
-    def __init__(self, cc, db_file, slave_socket, module_cc_session):
-        self._cc = cc
+    def __init__(self, db_file, slave_socket, module_cc_session):
+        self._mccs = module_cc_session
         self._check_sock = slave_socket
         self._db_file = db_file
         self._zonemgr_refresh_info = {}
@@ -277,15 +277,15 @@ class ZonemgrRefresh:
 
     def _send_command(self, module_name, command_name, params):
         """Send command between modules."""
-        msg = create_command(command_name, params)
         try:
-            seq = self._cc.group_sendmsg(msg, module_name)
-            try:
-                answer, env = self._cc.group_recvmsg(False, seq)
-            except isc.cc.session.SessionTimeout:
-                pass        # for now we just ignore the failure
+            self._mccs.rpc_call(command_name, module_name, params=params)
         except socket.error:
+            # FIXME: WTF? Where does socket.error come from? And how do we ever
+            # dare ignore such serious error? It can only be broken link to
+            # msgq, we need to terminate then.
             logger.error(ZONEMGR_SEND_FAIL, module_name)
+        except (isc.cc.session.SessionTimeout, isc.config.RPCError):
+            pass        # for now we just ignore the failure
 
     def _find_need_do_refresh_zone(self):
         """Find the first zone need do refresh, if no zone need
@@ -525,7 +525,7 @@ class Zonemgr:
         self._db_file = self.get_db_file()
         # Create socket pair for communicating between main thread and zonemgr timer thread
         self._master_socket, self._slave_socket = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)
-        self._zone_refresh = ZonemgrRefresh(self._cc, self._db_file, self._slave_socket, self._module_cc)
+        self._zone_refresh = ZonemgrRefresh(self._db_file, self._slave_socket, self._module_cc)
         self._zone_refresh.run_timer()
 
         self._lock = threading.Lock()
@@ -536,7 +536,6 @@ class Zonemgr:
         """Setup two sessions for zonemgr, one(self._module_cc) is used for receiving
         commands and config data sent from other modules, another one (self._cc)
         is used to send commands to proper modules."""
-        self._cc = isc.cc.Session()
         self._module_cc = isc.config.ModuleCCSession(SPECFILE_LOCATION,
                                                   self.config_handler,
                                                   self.command_handler)

+ 1 - 1
src/lib/asiodns/sync_udp_server.cc

@@ -44,7 +44,7 @@ SyncUDPServer::SyncUDPServer(asio::io_service& io_service, const int fd,
     output_buffer_(new isc::util::OutputBuffer(0)),
     query_(new isc::dns::Message(isc::dns::Message::PARSE)),
     answer_(new isc::dns::Message(isc::dns::Message::RENDER)),
-    io_(io_service), checkin_callback_(checkin), lookup_callback_(lookup),
+    checkin_callback_(checkin), lookup_callback_(lookup),
     answer_callback_(answer), stopped_(false)
 {
     if (af != AF_INET && af != AF_INET6) {

+ 0 - 2
src/lib/asiodns/sync_udp_server.h

@@ -118,8 +118,6 @@ private:
     isc::dns::MessagePtr query_, answer_;
     // The socket used for the communication
     std::auto_ptr<asio::ip::udp::socket> socket_;
-    // The event loop we use
-    asio::io_service& io_;
     // Place the socket puts the sender of a packet when it is received
     asio::ip::udp::endpoint sender_;
     // Callbacks

+ 3 - 4
src/lib/asiolink/tests/tcp_socket_unittest.cc

@@ -82,7 +82,7 @@ public:
     struct PrivateData {
         PrivateData() :
             error_code_(), length_(0), cumulative_(0), expected_(0), offset_(0),
-            name_(""), queued_(NONE), called_(NONE)
+            name_(""), queued_(NONE), called_(NONE), data_(MIN_SIZE, 0)
         {}
 
         asio::error_code    error_code_;    ///< Completion error code
@@ -93,8 +93,7 @@ public:
         std::string         name_;          ///< Which of the objects this is
         Operation           queued_;        ///< Queued operation
         Operation           called_;        ///< Which callback called
-        uint8_t             data_[MIN_SIZE];  ///< Receive buffer
-
+        std::vector<uint8_t> data_;  ///< Receive buffer
     };
 
     /// \brief Constructor
@@ -169,7 +168,7 @@ public:
 
     /// \brief Get data member
     uint8_t* data() {
-        return (ptr_->data_);
+        return (&ptr_->data_[0]);
     }
 
     /// \brief Get flag to say what was queued

+ 6 - 3
src/lib/cache/local_zone_data.h

@@ -29,8 +29,12 @@ namespace cache {
 /// in the zone.
 class LocalZoneData {
 public:
-    LocalZoneData(uint16_t rrset_class) : class_(rrset_class)
-    {}
+    /// \brief Constructor.
+    ///
+    /// The passed parameter is expected to be an RR class value, but is not
+    /// currently unused.  And this library will be quite likely to
+    /// deprecated anyway, so we don't touch it heavily.
+    LocalZoneData(uint16_t) {}
 
     /// \brief Look up one rrset.
     ///
@@ -51,7 +55,6 @@ public:
 
 private:
     std::map<std::string, isc::dns::RRsetPtr> rrsets_map_; // RRsets of the zone
-    uint16_t class_; // The class of the zone
 };
 
 typedef boost::shared_ptr<LocalZoneData> LocalZoneDataPtr;

+ 1 - 0
src/lib/cc/proto_defs.cc

@@ -38,6 +38,7 @@ const char* const CC_COMMAND_SEND = "send";
 const char* const CC_TO_WILDCARD = "*";
 const char* const CC_INSTANCE_WILDCARD = "*";
 // Reply codes
+const int CC_REPLY_SUCCESS = 0;
 const int CC_REPLY_NO_RECPT = -1;
 
 }

+ 2 - 2
src/lib/dhcpsrv/dhcpsrv_messages.mes

@@ -128,7 +128,7 @@ with the specified address to the memory file backend database.
 A debug message issued when the server is about to add an IPv6 lease
 with the specified address to the memory file backend database.
 
-% DHCPSRV_MEMFILE_COMMIT commiting to memory file database
+% DHCPSRV_MEMFILE_COMMIT committing to memory file database
 The code has issued a commit call.  For the memory file database, this is
 a no-op.
 
@@ -212,7 +212,7 @@ with the specified address to the MySQL backend database.
 A debug message issued when the server is about to add an IPv6 lease
 with the specified address to the MySQL backend database.
 
-% DHCPSRV_MYSQL_COMMIT commiting to MySQl database
+% DHCPSRV_MYSQL_COMMIT committing to MySQL database
 The code has issued a commit call.  All outstanding transactions will be
 committed to the database.  Note that depending on the MySQL settings,
 the commital may not include a write to disk.

+ 3 - 2
src/lib/dhcpsrv/pool.cc

@@ -102,8 +102,9 @@ Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& prefix,
         isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
     }
 
-    // check if the prefix length is sane
-    if (prefix_len == 0 || prefix_len > 128) {
+    // check if the prefix length is sane (we use the member variable only
+    // for silencing some compilers; see #2705 and #2789).
+    if (prefix_len_ == 0 || prefix_len_ > 128) {
         isc_throw(BadValue, "Invalid prefix length");
     }
 

+ 66 - 3
src/lib/python/isc/config/ccsession.py

@@ -37,6 +37,7 @@
 """
 
 from isc.cc import Session
+from isc.cc.proto_defs import *
 from isc.config.config_data import ConfigData, MultiConfigData, BIND10_CONFIG_DATA_VERSION
 import isc.config.module_spec
 import isc
@@ -50,6 +51,31 @@ logger = isc.log.Logger("config")
 
 class ModuleCCSessionError(Exception): pass
 
+class RPCError(ModuleCCSessionError):
+    """
+    An exception raised by rpc_call in case the remote side reports
+    an error. It can be used to distinguish remote errors from protocol errors.
+    Also, it holds the code as well as the error message.
+    """
+    def __init__(self, code, message):
+        ModuleCCSessionError.__init__(self, message)
+        self.__code = code
+
+    def code(self):
+        """
+        The code as sent over the CC.
+        """
+        return self.__code
+
+class RPCRecipientMissing(RPCError):
+    """
+    Special version of the RPCError, for cases the recipient of the call
+    isn't connected to the bus. The code is always
+    isc.cc.proto_defs.CC_REPLY_NO_RECPT.
+    """
+    def __init__(self, message):
+        RPCError.__init__(self, CC_REPLY_NO_RECPT, message)
+
 def parse_answer(msg):
     """Returns a tuple (rcode, value), where value depends on the
        command that was called. If rcode != 0, value is a string
@@ -66,7 +92,8 @@ def parse_answer(msg):
         raise ModuleCCSessionError("wrong rcode type in answer message")
     else:
         if len(msg['result']) > 1:
-            if (msg['result'][0] != 0 and type(msg['result'][1]) != str):
+            if (msg['result'][0] != CC_REPLY_SUCCESS and
+                type(msg['result'][1]) != str):
                 raise ModuleCCSessionError("rcode in answer message is non-zero, value is not a string")
             return msg['result'][0], msg['result'][1]
         else:
@@ -79,7 +106,7 @@ def create_answer(rcode, arg = None):
        a string containing an error message"""
     if type(rcode) != int:
         raise ModuleCCSessionError("rcode in create_answer() must be an integer")
-    if rcode != 0 and type(arg) != str:
+    if rcode != CC_REPLY_SUCCESS and type(arg) != str:
         raise ModuleCCSessionError("arg in create_answer for rcode != 0 must be a string describing the error")
     if arg != None:
         return { 'result': [ rcode, arg ] }
@@ -299,7 +326,7 @@ class ModuleCCSession(ConfigData):
                         isc.cc.data.remove_identical(new_config, self.get_local_config())
                         answer = self._config_handler(new_config)
                         rcode, val = parse_answer(answer)
-                        if rcode == 0:
+                        if rcode == CC_REPLY_SUCCESS:
                             newc = self.get_local_config()
                             isc.cc.data.merge(newc, new_config)
                             self.set_local_config(newc)
@@ -474,6 +501,42 @@ class ModuleCCSession(ConfigData):
         except isc.cc.SessionTimeout:
             raise ModuleCCSessionError("CC Session timeout waiting for configuration manager")
 
+    def rpc_call(self, command, group, instance=CC_INSTANCE_WILDCARD,
+                 to=CC_TO_WILDCARD, params=None):
+        """
+        Create a command with the given name and parameters. Send it to a
+        recipient, wait for the answer and parse it.
+
+        This is a wrapper around the group_sendmsg and group_recvmsg on the CC
+        session. It exists mostly for convenience.
+
+        Params:
+        - command: Name of the command to call on the remote side.
+        - group, instance, to: Address specification of the recipient.
+        - params: Parameters to pass to the command (as keyword arguments).
+
+        Return: The return value of the remote call (just the value, no status
+          code or anything). May be None.
+
+        Raise:
+        - RPCRecipientMissing if the given recipient doesn't exist.
+        - RPCError if the other side sent an error response. The error string
+          is in the exception.
+        - ModuleCCSessionError in case of protocol errors, like malformed
+          answer.
+        """
+        cmd = create_command(command, params)
+        seq = self._session.group_sendmsg(cmd, group, instance=instance,
+                                          to=to, want_answer=True)
+        # For non-blocking, we'll have rpc_call_async (once the nonblock
+        # actualy works)
+        reply, rheaders = self._session.group_recvmsg(nonblock=False, seq=seq)
+        code, value = parse_answer(reply)
+        if code == CC_REPLY_NO_RECPT:
+            raise RPCRecipientMissing(value)
+        elif code != CC_REPLY_SUCCESS:
+            raise RPCError(code, value)
+        return value
 
 class UIModuleCCSession(MultiConfigData):
     """This class is used in a configuration user interface. It contains

+ 61 - 0
src/lib/python/isc/config/tests/ccsession_test.py

@@ -289,6 +289,67 @@ class TestModuleCCSession(unittest.TestCase):
         fake_session.close()
         mccs.__del__() # with closed fake_session
 
+    def rpc_check(self, reply):
+        fake_session = FakeModuleCCSession()
+        mccs = self.create_session("spec1.spec", None, None, fake_session)
+        fake_session.message_queue = [
+            ["Spec1", None, reply, False]
+        ]
+        exception = None
+        try:
+            result = mccs.rpc_call("test", "Spec2", params={
+                                       "param1": "Param 1",
+                                       "param2": "Param 2"
+                                   })
+        except Exception as e:
+            # We first want to check the value sent, raise the exception
+            # afterwards. So store it for a short while.
+            exception = e
+        self.assertEqual([
+                ["Spec2", "*", {"command": ["test", {
+                    "param1": "Param 1",
+                    "param2": "Param 2"
+                }]}, True]
+            ], fake_session.message_queue)
+        if exception is not None:
+            raise exception
+        return result
+
+    def test_rpc_call_success(self):
+        """
+        Test we can send an RPC (command) and get an answer. The answer is
+        success in this case.
+        """
+        result = self.rpc_check({"result": [0, {"Hello": "a"}]})
+        self.assertEqual({"Hello": "a"}, result)
+
+    def test_rpc_call_success_none(self):
+        """
+        Test the success case of RPC command, but the answer is empty
+        (eg. a "void" function on the remote side).
+        """
+        self.assertIsNone(self.rpc_check({"result": [0]}))
+
+    def test_rpc_call_malformed_answer(self):
+        """
+        Test it successfully raises ModuleCCSessionError when a malformed
+        reply is sent.
+        """
+        self.assertRaises(ModuleCCSessionError, self.rpc_check, ["Nonsense"])
+
+    def test_rpc_call_error(self):
+        """
+        Test it raises an exception when the remote side reports an error.
+        """
+        self.assertRaises(RPCError, self.rpc_check, {"result": [1, "Error"]})
+
+    def test_rpc_call_no_recpt(self):
+        """
+        Test RPC raises an error when the recipient is not there.
+        """
+        self.assertRaises(RPCRecipientMissing, self.rpc_check,
+                          {"result": [-1, "Error"]})
+
     def my_config_handler_ok(self, new_config):
         return isc.config.ccsession.create_answer(0)
 

+ 6 - 4
src/lib/python/isc/config/tests/unittest_fakesession.py

@@ -28,7 +28,7 @@ class WouldBlockForever(Exception):
 class FakeModuleCCSession:
     def __init__(self):
         self.subscriptions = {}
-        # each entry is of the form [ channel, instance, message ]
+        # each entry is of the form [ channel, instance, message, want_answer ]
         self.message_queue = []
         self._socket = "ok we just need something not-None here atm"
         # if self.timeout is set to anything other than 0, and
@@ -68,12 +68,14 @@ class FakeModuleCCSession:
         else:
             return False
 
-    def group_sendmsg(self, msg, channel, target = None):
-        self.message_queue.append([ channel, target, msg ])
+    def group_sendmsg(self, msg, group, instance=None, to=None,
+                      want_answer=False):
+        self.message_queue.append([ group, instance, msg, want_answer ])
+        return 42
 
     def group_reply(self, env, msg):
         if 'group' in env:
-            self.message_queue.append([ env['group'], None, msg])
+            self.message_queue.append([ env['group'], None, msg, False])
 
     def group_recvmsg(self, nonblock=True, seq = None):
         for qm in self.message_queue:

+ 9 - 5
src/lib/python/isc/server_common/auth_command.py

@@ -22,6 +22,13 @@ from isc.log_messages.server_common_messages import *
 from isc.server_common.logger import logger
 
 AUTH_MODULE_NAME = 'Auth'
+AUTH_LOADZONE_COMMAND = 'loadzone'
+
+def auth_loadzone_params(zone_name, zone_class):
+    return {
+        "origin": zone_name.to_text(),
+        "class": zone_class.to_text()
+    }
 
 def auth_loadzone_command(module_cc, zone_name, zone_class):
     '''Create a 'loadzone' command with a given zone for Auth server.
@@ -50,8 +57,5 @@ def auth_loadzone_command(module_cc, zone_name, zone_class):
     # to notification-driven approach, at which point the function would
     # be changed a lot.
 
-    param = {
-        "origin": zone_name.to_text(),
-        "class": zone_class.to_text()
-    }
-    return create_command("loadzone", param)
+    return create_command(AUTH_LOADZONE_COMMAND,
+                          auth_loadzone_params(zone_name, zone_class))

+ 71 - 68
src/lib/server_common/tests/socket_requestor_test.cc

@@ -35,6 +35,7 @@
 
 #include <util/io/fd.h>
 #include <util/io/fd_share.h>
+#include <util/unittests/check_valgrind.h>
 
 using namespace isc::data;
 using namespace isc::config;
@@ -509,81 +510,83 @@ private:
 };
 
 TEST_F(SocketRequestorTest, testSocketPassing) {
-    TestSocket ts;
-    std::vector<std::pair<std::string, int> > data;
-    data.push_back(std::pair<std::string, int>("foo\n", 1));
-    data.push_back(std::pair<std::string, int>("bar\n", 2));
-    data.push_back(std::pair<std::string, int>("foo\n", 3));
-    data.push_back(std::pair<std::string, int>("foo\n", 1));
-    data.push_back(std::pair<std::string, int>("foo\n", -1));
-    data.push_back(std::pair<std::string, int>("foo\n", -2));
-
-    // run() returns true iff we can specify read timeout so we avoid a
-    // deadlock.  Unless there's a bug the test should succeed even without the
-    // timeout, but we don't want to make the test hang up in case with an
-    // unexpected bug, so we'd rather skip most of the tests in that case.
-    const bool timo_ok = ts.run(data);
-    SocketRequestor::SocketID socket_id;
-    if (timo_ok) {
-        // 1 should be ok
-        addAnswer("foo", ts.getPath());
-        socket_id = doRequest();
-        EXPECT_EQ("foo", socket_id.second);
-        EXPECT_EQ(0, close(socket_id.first));
-
-        // 2 should be ok too
-        addAnswer("bar", ts.getPath());
-        socket_id = doRequest();
-        EXPECT_EQ("bar", socket_id.second);
-        EXPECT_EQ(0, close(socket_id.first));
-
-        // 3 should be ok too (reuse earlier token)
-        addAnswer("foo", ts.getPath());
-        socket_id = doRequest();
-        EXPECT_EQ("foo", socket_id.second);
-        EXPECT_EQ(0, close(socket_id.first));
-    }
-    // Create a second socket server, to test that multiple different
-    // domains sockets would work as well (even though we don't actually
-    // use that feature)
-    TestSocket ts2;
-    std::vector<std::pair<std::string, int> > data2;
-    data2.push_back(std::pair<std::string, int>("foo\n", 1));
-    const bool timo_ok2 = ts2.run(data2);
-
-    if (timo_ok2) {
-        // 1 should be ok
-        addAnswer("foo", ts2.getPath());
-        socket_id = doRequest();
-        EXPECT_EQ("foo", socket_id.second);
-        EXPECT_EQ(0, close(socket_id.first));
-    }
+    if (!isc::util::unittests::runningOnValgrind()) {
+        TestSocket ts;
+        std::vector<std::pair<std::string, int> > data;
+        data.push_back(std::pair<std::string, int>("foo\n", 1));
+        data.push_back(std::pair<std::string, int>("bar\n", 2));
+        data.push_back(std::pair<std::string, int>("foo\n", 3));
+        data.push_back(std::pair<std::string, int>("foo\n", 1));
+        data.push_back(std::pair<std::string, int>("foo\n", -1));
+        data.push_back(std::pair<std::string, int>("foo\n", -2));
+
+        // run() returns true iff we can specify read timeout so we avoid a
+        // deadlock.  Unless there's a bug the test should succeed even without the
+        // timeout, but we don't want to make the test hang up in case with an
+        // unexpected bug, so we'd rather skip most of the tests in that case.
+        const bool timo_ok = ts.run(data);
+        SocketRequestor::SocketID socket_id;
+        if (timo_ok) {
+            // 1 should be ok
+            addAnswer("foo", ts.getPath());
+            socket_id = doRequest();
+            EXPECT_EQ("foo", socket_id.second);
+            EXPECT_EQ(0, close(socket_id.first));
+
+            // 2 should be ok too
+            addAnswer("bar", ts.getPath());
+            socket_id = doRequest();
+            EXPECT_EQ("bar", socket_id.second);
+            EXPECT_EQ(0, close(socket_id.first));
+
+            // 3 should be ok too (reuse earlier token)
+            addAnswer("foo", ts.getPath());
+            socket_id = doRequest();
+            EXPECT_EQ("foo", socket_id.second);
+            EXPECT_EQ(0, close(socket_id.first));
+        }
+        // Create a second socket server, to test that multiple different
+        // domains sockets would work as well (even though we don't actually
+        // use that feature)
+        TestSocket ts2;
+        std::vector<std::pair<std::string, int> > data2;
+        data2.push_back(std::pair<std::string, int>("foo\n", 1));
+        const bool timo_ok2 = ts2.run(data2);
+
+        if (timo_ok2) {
+            // 1 should be ok
+            addAnswer("foo", ts2.getPath());
+            socket_id = doRequest();
+            EXPECT_EQ("foo", socket_id.second);
+            EXPECT_EQ(0, close(socket_id.first));
+        }
 
-    if (timo_ok) {
-        // Now use first socket again
-        addAnswer("foo", ts.getPath());
-        socket_id = doRequest();
-        EXPECT_EQ("foo", socket_id.second);
-        EXPECT_EQ(0, close(socket_id.first));
+        if (timo_ok) {
+            // Now use first socket again
+            addAnswer("foo", ts.getPath());
+            socket_id = doRequest();
+            EXPECT_EQ("foo", socket_id.second);
+            EXPECT_EQ(0, close(socket_id.first));
+
+            // -1 is a "normal" error
+            addAnswer("foo", ts.getPath());
+            EXPECT_THROW(doRequest(), SocketRequestor::SocketError);
+
+            // -2 is an unexpected error.  After this point it's not guaranteed the
+            // connection works as intended.
+            addAnswer("foo", ts.getPath());
+            EXPECT_THROW(doRequest(), SocketRequestor::SocketError);
+        }
 
-        // -1 is a "normal" error
+        // Vector is of first socket is now empty, so the socket should be gone
         addAnswer("foo", ts.getPath());
         EXPECT_THROW(doRequest(), SocketRequestor::SocketError);
 
-        // -2 is an unexpected error.  After this point it's not guaranteed the
-        // connection works as intended.
-        addAnswer("foo", ts.getPath());
+        // Vector is of second socket is now empty too, so the socket should be
+        // gone
+        addAnswer("foo", ts2.getPath());
         EXPECT_THROW(doRequest(), SocketRequestor::SocketError);
     }
-
-    // Vector is of first socket is now empty, so the socket should be gone
-    addAnswer("foo", ts.getPath());
-    EXPECT_THROW(doRequest(), SocketRequestor::SocketError);
-
-    // Vector is of second socket is now empty too, so the socket should be
-    // gone
-    addAnswer("foo", ts2.getPath());
-    EXPECT_THROW(doRequest(), SocketRequestor::SocketError);
 }
 
 }

+ 6 - 15
src/lib/statistics/counter.h

@@ -22,10 +22,6 @@
 
 #include <vector>
 
-namespace {
-const unsigned int InitialValue = 0;
-} // anonymous namespace
-
 namespace isc {
 namespace statistics {
 
@@ -40,24 +36,19 @@ private:
 public:
     /// The constructor.
     ///
-    /// This constructor is mostly exception free. But it may still throw
-    /// a standard exception if memory allocation fails inside the method.
+    /// This constructor prepares a set of counters which has \a items
+    /// elements. The counters will be initialized with 0.
     ///
     /// \param items A number of counter items to hold (greater than 0)
     ///
     /// \throw isc::InvalidParameter \a items is 0
     explicit Counter(const size_t items) :
-        counters_(items, InitialValue)
+        counters_(items, 0)
     {
         if (items == 0) {
             isc_throw(isc::InvalidParameter, "Items must not be 0");
         }
-    };
-
-    /// The destructor.
-    ///
-    /// This method never throws an exception.
-    ~Counter() {};
+    }
 
     /// \brief Increment a counter item specified with \a type.
     ///
@@ -70,7 +61,7 @@ public:
         }
         ++counters_.at(type);
         return;
-    };
+    }
 
     /// \brief Get the value of a counter item specified with \a type.
     ///
@@ -82,7 +73,7 @@ public:
             isc_throw(isc::OutOfRange, "Counter type is out of range");
         }
         return (counters_.at(type));
-    };
+    }
 };
 
 }   // namespace statistics

+ 56 - 31
src/lib/statistics/counter_dict.h

@@ -26,28 +26,34 @@
 #include <cassert>
 #include <stdexcept>
 #include <string>
-#include <vector>
 #include <map>
 #include <iterator>
 #include <utility>
 
-namespace {
-typedef boost::shared_ptr<isc::statistics::Counter> CounterPtr;
-typedef std::map<std::string, CounterPtr> DictionaryMap;
-}
 
 namespace isc {
 namespace statistics {
 
 class CounterDictionary : boost::noncopyable {
 private:
+    typedef boost::shared_ptr<isc::statistics::Counter> CounterPtr;
+    typedef std::map<std::string, CounterPtr> DictionaryMap;
     DictionaryMap dictionary_;
-    std::vector<std::string> elements_;
     const size_t items_;
     // Default constructor is forbidden; number of counter items must be
     // specified at the construction of this class.
     CounterDictionary();
 public:
+    /// The constructor.
+    ///
+    /// This constructor prepares a dictionary of set of counters.
+    /// Initially the dictionary is empty.
+    /// Each counter has \a items elements. The counters will be initialized
+    /// with 0.
+    ///
+    /// \param items A number of counter items to hold (greater than 0)
+    ///
+    /// \throw isc::InvalidParameter \a items is 0
     explicit CounterDictionary(const size_t items) :
         items_(items)
     {
@@ -55,11 +61,17 @@ public:
         if (items == 0) {
             isc_throw(isc::InvalidParameter, "Items must not be 0");
         }
-    };
-    ~CounterDictionary() {};
+    }
+
+    /// \brief Add an element which has a key \a name to the dictionary.
+    ///
+    /// \param name A key of the element to add
+    ///
+    /// \throw isc::InvalidParameter an element which has \a name as key
+    ///                              already exists
     void addElement(const std::string& name) {
         // throw if the element already exists
-        if (dictionary_.count(name) != 0) {
+        if (dictionary_.find(name) != dictionary_.end()) {
             isc_throw(isc::InvalidParameter,
                       "Element " << name << " already exists");
         }
@@ -67,16 +79,30 @@ public:
         // Create a new Counter and add to the map
         dictionary_.insert(
             DictionaryMap::value_type(name, CounterPtr(new Counter(items_))));
-    };
+    }
+
+    /// \brief Delete the element which has a key \a name from the dictionary.
+    ///
+    /// \param name A key of the element to delete
+    ///
+    /// \throw isc::OutOfRange an element which has \a name as key does not
+    ///                        exist
     void deleteElement(const std::string& name) {
-        size_t result = dictionary_.erase(name);
+        const size_t result = dictionary_.erase(name);
         if (result != 1) {
             // If an element with specified name does not exist, throw
             // isc::OutOfRange.
             isc_throw(isc::OutOfRange,
                       "Element " << name << " does not exist");
         }
-    };
+    }
+
+    /// \brief Get a reference to a %Counter which has \a name as key
+    ///
+    /// \param name A key of the element
+    ///
+    /// \throw isc::OutOfRange an element which has \a name as key does not
+    ///                        exist
     Counter& getElement(const std::string& name) {
         DictionaryMap::const_iterator i = dictionary_.find(name);
         if (i != dictionary_.end()) {
@@ -88,10 +114,13 @@ public:
             isc_throw(isc::OutOfRange,
                       "Element " << name << " does not exist");
         }
-    };
+    }
+
+    /// \brief Same as \c getElement()
     Counter& operator[](const std::string& name) {
         return (getElement(name));
-    };
+    }
+
     /// \brief \c ConstIterator is a constant iterator that provides an
     /// interface for enumerating name of zones stored in CounterDictionary.
     ///
@@ -107,35 +136,28 @@ public:
                                 boost::forward_traversal_tag>
     {
         public:
-            /// The constructor.
-            ///
-            /// This constructor is mostly exception free. But it may still
-            /// throw a standard exception if memory allocation fails
-            /// inside the method.
+            /// \brief The constructor.
             ConstIterator() {}
-            /// The destructor.
-            ///
-            /// This method never throws an exception.
-            ~ConstIterator() {}
-            /// Constructor from implementation detail DictionaryMap::const_iterator
-            ConstIterator(
-                DictionaryMap::const_iterator iterator) :
+
+            /// \brief Constructor from implementation detail
+            /// DictionaryMap::const_iterator
+            ConstIterator(DictionaryMap::const_iterator iterator) :
                 iterator_(iterator)
             {}
 
         private:
-            /// \brief An internal method to increment this iterator.
+            // An internal method to increment this iterator.
             void increment() {
                 ++iterator_;
                 return;
             }
 
-            /// \brief An internal method to check equality.
+            // An internal method to check equality.
             bool equal(const ConstIterator& other) const {
                 return (iterator_ == other.iterator_);
             }
 
-            /// \brief An internal method to dereference this iterator.
+            // An internal method to dereference this iterator.
             const value_type& dereference() const {
                 return (iterator_->first);
             }
@@ -145,12 +167,15 @@ public:
             DictionaryMap::const_iterator iterator_;
     };
 
+    /// \brief Get an iterator for the beginning of the dictionary.
     ConstIterator begin() const {
         return (CounterDictionary::ConstIterator(dictionary_.begin()));
-    };
+    }
+
+    /// \brief Get an iterator for the end of the dictionary.
     ConstIterator end() const {
         return (CounterDictionary::ConstIterator(dictionary_.end()));
-    };
+    }
 
     typedef ConstIterator const_iterator;
 };

+ 8 - 0
src/lib/testutils/testdata/example.com

@@ -6,3 +6,11 @@ ns.example.com.	A 192.0.2.1
 ;; bogus RDATA for CNAME RR, but the loadzone tool accepts it.  looking up this
 ;; record will trigger an exception.
 broken.example.com. CNAME 0123456789012345678901234567890123456789012345678901234567890123456789.example.com.
+
+;; very large RDATA. it exceeds 512 octets.
+large-rdata.example.com. TXT "0-1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
+large-rdata.example.com. TXT "1-1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
+large-rdata.example.com. TXT "2-1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
+large-rdata.example.com. TXT "3-1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
+large-rdata.example.com. TXT "4-1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
+large-rdata.example.com. TXT "5-1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"

BIN
src/lib/testutils/testdata/example.sqlite3


+ 41 - 37
src/lib/util/tests/fd_share_tests.cc

@@ -15,6 +15,7 @@
 #include <util/io/fd.h>
 #include <util/io/fd_share.h>
 
+#include <util/unittests/check_valgrind.h>
 #include <util/unittests/fork.h>
 
 #include <gtest/gtest.h>
@@ -30,44 +31,47 @@ namespace {
 
 // We test that we can transfer a pipe over other pipe
 TEST(FDShare, transfer) {
-    // Get a pipe and fork
-    int pipes[2];
-    ASSERT_NE(-1, socketpair(AF_UNIX, SOCK_STREAM, 0, pipes));
-    pid_t sender(fork());
-    ASSERT_NE(-1, sender);
-    if(sender) { // We are in parent
-        // Close the other side of pipe, we want only writible one
-        EXPECT_NE(-1, close(pipes[0]));
-        // Get a process to check data
-        int fd(0);
-        pid_t checker(check_output(&fd, "data", 4));
-        ASSERT_NE(-1, checker);
-        // Now, send the file descriptor, close it and close the pipe
-        EXPECT_NE(-1, send_fd(pipes[1], fd));
-        EXPECT_NE(-1, close(pipes[1]));
-        EXPECT_NE(-1, close(fd));
-        // Check both subprocesses ended well
-        EXPECT_TRUE(process_ok(sender));
-        EXPECT_TRUE(process_ok(checker));
-    } else { // We are in child. We do not use ASSERT here
-        // Close the write end, we only read
-        if(close(pipes[1])) {
-            exit(1);
-        }
-        // Get the file descriptor
-        int fd(recv_fd(pipes[0]));
-        if(fd == -1) {
-            exit(1);
-        }
-        // This pipe is not needed
-        if(close(pipes[0])) {
-            exit(1);
-        }
-        // Send "data" trough the received fd, close it and be done
-        if(!write_data(fd, "data", 4) || close(fd) == -1) {
-            exit(1);
+
+    if (!isc::util::unittests::runningOnValgrind()) {
+        // Get a pipe and fork
+        int pipes[2];
+        ASSERT_NE(-1, socketpair(AF_UNIX, SOCK_STREAM, 0, pipes));
+        const pid_t sender(fork());
+        ASSERT_NE(-1, sender);
+        if (sender) { // We are in parent
+            // Close the other side of pipe, we want only writible one
+            EXPECT_NE(-1, close(pipes[0]));
+            // Get a process to check data
+            int fd(0);
+            const pid_t checker(check_output(&fd, "data", 4));
+            ASSERT_NE(-1, checker);
+            // Now, send the file descriptor, close it and close the pipe
+            EXPECT_NE(-1, send_fd(pipes[1], fd));
+            EXPECT_NE(-1, close(pipes[1]));
+            EXPECT_NE(-1, close(fd));
+            // Check both subprocesses ended well
+            EXPECT_TRUE(process_ok(sender));
+            EXPECT_TRUE(process_ok(checker));
+        } else { // We are in child. We do not use ASSERT here
+            // Close the write end, we only read
+            if (close(pipes[1])) {
+                exit(1);
+            }
+            // Get the file descriptor
+            const int fd(recv_fd(pipes[0]));
+            if (fd == -1) {
+                exit(1);
+            }
+            // This pipe is not needed
+            if (close(pipes[0])) {
+                exit(1);
+            }
+            // Send "data" trough the received fd, close it and be done
+            if (!write_data(fd, "data", 4) || close(fd) == -1) {
+                exit(1);
+            }
+            exit(0);
         }
-        exit(0);
     }
 }
 

+ 20 - 14
src/lib/util/tests/fd_tests.cc

@@ -12,6 +12,8 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <util/unittests/check_valgrind.h>
+
 #include <util/io/fd.h>
 
 #include <util/unittests/fork.h>
@@ -45,24 +47,28 @@ class FDTest : public ::testing::Test {
 
 // Test we read what was sent
 TEST_F(FDTest, read) {
-    int read_pipe(0);
-    buffer = new unsigned char[TEST_DATA_SIZE];
-    pid_t feeder(provide_input(&read_pipe, data, TEST_DATA_SIZE));
-    ASSERT_GE(feeder, 0);
-    ssize_t received(read_data(read_pipe, buffer, TEST_DATA_SIZE));
-    EXPECT_TRUE(process_ok(feeder));
-    EXPECT_EQ(TEST_DATA_SIZE, received);
-    EXPECT_EQ(0, memcmp(data, buffer, received));
+    if (!isc::util::unittests::runningOnValgrind()) {
+        int read_pipe(0);
+        buffer = new unsigned char[TEST_DATA_SIZE];
+        pid_t feeder(provide_input(&read_pipe, data, TEST_DATA_SIZE));
+        ASSERT_GE(feeder, 0);
+        ssize_t received(read_data(read_pipe, buffer, TEST_DATA_SIZE));
+        EXPECT_TRUE(process_ok(feeder));
+        EXPECT_EQ(TEST_DATA_SIZE, received);
+        EXPECT_EQ(0, memcmp(data, buffer, received));
+    }
 }
 
 // Test we write the correct thing
 TEST_F(FDTest, write) {
-    int write_pipe(0);
-    pid_t checker(check_output(&write_pipe, data, TEST_DATA_SIZE));
-    ASSERT_GE(checker, 0);
-    EXPECT_TRUE(write_data(write_pipe, data, TEST_DATA_SIZE));
-    EXPECT_EQ(0, close(write_pipe));
-    EXPECT_TRUE(process_ok(checker));
+    if (!isc::util::unittests::runningOnValgrind()) {
+        int write_pipe(0);
+        pid_t checker(check_output(&write_pipe, data, TEST_DATA_SIZE));
+        ASSERT_GE(checker, 0);
+        EXPECT_TRUE(write_data(write_pipe, data, TEST_DATA_SIZE));
+        EXPECT_EQ(0, close(write_pipe));
+        EXPECT_TRUE(process_ok(checker));
+    }
 }
 
 }

+ 77 - 68
src/lib/util/tests/interprocess_sync_file_unittest.cc

@@ -13,6 +13,8 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include "util/interprocess_sync_file.h"
+
+#include <util/unittests/check_valgrind.h>
 #include <gtest/gtest.h>
 #include <unistd.h>
 
@@ -53,59 +55,62 @@ parentReadLockedState (int fd) {
 }
 
 TEST(InterprocessSyncFileTest, TestLock) {
-  InterprocessSyncFile sync("test");
-  InterprocessSyncLocker locker(sync);
+    InterprocessSyncFile sync("test");
+    InterprocessSyncLocker locker(sync);
 
-  EXPECT_FALSE(locker.isLocked());
-  EXPECT_TRUE(locker.lock());
-  EXPECT_TRUE(locker.isLocked());
+    EXPECT_FALSE(locker.isLocked());
+    EXPECT_TRUE(locker.lock());
+    EXPECT_TRUE(locker.isLocked());
 
-  int fds[2];
+    if (!isc::util::unittests::runningOnValgrind()) {
 
-  // Here, we check that a lock has been taken by forking and
-  // checking from the child that a lock exists. This has to be
-  // done from a separate process as we test by trying to lock the
-  // range again on the lock file. The lock attempt would pass if
-  // done from the same process for the granted range. The lock
-  // attempt must fail to pass our check.
+        int fds[2];
 
-  EXPECT_EQ(0, pipe(fds));
+        // Here, we check that a lock has been taken by forking and
+        // checking from the child that a lock exists. This has to be
+        // done from a separate process as we test by trying to lock the
+        // range again on the lock file. The lock attempt would pass if
+        // done from the same process for the granted range. The lock
+        // attempt must fail to pass our check.
 
-  if (fork() == 0) {
-      unsigned char locked = 0;
-      // Child writes to pipe
-      close(fds[0]);
+        EXPECT_EQ(0, pipe(fds));
 
-      InterprocessSyncFile sync2("test");
-      InterprocessSyncLocker locker2(sync2);
+        if (fork() == 0) {
+            unsigned char locked = 0;
+            // Child writes to pipe
+            close(fds[0]);
 
-      if (!locker2.tryLock()) {
-          EXPECT_FALSE(locker2.isLocked());
-          locked = 1;
-      } else {
-          EXPECT_TRUE(locker2.isLocked());
-      }
+            InterprocessSyncFile sync2("test");
+            InterprocessSyncLocker locker2(sync2);
 
-      ssize_t bytes_written = write(fds[1], &locked, sizeof(locked));
-      EXPECT_EQ(sizeof(locked), bytes_written);
+            if (!locker2.tryLock()) {
+                EXPECT_FALSE(locker2.isLocked());
+                locked = 1;
+            } else {
+                EXPECT_TRUE(locker2.isLocked());
+            }
 
-      close(fds[1]);
-      exit(0);
-  } else {
-      // Parent reads from pipe
-      close(fds[1]);
+            ssize_t bytes_written = write(fds[1], &locked, sizeof(locked));
+            EXPECT_EQ(sizeof(locked), bytes_written);
 
-      const unsigned char locked = parentReadLockedState(fds[0]);
+            close(fds[1]);
+            exit(0);
+        } else {
+            // Parent reads from pipe
+            close(fds[1]);
 
-      close(fds[0]);
+            const unsigned char locked = parentReadLockedState(fds[0]);
 
-      EXPECT_EQ(1, locked);
-  }
+            close(fds[0]);
 
-  EXPECT_TRUE(locker.unlock());
-  EXPECT_FALSE(locker.isLocked());
+            EXPECT_EQ(1, locked);
+        }
+    }
+
+    EXPECT_TRUE(locker.unlock());
+    EXPECT_FALSE(locker.isLocked());
 
-  EXPECT_EQ (0, unlink(TEST_DATA_TOPBUILDDIR "/test_lockfile"));
+    EXPECT_EQ (0, unlink(TEST_DATA_TOPBUILDDIR "/test_lockfile"));
 }
 
 TEST(InterprocessSyncFileTest, TestMultipleFilesDirect) {
@@ -126,49 +131,53 @@ TEST(InterprocessSyncFileTest, TestMultipleFilesDirect) {
 }
 
 TEST(InterprocessSyncFileTest, TestMultipleFilesForked) {
-  InterprocessSyncFile sync("test1");
-  InterprocessSyncLocker locker(sync);
+    InterprocessSyncFile sync("test1");
+    InterprocessSyncLocker locker(sync);
 
-  EXPECT_TRUE(locker.lock());
+    EXPECT_TRUE(locker.lock());
 
-  int fds[2];
+    if (!isc::util::unittests::runningOnValgrind()) {
 
-  EXPECT_EQ(0, pipe(fds));
+        int fds[2];
 
-  if (fork() == 0) {
-      unsigned char locked = 0xff;
-      // Child writes to pipe
-      close(fds[0]);
+        EXPECT_EQ(0, pipe(fds));
 
-      InterprocessSyncFile sync2("test2");
-      InterprocessSyncLocker locker2(sync2);
+        if (fork() == 0) {
+            unsigned char locked = 0xff;
+            // Child writes to pipe
+            close(fds[0]);
 
-      if (locker2.tryLock()) {
-          locked = 0;
-      }
+            InterprocessSyncFile sync2("test2");
+            InterprocessSyncLocker locker2(sync2);
 
-      ssize_t bytes_written = write(fds[1], &locked, sizeof(locked));
-      EXPECT_EQ(sizeof(locked), bytes_written);
+            if (locker2.tryLock()) {
+                locked = 0;
+            }
 
-      close(fds[1]);
-      exit(0);
-  } else {
-      // Parent reads from pipe
-      close(fds[1]);
+            ssize_t bytes_written = write(fds[1], &locked, sizeof(locked));
+            EXPECT_EQ(sizeof(locked), bytes_written);
 
-      const unsigned char locked = parentReadLockedState(fds[0]);
+            close(fds[1]);
+            exit(0);
+        } else {
+            // Parent reads from pipe
+            close(fds[1]);
 
-      close(fds[0]);
+            const unsigned char locked = parentReadLockedState(fds[0]);
 
-      EXPECT_EQ(0, locked);
-  }
+            close(fds[0]);
 
-  EXPECT_TRUE(locker.unlock());
+            EXPECT_EQ(0, locked);
+        }
 
-  EXPECT_EQ (0, unlink(TEST_DATA_TOPBUILDDIR "/test1_lockfile"));
-  EXPECT_EQ (0, unlink(TEST_DATA_TOPBUILDDIR "/test2_lockfile"));
-}
+        EXPECT_EQ (0, unlink(TEST_DATA_TOPBUILDDIR "/test2_lockfile"));
+    }
+
+    EXPECT_TRUE(locker.unlock());
+
+    EXPECT_EQ (0, unlink(TEST_DATA_TOPBUILDDIR "/test1_lockfile"));
 }
 
+} // anonymous namespace
 } // namespace util
 } // namespace isc

+ 17 - 12
src/lib/util/threads/tests/condvar_unittest.cc

@@ -15,6 +15,7 @@
 #include <config.h>
 
 #include <exceptions/exceptions.h>
+#include <util/unittests/check_valgrind.h>
 
 #include <util/threads/sync.h>
 #include <util/threads/thread.h>
@@ -84,12 +85,14 @@ ringSignal(CondVar* condvar, Mutex* mutex, int* arg) {
 
 // A simple wait-signal operation on a condition variable.
 TEST_F(CondVarTest, waitAndSignal) {
-    Mutex::Locker locker(mutex_);
-    int shared_var = 0; // let the other thread increment this
-    Thread t(boost::bind(&ringSignal, &condvar_, &mutex_, &shared_var));
-    condvar_.wait(mutex_);
-    t.wait();
-    EXPECT_EQ(1, shared_var);
+    if (!isc::util::unittests::runningOnValgrind()) {
+        Mutex::Locker locker(mutex_);
+        int shared_var = 0; // let the other thread increment this
+        Thread t(boost::bind(&ringSignal, &condvar_, &mutex_, &shared_var));
+        condvar_.wait(mutex_);
+        t.wait();
+        EXPECT_EQ(1, shared_var);
+    }
 }
 
 // Thread's main code for the next test
@@ -143,12 +146,14 @@ signalAndWait(CondVar* condvar, Mutex* mutex) {
 TEST_F(CondVarTest, destroyWhileWait) {
     // We'll destroy a CondVar object while the thread is still waiting
     // on it.  This will trigger an assertion failure.
-    EXPECT_DEATH_IF_SUPPORTED({
-            CondVar cond;
-            Mutex::Locker locker(mutex_);
-            Thread t(boost::bind(&signalAndWait, &cond, &mutex_));
-            cond.wait(mutex_);
-        }, "");
+    if (!isc::util::unittests::runningOnValgrind()) {
+        EXPECT_DEATH_IF_SUPPORTED({
+                CondVar cond;
+                Mutex::Locker locker(mutex_);
+                Thread t(boost::bind(&signalAndWait, &cond, &mutex_));
+                cond.wait(mutex_);
+            }, "");
+    }
 }
 #endif // !HAS_UNDEFINED_PTHREAD_BEHAVIOR
 

+ 29 - 27
src/lib/util/threads/tests/lock_unittest.cc

@@ -94,33 +94,35 @@ void
 noHandler(int) {}
 
 TEST(MutexTest, swarm) {
-    // Create a timeout in case something got stuck here
-    struct sigaction ignored, original;
-    memset(&ignored, 0, sizeof(ignored));
-    ignored.sa_handler = noHandler;
-    if (sigaction(SIGALRM, &ignored, &original)) {
-        FAIL() << "Couldn't set alarm";
-    }
-    alarm(10);
-    // This type has a low chance of being atomic itself, further raising
-    // the chance of problems appearing.
-    double canary = 0;
-    Mutex mutex;
-    // Run two parallel threads
-    bool ready1 = false;
-    bool ready2 = false;
-    Thread t1(boost::bind(&performIncrement, &canary, &ready1, &ready2,
-                          &mutex));
-    Thread t2(boost::bind(&performIncrement, &canary, &ready2, &ready1,
-                          &mutex));
-    t1.wait();
-    t2.wait();
-    // Check it the sum is the expected value.
-    EXPECT_EQ(iterations * 2, canary) << "Threads are badly synchronized";
-    // Cancel the alarm and return the original handler
-    alarm(0);
-    if (sigaction(SIGALRM, &original, NULL)) {
-        FAIL() << "Couldn't restore alarm";
+    if (!isc::util::unittests::runningOnValgrind()) {
+        // Create a timeout in case something got stuck here
+        struct sigaction ignored, original;
+        memset(&ignored, 0, sizeof(ignored));
+        ignored.sa_handler = noHandler;
+        if (sigaction(SIGALRM, &ignored, &original)) {
+            FAIL() << "Couldn't set alarm";
+        }
+        alarm(10);
+        // This type has a low chance of being atomic itself, further raising
+        // the chance of problems appearing.
+        double canary = 0;
+        Mutex mutex;
+        // Run two parallel threads
+        bool ready1 = false;
+        bool ready2 = false;
+        Thread t1(boost::bind(&performIncrement, &canary, &ready1, &ready2,
+                              &mutex));
+        Thread t2(boost::bind(&performIncrement, &canary, &ready2, &ready1,
+                              &mutex));
+        t1.wait();
+        t2.wait();
+        // Check it the sum is the expected value.
+        EXPECT_EQ(iterations * 2, canary) << "Threads are badly synchronized";
+        // Cancel the alarm and return the original handler
+        alarm(0);
+        if (sigaction(SIGALRM, &original, NULL)) {
+            FAIL() << "Couldn't restore alarm";
+        }
     }
 }
 

+ 29 - 20
src/lib/util/threads/tests/thread_unittest.cc

@@ -13,6 +13,7 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include <util/threads/thread.h>
+#include <util/unittests/check_valgrind.h>
 
 #include <boost/bind.hpp>
 
@@ -41,9 +42,11 @@ doSomething(int*) { }
 // We just test that we can forget about the thread and nothing
 // bad will happen on our side.
 TEST(ThreadTest, detached) {
-    int x;
-    for (size_t i = 0; i < detached_iterations; ++i) {
-        Thread thread(boost::bind(&doSomething, &x));
+    if (!isc::util::unittests::runningOnValgrind()) {
+        int x;
+        for (size_t i = 0; i < detached_iterations; ++i) {
+            Thread thread(boost::bind(&doSomething, &x));
+        }
     }
 }
 
@@ -55,13 +58,15 @@ markRun(bool* mark) {
 
 // Wait for a thread to end first. The variable must be set at the time.
 TEST(ThreadTest, wait) {
-    for (size_t i = 0; i < iterations; ++i) {
-        bool mark = false;
-        Thread thread(boost::bind(markRun, &mark));
-        thread.wait();
-        ASSERT_TRUE(mark) << "Not finished yet in " << i << "th iteration";
-        // Can't wait second time
-        ASSERT_THROW(thread.wait(), isc::InvalidOperation);
+    if (!isc::util::unittests::runningOnValgrind()) {
+        for (size_t i = 0; i < iterations; ++i) {
+            bool mark = false;
+            Thread thread(boost::bind(markRun, &mark));
+            thread.wait();
+            ASSERT_TRUE(mark) << "Not finished yet in " << i << "th iteration";
+            // Can't wait second time
+            ASSERT_THROW(thread.wait(), isc::InvalidOperation);
+        }
     }
 }
 
@@ -77,21 +82,25 @@ throwException() {
 
 // Exception in the thread we forget about should not do anything to us
 TEST(ThreadTest, detachedException) {
-    for (size_t i = 0; i < detached_iterations; ++i) {
-        Thread thread(throwSomething);
-    }
-    for (size_t i = 0; i < detached_iterations; ++i) {
-        Thread thread(throwException);
+    if (!isc::util::unittests::runningOnValgrind()) {
+        for (size_t i = 0; i < detached_iterations; ++i) {
+            Thread thread(throwSomething);
+        }
+        for (size_t i = 0; i < detached_iterations; ++i) {
+            Thread thread(throwException);
+        }
     }
 }
 
 // An uncaught exception in the thread should propagate through wait
 TEST(ThreadTest, exception) {
-    for (size_t i = 0; i < iterations; ++i) {
-        Thread thread(throwSomething);
-        Thread thread2(throwException);
-        ASSERT_THROW(thread.wait(), Thread::UncaughtException);
-        ASSERT_THROW(thread2.wait(), Thread::UncaughtException);
+    if (!isc::util::unittests::runningOnValgrind()) {
+        for (size_t i = 0; i < iterations; ++i) {
+            Thread thread(throwSomething);
+            Thread thread2(throwException);
+            ASSERT_THROW(thread.wait(), Thread::UncaughtException);
+            ASSERT_THROW(thread2.wait(), Thread::UncaughtException);
+        }
     }
 }
 

+ 5 - 0
src/lib/util/unittests/check_valgrind.h

@@ -38,6 +38,11 @@ namespace unittests {
 
 /// \brief Check if the program is run in valgrind
 ///
+/// This is used to check for valgrind and skip (parts of) tests that fork,
+/// such as death tests, and general forking tests, and some threading tests;
+/// These tend to cause valgrind to report errors, which would hide other
+/// potential valgrind reports.
+///
 /// \return true if valgrind headers are available, and valgrind is running,
 ///         false if the headers are not available, or if valgrind is not
 ///         running

+ 2 - 0
src/lib/util/unittests/fork.cc

@@ -77,6 +77,7 @@ provide_input(int *read_pipe, const void *input, const size_t length)
         return -1;
     }
     *read_pipe = pipes[0];
+
     pid_t pid(fork());
     if (pid) { // We are in the parent
         return pid;
@@ -91,6 +92,7 @@ provide_input(int *read_pipe, const void *input, const size_t length)
     }
 }
 
+
 /*
  * This creates a pipe, forks and reads the pipe and compares it
  * with given data. Used to check output of run in an asynchronous way.

+ 4 - 0
tests/lettuce/configurations/example.org.inmem.config

@@ -26,9 +26,13 @@
             ]
         }
     },
+    "Stats": {
+        "poll-interval": 1
+    },
     "Init": {
         "components": {
             "b10-auth": { "kind": "needed", "special": "auth" },
+            "b10-stats": { "address": "Stats", "kind": "dispensable" },
             "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
         }
     }

+ 189 - 3
tests/lettuce/features/queries.feature

@@ -8,15 +8,27 @@ Feature: Querying feature
         And wait for bind10 stderr message BIND10_STARTED_CC
         And wait for bind10 stderr message CMDCTL_STARTED
         And wait for bind10 stderr message AUTH_SERVER_STARTED
+        And wait for bind10 stderr message STATS_STARTING
 
         bind10 module Auth should be running
+        And bind10 module Stats should be running
         And bind10 module Resolver should not be running
         And bind10 module Xfrout should not be running
         And bind10 module Zonemgr should not be running
         And bind10 module Xfrin should not be running
-        And bind10 module Stats should not be running
         And bind10 module StatsHttpd should not be running
 
+        When I wait for new bind10 stderr message STATS_SEND_STATISTICS_REQUEST
+        # make sure Auth module receives a command
+        And wait for new bind10 stderr message AUTH_RECEIVED_COMMAND
+        # make sure Auth module replied to the command
+        And wait for new bind10 stderr message CC_REPLY
+        # make sure the response is for 'getstats'
+        And wait for new bind10 stderr message v4
+        Then I query statistics zones of bind10 module Auth
+        And last bindctl output should not contain "error"
+        The statistics counters are 0 in category .Auth.zones._SERVER_
+
         A query for www.example.org should have rcode NOERROR
         The last query response should have flags qr aa rd
         The last query response should have ancount 1
@@ -38,6 +50,26 @@ Feature: Querying feature
         ns2.example.org.        3600    IN      A       192.0.2.4
         """
 
+        When I wait for new bind10 stderr message STATS_SEND_STATISTICS_REQUEST
+        # make sure Auth module receives a command
+        And wait for new bind10 stderr message AUTH_RECEIVED_COMMAND
+        # make sure Auth module replied to the command
+        And wait for new bind10 stderr message CC_REPLY
+        # make sure the response is for 'getstats'
+        And wait for new bind10 stderr message v4
+        Then I query statistics zones of bind10 module Auth
+        And last bindctl output should not contain "error"
+        The statistics counters are 0 in category .Auth.zones._SERVER_ except for the following items
+          | item_name     | item_value |
+          | request.v4    |          1 |
+          | request.udp   |          1 |
+          | opcode.query  |          1 |
+          | responses     |          1 |
+          | qrysuccess    |          1 |
+          | qryauthans    |          1 |
+          | rcode.noerror |          1 |
+
+
         # Repeat of the above
         A query for www.example.org should have rcode NOERROR
         The last query response should have flags qr aa rd
@@ -60,6 +92,25 @@ Feature: Querying feature
         ns2.example.org.        3600    IN      A       192.0.2.4
         """
 
+        When I wait for new bind10 stderr message STATS_SEND_STATISTICS_REQUEST
+        # make sure Auth module receives a command
+        And wait for new bind10 stderr message AUTH_RECEIVED_COMMAND
+        # make sure Auth module replied to the command
+        And wait for new bind10 stderr message CC_REPLY
+        # make sure the response is for 'getstats'
+        And wait for new bind10 stderr message v4
+        Then I query statistics zones of bind10 module Auth
+        And last bindctl output should not contain "error"
+        The statistics counters are 0 in category .Auth.zones._SERVER_ except for the following items
+          | item_name     | item_value |
+          | request.v4    |          2 |
+          | request.udp   |          2 |
+          | opcode.query  |          2 |
+          | responses     |          2 |
+          | qrysuccess    |          2 |
+          | qryauthans    |          2 |
+          | rcode.noerror |          2 |
+
         # And now query something completely different
         A query for nosuchname.example.org should have rcode NXDOMAIN
         The last query response should have flags qr aa rd
@@ -71,6 +122,26 @@ Feature: Querying feature
         example.org.            3600    IN      SOA     ns1.example.org. admin.example.org. 1234 3600 1800 2419200 7200
         """
 
+        When I wait for new bind10 stderr message STATS_SEND_STATISTICS_REQUEST
+        # make sure Auth module receives a command
+        And wait for new bind10 stderr message AUTH_RECEIVED_COMMAND
+        # make sure Auth module replied to the command
+        And wait for new bind10 stderr message CC_REPLY
+        # make sure the response is for 'getstats'
+        And wait for new bind10 stderr message v4
+        Then I query statistics zones of bind10 module Auth
+        And last bindctl output should not contain "error"
+        The statistics counters are 0 in category .Auth.zones._SERVER_ except for the following items
+          | item_name      | item_value |
+          | request.v4     |          3 |
+          | request.udp    |          3 |
+          | opcode.query   |          3 |
+          | responses      |          3 |
+          | qrysuccess     |          2 |
+          | qryauthans     |          3 |
+          | rcode.noerror  |          2 |
+          | rcode.nxdomain |          1 |
+
     Scenario: ANY query
         Given I have bind10 running with configuration example.org.inmem.config
         And wait for bind10 stderr message BIND10_STARTED_CC
@@ -78,13 +149,24 @@ Feature: Querying feature
         And wait for bind10 stderr message AUTH_SERVER_STARTED
 
         bind10 module Auth should be running
+        And bind10 module Stats should be running
         And bind10 module Resolver should not be running
         And bind10 module Xfrout should not be running
         And bind10 module Zonemgr should not be running
         And bind10 module Xfrin should not be running
-        And bind10 module Stats should not be running
         And bind10 module StatsHttpd should not be running
 
+        When I wait for new bind10 stderr message STATS_SEND_STATISTICS_REQUEST
+        # make sure Auth module receives a command
+        And wait for new bind10 stderr message AUTH_RECEIVED_COMMAND
+        # make sure Auth module replied to the command
+        And wait for new bind10 stderr message CC_REPLY
+        # make sure the response is for 'getstats'
+        And wait for new bind10 stderr message v4
+        Then I query statistics zones of bind10 module Auth
+        And last bindctl output should not contain "error"
+        The statistics counters are 0 in category .Auth.zones._SERVER_
+
         A query for example.org type ANY should have rcode NOERROR
         The last query response should have flags qr aa rd
         The last query response should have ancount 4
@@ -104,11 +186,42 @@ Feature: Querying feature
         mail.example.org.       3600    IN      A       192.0.2.10
         """
 
+        When I wait for new bind10 stderr message STATS_SEND_STATISTICS_REQUEST
+        # make sure Auth module receives a command
+        And wait for new bind10 stderr message AUTH_RECEIVED_COMMAND
+        # make sure Auth module replied to the command
+        And wait for new bind10 stderr message CC_REPLY
+        # make sure the response is for 'getstats'
+        And wait for new bind10 stderr message v4
+        Then I query statistics zones of bind10 module Auth
+        And last bindctl output should not contain "error"
+        The statistics counters are 0 in category .Auth.zones._SERVER_ except for the following items
+          | item_name     | item_value |
+          | request.v4    |          1 |
+          | request.udp   |          1 |
+          | opcode.query  |          1 |
+          | responses     |          1 |
+          | qrysuccess    |          1 |
+          | qryauthans    |          1 |
+          | rcode.noerror |          1 |
+
     Scenario: Delegation query for unsigned child zone
         Given I have bind10 running with configuration example.org.inmem.config
         And wait for bind10 stderr message BIND10_STARTED_CC
         And wait for bind10 stderr message CMDCTL_STARTED
         And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        When I wait for new bind10 stderr message STATS_SEND_STATISTICS_REQUEST
+        # make sure Auth module receives a command
+        And wait for new bind10 stderr message AUTH_RECEIVED_COMMAND
+        # make sure Auth module replied to the command
+        And wait for new bind10 stderr message CC_REPLY
+        # make sure the response is for 'getstats'
+        And wait for new bind10 stderr message v4
+        Then I query statistics zones of bind10 module Auth
+        And last bindctl output should not contain "error"
+        The statistics counters are 0 in category .Auth.zones._SERVER_
+
         A dnssec query for www.sub.example.org type AAAA should have rcode NOERROR
         The last query response should have flags qr rd
         The last query response should have edns_flags do
@@ -124,6 +237,28 @@ Feature: Querying feature
         ns.sub.example.org.	3600	IN	A	192.0.2.101
         """
 
+        When I wait for new bind10 stderr message STATS_SEND_STATISTICS_REQUEST
+        # make sure Auth module receives a command
+        And wait for new bind10 stderr message AUTH_RECEIVED_COMMAND
+        # make sure Auth module replied to the command
+        And wait for new bind10 stderr message CC_REPLY
+        # make sure the response is for 'getstats'
+        And wait for new bind10 stderr message v4
+        Then I query statistics zones of bind10 module Auth
+        And last bindctl output should not contain "error"
+        The statistics counters are 0 in category .Auth.zones._SERVER_ except for the following items
+          | item_name         | item_value |
+          | request.v4        |          1 |
+          | request.udp       |          1 |
+          | request.edns0     |          1 |
+          | request.dnssec_ok |          1 |
+          | opcode.query      |          1 |
+          | responses         |          1 |
+          | response.edns0    |          1 |
+          | qrynoauthans      |          1 |
+          | qryreferral       |          1 |
+          | rcode.noerror     |          1 |
+
     Scenario: SSHFP query
         # We are testing one more RR type for a normal successful case
         Given I have bind10 running with configuration example.org.inmem.config
@@ -132,18 +267,69 @@ Feature: Querying feature
         And wait for bind10 stderr message AUTH_SERVER_STARTED
 
         bind10 module Auth should be running
+        And bind10 module Stats should be running
         And bind10 module Resolver should not be running
         And bind10 module Xfrout should not be running
         And bind10 module Zonemgr should not be running
         And bind10 module Xfrin should not be running
-        And bind10 module Stats should not be running
         And bind10 module StatsHttpd should not be running
 
+        When I wait for new bind10 stderr message STATS_SEND_STATISTICS_REQUEST
+        # make sure Auth module receives a command
+        And wait for new bind10 stderr message AUTH_RECEIVED_COMMAND
+        # make sure Auth module replied to the command
+        And wait for new bind10 stderr message CC_REPLY
+        # make sure the response is for 'getstats'
+        And wait for new bind10 stderr message v4
+        Then I query statistics zones of bind10 module Auth
+        And last bindctl output should not contain "error"
+        The statistics counters are 0 in category .Auth.zones._SERVER_
+
         A query for example.org type SSHFP should have rcode NOERROR
         The last query response should have ancount 0
+
+        When I wait for new bind10 stderr message STATS_SEND_STATISTICS_REQUEST
+        # make sure Auth module receives a command
+        And wait for new bind10 stderr message AUTH_RECEIVED_COMMAND
+        # make sure Auth module replied to the command
+        And wait for new bind10 stderr message CC_REPLY
+        # make sure the response is for 'getstats'
+        And wait for new bind10 stderr message v4
+        Then I query statistics zones of bind10 module Auth
+        And last bindctl output should not contain "error"
+        The statistics counters are 0 in category .Auth.zones._SERVER_ except for the following items
+          | item_name     | item_value |
+          | request.v4    |          1 |
+          | request.udp   |          1 |
+          | opcode.query  |          1 |
+          | responses     |          1 |
+          | qryauthans    |          1 |
+          | qrynxrrset    |          1 |
+          | rcode.noerror |          1 |
+
         A query for shell.example.org type SSHFP should have rcode NOERROR
         The last query response should have ancount 1
         The answer section of the last query response should be
         """
         shell.example.org.      3600    IN      SSHFP   2 1 123456789abcdef67890123456789abcdef67890
         """
+
+        When I wait for new bind10 stderr message STATS_SEND_STATISTICS_REQUEST
+        # make sure Auth module receives a command
+        And wait for new bind10 stderr message AUTH_RECEIVED_COMMAND
+        # make sure Auth module replied to the command
+        And wait for new bind10 stderr message CC_REPLY
+        # make sure the response is for 'getstats'
+        And wait for new bind10 stderr message v4
+        Then I query statistics zones of bind10 module Auth
+        And last bindctl output should not contain "error"
+        The statistics counters are 0 in category .Auth.zones._SERVER_ except for the following items
+          | item_name     | item_value |
+          | request.v4    |          2 |
+          | request.udp   |          2 |
+          | opcode.query  |          2 |
+          | responses     |          2 |
+          | qrysuccess    |          1 |
+          | qryauthans    |          2 |
+          | qrynxrrset    |          1 |
+          | rcode.noerror |          2 |

+ 58 - 6
tests/lettuce/features/terrain/bind10_control.py

@@ -391,14 +391,16 @@ def find_value(dictionary, key):
         for v in dictionary.values():
             return find_value(v, key)
 
-@step('the statistics counter (\S+)(?: for the zone (\S+))? should be' + \
+@step('the statistics counter (\S+)(?: in the category (\S+))?'+ \
+          '(?: for the zone (\S+))? should be' + \
           '(?:( greater than| less than| between))? (\-?\d+)(?: and (\-?\d+))?')
-def check_statistics(step, counter, zone, gtltbt, number, upper):
+def check_statistics(step, counter, category, zone, gtltbt, number, upper):
     """
     check the output of bindctl for statistics of specified counter
     and zone.
     Parameters:
     counter ('counter <counter>'): The counter name of statistics.
+    category ('category <category>', optional): The category of counter.
     zone ('zone <zone>', optional): The zone name.
     gtltbt (' greater than'|' less than'|' between', optional): greater than
           <number> or less than <number> or between <number> and <upper>.
@@ -409,14 +411,21 @@ def check_statistics(step, counter, zone, gtltbt, number, upper):
     """
     output = parse_bindctl_output_as_data_structure()
     found = None
+    category_str = ""
     zone_str = ""
+    depth = []
+    if category:
+        depth.insert(0, category)
+        category_str = " for category %s" % category
     if zone:
-        found = find_value(find_value(output, zone), counter)
+        depth.insert(0, zone)
         zone_str = " for zone %s" % zone
-    else:
-        found = find_value(output, counter)
+    for level in depth:
+        output = find_value(output, level)
+    found = find_value(output, counter)
     assert found is not None, \
-        'Not found statistics counter %s%s' % (counter, zone_str)
+        'Not found statistics counter %s%s%s' % \
+            (counter, category_str, zone_str)
     msg = "Got %s, expected%s %s as counter %s%s" % \
         (found, gtltbt, number, counter, zone_str)
     if gtltbt and 'between' in gtltbt and upper:
@@ -430,3 +439,46 @@ def check_statistics(step, counter, zone, gtltbt, number, upper):
         assert int(found) < int(number), msg
     else:
         assert int(found) == int(number), msg
+
+@step('statistics counters are 0 in category (\S+)( except for the' + \
+          ' following items)?')
+def check_statistics_items(step, category, has_except_for):
+    """
+    check the output of bindctl for statistics of specified counter.
+    Parameters:
+    category ('category <category>'): The category of counter.
+    has_except_for ('except for the following items'): checks values of items
+        with the multiline part.
+
+    Expected values of items are taken from the multiline part of the step in
+    the scenario. The multiline part has two columns: item_name and item_value.
+    item_name is a relative name to category. item_value is an expected value
+    for item_name.
+    """
+
+    def flatten(dictionary, prefix=''):
+        h = {}
+        for k, v in dictionary.items():
+            if type(v) is dict:
+                h.update(flatten(v, prefix+'.'+k))
+            else:
+                h[prefix+'.'+k] = v
+        return h
+
+    stats = flatten(parse_bindctl_output_as_data_structure())
+    if has_except_for:
+        # fetch step tables in the scnario as hashes
+        for item in step.hashes:
+            name = category+'.'+item['item_name']
+            value = item['item_value']
+            assert stats.has_key(name), \
+                'Statistics item %s was not found' % (name)
+            found = stats[name]
+            assert int(found) == int(value), \
+                'Statistics item %s has unexpected value %s (expect %s)' % \
+                    (name, found, value)
+            del(stats[name])
+    for name, found in stats.items():
+        assert int(found) == 0, \
+            'Statistics item %s has unexpected value %s (expect %s)' % \
+                (name, found, 0)

+ 73 - 36
tests/system/bindctl/tests.sh

@@ -25,12 +25,66 @@ status=0
 n=0
 
 # TODO: consider consistency with statistics definition in auth.spec
-cnt_name1="\<queries\.tcp\>"
-cnt_name2="\<queries\.udp\>"
-cnt_name3="\<opcode\.query\>"
-cnt_value1=0
-cnt_value2=0
-cnt_value3=0
+
+# flatten JSON
+awk_flatten_json='
+function join(ary, len) {
+    ret = "";
+    for (i = 1; i <= len; ++i) {
+        ret = ret""ary[i];
+    }
+    return ret;
+}
+BEGIN {
+    depth = 0;
+}
+/.+{$/ {
+    label[++depth] = $1;
+    next;
+}
+/},?/ {
+    --depth;
+    next;
+}
+/:/ {
+    print join(label,depth)""$1" "$2;
+}
+'
+# Check the counters have expected values given with 1st argument.
+# This function tests only these counters will be incremented in every checks
+# since the content of datasource and requests are not changed in this test. 
+test_counters () {
+    status=0
+    $AWK "$awk_flatten_json" bindctl.out.$n | \
+        grep '"Auth":"zones":"_SERVER_":"request":"v4": '$1 > \
+        /dev/null || status=1
+    $AWK "$awk_flatten_json" bindctl.out.$n | \
+        grep '"Auth":"zones":"_SERVER_":"request":"v6": '0 > \
+        /dev/null || status=1
+    $AWK "$awk_flatten_json" bindctl.out.$n | \
+        grep '"Auth":"zones":"_SERVER_":"request":"udp": '$1 > \
+        /dev/null || status=1
+    $AWK "$awk_flatten_json" bindctl.out.$n | \
+        grep '"Auth":"zones":"_SERVER_":"request":"tcp": '0 > \
+        /dev/null || status=1
+    $AWK "$awk_flatten_json" bindctl.out.$n | \
+        grep '"Auth":"zones":"_SERVER_":"opcode":"query": '$1 > \
+        /dev/null || status=1
+    $AWK "$awk_flatten_json" bindctl.out.$n | \
+        grep '"Auth":"zones":"_SERVER_":"responses": '$1 > \
+        /dev/null || status=1
+    $AWK "$awk_flatten_json" bindctl.out.$n | \
+        grep '"Auth":"zones":"_SERVER_":"rcode":"noerror": '$1 > \
+        /dev/null || status=1
+    $AWK "$awk_flatten_json" bindctl.out.$n | \
+        grep '"Auth":"zones":"_SERVER_":"qrysuccess": '$1 > \
+        /dev/null || status=1
+    $AWK "$awk_flatten_json" bindctl.out.$n | \
+        grep '"Auth":"zones":"_SERVER_":"qryauthans": '$1 > \
+        /dev/null || status=1
+    return $status
+}
+expected_count=0
 
 echo "I:Checking b10-auth is disabled by default ($n)"
 $DIG +norec @10.53.0.1 -p 53210 ns.example.com. A > /dev/null && status=1
@@ -56,15 +110,10 @@ sleep 2
 echo 'Stats show
 ' | $RUN_BINDCTL \
 	--csv-file-dir=$BINDCTL_CSV_DIR > bindctl.out.$n || status=1
-# the server should have received 1 UDP and 0 TCP queries (the server
-# startup script no longer sends any TCP queries)
-cnt_value1=`expr $cnt_value1 + 0`
-cnt_value2=`expr $cnt_value2 + 1`
-cnt_value3=`expr $cnt_value1 + $cnt_value2`
-grep $cnt_name1".*\<"$cnt_value1"\>" bindctl.out.$n > /dev/null || status=1
-grep $cnt_name2".*\<"$cnt_value2"\>" bindctl.out.$n > /dev/null || status=1
-grep $cnt_name3".*\<"$cnt_value3"\>" bindctl.out.$n > /dev/null || status=1
-if [ $status != 0 ]; then echo "I:failed"; fi
+# the server should have received 1 request
+expected_count=`expr $expected_count + 1`
+test_counters $expected_count
+if [ $? != 0 ]; then echo "I:failed"; fi
 n=`expr $n + 1`
 
 echo "I:Stopping b10-auth and checking that ($n)"
@@ -102,13 +151,9 @@ echo 'Stats show
 # auth sent. Then it cumulates them and new counts which the living
 # auth sends. This note assumes that the issue would have been
 # resolved : "#1941 stats lossage (multiple auth servers)".
-cnt_value1=`expr $cnt_value1 + 0`
-cnt_value2=`expr $cnt_value2 + 1`
-cnt_value3=`expr $cnt_value1 + $cnt_value2`
-grep $cnt_name1".*\<"$cnt_value1"\>" bindctl.out.$n > /dev/null || status=1
-grep $cnt_name2".*\<"$cnt_value2"\>" bindctl.out.$n > /dev/null || status=1
-grep $cnt_name3".*\<"$cnt_value3"\>" bindctl.out.$n > /dev/null || status=1
-if [ $status != 0 ]; then echo "I:failed"; fi
+expected_count=`expr $expected_count + 1`
+test_counters $expected_count
+if [ $? != 0 ]; then echo "I:failed"; fi
 n=`expr $n + 1`
 
 echo "I:Changing the data source from sqlite3 to in-memory ($n)"
@@ -131,13 +176,9 @@ echo 'Stats show
 ' | $RUN_BINDCTL \
 	--csv-file-dir=$BINDCTL_CSV_DIR > bindctl.out.$n || status=1
 # The statistics counters shouldn't be reset due to hot-swapping datasource.
-cnt_value1=`expr $cnt_value1 + 0`
-cnt_value2=`expr $cnt_value2 + 1`
-cnt_value3=`expr $cnt_value1 + $cnt_value2`
-grep $cnt_name1".*\<"$cnt_value1"\>" bindctl.out.$n > /dev/null || status=1
-grep $cnt_name2".*\<"$cnt_value2"\>" bindctl.out.$n > /dev/null || status=1
-grep $cnt_name3".*\<"$cnt_value3"\>" bindctl.out.$n > /dev/null || status=1
-if [ $status != 0 ]; then echo "I:failed"; fi
+expected_count=`expr $expected_count + 1`
+test_counters $expected_count
+if [ $? != 0 ]; then echo "I:failed"; fi
 n=`expr $n + 1`
 
 echo "I:Starting more b10-auths and checking that ($n)"
@@ -158,9 +199,7 @@ n=`expr $n + 1`
 
 echo "I:Rechecking BIND 10 statistics consistency after a pause ($n)"
 sleep 2
-cnt_value1=`expr $cnt_value1 + 0`
-cnt_value2=`expr $cnt_value2 + 1`
-cnt_value3=`expr $cnt_value1 + $cnt_value2`
+expected_count=`expr $expected_count + 1`
 # Rechecking some times
 for i in 1 2 3 4
 do
@@ -170,10 +209,8 @@ do
     # The statistics counters should keep being consistent even while
     # multiple b10-auths are running.
 
-    grep $cnt_name1".*\<"$cnt_value1"\>" bindctl.out.$n > /dev/null || status=1
-    grep $cnt_name2".*\<"$cnt_value2"\>" bindctl.out.$n > /dev/null || status=1
-    grep $cnt_name3".*\<"$cnt_value3"\>" bindctl.out.$n > /dev/null || status=1
-    if [ $status != 0 ]; then echo "I:failed "; break ; fi
+    test_counters $expected_count
+    if [ $? != 0 ]; then echo "I:failed "; break ; fi
 done
 n=`expr $n + 1`
 

+ 12 - 11
tests/tools/perfdhcp/test_control.cc

@@ -13,27 +13,28 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 
-#include <fstream>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <signal.h>
-#include <sys/wait.h>
-
-#include <boost/date_time/posix_time/posix_time.hpp>
-
 #include <exceptions/exceptions.h>
 #include <asiolink/io_address.h>
 #include <dhcp/libdhcp++.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcp/dhcp4.h>
 #include <dhcp/option6_ia.h>
+#include <util/unittests/check_valgrind.h>
 #include "test_control.h"
 #include "command_options.h"
 #include "perf_pkt4.h"
 #include "perf_pkt6.h"
 
+#include <fstream>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+
 using namespace std;
 using namespace boost::posix_time;
 using namespace isc;
@@ -724,7 +725,7 @@ TestControl::printDiagnostics() const {
         std::cout << "Set MAC to " << vector2Hex(options.getMacTemplate(), "::")
                   << std::endl;
         if (options.getDuidTemplate().size() > 0) {
-            std::cout << "Set DUID to " << vector2Hex(options.getDuidTemplate()) << std::endl; 
+            std::cout << "Set DUID to " << vector2Hex(options.getDuidTemplate()) << std::endl;
         }
     }
 }