// Copyright (C) 2011-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace isc::dns; using namespace isc::dns::rdata; using namespace isc::util; using isc::UnitTestUtil; using isc::util::unittests::matchWireData; using boost::lexical_cast; 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; #ifdef TYPED_TEST_SUITE TYPED_TEST_SUITE(NSECLikeBitmapTest, TestRdataTypes); #else TYPED_TEST_CASE(NSECLikeBitmapTest, TestRdataTypes); #endif // 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); } // 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); matchWireData(&data[0], data.size(), obuffer.getData(), obuffer.getLength()); // Same for MessageRenderer. obuffer.clear(); MessageRenderer renderer; renderer.writeUint16(rdlen); empty_nsec3.toWire(renderer); matchWireData(&data[0], data.size(), renderer.getData(), renderer.getLength()); } }