Browse Source

[master] Merge branch 'trac1613'

Conflicts:
	src/bin/auth/auth.spec.pre.in
(due to removal of spaces in earlier spec descriptions)
Jelte Jansen 13 years ago
parent
commit
e98da500d7

+ 136 - 0
src/bin/auth/auth.spec.pre.in

@@ -273,6 +273,142 @@
         "item_default": 0,
         "item_title": "Received requests opcode 15",
         "item_description": "The number of total request counts whose opcode is 15 (reserved)"
+      },
+      {
+        "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.reserved11",
+        "item_type": "integer",
+        "item_optional": true,
+        "item_default": 0,
+        "item_title": "Sent response with rcode 11",
+        "item_description": "The number of total responses with rcode 11 (reserved)"
+      },
+      {
+        "item_name": "rcode.reserved12",
+        "item_type": "integer",
+        "item_optional": true,
+        "item_default": 0,
+        "item_title": "Sent response with rcode 12",
+        "item_description": "The number of total responses with rcode 12 (reserved)"
+      },
+      {
+        "item_name": "rcode.reserved13",
+        "item_type": "integer",
+        "item_optional": true,
+        "item_default": 0,
+        "item_title": "Sent response with rcode 13",
+        "item_description": "The number of total responses with rcode 13 (reserved)"
+      },
+      {
+        "item_name": "rcode.reserved14",
+        "item_type": "integer",
+        "item_optional": true,
+        "item_default": 0,
+        "item_title": "Sent response with rcode 14",
+        "item_description": "The number of total responses with rcode 14 (reserved)"
+      },
+      {
+        "item_name": "rcode.reserved15",
+        "item_type": "integer",
+        "item_optional": true,
+        "item_default": 0,
+        "item_title": "Sent response with rcode 15",
+        "item_description": "The number of total responses with rcode 15 (reserved)"
+      },
+      {
+        "item_name": "rcode.badvers",
+        "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)"
       }
     ]
   }

+ 35 - 6
src/bin/auth/auth_srv.cc

@@ -128,6 +128,22 @@ public:
     /// Bind the ModuleSpec object in config_session_ with
     /// isc:config::ModuleSpec::validateStatistics.
     void registerStatisticsValidator();
+
+    /// \brief Resume the server
+    ///
+    /// This is a wrapper call for DNSServer::resume(done), if 'done' is true,
+    /// the Rcode set in the given Message is counted in the statistics
+    /// counter.
+    ///
+    /// This method is expected to be called by processMessage()
+    ///
+    /// \param server The DNSServer as passed to processMessage()
+    /// \param message The response as constructed by processMessage()
+    /// \param done If true, the Rcode from the given message is counted,
+    ///             this value is then passed to server->resume(bool)
+    void resumeServer(isc::asiodns::DNSServer* server,
+                      isc::dns::MessagePtr message,
+                      bool done);
 private:
     std::string db_file_;
 
@@ -409,13 +425,13 @@ AuthSrv::processMessage(const IOMessage& io_message, MessagePtr message,
         // Ignore all responses.
         if (message->getHeaderFlag(Message::HEADERFLAG_QR)) {
             LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_RECEIVED);
-            server->resume(false);
+            impl_->resumeServer(server, message, false);
             return;
         }
     } catch (const Exception& ex) {
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_HEADER_PARSE_FAIL)
                   .arg(ex.what());
-        server->resume(false);
+        impl_->resumeServer(server, message, false);
         return;
     }
 
@@ -426,13 +442,13 @@ AuthSrv::processMessage(const IOMessage& io_message, MessagePtr message,
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PROTOCOL_ERROR)
                   .arg(error.getRcode().toText()).arg(error.what());
         makeErrorMessage(message, buffer, error.getRcode());
-        server->resume(true);
+        impl_->resumeServer(server, message, true);
         return;
     } catch (const Exception& ex) {
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PARSE_ERROR)
                   .arg(ex.what());
         makeErrorMessage(message, buffer, Rcode::SERVFAIL());
-        server->resume(true);
+        impl_->resumeServer(server, message, true);
         return;
     } // other exceptions will be handled at a higher layer.
 
@@ -459,7 +475,7 @@ AuthSrv::processMessage(const IOMessage& io_message, MessagePtr message,
 
     if (tsig_error != TSIGError::NOERROR()) {
         makeErrorMessage(message, buffer, tsig_error.toRcode(), tsig_context);
-        server->resume(true);
+        impl_->resumeServer(server, message, true);
         return;
     }
 
@@ -492,7 +508,7 @@ AuthSrv::processMessage(const IOMessage& io_message, MessagePtr message,
         }
     }
 
-    server->resume(send_answer);
+    impl_->resumeServer(server, message, send_answer);
 }
 
 bool
@@ -755,6 +771,14 @@ AuthSrvImpl::setDbFile(ConstElementPtr config) {
     return (answer);
 }
 
+void
+AuthSrvImpl::resumeServer(DNSServer* server, MessagePtr message, bool done) {
+    if (done) {
+        counters_.inc(message->getRcode());
+    }
+    server->resume(done);
+}
+
 ConstElementPtr
 AuthSrv::updateConfig(ConstElementPtr new_config) {
     try {
@@ -784,6 +808,11 @@ AuthSrv::getCounter(const Opcode opcode) const {
     return (impl_->counters_.getCounter(opcode));
 }
 
+uint64_t
+AuthSrv::getCounter(const Rcode rcode) const {
+    return (impl_->counters_.getCounter(rcode));
+}
+
 const AddressList&
 AuthSrv::getListenAddresses() const {
     return (impl_->listen_addresses_);

+ 14 - 0
src/bin/auth/auth_srv.h

@@ -361,6 +361,20 @@ public:
     /// \return the value of the counter.
     uint64_t getCounter(const isc::dns::Opcode opcode) const;
 
+    /// \brief Get the value of per Rcode counter in the Auth Counters.
+    ///
+    /// This function calls AuthCounters::getCounter(isc::dns::Rcode) and
+    /// returns its return value.
+    ///
+    /// \note This is a tentative interface as an attempt of experimentally
+    /// supporting more statistics counters.  This should eventually be more
+    /// generalized.  In any case, this method is mainly for testing.
+    ///
+    /// \throw None
+    /// \param rcode The rcode of the counter to get the value of
+    /// \return the value of the counter.
+    uint64_t getCounter(const isc::dns::Rcode rcode) const;
+
     /**
      * \brief Set and get the addresses we listen on.
      */

+ 32 - 1
src/bin/auth/statistics.cc

@@ -50,6 +50,9 @@ public:
     void inc(const Opcode opcode) {
         opcode_counter_.inc(opcode.getCode());
     }
+    void inc(const Rcode rcode) {
+        rcode_counter_.inc(rcode.getCode());
+    }
     void inc(const std::string& zone,
              const AuthCounters::PerZoneCounterType type);
     bool submitStatistics() const;
@@ -61,10 +64,15 @@ public:
     uint64_t getCounter(const Opcode opcode) const {
         return (opcode_counter_.get(opcode.getCode()));
     }
+    uint64_t getCounter(const Rcode rcode) const {
+        return (rcode_counter_.get(rcode.getCode()));
+    }
 private:
     Counter server_counter_;
     Counter opcode_counter_;
     static const size_t NUM_OPCODES = 16;
+    Counter rcode_counter_;
+    static const size_t NUM_RCODES = 17;
     CounterDictionary per_zone_counter_;
     isc::cc::AbstractSession* statistics_session_;
     AuthCounters::validator_type validator_;
@@ -75,7 +83,7 @@ AuthCountersImpl::AuthCountersImpl() :
     // size of server_counter_: AuthCounters::SERVER_COUNTER_TYPES
     // size of per_zone_counter_: AuthCounters::PER_ZONE_COUNTER_TYPES
     server_counter_(AuthCounters::SERVER_COUNTER_TYPES),
-    opcode_counter_(NUM_OPCODES),
+    opcode_counter_(NUM_OPCODES), rcode_counter_(NUM_RCODES),
     per_zone_counter_(AuthCounters::PER_ZONE_COUNTER_TYPES),
     statistics_session_(NULL)
 {
@@ -125,6 +133,19 @@ AuthCountersImpl::submitStatistics() const {
                               << counter;
         }
     }
+    // Insert non 0 Rcode counters.
+    for (int i = 0; i < NUM_RCODES; ++i) {
+        const Counter::Type counter = rcode_counter_.get(i);
+        if (counter != 0) {
+            // The counter item name should be derived lower-cased textual
+            // representation of the code.
+            std::string rcode_txt = Rcode(i).toText();
+            std::transform(rcode_txt.begin(), rcode_txt.end(),
+                           rcode_txt.begin(), ::tolower);
+            statistics_string << ", \"rcode." << rcode_txt << "\": "
+                              << counter;
+        }
+    }
     statistics_string <<   " }"
                       <<   "}"
                       << "]}";
@@ -194,6 +215,11 @@ AuthCounters::inc(const Opcode opcode) {
     impl_->inc(opcode);
 }
 
+void
+AuthCounters::inc(const Rcode rcode) {
+    impl_->inc(rcode);
+}
+
 bool
 AuthCounters::submitStatistics() const {
     return (impl_->submitStatistics());
@@ -216,6 +242,11 @@ AuthCounters::getCounter(const Opcode opcode) const {
     return (impl_->getCounter(opcode));
 }
 
+uint64_t
+AuthCounters::getCounter(const Rcode rcode) const {
+    return (impl_->getCounter(rcode));
+}
+
 void
 AuthCounters::registerStatisticsValidator
     (AuthCounters::validator_type validator) const

+ 25 - 0
src/bin/auth/statistics.h

@@ -16,6 +16,7 @@
 #define __STATISTICS_H 1
 
 #include <dns/opcode.h>
+#include <dns/rcode.h>
 
 #include <cc/session.h>
 #include <stdint.h>
@@ -98,6 +99,15 @@ public:
     /// \throw None
     void inc(const isc::dns::Opcode opcode);
 
+    /// \brief Increment the counter of a per rcode counter.
+    ///
+    /// \note This is a tentative interface.  See \c getCounter().
+    ///
+    /// \param rcode The rcode of the counter to increment.
+    ///
+    /// \throw None
+    void inc(const isc::dns::Rcode rcode);
+
     /// \brief Submit statistics counters to statistics module.
     ///
     /// This method is desinged to be called periodically
@@ -162,6 +172,21 @@ public:
     /// \return the value of the counter.
     uint64_t getCounter(const isc::dns::Opcode opcode) const;
 
+    /// \brief Get the value of a per rcode counter.
+    ///
+    /// This method returns the value of the per rcode counter for the
+    /// specified \c rcode.
+    ///
+    /// \note As mentioned in getCounter(const isc::dns::Opcode opcode),
+    /// This is a tentative interface as an attempt of experimentally
+    /// supporting more statistics counters.  This should eventually be more
+    /// generalized.  In any case, this method is mainly for testing.
+    ///
+    /// \throw None
+    /// \param rcode The rcode of the counter to get the value of
+    /// \return the value of the counter.
+    uint64_t getCounter(const isc::dns::Rcode rcode) const;
+
     /// \brief A type of validation function for the specification in
     /// isc::config::ModuleSpec.
     ///

+ 59 - 0
src/bin/auth/tests/auth_srv_unittest.cc

@@ -91,6 +91,35 @@ protected:
         server.processMessage(*io_message, parse_message, response_obuffer,
                               &dnsserv);
     }
+
+    // Helper for checking Rcode statistic counters;
+    // Checks for one specific Rcode statistics counter value
+    void checkRcodeCounter(const Rcode& rcode, int expected_value) const {
+        EXPECT_EQ(expected_value, server.getCounter(rcode)) <<
+                  "Expected Rcode count for " << rcode.toText() <<
+                  " " << expected_value << ", was: " <<
+                  server.getCounter(rcode);
+    }
+
+    // Checks whether all Rcode counters are set to zero
+    void checkAllRcodeCountersZero() const {
+        for (int i = 0; i < 17; i++) {
+            checkRcodeCounter(Rcode(i), 0);
+        }
+    }
+
+    // Checks whether all Rcode counters are set to zero except the given
+    // rcode (it is checked to be set to 'value')
+    void checkAllRcodeCountersZeroExcept(const Rcode& rcode, int value) const {
+        for (int i = 0; i < 17; i++) {
+            const Rcode rc(i);
+            if (rc == rcode) {
+                checkRcodeCounter(Rcode(i), value);
+            } else {
+                checkRcodeCounter(Rcode(i), 0);
+            }
+        }
+    }
     IOService ios_;
     DNSService dnss_;
     MockSession statistics_session;
@@ -152,6 +181,7 @@ TEST_F(AuthSrvTest, builtInQuery) {
                         response_obuffer->getData(),
                         response_obuffer->getLength(),
                         &response_data[0], response_data.size());
+    checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
 }
 
 // Same test emulating the UDPServer class behavior (defined in libasiolink).
@@ -202,38 +232,46 @@ TEST_F(AuthSrvTest, iqueryViaDNSServer) {
 // Unsupported requests.  Should result in NOTIMP.
 TEST_F(AuthSrvTest, unsupportedRequest) {
     unsupportedRequest();
+    // unsupportedRequest tries 14 different opcodes
+    checkAllRcodeCountersZeroExcept(Rcode::NOTIMP(), 14);
 }
 
 // Multiple questions.  Should result in FORMERR.
 TEST_F(AuthSrvTest, multiQuestion) {
     multiQuestion();
+    checkAllRcodeCountersZeroExcept(Rcode::FORMERR(), 1);
 }
 
 // Incoming data doesn't even contain the complete header.  Must be silently
 // dropped.
 TEST_F(AuthSrvTest, shortMessage) {
     shortMessage();
+    checkAllRcodeCountersZero();
 }
 
 // Response messages.  Must be silently dropped, whether it's a valid response
 // or malformed or could otherwise cause a protocol error.
 TEST_F(AuthSrvTest, response) {
     response();
+    checkAllRcodeCountersZero();
 }
 
 // Query with a broken question
 TEST_F(AuthSrvTest, shortQuestion) {
     shortQuestion();
+    checkAllRcodeCountersZeroExcept(Rcode::FORMERR(), 1);
 }
 
 // Query with a broken answer section
 TEST_F(AuthSrvTest, shortAnswer) {
     shortAnswer();
+    checkAllRcodeCountersZeroExcept(Rcode::FORMERR(), 1);
 }
 
 // Query with unsupported version of EDNS.
 TEST_F(AuthSrvTest, ednsBadVers) {
     ednsBadVers();
+    checkAllRcodeCountersZeroExcept(Rcode::BADVERS(), 1);
 }
 
 TEST_F(AuthSrvTest, AXFROverUDP) {
@@ -251,6 +289,7 @@ TEST_F(AuthSrvTest, AXFRSuccess) {
     server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
     EXPECT_FALSE(dnsserv.hasAnswer());
     EXPECT_TRUE(xfrout.isConnected());
+    checkAllRcodeCountersZero();
 }
 
 // Try giving the server a TSIG signed request and see it can anwer signed as
@@ -286,6 +325,8 @@ TEST_F(AuthSrvTest, TSIGSigned) {
                                    response_obuffer->getLength()));
     EXPECT_EQ(TSIGError::NOERROR(), error) <<
         "The server signed the response, but it doesn't seem to be valid";
+
+    checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
 }
 
 // Give the server a signed request, but don't give it the key. It will
@@ -318,6 +359,8 @@ TEST_F(AuthSrvTest, TSIGSignedBadKey) {
     EXPECT_EQ(TSIGError::BAD_KEY_CODE, tsig->getRdata().getError());
     EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
         "It should be unsigned with this error";
+
+    checkAllRcodeCountersZeroExcept(Rcode::NOTAUTH(), 1);
 }
 
 // Give the server a signed request, but signed by a different key
@@ -351,6 +394,8 @@ TEST_F(AuthSrvTest, TSIGBadSig) {
     EXPECT_EQ(TSIGError::BAD_SIG_CODE, tsig->getRdata().getError());
     EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
         "It should be unsigned with this error";
+
+    checkAllRcodeCountersZeroExcept(Rcode::NOTAUTH(), 1);
 }
 
 // Give the server a signed unsupported request with a bad signature.
@@ -390,6 +435,8 @@ TEST_F(AuthSrvTest, TSIGCheckFirst) {
     // TSIG should have failed, and so the per opcode counter shouldn't be
     // incremented.
     EXPECT_EQ(0, server.getCounter(Opcode::RESERVED14()));
+
+    checkAllRcodeCountersZeroExcept(Rcode::NOTAUTH(), 1);
 }
 
 TEST_F(AuthSrvTest, AXFRConnectFail) {
@@ -538,6 +585,8 @@ TEST_F(AuthSrvTest, notify) {
     EXPECT_EQ(Name("example.com"), question->getName());
     EXPECT_EQ(RRClass::IN(), question->getClass());
     EXPECT_EQ(RRType::SOA(), question->getType());
+
+    checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
 }
 
 TEST_F(AuthSrvTest, notifyForCHClass) {
@@ -841,6 +890,10 @@ TEST_F(AuthSrvTest, queryCounterUDPNormal) {
                           &dnsserv);
     // After processing UDP query, the counter should be 1.
     EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_UDP_QUERY));
+    // The counter for opcode Query should also be one
+    EXPECT_EQ(1, server.getCounter(Opcode::QUERY()));
+    // The counter for REFUSED responses should also be one, the rest zero
+    checkAllRcodeCountersZeroExcept(Rcode::REFUSED(), 1);
 }
 
 // Submit TCP normal query and check query counter
@@ -856,6 +909,10 @@ TEST_F(AuthSrvTest, queryCounterTCPNormal) {
                           &dnsserv);
     // After processing TCP query, the counter should be 1.
     EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
+    // The counter for SUCCESS responses should also be one
+    EXPECT_EQ(1, server.getCounter(Opcode::QUERY()));
+    // The counter for REFUSED responses should also be one, the rest zero
+    checkAllRcodeCountersZeroExcept(Rcode::REFUSED(), 1);
 }
 
 // Submit TCP AXFR query and check query counter
@@ -871,6 +928,8 @@ TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
     EXPECT_FALSE(dnsserv.hasAnswer());
     // After processing TCP AXFR query, the counter should be 1.
     EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
+    // No rcodes should be incremented
+    checkAllRcodeCountersZero();
 }
 
 // Submit TCP IXFR query and check query counter

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

@@ -21,6 +21,7 @@
 #include <boost/bind.hpp>
 
 #include <dns/opcode.h>
+#include <dns/rcode.h>
 
 #include <cc/data.h>
 #include <cc/session.h>
@@ -184,6 +185,16 @@ TEST_F(AuthCountersTest, incrementOpcodeCounter) {
     }
 }
 
+TEST_F(AuthCountersTest, incrementRcodeCounter) {
+    // The counter should be initialized to 0.  If we increment it by 1
+    // the counter should be 1.
+    for (int i = 0; i < 17; ++i) {
+        EXPECT_EQ(0, counters.getCounter(Rcode(i)));
+        counters.inc(Rcode(i));
+        EXPECT_EQ(1, counters.getCounter(Rcode(i)));
+    }
+}
+
 TEST_F(AuthCountersTest, submitStatisticsWithoutSession) {
     // Set statistics_session to NULL and call submitStatistics().
     // Expect to return false.
@@ -225,6 +236,29 @@ opcodeDataCheck(ConstElementPtr data, const int expected[16]) {
     ASSERT_EQ(static_cast<const char*>(NULL), item_names[i]);
 }
 
+void
+rcodeDataCheck(ConstElementPtr data, const int expected[17]) {
+    const char* item_names[] = {
+        "noerror", "formerr", "servfail", "nxdomain", "notimp", "refused",
+        "yxdomain", "yxrrset", "nxrrset", "notauth", "notzone", "reserved11",
+        "reserved12", "reserved13", "reserved14", "reserved15", "badvers",
+        NULL
+    };
+    int i;
+    for (i = 0; i < 17; ++i) {
+        ASSERT_NE(static_cast<const char*>(NULL), item_names[i]);
+        const string item_name = "rcode." + string(item_names[i]);
+        if (expected[i] == 0) {
+            EXPECT_FALSE(data->get(item_name));
+        } else {
+            EXPECT_EQ(expected[i], data->get(item_name)->intValue());
+        }
+    }
+    // We should have examined all names
+    ASSERT_EQ(static_cast<const char*>(NULL), item_names[i]);
+}
+
+
 TEST_F(AuthCountersTest, submitStatisticsWithoutValidator) {
     // Submit statistics data.
     // Validate if it submits correct data.
@@ -258,6 +292,10 @@ TEST_F(AuthCountersTest, submitStatisticsWithoutValidator) {
     const int opcode_results[16] = { 0, 0, 0, 0, 0, 0, 0, 0,
                                      0, 0, 0, 0, 0, 0, 0, 0 };
     opcodeDataCheck(statistics_data, opcode_results);
+    // By default rcode counters are all 0 and omitted
+    const int rcode_results[17] = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0 };
+    rcodeDataCheck(statistics_data, rcode_results);
 }
 
 void
@@ -269,6 +307,15 @@ updateOpcodeCounters(AuthCounters &counters, const int expected[16]) {
     }
 }
 
+void
+updateRcodeCounters(AuthCounters &counters, const int expected[17]) {
+    for (int i = 0; i < 17; ++i) {
+        for (int j = 0; j < expected[i]; ++j) {
+            counters.inc(Rcode(i));
+        }
+    }
+}
+
 TEST_F(AuthCountersTest, submitStatisticsWithOpcodeCounters) {
     // Increment some of the opcode counters.  Then they should appear in the
     // submitted data; others shouldn't
@@ -293,6 +340,30 @@ TEST_F(AuthCountersTest, submitStatisticsWithAllOpcodeCounters) {
     opcodeDataCheck(statistics_data, opcode_results);
 }
 
+TEST_F(AuthCountersTest, submitStatisticsWithRcodeCounters) {
+    // Increment some of the rcode counters.  Then they should appear in the
+    // submitted data; others shouldn't
+    const int rcode_results[17] = { 1, 2, 3, 4, 5, 6, 7, 8, 9,
+                                    10, 0, 0, 0, 0, 0, 0, 11 };
+    updateRcodeCounters(counters, rcode_results);
+    counters.submitStatistics();
+    ConstElementPtr statistics_data = statistics_session_.sent_msg
+        ->get("command")->get(1)->get("data");
+    rcodeDataCheck(statistics_data, rcode_results);
+}
+
+TEST_F(AuthCountersTest, submitStatisticsWithAllRcodeCounters) {
+    // Increment all rcode counters.  Then they should appear in the
+    // submitted data.
+    const int rcode_results[17] = { 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                     1, 1, 1, 1, 1, 1, 1, 1 };
+    updateOpcodeCounters(counters, rcode_results);
+    counters.submitStatistics();
+    ConstElementPtr statistics_data = statistics_session_.sent_msg
+        ->get("command")->get(1)->get("data");
+    opcodeDataCheck(statistics_data, rcode_results);
+}
+
 TEST_F(AuthCountersTest, submitStatisticsWithValidator) {
 
     //a validator for the unittest

+ 1 - 0
src/lib/testutils/srv_test.cc

@@ -86,6 +86,7 @@ SrvTestBase::createRequestPacket(Message& message,
                                   IOAddress(DEFAULT_REMOTE_ADDRESS), 53210);
     io_sock = (protocol == IPPROTO_UDP) ? &IOSocket::getDummyUDPSocket() :
         &IOSocket::getDummyTCPSocket();
+
     io_message = new IOMessage(request_renderer.getData(),
                                request_renderer.getLength(),
                                *io_sock, *endpoint);