123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321 |
- // 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 <util/buffer.h>
- #include <util/memory_segment_local.h>
- #include <datasrc/memory/treenode_rrset.h>
- #include <datasrc/memory/rdataset.h>
- #include <datasrc/memory/rdata_serialization.h>
- #include <datasrc/memory/zone_data.h>
- #include <util/unittests/wiredata.h>
- #include <testutils/dnsmessage_test.h>
- #include <gtest/gtest.h>
- #include <string>
- #include <vector>
- using std::vector;
- using std::string;
- using namespace isc::dns;
- using namespace isc::dns::rdata;
- using namespace isc::datasrc::memory;
- using namespace isc::testutils;
- using isc::util::unittests::matchWireData;
- using isc::util::OutputBuffer;
- namespace {
- class TreeNodeRRsetTest : public ::testing::Test {
- protected:
- TreeNodeRRsetTest() :
- rrclass_(RRClass::IN()),
- origin_name_("example.com"), www_name_("www.example.com"),
- ns_rrset_(textToRRset("example.com. 3600 IN NS ns.example.com.")),
- a_rrset_(textToRRset("www.example.com. 3600 IN A 192.0.2.1\n"
- "www.example.com. 3600 IN A 192.0.2.2")),
- aaaa_rrset_(textToRRset("www.example.com. 3600 IN AAAA "
- "2001:db8::1\n")),
- dname_rrset_(textToRRset("example.com. 3600 IN DNAME d.example.org.")),
- a_rrsig_rrset_(textToRRset("www.example.com. 3600 IN RRSIG "
- "A 5 2 3600 20120814220826 20120715220826 "
- "1234 example.com. FAKE")),
- aaaa_rrsig_rrset_(textToRRset("www.example.com. 3600 IN RRSIG AAAA 5 2"
- " 3600 20120814220826 20120715220826 "
- "1234 example.com. FAKE\n"
- "www.example.com. 3600 IN RRSIG AAAA 5 2"
- " 3600 20120814220826 20120715220826 "
- "4321 example.com. FAKE\n")),
- zone_data_(NULL)
- {}
- void SetUp() {
- // We create some common test data here in SetUp() so it will be
- // as exception safe as possible.
- zone_data_ = ZoneData::create(mem_sgmt_, origin_name_);
- zone_data_->insertName(mem_sgmt_, origin_name_, &origin_node_);
- ns_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, ns_rrset_,
- ConstRRsetPtr());
- origin_node_->setData(ns_rdataset_);
- dname_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, dname_rrset_,
- ConstRRsetPtr());
- ns_rdataset_->next = dname_rdataset_;
- zone_data_->insertName(mem_sgmt_, www_name_, &www_node_);
- a_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
- a_rrsig_rrset_);
- www_node_->setData(a_rdataset_);
- aaaa_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, aaaa_rrset_,
- aaaa_rrsig_rrset_);
- a_rdataset_->next = aaaa_rdataset_;
- }
- void TearDown() {
- ZoneData::destroy(mem_sgmt_, zone_data_, rrclass_);
- // detect any memory leak
- EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated());
- }
- const RRClass rrclass_;
- const Name origin_name_, www_name_;
- isc::util::MemorySegmentLocal mem_sgmt_;
- RdataEncoder encoder_;
- MessageRenderer renderer_, renderer_expected_;
- ConstRRsetPtr ns_rrset_, a_rrset_, aaaa_rrset_, dname_rrset_,
- a_rrsig_rrset_, aaaa_rrsig_rrset_;
- ZoneData* zone_data_;
- ZoneNode* origin_node_;
- ZoneNode* www_node_;
- RdataSet* ns_rdataset_;
- RdataSet* dname_rdataset_;
- RdataSet* a_rdataset_;
- RdataSet* aaaa_rdataset_;
- };
- // Check some trivial fields of a constructed TreeNodeRRset (passed as
- // AbstractRRset as we'd normally use it in polymorphic way).
- // Other complicated fields are checked through rendering tests.
- void
- checkBasicFields(const AbstractRRset& actual_rrset, const Name& expected_name,
- const RRClass& expected_class, const RRType& expected_type,
- size_t expected_rdatacount, size_t expected_sigcount)
- {
- EXPECT_EQ(expected_name, actual_rrset.getName());
- EXPECT_EQ(expected_class, actual_rrset.getClass());
- EXPECT_EQ(expected_type, actual_rrset.getType());
- EXPECT_EQ(expected_rdatacount, actual_rrset.getRdataCount());
- EXPECT_EQ(expected_sigcount, actual_rrset.getRRsigDataCount());
- }
- TEST_F(TreeNodeRRsetTest, create) {
- // Constructed with RRSIG, and it should be visible.
- checkBasicFields(TreeNodeRRset(rrclass_, www_node_, a_rdataset_, true),
- www_name_, rrclass_, RRType::A(), 2, 1);
- // Constructed with RRSIG, and it should be invisible.
- checkBasicFields(TreeNodeRRset(rrclass_, www_node_, a_rdataset_, false),
- www_name_, rrclass_, RRType::A(), 2, 0);
- // Constructed without RRSIG, and it would be visible (but of course won't)
- checkBasicFields(TreeNodeRRset(rrclass_, origin_node_, ns_rdataset_, true),
- origin_name_, rrclass_, RRType::NS(), 1, 0);
- // Constructed without RRSIG, and it should be visible
- checkBasicFields(TreeNodeRRset(rrclass_, origin_node_, ns_rdataset_,
- false),
- origin_name_, rrclass_, RRType::NS(), 1, 0);
- }
- // Templated if and when we support OutputBuffer version of toWire().
- // Right now, we take a minimalist approach, only implementing testing the
- // renderer version.
- template <typename OutputType>
- void
- checkToWireResult(OutputType& expected_output, OutputType& actual_output,
- const AbstractRRset& actual_rrset,
- const Name& prepended_name,
- ConstRRsetPtr rrset, ConstRRsetPtr rrsig_rrset,
- bool dnssec_ok)
- {
- expected_output.clear();
- actual_output.clear();
- // Prepare "actual" rendered data. We prepend a name to confirm the
- // owner name should be compressed in both cases.
- prepended_name.toWire(actual_output);
- const int expected_ret = (dnssec_ok && rrsig_rrset) ?
- rrset->getRdataCount() + rrsig_rrset->getRdataCount() :
- rrset->getRdataCount();
- EXPECT_EQ(expected_ret, actual_rrset.toWire(actual_output));
- // Prepare "expected" data.
- prepended_name.toWire(expected_output);
- rrset->toWire(expected_output);
- if (dnssec_ok && rrsig_rrset) {
- rrsig_rrset->toWire(expected_output);
- }
- // Compare the two.
- matchWireData(expected_output.getData(), expected_output.getLength(),
- actual_output.getData(), actual_output.getLength());
- }
- TEST_F(TreeNodeRRsetTest, toWire) {
- MessageRenderer expected_renderer, actual_renderer;
- {
- SCOPED_TRACE("with RRSIG, DNSSEC OK");
- const TreeNodeRRset rrset1(rrclass_, www_node_, a_rdataset_, true);
- checkToWireResult(expected_renderer, actual_renderer, rrset1,
- www_name_, a_rrset_, a_rrsig_rrset_, true);
- }
- {
- SCOPED_TRACE("with RRSIG, DNSSEC not OK");
- const TreeNodeRRset rrset2(rrclass_, www_node_, a_rdataset_, false);
- checkToWireResult(expected_renderer, actual_renderer, rrset2,
- www_name_, a_rrset_, a_rrsig_rrset_, false);
- }
- {
- SCOPED_TRACE("without RRSIG, DNSSEC OK");
- const TreeNodeRRset rrset3(rrclass_, origin_node_, ns_rdataset_, true);
- checkToWireResult(expected_renderer, actual_renderer, rrset3,
- origin_name_, ns_rrset_, ConstRRsetPtr(), true);
- }
- {
- SCOPED_TRACE("without RRSIG, DNSSEC not OK");
- const TreeNodeRRset rrset4(rrclass_, origin_node_, ns_rdataset_,
- false);
- checkToWireResult(expected_renderer, actual_renderer, rrset4,
- origin_name_, ns_rrset_, ConstRRsetPtr(), false);
- }
- {
- // RDATA of DNAME DR shouldn't be compressed. Prepending "example.org"
- // will check that.
- SCOPED_TRACE("uncompressed RDATA");
- const TreeNodeRRset rrset5(rrclass_, origin_node_, dname_rdataset_,
- false);
- checkToWireResult(expected_renderer, actual_renderer, rrset5,
- Name("example.org"), dname_rrset_, ConstRRsetPtr(),
- false);
- }
- }
- void
- checkTruncationResult(MessageRenderer& expected_renderer,
- MessageRenderer& actual_renderer,
- const AbstractRRset& actual_rrset,
- ConstRRsetPtr rrset, ConstRRsetPtr rrsig_rrset,
- bool dnssec_ok, size_t len_limit, size_t expected_result)
- {
- expected_renderer.clear();
- actual_renderer.clear();
- actual_renderer.setLengthLimit(len_limit);
- EXPECT_EQ(expected_result, actual_rrset.toWire(actual_renderer));
- EXPECT_TRUE(actual_renderer.isTruncated()); // always true in this test
- expected_renderer.setLengthLimit(len_limit);
- rrset->toWire(expected_renderer);
- if (!expected_renderer.isTruncated() && dnssec_ok && rrsig_rrset) {
- rrsig_rrset->toWire(expected_renderer);
- }
- matchWireData(expected_renderer.getData(), expected_renderer.getLength(),
- actual_renderer.getData(), actual_renderer.getLength());
- }
- TEST_F(TreeNodeRRsetTest, toWireTruncated) {
- MessageRenderer expected_renderer, actual_renderer;
- // Set the truncation limit to name len + 14 bytes of fixed data for A RR
- // (type, class, TTL, rdlen, and 4-byte IPv4 address). Then we can only
- // render just one RR, without any garbage trailing data.
- checkTruncationResult(expected_renderer, actual_renderer,
- TreeNodeRRset(rrclass_, www_node_, a_rdataset_,
- true),
- a_rrset_, a_rrsig_rrset_, true,
- www_name_.getLength() + 14,
- 1); // 1 main RR, no RRSIG
- // The first main RRs should fit in the renderer (the name will be
- // fully compressed, so its size is 2 bytes), but the RRSIG doesn't.
- checkTruncationResult(expected_renderer, actual_renderer,
- TreeNodeRRset(rrclass_, www_node_, a_rdataset_,
- true),
- a_rrset_, a_rrsig_rrset_, true,
- www_name_.getLength() + 14 + 2 + 14,
- 2); // 2 main RR, no RRSIG
- // This RRset has one main RR and two RRSIGs. Rendering the second RRSIG
- // causes truncation.
- // First, compute the rendered length for the main RR and a single RRSIG.
- // The length of the RRSIG should be the same if we "accidentally"
- // rendered the RRSIG for the A RR (which only contains one RRSIG).
- expected_renderer.clear();
- aaaa_rrset_->toWire(expected_renderer);
- a_rrsig_rrset_->toWire(expected_renderer);
- const size_t limit_len = expected_renderer.getLength();
- // Then perform the test
- checkTruncationResult(expected_renderer, actual_renderer,
- TreeNodeRRset(rrclass_, www_node_, aaaa_rdataset_,
- true),
- aaaa_rrset_, aaaa_rrsig_rrset_, true, limit_len,
- 2); // 1 main RR, 1 RRSIG
- }
- void
- checkRdataIterator(const vector<string>& expected, RdataIteratorPtr rit) {
-
- for (vector<string>::const_iterator it = expected.begin();
- it != expected.end();
- ++it)
- {
- ASSERT_FALSE(rit->isLast());
- EXPECT_EQ(*it, rit->getCurrent().toText());
- rit->next();
- }
- // We should have reached the end of RDATA
- EXPECT_TRUE(rit->isLast());
- // move to the first RDATA again, and check the value.
- rit->first();
- EXPECT_EQ(expected[0], rit->getCurrent().toText());
- }
- TEST_F(TreeNodeRRsetTest, getRdataIterator) {
- // This RRset should have 2 A RDATAs
- vector<string> expected;
- expected.push_back("192.0.2.1");
- expected.push_back("192.0.2.2");
- checkRdataIterator(expected,
- TreeNodeRRset(rrclass_, www_node_, a_rdataset_, true).
- getRdataIterator());
- // The iterator shouldn't work different with or without RRSIG
- checkRdataIterator(expected,
- TreeNodeRRset(rrclass_, www_node_, a_rdataset_, false).
- getRdataIterator());
- // This RRset should have 1 NS RDATA (containing name field)
- expected.clear();
- expected.push_back("ns.example.com.");
- checkRdataIterator(expected,
- TreeNodeRRset(rrclass_, origin_node_, ns_rdataset_,
- false).getRdataIterator());
- }
- }
|