diff options
author | Michal 'vorner' Vaner <michal.vaner@nic.cz> | 2011-04-19 23:29:25 +0200 |
---|---|---|
committer | Michal 'vorner' Vaner <michal.vaner@nic.cz> | 2011-04-19 23:29:25 +0200 |
commit | 9ce716d340a89f0212302af25ae75ba68bf69b75 (patch) | |
tree | 8692b310527342d8390bc38b5248e03f18fbab16 /src/lib/dns/tests/rdatafields_unittest.cc | |
parent | [master] update ChangeLog for #749 (diff) | |
parent | [trac404] Code style (diff) | |
download | kea-9ce716d340a89f0212302af25ae75ba68bf69b75.tar.xz kea-9ce716d340a89f0212302af25ae75ba68bf69b75.zip |
Merge branch 'work/rdatafields'
Conflicts:
src/lib/dns/gen-rdatacode.py.in
src/lib/dns/messagerenderer.cc
src/lib/dns/messagerenderer.h
src/lib/dns/name.h
src/lib/dns/rdata.h
src/lib/dns/rrtype-placeholder.h
Diffstat (limited to 'src/lib/dns/tests/rdatafields_unittest.cc')
-rw-r--r-- | src/lib/dns/tests/rdatafields_unittest.cc | 380 |
1 files changed, 380 insertions, 0 deletions
diff --git a/src/lib/dns/tests/rdatafields_unittest.cc b/src/lib/dns/tests/rdatafields_unittest.cc new file mode 100644 index 0000000000..d6192209ff --- /dev/null +++ b/src/lib/dns/tests/rdatafields_unittest.cc @@ -0,0 +1,380 @@ +// 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 <vector> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdatafields.h> +#include <dns/tests/unittest_util.h> + +#include <gtest/gtest.h> + +using isc::UnitTestUtil; +using namespace std; +using namespace isc::dns; +using namespace isc::dns::rdata; +using isc::util::OutputBuffer; +using isc::util::InputBuffer; + +namespace { +class RdataFieldsTest : public ::testing::Test { +protected: + RdataFieldsTest() : obuffer(0), renderer_buffer(0), + renderer(renderer_buffer), + ns_name("example.com"), + other_name("www.example.com") + {} + void constructCommonTests(const RdataFields& fields, + const uint8_t* const expected_data, + const size_t expected_data_len); + void constructCommonTestsNS(const RdataFields& fields); + void constructCommonTestsTXT(const RdataFields& fields); + void constructCommonTestsRRSIG(const RdataFields& fields); + void constructCommonTestsOPT(const RdataFields& fields); + OutputBuffer obuffer; + OutputBuffer renderer_buffer; + MessageRenderer renderer; + const Name ns_name; + const Name other_name; + vector<unsigned char> expected_wire; + vector<unsigned char> fields_wire; +}; + +const uint8_t in_a_data[] = { 192, 0, 2, 1 }; +// binary representation of example.com. +const uint8_t ns_data[] = { 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, + 0x03, 0x63, 0x6f, 0x6d, 0x00 }; + +// +// IN/A RDATA: fixed length, single data field +// +void +RdataFieldsTest::constructCommonTests(const RdataFields& fields, + const uint8_t* const expected_data, + const size_t expected_data_len) +{ + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, expected_data, + expected_data_len, fields.getData(), + fields.getDataLength()); + EXPECT_EQ(sizeof(RdataFields::FieldSpec), fields.getFieldSpecDataSize()); + EXPECT_EQ(1, fields.getFieldCount()); + EXPECT_EQ(RdataFields::DATA, fields.getFieldSpec(0).type); + EXPECT_EQ(4, fields.getFieldSpec(0).len); + + fields.toWire(obuffer); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, expected_data, + expected_data_len, obuffer.getData(), + obuffer.getLength()); + + fields.toWire(renderer); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, expected_data, + expected_data_len, renderer.getData(), + renderer.getLength()); +} + +TEST_F(RdataFieldsTest, constructFromRdata) { + const RdataFields fields(in::A("192.0.2.1")); + constructCommonTests(fields, in_a_data, sizeof(in_a_data)); +} + +TEST_F(RdataFieldsTest, constructFromParams) { + const RdataFields::FieldSpec spec(RdataFields::DATA, 4); + const RdataFields fields(&spec, sizeof(spec), in_a_data, + sizeof(in_a_data)); + constructCommonTests(fields, in_a_data, sizeof(in_a_data)); +} + +// +// NS RDATA: containing a compressible name. +// +void +RdataFieldsTest::constructCommonTestsNS(const RdataFields& fields) { + EXPECT_EQ(sizeof(RdataFields::FieldSpec), fields.getFieldSpecDataSize()); + EXPECT_EQ(1, fields.getFieldCount()); + EXPECT_EQ(RdataFields::COMPRESSIBLE_NAME, fields.getFieldSpec(0).type); + EXPECT_EQ(ns_name.getLength(), fields.getFieldSpec(0).len); + + expected_wire.clear(); + UnitTestUtil::readWireData("rdatafields1.wire", expected_wire); + other_name.toWire(obuffer); + fields.toWire(obuffer); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &expected_wire[0], + expected_wire.size(), obuffer.getData(), + obuffer.getLength()); + + expected_wire.clear(); + UnitTestUtil::readWireData("rdatafields2.wire", expected_wire); + other_name.toWire(renderer); + fields.toWire(renderer); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &expected_wire[0], + expected_wire.size(), renderer.getData(), + renderer.getLength()); +} + +TEST_F(RdataFieldsTest, constructFromRdataNS) { + const RdataFields fields_ns((generic::NS(ns_name))); + constructCommonTestsNS(fields_ns); +} + +TEST_F(RdataFieldsTest, constructFromParamsNS) { + const RdataFields::FieldSpec spec(RdataFields::COMPRESSIBLE_NAME, + sizeof(ns_data)); + const RdataFields fields_ns(&spec, sizeof(spec), ns_data, sizeof(ns_data)); + constructCommonTestsNS(fields_ns); +} + +// +// TXT RDATA: multiple fields, lengths vary +// +void +RdataFieldsTest::constructCommonTestsTXT(const RdataFields& fields) { + // Since all fields are plain data, they are handled as a single data + // field. + EXPECT_EQ(sizeof(RdataFields::FieldSpec), fields.getFieldSpecDataSize()); + EXPECT_EQ(1, fields.getFieldCount()); + EXPECT_EQ(RdataFields::DATA, fields.getFieldSpec(0).type); + EXPECT_EQ(expected_wire.size(), fields.getFieldSpec(0).len); + + fields.toWire(obuffer); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &expected_wire[0], + expected_wire.size(), obuffer.getData(), + obuffer.getLength()); + + fields.toWire(renderer); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &expected_wire[0], + expected_wire.size(), renderer.getData(), + renderer.getLength()); +} + +TEST_F(RdataFieldsTest, constructFromRdataTXT) { + UnitTestUtil::readWireData("rdatafields3.wire", expected_wire); + InputBuffer ibuffer(&expected_wire[0], expected_wire.size()); + const uint16_t rdlen = ibuffer.readUint16(); + const RdataFields fields(generic::TXT(ibuffer, rdlen)); + + // drop the RDLEN part + expected_wire.erase(expected_wire.begin(), expected_wire.begin() + 2); + + constructCommonTestsTXT(fields); +} + +TEST_F(RdataFieldsTest, constructFromParamsTXT) { + UnitTestUtil::readWireData("rdatafields3.wire", expected_wire); + expected_wire.erase(expected_wire.begin(), expected_wire.begin() + 2); + const RdataFields::FieldSpec spec(RdataFields::DATA, expected_wire.size()); + const RdataFields fields(&spec, sizeof(spec), &expected_wire[0], + expected_wire.size()); + constructCommonTestsTXT(fields); +} + +// +// RRSIG: multiple fields, with an incompressible name +// +void +RdataFieldsTest::constructCommonTestsRRSIG(const RdataFields& fields) { + // In terms of RdataFields RRSIG RDATA consists of 3 fields: + // - 18-byte data field (from the "type covered" field to "key tag" field) + // - an incompressible name field (for the signer's name field). + // this is a variable length field. In this test it's a 13-byte field. + // - a variable-length data field for the signature. In this tests + // it's a 15-byte field. + EXPECT_EQ(3 * sizeof(RdataFields::FieldSpec), + fields.getFieldSpecDataSize()); + EXPECT_EQ(3, fields.getFieldCount()); + EXPECT_EQ(RdataFields::DATA, fields.getFieldSpec(0).type); + EXPECT_EQ(18, fields.getFieldSpec(0).len); + EXPECT_EQ(RdataFields::INCOMPRESSIBLE_NAME, fields.getFieldSpec(1).type); + EXPECT_EQ(13, fields.getFieldSpec(1).len); + EXPECT_EQ(RdataFields::DATA, fields.getFieldSpec(2).type); + EXPECT_EQ(15, fields.getFieldSpec(2).len); + + expected_wire.clear(); + UnitTestUtil::readWireData("rdatafields5.wire", expected_wire); + Name("com").toWire(obuffer); + obuffer.writeUint16(fields.getDataLength()); + fields.toWire(obuffer); + other_name.toWire(obuffer); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &expected_wire[0], + expected_wire.size(), obuffer.getData(), + obuffer.getLength()); + + expected_wire.clear(); + UnitTestUtil::readWireData("rdatafields6.wire", expected_wire); + Name("com").toWire(renderer); + renderer.writeUint16(fields.getDataLength()); + fields.toWire(renderer); // the signer field won't be compressed + other_name.toWire(renderer); // but will be used as a compression target + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &expected_wire[0], + expected_wire.size(), renderer.getData(), + renderer.getLength()); +} + +TEST_F(RdataFieldsTest, constructFromRdataRRSIG) { + UnitTestUtil::readWireData("rdatafields4.wire", expected_wire); + InputBuffer ibuffer(&expected_wire[0], expected_wire.size()); + const uint16_t rdlen = ibuffer.readUint16(); + const RdataFields fields(generic::RRSIG(ibuffer, rdlen)); + + // drop the RDLEN part + expected_wire.erase(expected_wire.begin(), expected_wire.begin() + 2); + + constructCommonTestsRRSIG(fields); +} + +TEST_F(RdataFieldsTest, constructFromParamsRRSIG) { + UnitTestUtil::readWireData("rdatafields4.wire", fields_wire); + fields_wire.erase(fields_wire.begin(), fields_wire.begin() + 2); + + const RdataFields::FieldSpec specs[] = { + RdataFields::FieldSpec(RdataFields::DATA, 18), + RdataFields::FieldSpec(RdataFields::INCOMPRESSIBLE_NAME, 13), + RdataFields::FieldSpec(RdataFields::DATA, 15) + }; + const RdataFields fields(specs, sizeof(specs), &fields_wire[0], + fields_wire.size()); + constructCommonTestsRRSIG(fields); +} + +TEST_F(RdataFieldsTest, convertRdatatoParams) { + // Confirm we can restore the original data from the serialized data. + // We use RRSIG as a relatively complicated field structure. + UnitTestUtil::readWireData("rdatafields4.wire", expected_wire); + InputBuffer ibuffer(&expected_wire[0], expected_wire.size()); + const uint16_t rdlen = ibuffer.readUint16(); + const RdataFields fields(generic::RRSIG(ibuffer, rdlen)); + + expected_wire.erase(expected_wire.begin(), expected_wire.begin() + 2); + + // Copy the data in separate storage + vector<uint8_t> spec_store(fields.getFieldSpecDataSize()); + void* cp_spec = &spec_store[0]; + memcpy(cp_spec, fields.getFieldSpecData(), spec_store.size()); + vector<uint8_t> data_store(fields.getDataLength()); + memcpy(&data_store[0], fields.getData(), fields.getDataLength()); + + // Restore the data in the form of RdataFields + const RdataFields fields_byparams(cp_spec, fields.getFieldSpecDataSize(), + &data_store[0], fields.getDataLength()); + + // Check it's valid + constructCommonTestsRRSIG(fields_byparams); +} + +// +// OPT: an empty RDATA +// +void +RdataFieldsTest::constructCommonTestsOPT(const RdataFields& fields) { + EXPECT_EQ(0, fields.getFieldSpecDataSize()); + EXPECT_EQ(0, fields.getFieldCount()); + EXPECT_EQ(0, fields.getDataLength()); + EXPECT_EQ((const uint8_t*) NULL, fields.getData()); + fields.toWire(obuffer); + EXPECT_EQ(0, obuffer.getLength()); + fields.toWire(renderer); + EXPECT_EQ(0, renderer.getLength()); +} + +TEST_F(RdataFieldsTest, constructFromRdataOPT) { + InputBuffer ibuffer(NULL, 0); + const RdataFields fields(generic::OPT(ibuffer, 0)); + constructCommonTestsOPT(fields); +} + +TEST_F(RdataFieldsTest, constructFromParamsOPT) { + const RdataFields fields(NULL, 0, NULL, 0); + constructCommonTestsOPT(fields); +} + +// Invalid input to the "from parameter" constructor: sum of the field lengths +// is not equal to the data length. +TEST_F(RdataFieldsTest, invalidFieldLength) { + UnitTestUtil::readWireData("rdatafields4.wire", fields_wire); + fields_wire.erase(fields_wire.begin(), fields_wire.begin() + 2); + + const RdataFields::FieldSpec specs[] = { + RdataFields::FieldSpec(RdataFields::DATA, 18), + RdataFields::FieldSpec(RdataFields::INCOMPRESSIBLE_NAME, 13), + RdataFields::FieldSpec(RdataFields::DATA, 14) + }; + // sum of field len < data len + EXPECT_THROW(RdataFields(specs, 3, &fields_wire[0], fields_wire.size()), + isc::InvalidParameter); + // sum of field len > data len + EXPECT_THROW(RdataFields(specs, 3, &fields_wire[0], + fields_wire.size() - 2), + isc::InvalidParameter); +} + +// Invalid input to the "from parameter" constructor: NULL vs length mismatch +TEST_F(RdataFieldsTest, mismatchFieldLengthAndData) { + const unsigned char dummy_data = 0; + const RdataFields::FieldSpec dummy_spec(RdataFields::DATA, 1); + + EXPECT_THROW(RdataFields(NULL, 1, &dummy_data, 1), isc::InvalidParameter); + EXPECT_THROW(RdataFields(&dummy_spec, 0, NULL, 0), isc::InvalidParameter); + EXPECT_THROW(RdataFields(&dummy_spec, 1, NULL, 1), isc::InvalidParameter); + EXPECT_THROW(RdataFields(NULL, 0, &dummy_data, 0), isc::InvalidParameter); +} + +// Bogus input to getFieldSpec() +TEST_F(RdataFieldsTest, getFieldSpecWithBadFieldId) { + const RdataFields fields_in_a(in::A("192.0.2.1")); + EXPECT_THROW(fields_in_a.getFieldSpec(1), isc::OutOfRange); +} + +// Tests for unexpected methods in RdataFieldComposerTest. Confirm +// a call to these methods triggers an exception. Expected methods are +// tested via other tests above. +class DummyRdata : public Rdata { +public: + enum Mode { CLEAR, SKIP, TRIM }; + explicit DummyRdata(Mode mode) : mode_(mode) {} + DummyRdata(const DummyRdata& source) : Rdata(), mode_(source.mode_) {} + virtual ~DummyRdata() {} + virtual void toWire(AbstractMessageRenderer& renderer) const { + // call the unexpected method corresponding to the test mode. + // method parameters don't matter. + switch (mode_) { + case CLEAR: + renderer.clear(); + break; + case SKIP: + renderer.skip(2); + break; + case TRIM: + renderer.trim(2); + break; + } + } + + // These are defined only to make the compiler happy. We don't use them + // for the test. + virtual string toText() const { return (""); } + virtual void toWire(OutputBuffer&) const {} + virtual int compare(const Rdata&) const { return (0); } +private: + const int mode_; +}; + +TEST(RdataFieldComposerTest, unusedMethods) { + EXPECT_THROW(RdataFields(DummyRdata(DummyRdata::CLEAR)), isc::Unexpected); +} +} |