// Copyright (C) 2011 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 using namespace std; using boost::lexical_cast; using isc::UnitTestUtil; using namespace isc::dns; using namespace isc::dns::rdata; namespace { // Template for shared tests for NSEC and NSEC3 bitmaps template class NSECLikeBitmapTest : public RdataTest { protected: RDATA_TYPE fromText(const string& rdata_text) { return (RDATA_TYPE(rdata_text)); } vector compare_set; // used in compare() tests void compareCheck() const { typename vector::const_iterator it; typename vector::const_iterator const it_end = compare_set.end(); for (it = compare_set.begin(); it != it_end - 1; ++it) { SCOPED_TRACE("compare " + it->toText() + " to " + (it + 1)->toText()); EXPECT_GT(0, (*it).compare(*(it + 1))); EXPECT_LT(0, (*(it + 1)).compare(*it)); } } // These depend on the specific RR type. We use specialized methods // for them. static RRType getType(); // return either RRType::NSEC() or NSEC3() static string getWireFilePrefix(); static string getCommonText(); // commonly used part of textual form }; // Instantiate specific typed tests typedef ::testing::Types TestRdataTypes; TYPED_TEST_CASE(NSECLikeBitmapTest, TestRdataTypes); // NSEC and NSEC3 bitmaps have some subtle differences, in which case we // need to test them separately. Using these typedef type names with TEST_F // will do the trick. typedef NSECLikeBitmapTest NSEC3BitmapTest; typedef NSECLikeBitmapTest NSECBitmapTest; template <> string NSECLikeBitmapTest::getWireFilePrefix() { return ("rdata_nsec_"); } template <> RRType NSECLikeBitmapTest::getType() { return (RRType::NSEC()); } template <> string NSECLikeBitmapTest::getWireFilePrefix() { return ("rdata_nsec3_"); } template <> RRType NSECLikeBitmapTest::getType() { return (RRType::NSEC3()); } template <> string NSECLikeBitmapTest::getCommonText() { return ("next. "); } template <> string NSECLikeBitmapTest::getCommonText() { return ("1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR "); } // Tests against various types of bogus NSEC/NSEC3 type bitmaps. // The syntax and semantics are common for both RR types, and our // implementation of that part is shared, so in theory it should be sufficient // to test for only one RR type. But we check for both just in case. TYPED_TEST(NSECLikeBitmapTest, createFromWire) { // A malformed NSEC bitmap length field that could cause overflow. EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(), (this->getWireFilePrefix() + "fromWire4.wire").c_str()), DNSMessageFORMERR); // The bitmap field is incomplete (only the first byte is included) EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(), (this->getWireFilePrefix() + "fromWire5.wire").c_str()), DNSMessageFORMERR); // Bitmap length is 0, which is invalid. EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(), (this->getWireFilePrefix() + "fromWire6.wire").c_str()), DNSMessageFORMERR); // Too large bitmap length with a short buffer. EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(), (this->getWireFilePrefix() + "fromWire3").c_str()), DNSMessageFORMERR); // A boundary case: longest possible bitmaps (32 maps). This should be // accepted. EXPECT_NO_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(), (this->getWireFilePrefix() + "fromWire7.wire").c_str())); // Another boundary condition: 33 bitmaps, which should be rejected. EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(), (this->getWireFilePrefix() + "fromWire8.wire").c_str()), DNSMessageFORMERR); // Disordered bitmap window blocks. EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(), (this->getWireFilePrefix() + "fromWire9.wire").c_str()), DNSMessageFORMERR); // Bitmap ending with all-zero bytes. Not necessarily harmful except // the additional overhead of parsing, but invalid according to the // spec anyway. EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(), (this->getWireFilePrefix() + "fromWire10.wire").c_str()), DNSMessageFORMERR); } TYPED_TEST(NSECLikeBitmapTest, badText) { // redundant space after the sequence EXPECT_THROW(this->fromText(this->getCommonText() + "A "), InvalidRdataText); } // This tests the result of toText() with various kinds of NSEC/NSEC3 bitmaps. // It also tests the "from text" constructor as a result. TYPED_TEST(NSECLikeBitmapTest, toText) { // A simple case (some commonly seen RR types in NSEC(3) bitmaps) string rdata_text = this->getCommonText() + "NS SOA RRSIG DNSKEY"; EXPECT_EQ(rdata_text, this->fromText(rdata_text).toText()); // Similar to above, but involves more than one bitmap window blocks. rdata_text = this->getCommonText() + "NS DLV"; EXPECT_EQ(rdata_text, this->fromText(rdata_text).toText()); // Make sure all possible bits in a one-octet bitmap field are handled // correctly. // We use the range around 1024 (reasonably higher number) so it's // unlikely that they have predefined mnemonic and can be safely converted // to TYPEnnnn by toText(). for (unsigned int i = 1024; i < 1032; ++i) { rdata_text = this->getCommonText() + "TYPE" + lexical_cast(i); EXPECT_EQ(rdata_text, this->fromText(rdata_text).toText()); } // Make sure all possible 32 octets in a longest possible block are // handled correctly. for (unsigned int i = 1024; i < 1024 + 256; i += 8) { rdata_text = this->getCommonText() + "TYPE" + lexical_cast(i); EXPECT_EQ(rdata_text, this->fromText(rdata_text).toText()); } // Check for the highest window block. rdata_text = this->getCommonText() + "TYPE65535"; EXPECT_EQ(rdata_text, this->fromText(rdata_text).toText()); } TYPED_TEST(NSECLikeBitmapTest, compare) { // Bit map: [win=0][len=1] 00000010 this->compare_set.push_back(this->fromText(this->getCommonText() + "SOA")); // Bit map: [win=0][len=1] 00000010, [win=4][len=1] 10000000 this->compare_set.push_back(this->fromText(this->getCommonText() + "SOA TYPE1024")); // Bit map: [win=0][len=1] 00100000 this->compare_set.push_back(this->fromText(this->getCommonText() + "NS")); // Bit map: [win=0][len=1] 00100010 this->compare_set.push_back(this->fromText(this->getCommonText() + "NS SOA")); // Bit map: [win=0][len=2] 00100000, 00000001 this->compare_set.push_back(this->fromText(this->getCommonText() + "NS MX")); // Bit map: [win=4][len=1] 10000000 this->compare_set.push_back(this->fromText(this->getCommonText() + "TYPE1024")); this->compareCheck(); } // NSEC bitmaps must not be empty TEST_F(NSECBitmapTest, emptyMap) { EXPECT_THROW(this->fromText("next.example").toText(), InvalidRdataText); EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(), (this->getWireFilePrefix() + "fromWire16.wire").c_str()), DNSMessageFORMERR); } // NSEC3 bitmaps can be empty TEST_F(NSEC3BitmapTest, emptyMap) { // Read wire data wit an empty NSEC3 bitmap. This should succeed. vector data; UnitTestUtil::readWireData((this->getWireFilePrefix() + "fromWire16.wire").c_str(), data); InputBuffer buffer(&data[0], data.size()); const uint16_t rdlen = buffer.readUint16(); const generic::NSEC3 empty_nsec3 = dynamic_cast(*createRdata( RRType::NSEC3(), RRClass::IN(), buffer, rdlen)); // Check the toText() result. EXPECT_EQ("1 0 1 7373737373 D1K6GQ38D1K6GQ38D1K6GQ38D1K6GQ38", empty_nsec3.toText()); // Check the toWire() result. OutputBuffer obuffer(0); obuffer.writeUint16(rdlen); empty_nsec3.toWire(obuffer); EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(), obuffer.getLength(), &data[0], data.size()); // Same for MessageRenderer. obuffer.clear(); MessageRenderer renderer; renderer.writeUint16(rdlen); empty_nsec3.toWire(renderer); EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(), renderer.getLength(), &data[0], data.size()); } }