Browse Source

Merge branch 'trac2157_merge'

Conflicts:
	ChangeLog
Yoshitaka Aharen 12 years ago
parent
commit
e653adac03

+ 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 TBD)
+
 bind10-1.0.0-rc released on February 14, 2013
 bind10-1.0.0-rc released on February 14, 2013
 
 
 580.	[func]*		muks
 580.	[func]*		muks

+ 1 - 0
configure.ac

@@ -1359,6 +1359,7 @@ AC_OUTPUT([doc/version.ent
            src/bin/auth/tests/testdata/example.zone
            src/bin/auth/tests/testdata/example.zone
            src/bin/auth/tests/testdata/example-base.zone
            src/bin/auth/tests/testdata/example-base.zone
            src/bin/auth/tests/testdata/example-nsec3.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/dhcp4/spec_config.h.pre
            src/bin/dhcp6/spec_config.h.pre
            src/bin/dhcp6/spec_config.h.pre
            src/bin/tests/process_rename_test.py
            src/bin/tests/process_rename_test.py

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

@@ -18,6 +18,9 @@ pkglibexecdir = $(libexecdir)/@PACKAGE@
 
 
 CLEANFILES  = *.gcno *.gcda auth.spec spec_config.h
 CLEANFILES  = *.gcno *.gcda auth.spec spec_config.h
 CLEANFILES += auth_messages.h auth_messages.cc
 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
 man_MANS = b10-auth.8
 DISTCLEANFILES = $(man_MANS)
 DISTCLEANFILES = $(man_MANS)
@@ -26,7 +29,7 @@ EXTRA_DIST = $(man_MANS) b10-auth.xml
 if GENERATE_DOCS
 if GENERATE_DOCS
 
 
 b10-auth.8: b10-auth.xml
 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
 else
 
 
@@ -36,8 +39,18 @@ $(man_MANS):
 
 
 endif
 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
 spec_config.h: spec_config.h.pre
 	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" 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
 	$(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
 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
 pkglibexec_PROGRAMS = b10-auth
 b10_auth_SOURCES = query.cc query.h
 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 += auth_config.cc auth_config.h
 b10_auth_SOURCES += command.cc command.h
 b10_auth_SOURCES += command.cc command.h
 b10_auth_SOURCES += common.h common.cc
 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_clients_mgr.h
 b10_auth_SOURCES += datasrc_config.h datasrc_config.cc
 b10_auth_SOURCES += datasrc_config.h datasrc_config.cc
 b10_auth_SOURCES += main.cc
 b10_auth_SOURCES += main.cc
 
 
 nodist_b10_auth_SOURCES = auth_messages.h auth_messages.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 += 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/datasrc/libb10-datasrc.la
 b10_auth_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.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_name": "class", "item_type": "string",
             "item_optional": true, "item_default": "IN"
             "item_optional": true, "item_default": "IN"
           },
           },
-	  {
+          {
             "item_name": "origin", "item_type": "string",
             "item_name": "origin", "item_type": "string",
             "item_optional": false, "item_default": ""
             "item_optional": false, "item_default": ""
           }
           }
@@ -140,222 +140,6 @@
       }
       }
     ],
     ],
     "statistics": [
     "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::asiodns;
 using namespace isc::server_common::portconfig;
 using namespace isc::server_common::portconfig;
 using isc::auth::statistics::Counters;
 using isc::auth::statistics::Counters;
+using isc::auth::statistics::MessageAttributes;
 
 
 namespace {
 namespace {
 // A helper class for cleaning up message renderer.
 // A helper class for cleaning up message renderer.
@@ -102,17 +103,21 @@ namespace {
 // user of this class, so we hide it within the implementation.
 // user of this class, so we hide it within the implementation.
 class RendererHolder {
 class RendererHolder {
 public:
 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);
         renderer.setBuffer(buffer);
     }
     }
     ~RendererHolder() {
     ~RendererHolder() {
+        stats_attrs_.setResponseTruncated(renderer_.isTruncated());
         renderer_.setBuffer(NULL);
         renderer_.setBuffer(NULL);
         renderer_.clear();
         renderer_.clear();
     }
     }
 private:
 private:
     MessageRenderer& renderer_;
     MessageRenderer& renderer_;
+    MessageAttributes& stats_attrs_;
 };
 };
 
 
 // Similar to Renderer holder, this is a very basic RAII-style class
 // Similar to Renderer holder, this is a very basic RAII-style class
@@ -239,15 +244,19 @@ public:
                 BaseSocketSessionForwarder& ddns_forwarder);
                 BaseSocketSessionForwarder& ddns_forwarder);
     ~AuthSrvImpl();
     ~AuthSrvImpl();
 
 
-    bool processNormalQuery(const IOMessage& io_message, Message& message,
+    bool processNormalQuery(const IOMessage& io_message,
+                            ConstEDNSPtr remote_edns, Message& message,
                             OutputBuffer& buffer,
                             OutputBuffer& buffer,
-                            auto_ptr<TSIGContext> tsig_context);
+                            auto_ptr<TSIGContext> tsig_context,
+                            MessageAttributes& stats_attrs);
     bool processXfrQuery(const IOMessage& io_message, Message& message,
     bool processXfrQuery(const IOMessage& io_message, Message& message,
                          OutputBuffer& buffer,
                          OutputBuffer& buffer,
-                         auto_ptr<TSIGContext> tsig_context);
+                         auto_ptr<TSIGContext> tsig_context,
+                         MessageAttributes& stats_attrs);
     bool processNotify(const IOMessage& io_message, Message& message,
     bool processNotify(const IOMessage& io_message, Message& message,
                        OutputBuffer& buffer,
                        OutputBuffer& buffer,
-                       auto_ptr<TSIGContext> tsig_context);
+                       auto_ptr<TSIGContext> tsig_context,
+                       MessageAttributes& stats_attrs);
     bool processUpdate(const IOMessage& io_message);
     bool processUpdate(const IOMessage& io_message);
 
 
     IOService io_service_;
     IOService io_service_;
@@ -272,10 +281,6 @@ public:
     /// The data source client list manager
     /// The data source client list manager
     auth::DataSrcClientsMgr datasrc_clients_mgr_;
     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
     /// Socket session forwarder for dynamic update requests
     BaseSocketSessionForwarder& ddns_base_forwarder_;
     BaseSocketSessionForwarder& ddns_base_forwarder_;
 
 
@@ -293,25 +298,17 @@ public:
     ///
     ///
     /// \param server The DNSServer as passed to processMessage()
     /// \param server The DNSServer as passed to processMessage()
     /// \param message The response as constructed by 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.
     /// \param done If true, it indicates there is a response.
     ///             this value will be passed to server->resume(bool)
     ///             this value will be passed to server->resume(bool)
     void resumeServer(isc::asiodns::DNSServer* server,
     void resumeServer(isc::asiodns::DNSServer* server,
                       isc::dns::Message& message,
                       isc::dns::Message& message,
-                      statistics::QRAttributes& stats_attrs,
+                      MessageAttributes& stats_attrs,
                       const bool done);
                       const bool done);
 
 
 private:
 private:
     bool xfrout_connected_;
     bool xfrout_connected_;
     AbstractXfroutClient& xfrout_client_;
     AbstractXfroutClient& xfrout_client_;
 
 
-    // validateStatistics
-    bool validateStatistics(isc::data::ConstElementPtr data) const;
-
     auth::Query query_;
     auth::Query query_;
 };
 };
 
 
@@ -423,6 +420,7 @@ public:
 void
 void
 makeErrorMessage(MessageRenderer& renderer, Message& message,
 makeErrorMessage(MessageRenderer& renderer, Message& message,
                  OutputBuffer& buffer, const Rcode& rcode,
                  OutputBuffer& buffer, const Rcode& rcode,
+                 MessageAttributes& stats_attrs,
                  std::auto_ptr<TSIGContext> tsig_context =
                  std::auto_ptr<TSIGContext> tsig_context =
                  std::auto_ptr<TSIGContext>())
                  std::auto_ptr<TSIGContext>())
 {
 {
@@ -455,9 +453,10 @@ makeErrorMessage(MessageRenderer& renderer, Message& message,
 
 
     message.setRcode(rcode);
     message.setRcode(rcode);
 
 
-    RendererHolder holder(renderer, &buffer);
+    RendererHolder holder(renderer, &buffer, stats_attrs);
     if (tsig_context.get() != NULL) {
     if (tsig_context.get() != NULL) {
         message.toWire(renderer, *tsig_context);
         message.toWire(renderer, *tsig_context);
+        stats_attrs.setResponseTSIG(true);
     } else {
     } else {
         message.toWire(renderer);
         message.toWire(renderer);
     }
     }
@@ -484,7 +483,6 @@ AuthSrv::setXfrinSession(AbstractSession* xfrin_session) {
 void
 void
 AuthSrv::setConfigSession(ModuleCCSession* config_session) {
 AuthSrv::setConfigSession(ModuleCCSession* config_session) {
     impl_->config_session_ = config_session;
     impl_->config_session_ = config_session;
-    impl_->registerStatisticsValidator();
 }
 }
 
 
 ModuleCCSession*
 ModuleCCSession*
@@ -497,11 +495,11 @@ AuthSrv::processMessage(const IOMessage& io_message, Message& message,
                         OutputBuffer& buffer, DNSServer* server)
                         OutputBuffer& buffer, DNSServer* server)
 {
 {
     InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());
     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());
         io_message.getRemoteEndpoint().getProtocol());
 
 
     // First, check the header part.  If we fail even for the base header,
     // 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;
         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 {
     try {
         // Parse the message.
         // Parse the message.
         message.fromWire(request_buffer);
         message.fromWire(request_buffer);
     } catch (const DNSProtocolError& error) {
     } catch (const DNSProtocolError& error) {
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PROTOCOL_FAILURE)
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PROTOCOL_FAILURE)
                   .arg(error.getRcode().toText()).arg(error.what());
                   .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);
         impl_->resumeServer(server, message, stats_attrs, true);
         return;
         return;
     } catch (const Exception& ex) {
     } catch (const Exception& ex) {
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PARSE_FAILED)
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PARSE_FAILED)
                   .arg(ex.what());
                   .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);
         impl_->resumeServer(server, message, stats_attrs, true);
         return;
         return;
     } // other exceptions will be handled at a higher layer.
     } // other exceptions will be handled at a higher layer.
@@ -558,85 +563,82 @@ AuthSrv::processMessage(const IOMessage& io_message, Message& message,
                                            **impl_->keyring_));
                                            **impl_->keyring_));
         tsig_error = tsig_context->verify(tsig_record, io_message.getData(),
         tsig_error = tsig_context->verify(tsig_record, io_message.getData(),
                                           io_message.getDataSize());
                                           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()) {
     if (tsig_error != TSIGError::NOERROR()) {
         makeErrorMessage(impl_->renderer_, message, buffer,
         makeErrorMessage(impl_->renderer_, message, buffer,
-                         tsig_error.toRcode(), tsig_context);
+                         tsig_error.toRcode(), stats_attrs, tsig_context);
         impl_->resumeServer(server, message, stats_attrs, true);
         impl_->resumeServer(server, message, stats_attrs, true);
         return;
         return;
     }
     }
 
 
-    const Opcode opcode = message.getOpcode();
     bool send_answer = true;
     bool send_answer = true;
     try {
     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();
         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()) {
         if (opcode == Opcode::NOTIFY()) {
             send_answer = impl_->processNotify(io_message, message, buffer,
             send_answer = impl_->processNotify(io_message, message, buffer,
-                                               tsig_context);
+                                               tsig_context, stats_attrs);
         } else if (opcode == Opcode::UPDATE()) {
         } else if (opcode == Opcode::UPDATE()) {
             if (impl_->ddns_forwarder_) {
             if (impl_->ddns_forwarder_) {
                 send_answer = impl_->processUpdate(io_message);
                 send_answer = impl_->processUpdate(io_message);
             } else {
             } else {
                 makeErrorMessage(impl_->renderer_, message, buffer,
                 makeErrorMessage(impl_->renderer_, message, buffer,
-                                 Rcode::NOTIMP(), tsig_context);
+                                 Rcode::NOTIMP(), stats_attrs, tsig_context);
             }
             }
         } else if (opcode != Opcode::QUERY()) {
         } else if (opcode != Opcode::QUERY()) {
             LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_UNSUPPORTED_OPCODE)
             LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_UNSUPPORTED_OPCODE)
                       .arg(message.getOpcode().toText());
                       .arg(message.getOpcode().toText());
             makeErrorMessage(impl_->renderer_, message, buffer,
             makeErrorMessage(impl_->renderer_, message, buffer,
-                             Rcode::NOTIMP(), tsig_context);
+                             Rcode::NOTIMP(), stats_attrs, tsig_context);
         } else if (message.getRRCount(Message::SECTION_QUESTION) != 1) {
         } else if (message.getRRCount(Message::SECTION_QUESTION) != 1) {
             makeErrorMessage(impl_->renderer_, message, buffer,
             makeErrorMessage(impl_->renderer_, message, buffer,
-                             Rcode::FORMERR(), tsig_context);
+                             Rcode::FORMERR(), stats_attrs, tsig_context);
         } else {
         } else {
             ConstQuestionPtr question = *message.beginQuestion();
             ConstQuestionPtr question = *message.beginQuestion();
             const RRType& qtype = question->getType();
             const RRType& qtype = question->getType();
             if (qtype == RRType::AXFR()) {
             if (qtype == RRType::AXFR()) {
                 send_answer = impl_->processXfrQuery(io_message, message,
                 send_answer = impl_->processXfrQuery(io_message, message,
-                                                     buffer, tsig_context);
+                                                     buffer, tsig_context,
+                                                     stats_attrs);
             } else if (qtype == RRType::IXFR()) {
             } else if (qtype == RRType::IXFR()) {
                 send_answer = impl_->processXfrQuery(io_message, message,
                 send_answer = impl_->processXfrQuery(io_message, message,
-                                                     buffer, tsig_context);
+                                                     buffer, tsig_context,
+                                                     stats_attrs);
             } else {
             } 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) {
     } catch (const std::exception& ex) {
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_FAILURE)
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_FAILURE)
                   .arg(ex.what());
                   .arg(ex.what());
-        makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL());
+        makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL(),
+                         stats_attrs);
     } catch (...) {
     } catch (...) {
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_FAILURE_UNKNOWN);
         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);
     impl_->resumeServer(server, message, stats_attrs, send_answer);
 }
 }
 
 
 bool
 bool
-AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
+AuthSrvImpl::processNormalQuery(const IOMessage& io_message,
+                                ConstEDNSPtr remote_edns, Message& message,
                                 OutputBuffer& buffer,
                                 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 bool dnssec_ok = remote_edns && remote_edns->getDNSSECAwareness();
     const uint16_t remote_bufsize = remote_edns ? remote_edns->getUDPSize() :
     const uint16_t remote_bufsize = remote_edns ? remote_edns->getUDPSize() :
         Message::DEFAULT_MAX_UDPSIZE;
         Message::DEFAULT_MAX_UDPSIZE;
@@ -666,21 +668,24 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
             const Name& qname = question->getName();
             const Name& qname = question->getName();
             query_.process(*list, qname, qtype, message, dnssec_ok);
             query_.process(*list, qname, qtype, message, dnssec_ok);
         } else {
         } else {
-            makeErrorMessage(renderer_, message, buffer, Rcode::REFUSED());
+            makeErrorMessage(renderer_, message, buffer, Rcode::REFUSED(),
+                             stats_attrs);
             return (true);
             return (true);
         }
         }
     } catch (const Exception& ex) {
     } catch (const Exception& ex) {
         LOG_ERROR(auth_logger, AUTH_PROCESS_FAIL).arg(ex.what());
         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);
         return (true);
     }
     }
 
 
-    RendererHolder holder(renderer_, &buffer);
+    RendererHolder holder(renderer_, &buffer, stats_attrs);
     const bool udp_buffer =
     const bool udp_buffer =
         (io_message.getSocket().getProtocol() == IPPROTO_UDP);
         (io_message.getSocket().getProtocol() == IPPROTO_UDP);
     renderer_.setLengthLimit(udp_buffer ? remote_bufsize : 65535);
     renderer_.setLengthLimit(udp_buffer ? remote_bufsize : 65535);
     if (tsig_context.get() != NULL) {
     if (tsig_context.get() != NULL) {
         message.toWire(renderer_, *tsig_context);
         message.toWire(renderer_, *tsig_context);
+        stats_attrs.setResponseTSIG(true);
     } else {
     } else {
         message.toWire(renderer_);
         message.toWire(renderer_);
     }
     }
@@ -697,12 +702,13 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
 bool
 bool
 AuthSrvImpl::processXfrQuery(const IOMessage& io_message, Message& message,
 AuthSrvImpl::processXfrQuery(const IOMessage& io_message, Message& message,
                              OutputBuffer& buffer,
                              OutputBuffer& buffer,
-                             auto_ptr<TSIGContext> tsig_context)
+                             auto_ptr<TSIGContext> tsig_context,
+                             MessageAttributes& stats_attrs)
 {
 {
     if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
     if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_AXFR_UDP);
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_AXFR_UDP);
         makeErrorMessage(renderer_, message, buffer, Rcode::FORMERR(),
         makeErrorMessage(renderer_, message, buffer, Rcode::FORMERR(),
-                         tsig_context);
+                         stats_attrs, tsig_context);
         return (true);
         return (true);
     }
     }
 
 
@@ -728,7 +734,7 @@ AuthSrvImpl::processXfrQuery(const IOMessage& io_message, Message& message,
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_AXFR_PROBLEM)
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_AXFR_PROBLEM)
                   .arg(err.what());
                   .arg(err.what());
         makeErrorMessage(renderer_, message, buffer, Rcode::SERVFAIL(),
         makeErrorMessage(renderer_, message, buffer, Rcode::SERVFAIL(),
-                         tsig_context);
+                         stats_attrs, tsig_context);
         return (true);
         return (true);
     }
     }
 
 
@@ -738,7 +744,8 @@ AuthSrvImpl::processXfrQuery(const IOMessage& io_message, Message& message,
 bool
 bool
 AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
 AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
                            OutputBuffer& buffer,
                            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
     // The incoming notify must contain exactly one question for SOA of the
     // zone name.
     // zone name.
@@ -746,7 +753,7 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NOTIFY_QUESTIONS)
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NOTIFY_QUESTIONS)
                   .arg(message.getRRCount(Message::SECTION_QUESTION));
                   .arg(message.getRRCount(Message::SECTION_QUESTION));
         makeErrorMessage(renderer_, message, buffer, Rcode::FORMERR(),
         makeErrorMessage(renderer_, message, buffer, Rcode::FORMERR(),
-                         tsig_context);
+                         stats_attrs, tsig_context);
         return (true);
         return (true);
     }
     }
     ConstQuestionPtr question = *message.beginQuestion();
     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)
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NOTIFY_RRTYPE)
                   .arg(question->getType().toText());
                   .arg(question->getType().toText());
         makeErrorMessage(renderer_, message, buffer, Rcode::FORMERR(),
         makeErrorMessage(renderer_, message, buffer, Rcode::FORMERR(),
-                         tsig_context);
+                         stats_attrs, tsig_context);
         return (true);
         return (true);
     }
     }
 
 
@@ -812,9 +819,10 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
     message.setHeaderFlag(Message::HEADERFLAG_AA);
     message.setHeaderFlag(Message::HEADERFLAG_AA);
     message.setRcode(Rcode::NOERROR());
     message.setRcode(Rcode::NOERROR());
 
 
-    RendererHolder holder(renderer_, &buffer);
+    RendererHolder holder(renderer_, &buffer, stats_attrs);
     if (tsig_context.get() != NULL) {
     if (tsig_context.get() != NULL) {
         message.toWire(renderer_, *tsig_context);
         message.toWire(renderer_, *tsig_context);
+        stats_attrs.setResponseTSIG(true);
     } else {
     } else {
         message.toWire(renderer_);
         message.toWire(renderer_);
     }
     }
@@ -822,7 +830,8 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
 }
 }
 
 
 bool
 bool
-AuthSrvImpl::processUpdate(const IOMessage& io_message) {
+AuthSrvImpl::processUpdate(const IOMessage& io_message)
+{
     // Push the update request to a separate process via the forwarder.
     // Push the update request to a separate process via the forwarder.
     // On successful push, the request shouldn't be responded from b10-auth,
     // On successful push, the request shouldn't be responded from b10-auth,
     // so we return false.
     // so we return false.
@@ -831,31 +840,10 @@ AuthSrvImpl::processUpdate(const IOMessage& io_message) {
 }
 }
 
 
 void
 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,
 AuthSrvImpl::resumeServer(DNSServer* server, Message& message,
-                          statistics::QRAttributes& stats_attrs,
+                          MessageAttributes& stats_attrs,
                           const bool done) {
                           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);
     server->resume(done);
 }
 }
 
 
@@ -875,7 +863,7 @@ AuthSrv::updateConfig(ConstElementPtr new_config) {
 }
 }
 
 
 ConstElementPtr AuthSrv::getStatistics() const {
 ConstElementPtr AuthSrv::getStatistics() const {
-    return (impl_->counters_.getStatistics());
+    return (impl_->counters_.get());
 }
 }
 
 
 const AddressList&
 const AddressList&
@@ -905,7 +893,8 @@ void
 AuthSrv::createDDNSForwarder() {
 AuthSrv::createDDNSForwarder() {
     LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_START_DDNS_FORWARDER);
     LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_START_DDNS_FORWARDER);
     impl_->ddns_forwarder_.reset(
     impl_->ddns_forwarder_.reset(
-        new SocketSessionForwarderHolder("update", impl_->ddns_base_forwarder_));
+        new SocketSessionForwarderHolder("update",
+                                         impl_->ddns_base_forwarder_));
 }
 }
 
 
 void
 void

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

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

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

@@ -20,7 +20,7 @@
 <refentry>
 <refentry>
 
 
   <refentryinfo>
   <refentryinfo>
-    <date>December 18, 2012</date>
+    <date>February 5, 2013</date>
   </refentryinfo>
   </refentryinfo>
 
 
   <refmeta>
   <refmeta>
@@ -100,7 +100,7 @@
       <varname>database_file</varname> defines the path to the
       <varname>database_file</varname> defines the path to the
       SQLite3 zone file when using the sqlite datasource.
       SQLite3 zone file when using the sqlite datasource.
       The default is
       The default is
-      <filename>/usr/local/var/bind10/zone.sqlite3</filename>.
+      <filename>@@LOCALSTATEDIR@@/bind10/zone.sqlite3</filename>.
     </para>
     </para>
 
 
     <para>
     <para>
@@ -144,15 +144,6 @@
     </para>
     </para>
 
 
     <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
       <varname>tcp_recv_timeout</varname> is the timeout used on
       incoming TCP connections, in milliseconds. If the query
       incoming TCP connections, in milliseconds. If the query
       is not sent within this time, the connection is closed.
       is not sent within this time, the connection is closed.
@@ -191,6 +182,11 @@
     </para>
     </para>
 
 
     <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>.
       <command>shutdown</command> exits <command>b10-auth</command>.
       This has an optional <varname>pid</varname> argument to
       This has an optional <varname>pid</varname> argument to
       select the process ID to stop.
       select the process ID to stop.
@@ -230,32 +226,36 @@
       daemon for <quote>Auth</quote> include:
       daemon for <quote>Auth</quote> include:
     </para>
     </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>
 
 
   <refsect1>
   <refsect1>
     <title>FILES</title>
     <title>FILES</title>
     <para>
     <para>
-      <filename>/usr/local/var/bind10/zone.sqlite3</filename>
+      <filename>@@LOCALSTATEDIR@@/bind10/zone.sqlite3</filename>
       &mdash; Location for the SQLite3 zone database
       &mdash; Location for the SQLite3 zone database
       when <emphasis>database_file</emphasis> configuration is not
       when <emphasis>database_file</emphasis> configuration is not
       defined.
       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
 #ifndef STATISTICS_H
 #define STATISTICS_H 1
 #define STATISTICS_H 1
 
 
-#include <cc/session.h>
 #include <cc/data.h>
 #include <cc/data.h>
 
 
 #include <dns/message.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 <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 isc {
 namespace auth {
 namespace auth {
 namespace statistics {
 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.
 /// 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:
 private:
     // request attributes
     // request attributes
-    int req_ip_version_;            // IP version
+    int req_address_family_;        // IP version
     int req_transport_protocol_;    // Transport layer protocol
     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:
 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
     /// \throw None
-    void setQueryOpCode(const int opcode) {
+    void setRequestOpCode(const isc::dns::Opcode& opcode) {
         req_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
     /// \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
     /// \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;
         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
     /// \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
     /// \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
     /// \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
     /// \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
     /// \throw None
     void setResponseTruncated(const bool is_truncated) {
     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.
 /// 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
 /// We may eventually want to change the structure to hold values that are
 /// not counters (such as concurrent TCP connections), or seperate generic
 /// not counters (such as concurrent TCP connections), or seperate generic
 /// part to src/lib to share with the other modules.
 /// 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
 /// This class is constructed on startup of the server, so
 /// construction overhead of this approach should be acceptable.
 /// 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()
 /// \todo Consider overhead of \c Counters::inc()
-class Counters {
+class Counters : boost::noncopyable {
 private:
 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:
 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
     /// This constructor is mostly exception free. But it may still throw
     /// a standard exception if memory allocation fails inside the method.
     /// a standard exception if memory allocation fails inside the method.
-    ///
     Counters();
     Counters();
-    /// The destructor.
-    ///
-    /// This method never throws an exception.
-    ///
-    ~Counters();
 
 
     /// \brief Increment counters according to the parameters.
     /// \brief Increment counters according to the parameters.
     ///
     ///
-    /// \param qrattrs Query/Response attributes.
+    /// \param msgattrs DNS message attributes.
     /// \param response DNS response message.
     /// \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
     /// \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
 } // 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 =
 TESTS =
 if HAVE_GTEST
 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.h
 run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
 run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
 run_unittests_SOURCES += ../auth_srv.h ../auth_srv.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 += ../statistics.h ../statistics.cc ../statistics_items.h
 run_unittests_SOURCES += ../datasrc_config.h ../datasrc_config.cc
 run_unittests_SOURCES += ../datasrc_config.h ../datasrc_config.cc
 run_unittests_SOURCES += datasrc_util.h datasrc_util.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 += auth_srv_unittest.cc
 run_unittests_SOURCES += config_unittest.cc
 run_unittests_SOURCES += config_unittest.cc
 run_unittests_SOURCES += config_syntax_unittest.cc
 run_unittests_SOURCES += config_syntax_unittest.cc
 run_unittests_SOURCES += command_unittest.cc
 run_unittests_SOURCES += command_unittest.cc
 run_unittests_SOURCES += common_unittest.cc
 run_unittests_SOURCES += common_unittest.cc
 run_unittests_SOURCES += query_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 += test_datasrc_clients_mgr.h test_datasrc_clients_mgr.cc
 run_unittests_SOURCES += datasrc_clients_builder_unittest.cc
 run_unittests_SOURCES += datasrc_clients_builder_unittest.cc
 run_unittests_SOURCES += datasrc_clients_mgr_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
 run_unittests_SOURCES += run_unittests.cc
 
 
 nodist_run_unittests_SOURCES = ../auth_messages.h ../auth_messages.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_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 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
 # The following are definitions for auto-generating test data for query
 # tests.
 # 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-base.sqlite3
 BUILT_SOURCES += testdata/example-nsec3.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\"}" \
 		-c "{\"database_file\": \"$(builddir)/testdata/example-nsec3.sqlite3\"}" \
 		example.com testdata/example-nsec3.zone
 		example.com testdata/example-nsec3.zone
 
 
+EXTRA_DIST += gen-statisticsitems_test.py
+
 check-local:
 check-local:
 	B10_FROM_BUILD=${abs_top_builddir} ./run_unittests
 	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
 noinst_PROGRAMS = run_unittests
 
 

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

@@ -45,6 +45,8 @@
 #include <testutils/portconfig.h>
 #include <testutils/portconfig.h>
 #include <testutils/socket_request.h>
 #include <testutils/socket_request.h>
 
 
+#include "statistics_util.h"
+
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
 #include <boost/lexical_cast.hpp>
 #include <boost/lexical_cast.hpp>
@@ -73,6 +75,7 @@ using namespace isc::asiodns;
 using namespace isc::asiolink;
 using namespace isc::asiolink;
 using namespace isc::testutils;
 using namespace isc::testutils;
 using namespace isc::server_common::portconfig;
 using namespace isc::server_common::portconfig;
+using namespace isc::auth::unittest;
 using isc::datasrc::memory::ZoneTableSegment;
 using isc::datasrc::memory::ZoneTableSegment;
 using isc::UnitTestUtil;
 using isc::UnitTestUtil;
 using boost::scoped_ptr;
 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
 // This is a configuration that uses the in-memory data source containing
 // a signed example zone.
 // 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
 // shortcut commonly used in tests
 typedef boost::shared_ptr<ConfigurableClientList> ListPtr;
 typedef boost::shared_ptr<ConfigurableClientList> ListPtr;
@@ -108,6 +112,7 @@ protected:
         server.setDNSService(dnss_);
         server.setDNSService(dnss_);
         server.setXfrinSession(&notify_session);
         server.setXfrinSession(&notify_session);
         server.createDDNSForwarder();
         server.createDDNSForwarder();
+        checkCountersAreInitialized();
     }
     }
 
 
     ~AuthSrvTest() {
     ~AuthSrvTest() {
@@ -124,7 +129,8 @@ protected:
 
 
     // Helper for checking Rcode statistic counters;
     // Helper for checking Rcode statistic counters;
     // Checks for one specific Rcode statistics counter value
     // 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
                            const int expected_value) const
     {
     {
             EXPECT_EQ(expected_value, rcode_value) <<
             EXPECT_EQ(expected_value, rcode_value) <<
@@ -133,38 +139,26 @@ protected:
                       rcode_value;
                       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
     // Checks whether all Rcode counters are set to zero except the given
     // rcode (it is checked to be set to 'value')
     // rcode (it is checked to be set to 'value')
     void checkAllRcodeCountersZeroExcept(const Rcode& rcode, int value) const {
     void checkAllRcodeCountersZeroExcept(const Rcode& rcode, int value) const {
         std::string target_rcode_name = rcode.toText();
         std::string target_rcode_name = rcode.toText();
         std::transform(target_rcode_name.begin(), target_rcode_name.end(),
         std::transform(target_rcode_name.begin(), target_rcode_name.end(),
                        target_rcode_name.begin(), ::tolower);
                        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>
         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
         for (std::map<std::string, ConstElementPtr>::const_iterator
                  i = stats_map.begin(), e = stats_map.end();
                  i = stats_map.begin(), e = stats_map.end();
              i != e;
              i != e;
              ++i)
              ++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);
                               &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_;
     MockDNSService dnss_;
     MockXfroutClient xfrout;
     MockXfroutClient xfrout;
     MockSocketSessionForwarder ddns_forwarder;
     MockSocketSessionForwarder ddns_forwarder;
@@ -241,29 +244,6 @@ createBuiltinVersionResponse(const qid_t qid, vector<uint8_t>& data) {
                 renderer.getLength());
                 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
 // We did not configure any client lists. Therefore it should be REFUSED
 TEST_F(AuthSrvTest, noClientList) {
 TEST_F(AuthSrvTest, noClientList) {
     UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
     UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
@@ -276,6 +256,18 @@ TEST_F(AuthSrvTest, noClientList) {
     EXPECT_TRUE(dnsserv.hasAnswer());
     EXPECT_TRUE(dnsserv.hasAnswer());
     headerCheck(*parse_message, default_qid, Rcode::REFUSED(),
     headerCheck(*parse_message, default_qid, Rcode::REFUSED(),
                 opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
                 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.
 // Unsupported requests.  Should result in NOTIMP.
@@ -295,32 +287,74 @@ TEST_F(AuthSrvTest, multiQuestion) {
 // dropped.
 // dropped.
 TEST_F(AuthSrvTest, shortMessage) {
 TEST_F(AuthSrvTest, shortMessage) {
     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
 // Response messages.  Must be silently dropped, whether it's a valid response
 // or malformed or could otherwise cause a protocol error.
 // or malformed or could otherwise cause a protocol error.
 TEST_F(AuthSrvTest, response) {
 TEST_F(AuthSrvTest, response) {
+    // isc::testutils::SrvTestBase::response() processes 3 messages.
     response();
     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
 // Query with a broken question
 TEST_F(AuthSrvTest, shortQuestion) {
 TEST_F(AuthSrvTest, shortQuestion) {
     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
 // Query with a broken answer section
 TEST_F(AuthSrvTest, shortAnswer) {
 TEST_F(AuthSrvTest, shortAnswer) {
     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.
 // Query with unsupported version of EDNS.
 TEST_F(AuthSrvTest, ednsBadVers) {
 TEST_F(AuthSrvTest, ednsBadVers) {
     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) {
 TEST_F(AuthSrvTest, AXFROverUDP) {
@@ -339,7 +373,14 @@ TEST_F(AuthSrvTest, AXFRSuccess) {
                           &dnsserv);
                           &dnsserv);
     EXPECT_FALSE(dnsserv.hasAnswer());
     EXPECT_FALSE(dnsserv.hasAnswer());
     EXPECT_TRUE(xfrout.isConnected());
     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
 // 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()) <<
     EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
         "It should be unsigned with this error";
         "It should be unsigned with this error";
 
 
+    // check Rcode counters and TSIG counters
     checkAllRcodeCountersZeroExcept(Rcode::NOTAUTH(), 1);
     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
 // 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()) <<
     EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
         "It should be unsigned with this error";
         "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.
 // 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(TSIGError::BAD_SIG_CODE, tsig->getRdata().getError());
     EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
     EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
         "It should be unsigned with this error";
         "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) {
 TEST_F(AuthSrvTest, AXFRConnectFail) {
@@ -589,7 +660,8 @@ TEST_F(AuthSrvTest, notify) {
     // external module.  Check them.
     // external module.  Check them.
     EXPECT_EQ("Zonemgr", notify_session.getMessageDest());
     EXPECT_EQ("Zonemgr", notify_session.getMessageDest());
     EXPECT_EQ("notify",
     EXPECT_EQ("notify",
-              notify_session.getSentMessage()->get("command")->get(0)->stringValue());
+              notify_session.getSentMessage()->get("command")->get(0)->
+                  stringValue());
     ConstElementPtr notify_args =
     ConstElementPtr notify_args =
         notify_session.getSentMessage()->get("command")->get(1);
         notify_session.getSentMessage()->get("command")->get(1);
     EXPECT_EQ("example.com.", notify_args->get("zone_name")->stringValue());
     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(RRClass::IN(), question->getClass());
     EXPECT_EQ(RRType::SOA(), question->getType());
     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) {
 TEST_F(AuthSrvTest, notifyForCHClass) {
@@ -626,6 +706,16 @@ TEST_F(AuthSrvTest, notifyForCHClass) {
     ConstElementPtr notify_args =
     ConstElementPtr notify_args =
         notify_session.getSentMessage()->get("command")->get(1);
         notify_session.getSentMessage()->get("command")->get(1);
     EXPECT_EQ("CH", notify_args->get("zone_class")->stringValue());
     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) {
 TEST_F(AuthSrvTest, notifyEmptyQuestion) {
@@ -641,6 +731,16 @@ TEST_F(AuthSrvTest, notifyEmptyQuestion) {
     EXPECT_TRUE(dnsserv.hasAnswer());
     EXPECT_TRUE(dnsserv.hasAnswer());
     headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
     headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
                 Opcode::NOTIFY().getCode(), QR_FLAG, 0, 0, 0, 0);
                 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) {
 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";
         "The server signed the response, but it doesn't seem to be valid";
 
 
     checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
     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).
 // 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,
     server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
                           &dnsserv);
     EXPECT_TRUE(dnsserv.hasAnswer());
     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
 #ifdef USE_STATIC_LINK
@@ -975,6 +1088,18 @@ TEST_F(AuthSrvTest, datasourceFail) {
     EXPECT_TRUE(dnsserv.hasAnswer());
     EXPECT_TRUE(dnsserv.hasAnswer());
     headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
     headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
                 opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
                 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
 #ifdef USE_STATIC_LINK
@@ -1083,14 +1208,39 @@ TEST_F(AuthSrvTest,
                 opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 1, 0);
                 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
 // Submit UDP normal query and check query counter
 TEST_F(AuthSrvTest, queryCounterUDPNormal) {
 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.
     // Create UDP message and process.
     UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
     UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
                                        default_qid, Name("example.com"),
                                        default_qid, Name("example.com"),
@@ -1098,25 +1248,50 @@ TEST_F(AuthSrvTest, queryCounterUDPNormal) {
     createRequestPacket(request_message, IPPROTO_UDP);
     createRequestPacket(request_message, IPPROTO_UDP);
     server.processMessage(*io_message, *parse_message, *response_obuffer,
     server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
                           &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
 // Submit TCP normal query and check query counter
 TEST_F(AuthSrvTest, queryCounterTCPNormal) {
 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.
     // Create TCP message and process.
     UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
     UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
                                        default_qid, Name("example.com"),
                                        default_qid, Name("example.com"),
@@ -1124,24 +1299,22 @@ TEST_F(AuthSrvTest, queryCounterTCPNormal) {
     createRequestPacket(request_message, IPPROTO_TCP);
     createRequestPacket(request_message, IPPROTO_TCP);
     server.processMessage(*io_message, *parse_message, *response_obuffer,
     server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
                           &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
 // Submit TCP AXFR query and check query counter
 TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
 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,
     UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
                          Name("example.com"), RRClass::IN(), RRType::AXFR());
                          Name("example.com"), RRClass::IN(), RRType::AXFR());
     createRequestPacket(request_message, IPPROTO_TCP);
     createRequestPacket(request_message, IPPROTO_TCP);
@@ -1150,24 +1323,18 @@ TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
     server.processMessage(*io_message, *parse_message, *response_obuffer,
     server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
                           &dnsserv);
     EXPECT_FALSE(dnsserv.hasAnswer());
     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
 // Submit TCP IXFR query and check query counter
 TEST_F(AuthSrvTest, queryCounterTCPIXFR) {
 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,
     UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
                          Name("example.com"), RRClass::IN(), RRType::IXFR());
                          Name("example.com"), RRClass::IN(), RRType::IXFR());
     createRequestPacket(request_message, IPPROTO_TCP);
     createRequestPacket(request_message, IPPROTO_TCP);
@@ -1176,27 +1343,38 @@ TEST_F(AuthSrvTest, queryCounterTCPIXFR) {
     server.processMessage(*io_message, *parse_message, *response_obuffer,
     server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
                           &dnsserv);
     EXPECT_FALSE(dnsserv.hasAnswer());
     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) {
 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
         // For each possible opcode, create a request message and send it
         UnitTestUtil::createRequestMessage(request_message, Opcode(i),
         UnitTestUtil::createRequestMessage(request_message, Opcode(i),
@@ -1214,45 +1392,11 @@ TEST_F(AuthSrvTest, queryCounterOpcodes) {
         }
         }
 
 
         // Confirm the counter.
         // 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:

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

@@ -22,10 +22,6 @@
 
 
 #include <vector>
 #include <vector>
 
 
-namespace {
-const unsigned int InitialValue = 0;
-} // anonymous namespace
-
 namespace isc {
 namespace isc {
 namespace statistics {
 namespace statistics {
 
 
@@ -40,24 +36,19 @@ private:
 public:
 public:
     /// The constructor.
     /// 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)
     /// \param items A number of counter items to hold (greater than 0)
     ///
     ///
     /// \throw isc::InvalidParameter \a items is 0
     /// \throw isc::InvalidParameter \a items is 0
     explicit Counter(const size_t items) :
     explicit Counter(const size_t items) :
-        counters_(items, InitialValue)
+        counters_(items, 0)
     {
     {
         if (items == 0) {
         if (items == 0) {
             isc_throw(isc::InvalidParameter, "Items must not be 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.
     /// \brief Increment a counter item specified with \a type.
     ///
     ///
@@ -70,7 +61,7 @@ public:
         }
         }
         ++counters_.at(type);
         ++counters_.at(type);
         return;
         return;
-    };
+    }
 
 
     /// \brief Get the value of a counter item specified with \a type.
     /// \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");
             isc_throw(isc::OutOfRange, "Counter type is out of range");
         }
         }
         return (counters_.at(type));
         return (counters_.at(type));
-    };
+    }
 };
 };
 
 
 }   // namespace statistics
 }   // namespace statistics

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

@@ -26,28 +26,34 @@
 #include <cassert>
 #include <cassert>
 #include <stdexcept>
 #include <stdexcept>
 #include <string>
 #include <string>
-#include <vector>
 #include <map>
 #include <map>
 #include <iterator>
 #include <iterator>
 #include <utility>
 #include <utility>
 
 
-namespace {
-typedef boost::shared_ptr<isc::statistics::Counter> CounterPtr;
-typedef std::map<std::string, CounterPtr> DictionaryMap;
-}
 
 
 namespace isc {
 namespace isc {
 namespace statistics {
 namespace statistics {
 
 
 class CounterDictionary : boost::noncopyable {
 class CounterDictionary : boost::noncopyable {
 private:
 private:
+    typedef boost::shared_ptr<isc::statistics::Counter> CounterPtr;
+    typedef std::map<std::string, CounterPtr> DictionaryMap;
     DictionaryMap dictionary_;
     DictionaryMap dictionary_;
-    std::vector<std::string> elements_;
     const size_t items_;
     const size_t items_;
     // Default constructor is forbidden; number of counter items must be
     // Default constructor is forbidden; number of counter items must be
     // specified at the construction of this class.
     // specified at the construction of this class.
     CounterDictionary();
     CounterDictionary();
 public:
 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) :
     explicit CounterDictionary(const size_t items) :
         items_(items)
         items_(items)
     {
     {
@@ -55,11 +61,17 @@ public:
         if (items == 0) {
         if (items == 0) {
             isc_throw(isc::InvalidParameter, "Items must not be 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) {
     void addElement(const std::string& name) {
         // throw if the element already exists
         // throw if the element already exists
-        if (dictionary_.count(name) != 0) {
+        if (dictionary_.find(name) != dictionary_.end()) {
             isc_throw(isc::InvalidParameter,
             isc_throw(isc::InvalidParameter,
                       "Element " << name << " already exists");
                       "Element " << name << " already exists");
         }
         }
@@ -67,16 +79,30 @@ public:
         // Create a new Counter and add to the map
         // Create a new Counter and add to the map
         dictionary_.insert(
         dictionary_.insert(
             DictionaryMap::value_type(name, CounterPtr(new Counter(items_))));
             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) {
     void deleteElement(const std::string& name) {
-        size_t result = dictionary_.erase(name);
+        const size_t result = dictionary_.erase(name);
         if (result != 1) {
         if (result != 1) {
             // If an element with specified name does not exist, throw
             // If an element with specified name does not exist, throw
             // isc::OutOfRange.
             // isc::OutOfRange.
             isc_throw(isc::OutOfRange,
             isc_throw(isc::OutOfRange,
                       "Element " << name << " does not exist");
                       "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) {
     Counter& getElement(const std::string& name) {
         DictionaryMap::const_iterator i = dictionary_.find(name);
         DictionaryMap::const_iterator i = dictionary_.find(name);
         if (i != dictionary_.end()) {
         if (i != dictionary_.end()) {
@@ -88,10 +114,13 @@ public:
             isc_throw(isc::OutOfRange,
             isc_throw(isc::OutOfRange,
                       "Element " << name << " does not exist");
                       "Element " << name << " does not exist");
         }
         }
-    };
+    }
+
+    /// \brief Same as \c getElement()
     Counter& operator[](const std::string& name) {
     Counter& operator[](const std::string& name) {
         return (getElement(name));
         return (getElement(name));
-    };
+    }
+
     /// \brief \c ConstIterator is a constant iterator that provides an
     /// \brief \c ConstIterator is a constant iterator that provides an
     /// interface for enumerating name of zones stored in CounterDictionary.
     /// interface for enumerating name of zones stored in CounterDictionary.
     ///
     ///
@@ -107,35 +136,28 @@ public:
                                 boost::forward_traversal_tag>
                                 boost::forward_traversal_tag>
     {
     {
         public:
         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() {}
             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)
                 iterator_(iterator)
             {}
             {}
 
 
         private:
         private:
-            /// \brief An internal method to increment this iterator.
+            // An internal method to increment this iterator.
             void increment() {
             void increment() {
                 ++iterator_;
                 ++iterator_;
                 return;
                 return;
             }
             }
 
 
-            /// \brief An internal method to check equality.
+            // An internal method to check equality.
             bool equal(const ConstIterator& other) const {
             bool equal(const ConstIterator& other) const {
                 return (iterator_ == other.iterator_);
                 return (iterator_ == other.iterator_);
             }
             }
 
 
-            /// \brief An internal method to dereference this iterator.
+            // An internal method to dereference this iterator.
             const value_type& dereference() const {
             const value_type& dereference() const {
                 return (iterator_->first);
                 return (iterator_->first);
             }
             }
@@ -145,12 +167,15 @@ public:
             DictionaryMap::const_iterator iterator_;
             DictionaryMap::const_iterator iterator_;
     };
     };
 
 
+    /// \brief Get an iterator for the beginning of the dictionary.
     ConstIterator begin() const {
     ConstIterator begin() const {
         return (CounterDictionary::ConstIterator(dictionary_.begin()));
         return (CounterDictionary::ConstIterator(dictionary_.begin()));
-    };
+    }
+
+    /// \brief Get an iterator for the end of the dictionary.
     ConstIterator end() const {
     ConstIterator end() const {
         return (CounterDictionary::ConstIterator(dictionary_.end()));
         return (CounterDictionary::ConstIterator(dictionary_.end()));
-    };
+    }
 
 
     typedef ConstIterator const_iterator;
     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
 ;; bogus RDATA for CNAME RR, but the loadzone tool accepts it.  looking up this
 ;; record will trigger an exception.
 ;; record will trigger an exception.
 broken.example.com. CNAME 0123456789012345678901234567890123456789012345678901234567890123456789.example.com.
 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


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

@@ -26,9 +26,13 @@
             ]
             ]
         }
         }
     },
     },
+    "Stats": {
+        "poll-interval": 1
+    },
     "Init": {
     "Init": {
         "components": {
         "components": {
             "b10-auth": { "kind": "needed", "special": "auth" },
             "b10-auth": { "kind": "needed", "special": "auth" },
+            "b10-stats": { "address": "Stats", "kind": "dispensable" },
             "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
             "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 BIND10_STARTED_CC
         And wait for bind10 stderr message CMDCTL_STARTED
         And wait for bind10 stderr message CMDCTL_STARTED
         And wait for bind10 stderr message AUTH_SERVER_STARTED
         And wait for bind10 stderr message AUTH_SERVER_STARTED
+        And wait for bind10 stderr message STATS_STARTING
 
 
         bind10 module Auth should be running
         bind10 module Auth should be running
+        And bind10 module Stats should be running
         And bind10 module Resolver should not be running
         And bind10 module Resolver should not be running
         And bind10 module Xfrout should not be running
         And bind10 module Xfrout should not be running
         And bind10 module Zonemgr should not be running
         And bind10 module Zonemgr should not be running
         And bind10 module Xfrin 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
         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
         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 flags qr aa rd
         The last query response should have ancount 1
         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
         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
         # Repeat of the above
         A query for www.example.org should have rcode NOERROR
         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 flags qr aa rd
@@ -60,6 +92,25 @@ Feature: Querying feature
         ns2.example.org.        3600    IN      A       192.0.2.4
         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
         # And now query something completely different
         A query for nosuchname.example.org should have rcode NXDOMAIN
         A query for nosuchname.example.org should have rcode NXDOMAIN
         The last query response should have flags qr aa rd
         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
         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
     Scenario: ANY query
         Given I have bind10 running with configuration example.org.inmem.config
         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 BIND10_STARTED_CC
@@ -78,13 +149,24 @@ Feature: Querying feature
         And wait for bind10 stderr message AUTH_SERVER_STARTED
         And wait for bind10 stderr message AUTH_SERVER_STARTED
 
 
         bind10 module Auth should be running
         bind10 module Auth should be running
+        And bind10 module Stats should be running
         And bind10 module Resolver should not be running
         And bind10 module Resolver should not be running
         And bind10 module Xfrout should not be running
         And bind10 module Xfrout should not be running
         And bind10 module Zonemgr should not be running
         And bind10 module Zonemgr should not be running
         And bind10 module Xfrin 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
         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
         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 flags qr aa rd
         The last query response should have ancount 4
         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
         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
     Scenario: Delegation query for unsigned child zone
         Given I have bind10 running with configuration example.org.inmem.config
         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 BIND10_STARTED_CC
         And wait for bind10 stderr message CMDCTL_STARTED
         And wait for bind10 stderr message CMDCTL_STARTED
         And wait for bind10 stderr message AUTH_SERVER_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
         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 flags qr rd
         The last query response should have edns_flags do
         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
         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
     Scenario: SSHFP query
         # We are testing one more RR type for a normal successful case
         # We are testing one more RR type for a normal successful case
         Given I have bind10 running with configuration example.org.inmem.config
         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
         And wait for bind10 stderr message AUTH_SERVER_STARTED
 
 
         bind10 module Auth should be running
         bind10 module Auth should be running
+        And bind10 module Stats should be running
         And bind10 module Resolver should not be running
         And bind10 module Resolver should not be running
         And bind10 module Xfrout should not be running
         And bind10 module Xfrout should not be running
         And bind10 module Zonemgr should not be running
         And bind10 module Zonemgr should not be running
         And bind10 module Xfrin 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
         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
         A query for example.org type SSHFP should have rcode NOERROR
         The last query response should have ancount 0
         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
         A query for shell.example.org type SSHFP should have rcode NOERROR
         The last query response should have ancount 1
         The last query response should have ancount 1
         The answer section of the last query response should be
         The answer section of the last query response should be
         """
         """
         shell.example.org.      3600    IN      SSHFP   2 1 123456789abcdef67890123456789abcdef67890
         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():
         for v in dictionary.values():
             return find_value(v, key)
             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+))?')
           '(?:( 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
     check the output of bindctl for statistics of specified counter
     and zone.
     and zone.
     Parameters:
     Parameters:
     counter ('counter <counter>'): The counter name of statistics.
     counter ('counter <counter>'): The counter name of statistics.
+    category ('category <category>', optional): The category of counter.
     zone ('zone <zone>', optional): The zone name.
     zone ('zone <zone>', optional): The zone name.
     gtltbt (' greater than'|' less than'|' between', optional): greater than
     gtltbt (' greater than'|' less than'|' between', optional): greater than
           <number> or less than <number> or between <number> and <upper>.
           <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()
     output = parse_bindctl_output_as_data_structure()
     found = None
     found = None
+    category_str = ""
     zone_str = ""
     zone_str = ""
+    depth = []
+    if category:
+        depth.insert(0, category)
+        category_str = " for category %s" % category
     if zone:
     if zone:
-        found = find_value(find_value(output, zone), counter)
+        depth.insert(0, zone)
         zone_str = " for zone %s" % 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, \
     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" % \
     msg = "Got %s, expected%s %s as counter %s%s" % \
         (found, gtltbt, number, counter, zone_str)
         (found, gtltbt, number, counter, zone_str)
     if gtltbt and 'between' in gtltbt and upper:
     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
         assert int(found) < int(number), msg
     else:
     else:
         assert int(found) == int(number), msg
         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
 n=0
 
 
 # TODO: consider consistency with statistics definition in auth.spec
 # 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)"
 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
 $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
 echo 'Stats show
 ' | $RUN_BINDCTL \
 ' | $RUN_BINDCTL \
 	--csv-file-dir=$BINDCTL_CSV_DIR > bindctl.out.$n || status=1
 	--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`
 n=`expr $n + 1`
 
 
 echo "I:Stopping b10-auth and checking that ($n)"
 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 sent. Then it cumulates them and new counts which the living
 # auth sends. This note assumes that the issue would have been
 # auth sends. This note assumes that the issue would have been
 # resolved : "#1941 stats lossage (multiple auth servers)".
 # 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`
 n=`expr $n + 1`
 
 
 echo "I:Changing the data source from sqlite3 to in-memory ($n)"
 echo "I:Changing the data source from sqlite3 to in-memory ($n)"
@@ -131,13 +176,9 @@ echo 'Stats show
 ' | $RUN_BINDCTL \
 ' | $RUN_BINDCTL \
 	--csv-file-dir=$BINDCTL_CSV_DIR > bindctl.out.$n || status=1
 	--csv-file-dir=$BINDCTL_CSV_DIR > bindctl.out.$n || status=1
 # The statistics counters shouldn't be reset due to hot-swapping datasource.
 # 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`
 n=`expr $n + 1`
 
 
 echo "I:Starting more b10-auths and checking that ($n)"
 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)"
 echo "I:Rechecking BIND 10 statistics consistency after a pause ($n)"
 sleep 2
 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
 # Rechecking some times
 for i in 1 2 3 4
 for i in 1 2 3 4
 do
 do
@@ -170,10 +209,8 @@ do
     # The statistics counters should keep being consistent even while
     # The statistics counters should keep being consistent even while
     # multiple b10-auths are running.
     # 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
 done
 n=`expr $n + 1`
 n=`expr $n + 1`