// 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace isc::dns; using namespace isc::dns::rdata; using namespace isc::datasrc::memory; using namespace isc::testutils; using isc::datasrc::memory::detail::SegmentObjectHolder; using boost::lexical_cast; namespace { class RdataSetTest : public ::testing::Test { protected: RdataSetTest() : // 1076895760 = 0x40302010. Use this so we fill in all 8-bit "field" // of the 32-bit TTL a_rrset_(textToRRset("www.example.com. 1076895760 IN A 192.0.2.1")), rrsig_rrset_(textToRRset("www.example.com. 1076895760 IN RRSIG " "A 5 2 3600 20120814220826 20120715220826 " "1234 example.com. FAKE")) {} void TearDown() { EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated()); } ConstRRsetPtr a_rrset_, rrsig_rrset_; isc::util::MemorySegmentLocal mem_sgmt_; RdataEncoder encoder_; }; // Convert the given 32-bit integer (network byte order) to the corresponding // RRTTL object. RRTTL restoreTTL(const void* ttl_data) { isc::util::InputBuffer b(ttl_data, sizeof(uint32_t)); return (RRTTL(b)); } // A helper callback for checkRdataSet. This confirms the given data // is the expected in::A RDATA (the value is taken from the RdataSetTest // constructor). void checkData(const void* data, size_t size) { isc::util::InputBuffer b(data, size); EXPECT_EQ(0, in::A(b, size).compare(in::A("192.0.2.1"))); } // This is a set of checks for an RdataSet created with some simple // conditions. with_rrset/with_rrsig is true iff the RdataSet is supposed to // contain normal/RRSIG RDATA. void checkRdataSet(const RdataSet& rdataset, bool with_rrset, bool with_rrsig) { EXPECT_FALSE(rdataset.next); // by default the next pointer should be NULL EXPECT_EQ(RRType::A(), rdataset.type); // See the RdataSetTest constructor for the magic number. EXPECT_EQ(RRTTL(1076895760), restoreTTL(rdataset.getTTLData())); EXPECT_EQ(with_rrset ? 1 : 0, rdataset.getRdataCount()); EXPECT_EQ(with_rrsig ? 1 : 0, rdataset.getSigRdataCount()); // A simple test for the data content. Details tests for the encoder/ // reader should be basically sufficient for various cases of the data, // and the fact that this test doesn't detect memory leak should be // reasonably sufficient that the implementation handles the data region // correctly. Here we check one simple case for a simple form of RDATA, // mainly for checking the behavior of getDataBuf(). RdataReader reader(RRClass::IN(), RRType::A(), reinterpret_cast( rdataset.getDataBuf()), rdataset.getRdataCount(), rdataset.getSigRdataCount(), &RdataReader::emptyNameAction, checkData); reader.iterate(); } TEST_F(RdataSetTest, create) { // A simple case of creating an RdataSet. Confirming the resulting // fields have the expected values, and then destroying it (TearDown() // would detect any memory leak) RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_, ConstRRsetPtr()); checkRdataSet(*rdataset, true, false); RdataSet::destroy(mem_sgmt_, rdataset, RRClass::IN()); } TEST_F(RdataSetTest, getNext) { RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_, ConstRRsetPtr()); // By default, the next pointer should be NULL (already tested in other // test cases), which should be the case with getNext(). We test both // mutable and immutable versions of getNext(). EXPECT_EQ(static_cast(NULL), rdataset->getNext()); EXPECT_EQ(static_cast(NULL), static_cast(rdataset)->getNext()); // making a link (it would form an infinite loop, but it doesn't matter // in this test), and check the pointer returned by getNext(). rdataset->next = rdataset; EXPECT_EQ(rdataset, static_cast(rdataset)->getNext()); RdataSet::destroy(mem_sgmt_, rdataset, RRClass::IN()); } TEST_F(RdataSetTest, find) { // Create some RdataSets and make a chain of them. SegmentObjectHolder holder1( mem_sgmt_, RdataSet::create(mem_sgmt_, encoder_, a_rrset_, ConstRRsetPtr()), RRClass::IN()); ConstRRsetPtr aaaa_rrset = textToRRset("www.example.com. 1076895760 IN AAAA 2001:db8::1"); SegmentObjectHolder holder2( mem_sgmt_, RdataSet::create(mem_sgmt_, encoder_, aaaa_rrset, ConstRRsetPtr()), RRClass::IN()); ConstRRsetPtr sigonly_rrset = textToRRset("www.example.com. 1076895760 IN RRSIG " "TXT 5 2 3600 20120814220826 20120715220826 " "1234 example.com. FAKE"); SegmentObjectHolder holder3( mem_sgmt_, RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(), sigonly_rrset), RRClass::IN()); RdataSet* rdataset_a = holder1.get(); RdataSet* rdataset_aaaa = holder2.get(); RdataSet* rdataset_sigonly = holder3.get(); RdataSet* rdataset_null = NULL; rdataset_a->next = rdataset_aaaa; rdataset_aaaa->next = rdataset_sigonly; // If a non-RRSIG part of rdataset exists for the given type, it will be // returned regardless of the value of sigonly_ok. If it's RRSIG-only // rdataset, it returns non NULL iff sigonly_ok is explicitly set to true. EXPECT_EQ(rdataset_aaaa, RdataSet::find(rdataset_a, RRType::AAAA())); EXPECT_EQ(rdataset_aaaa, RdataSet::find(rdataset_a, RRType::AAAA(), true)); EXPECT_EQ(rdataset_aaaa, RdataSet::find(rdataset_a, RRType::AAAA(), false)); EXPECT_EQ(rdataset_null, RdataSet::find(rdataset_a, RRType::TXT())); EXPECT_EQ(rdataset_sigonly, RdataSet::find(rdataset_a, RRType::TXT(), true)); EXPECT_EQ(rdataset_null, RdataSet::find(rdataset_a, RRType::TXT(), false)); // Same tests for the const version of find(). const RdataSet* rdataset_a_const = holder1.get(); EXPECT_EQ(rdataset_aaaa, RdataSet::find(rdataset_a_const, RRType::AAAA())); EXPECT_EQ(rdataset_aaaa, RdataSet::find(rdataset_a_const, RRType::AAAA(), true)); EXPECT_EQ(rdataset_aaaa, RdataSet::find(rdataset_a_const, RRType::AAAA(), false)); EXPECT_EQ(rdataset_null, RdataSet::find(rdataset_a_const, RRType::TXT())); EXPECT_EQ(rdataset_sigonly, RdataSet::find(rdataset_a_const, RRType::TXT(), true)); EXPECT_EQ(rdataset_null, RdataSet::find(rdataset_a_const, RRType::TXT(), false)); } // A helper function to create an RRset containing the given number of // unique RDATAs. ConstRRsetPtr getRRsetWithRdataCount(size_t rdata_count) { RRsetPtr rrset(new RRset(Name("example.com"), RRClass::IN(), RRType::TXT(), RRTTL(3600))); for (size_t i = 0; i < rdata_count; ++i) { rrset->addRdata(rdata::createRdata(RRType::TXT(), RRClass::IN(), lexical_cast(i))); } return (rrset); } TEST_F(RdataSetTest, createManyRRs) { // RRset with possible maximum number of RDATAs RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, getRRsetWithRdataCount(8191), ConstRRsetPtr()); EXPECT_EQ(8191, rdataset->getRdataCount()); EXPECT_EQ(0, rdataset->getSigRdataCount()); RdataSet::destroy(mem_sgmt_, rdataset, RRClass::IN()); // Exceeding that will result in an exception. EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, getRRsetWithRdataCount(8192), ConstRRsetPtr()), RdataSetError); // To be very sure even try larger number than the threshold EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, getRRsetWithRdataCount(65535), ConstRRsetPtr()), RdataSetError); } TEST_F(RdataSetTest, createWithRRSIG) { // Normal case. RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_, rrsig_rrset_); checkRdataSet(*rdataset, true, true); RdataSet::destroy(mem_sgmt_, rdataset, RRClass::IN()); // Unusual case: TTL doesn't match. This implementation accepts that, // using the TTL of the covered RRset. ConstRRsetPtr rrsig_badttl(textToRRset( "www.example.com. 3600 IN RRSIG " "A 5 2 3600 20120814220826 " "20120715220826 1234 example.com. FAKE")); rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_, rrsig_badttl); checkRdataSet(*rdataset, true, true); RdataSet::destroy(mem_sgmt_, rdataset, RRClass::IN()); } // A helper function to create an RRSIG RRset containing the given number of // unique RDATAs. ConstRRsetPtr getRRSIGWithRdataCount(size_t sig_count) { RRsetPtr rrset(new RRset(Name("example.com"), RRClass::IN(), RRType::RRSIG(), RRTTL(3600))); // We use a base wire-format image and tweak the original TTL field to // generate unique RDATAs in the loop. (Creating them from corresponding // text is simpler, but doing so for a large number of RRSIGs is // relatively heavy and could be too long for unittests). ConstRdataPtr rrsig_base = rdata::createRdata(RRType::RRSIG(), RRClass::IN(), "A 5 2 3600 20120814220826 20120715220826 1234 " "example.com. FAKE"); isc::util::OutputBuffer ob(0); rrsig_base->toWire(ob); for (size_t i = 0; i < sig_count; ++i) { ob.writeUint16At((i >> 16) & 0xffff, 4); ob.writeUint16At(i & 0xffff, 6); isc::util::InputBuffer ib(ob.getData(), ob.getLength()); rrset->addRdata(rdata::createRdata(RRType::RRSIG(), RRClass::IN(), ib, ib.getLength())); } return (rrset); } TEST_F(RdataSetTest, createManyRRSIGs) { // 7 has a special meaning in the implementation: if the number of the // RRSIGs reaches this value, an extra 'sig count' field will be created. RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_, getRRSIGWithRdataCount(7)); EXPECT_EQ(7, rdataset->getSigRdataCount()); RdataSet::destroy(mem_sgmt_, rdataset, RRClass::IN()); // 8 would cause overflow in the normal 3-bit field if there were no extra // count field. rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_, getRRSIGWithRdataCount(8)); EXPECT_EQ(8, rdataset->getSigRdataCount()); RdataSet::destroy(mem_sgmt_, rdataset, RRClass::IN()); // Up to 2^16-1 RRSIGs are allowed (although that would be useless // in practice) rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_, getRRSIGWithRdataCount(65535)); EXPECT_EQ(65535, rdataset->getSigRdataCount()); RdataSet::destroy(mem_sgmt_, rdataset, RRClass::IN()); // Exceeding this limit will result in an exception. EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_, getRRSIGWithRdataCount(65536)), RdataSetError); // To be very sure even try larger number than the threshold EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_, getRRSIGWithRdataCount(70000)), RdataSetError); } TEST_F(RdataSetTest, createWithRRSIGOnly) { // A rare, but allowed, case: RdataSet without the main RRset but with // RRSIG. RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(), rrsig_rrset_); checkRdataSet(*rdataset, false, true); RdataSet::destroy(mem_sgmt_, rdataset, RRClass::IN()); } TEST_F(RdataSetTest, badCeate) { // Neither the RRset nor RRSIG RRset is given EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(), ConstRRsetPtr()), isc::BadValue); // Empty RRset (An RRset without RDATA) ConstRRsetPtr empty_rrset(new RRset(Name("example.com"), RRClass::IN(), RRType::A(), RRTTL(3600))); EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, empty_rrset, ConstRRsetPtr()), isc::BadValue); ConstRRsetPtr empty_rrsig(new RRset(Name("example.com"), RRClass::IN(), RRType::RRSIG(), RRTTL(3600))); EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(), empty_rrsig), isc::BadValue); // The RRset type and RRSIG's type covered don't match ConstRRsetPtr bad_rrsig(textToRRset( "www.example.com. 1076895760 IN RRSIG " "NS 5 2 3600 20120814220826 20120715220826 " "1234 example.com. FAKE")); EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_, bad_rrsig), isc::BadValue); // Pass non RRSIG for the sig parameter EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_, a_rrset_), isc::BadValue); // Pass RRSIG for normal RRset (the RdataEncoder will catch this and throw) EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, rrsig_rrset_, rrsig_rrset_), isc::BadValue); // RR class doesn't match between RRset and RRSIG ConstRRsetPtr badclass_rrsig(textToRRset( "www.example.com. 1076895760 CH RRSIG " "A 5 2 3600 20120814220826 " "20120715220826 1234 example.com. FAKE", RRClass::CH())); EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_, badclass_rrsig), isc::BadValue); } }