// 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 #include #include #include #include #include #include #include #include #include #include "statistics_util.h" #include #include #include #include #include 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(MessageAttributes::IP_VERSION_IPV4); msgattrs.setRequestTransportProtocol(MessageAttributes::TRANSPORT_UDP); msgattrs.setRequestOpCode(Opcode::QUERY()); msgattrs.setRequestEDNS0(true); msgattrs.setRequestDO(true); } TEST_F(CountersTest, invalidParameter) { MessageAttributes msgattrs; // Passing *_UNSPEC should trigger throwing an exception // isc::InvalidParameter. EXPECT_THROW( msgattrs.setRequestIPVersion(MessageAttributes::IP_VERSION_UNSPEC), isc::InvalidParameter); EXPECT_THROW( msgattrs.setRequestTransportProtocol( MessageAttributes::TRANSPORT_UNSPEC), isc::InvalidParameter); } TEST_F(CountersTest, invalidOperationForGetRequestOpCode) { MessageAttributes msgattrs; // getRequestOpCode() will throw isc::InvalidOperation when called before // opcode is set with setRequestOpCode(). EXPECT_THROW(msgattrs.getRequestOpCode(), isc::InvalidOperation); msgattrs.setRequestOpCode(Opcode::QUERY()); // getRequestOpCode() will not throw at this point. EXPECT_NO_THROW(msgattrs.getRequestOpCode()); } TEST_F(CountersTest, incrementResponse) { Message response(Message::RENDER); MessageAttributes msgattrs; std::map 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 expect; // Test these patterns: // ipversion protocol // ----------------- // ipv4 udp // ipv6 udp // ipv4 tcp // ipv6 tcp int count_v4 = 0, count_v6 = 0, count_udp = 0, count_tcp = 0; for (int i = 0; i < 4; ++i) { const enum MessageAttributes::IPVersionType ipversion = (i & 1) != 0 ? MessageAttributes::IP_VERSION_IPV4 : MessageAttributes::IP_VERSION_IPV6; const enum MessageAttributes::TransportProtocolType proto = (i & 2) != 0 ? MessageAttributes::TRANSPORT_UDP : MessageAttributes::TRANSPORT_TCP; buildSkeletonMessage(msgattrs); msgattrs.setRequestIPVersion(ipversion); 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 (ipversion == MessageAttributes::IP_VERSION_IPV4) { ++count_v4; } else { ++count_v6; } if (proto == MessageAttributes::TRANSPORT_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 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 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 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.setRequestSig(is_req_tsig, is_badsig); msgattrs.setResponseSigTSIG(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 - count_badsig; 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 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.setRequestSig(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 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.setRequestSig(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 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.setRequestSig(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 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.setRequestSig(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 expect; // Opcode = QUERY, Rcode = NOERROR, ANCOUNT > 0 msgattrs.setRequestIPVersion(MessageAttributes::IP_VERSION_IPV4); msgattrs.setRequestTransportProtocol(MessageAttributes::TRANSPORT_UDP); msgattrs.setRequestOpCode(Opcode::QUERY()); msgattrs.setRequestEDNS0(true); msgattrs.setRequestDO(true); msgattrs.setRequestSig(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 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(MessageAttributes::IP_VERSION_IPV4); msgattrs.setRequestTransportProtocol(MessageAttributes::TRANSPORT_UDP); msgattrs.setRequestOpCode(Opcode::QUERY()); msgattrs.setRequestEDNS0(true); msgattrs.setRequestDO(true); msgattrs.setRequestSig(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 expect; // Opcode = QUERY, Rcode = REFUSED, ANCOUNT = 0 (don't care) msgattrs.setRequestIPVersion(MessageAttributes::IP_VERSION_IPV4); msgattrs.setRequestTransportProtocol(MessageAttributes::TRANSPORT_UDP); msgattrs.setRequestOpCode(Opcode::QUERY()); msgattrs.setRequestEDNS0(true); msgattrs.setRequestDO(true); msgattrs.setRequestSig(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)); } }