|
@@ -12,6 +12,10 @@
|
|
|
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
|
// PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
|
+// Note: This file tests both the rdata_encoder and rdata_reader. They are
|
|
|
+// tested together because they form kind the oposite sides of the same
|
|
|
+// functionality.
|
|
|
+
|
|
|
#include <exceptions/exceptions.h>
|
|
|
|
|
|
#include <util/buffer.h>
|
|
@@ -111,9 +115,9 @@ renderDataField(MessageRenderer* renderer, const uint8_t* data,
|
|
|
renderer->writeData(data, data_len);
|
|
|
}
|
|
|
|
|
|
-class RdataEncoderTest : public ::testing::Test {
|
|
|
+class RdataSerializationTest : public ::testing::Test {
|
|
|
protected:
|
|
|
- RdataEncoderTest() : a_rdata_(createRdata(RRType::A(), RRClass::IN(),
|
|
|
+ RdataSerializationTest() : a_rdata_(createRdata(RRType::A(), RRClass::IN(),
|
|
|
"192.0.2.53")),
|
|
|
aaaa_rdata_(createRdata(RRType::AAAA(), RRClass::IN(),
|
|
|
"2001:db8::53")),
|
|
@@ -123,6 +127,29 @@ protected:
|
|
|
"20120715220826 12345 com. FAKE"))
|
|
|
{}
|
|
|
|
|
|
+ // A wraper for RdataEncoder::encode() with buffer overrun check.
|
|
|
+ void encodeWrapper(size_t data_len);
|
|
|
+
|
|
|
+ // Some commonly used RDATA
|
|
|
+ const ConstRdataPtr a_rdata_;
|
|
|
+ const ConstRdataPtr aaaa_rdata_;
|
|
|
+ const ConstRdataPtr rrsig_rdata_;
|
|
|
+
|
|
|
+ RdataEncoder encoder_;
|
|
|
+ vector<uint8_t> encoded_data_;
|
|
|
+ MessageRenderer expected_renderer_;
|
|
|
+ MessageRenderer actual_renderer_;
|
|
|
+ vector<ConstRdataPtr> rdata_list_;
|
|
|
+};
|
|
|
+
|
|
|
+// There are several ways to decode the data. For one, there are
|
|
|
+// more interfaces uses for RdataReader, and we use our own decoder,
|
|
|
+// to check the actual encoded data.
|
|
|
+//
|
|
|
+// These decoding ways are provided by the template parameter.
|
|
|
+template<class DecoderStyle>
|
|
|
+class RdataEncodeDecodeTest : public RdataSerializationTest {
|
|
|
+public:
|
|
|
// This helper test method constructs encodes the given list of RDATAs
|
|
|
// (in rdata_list), and then iterates over the data, rendering the fields
|
|
|
// in the wire format. It then compares the wire data with the one
|
|
@@ -132,27 +159,87 @@ protected:
|
|
|
const vector<ConstRdataPtr>& rdata_list,
|
|
|
size_t expected_varlen_fields,
|
|
|
const vector<ConstRdataPtr>& rrsig_list);
|
|
|
- // A wraper for RdataEncoder::encode() with buffer overrun check.
|
|
|
- void encodeWrapper(size_t data_len);
|
|
|
|
|
|
void addRdataCommon(const vector<ConstRdataPtr>& rrsigs);
|
|
|
void addRdataMultiCommon(const vector<ConstRdataPtr>& rrsigs);
|
|
|
+};
|
|
|
|
|
|
- // Some commonly used RDATA
|
|
|
- const ConstRdataPtr a_rdata_;
|
|
|
- const ConstRdataPtr aaaa_rdata_;
|
|
|
- const ConstRdataPtr rrsig_rdata_;
|
|
|
+// Used across more classes and scopes. But it's just uninteresting
|
|
|
+// constant.
|
|
|
+const Name dummy_name2("example.com");
|
|
|
+
|
|
|
+// A decoder that does not use RdataReader. Not recommended for use,
|
|
|
+// but it allows the tests to check the internals of the data.
|
|
|
+class ManualDecoderStyle {
|
|
|
+public:
|
|
|
+ static void decode(const isc::dns::RRClass& rrclass,
|
|
|
+ const isc::dns::RRType& rrtype,
|
|
|
+ size_t rdata_count,
|
|
|
+ size_t rrsig_count,
|
|
|
+ size_t expected_varlen_fields,
|
|
|
+ // Warning: this test actualy might change the
|
|
|
+ // encoded_data !
|
|
|
+ vector<uint8_t>& encoded_data,
|
|
|
+ MessageRenderer& renderer)
|
|
|
+ {
|
|
|
+ // If this type of RDATA is expected to contain variable-length fields,
|
|
|
+ // we brute force the encoded data, exploiting our knowledge of actual
|
|
|
+ // encoding, then adjust the encoded data excluding the list of length
|
|
|
+ // fields. This is ugly, but for tests only.
|
|
|
+ vector<uint16_t> varlen_list;
|
|
|
+ if (expected_varlen_fields > 0) {
|
|
|
+ const size_t varlen_list_size =
|
|
|
+ rdata_count * expected_varlen_fields * sizeof(uint16_t);
|
|
|
+ ASSERT_LE(varlen_list_size, encoded_data.size());
|
|
|
+ varlen_list.resize(rdata_count * expected_varlen_fields);
|
|
|
+ std::memcpy(&varlen_list[0], &encoded_data[0], varlen_list_size);
|
|
|
+ encoded_data.assign(encoded_data.begin() + varlen_list_size,
|
|
|
+ encoded_data.end());
|
|
|
+ }
|
|
|
+
|
|
|
+ // If RRSIGs are given, we need to extract the list of the RRSIG lengths
|
|
|
+ // and adjust encoded_data_ further.
|
|
|
+ vector<uint16_t> rrsiglen_list;
|
|
|
+ if (rrsig_count > 0) {
|
|
|
+ const size_t rrsig_len_size = rrsig_count * sizeof(uint16_t);
|
|
|
+ ASSERT_LE(rrsig_len_size, encoded_data.size());
|
|
|
+ rrsiglen_list.resize(rrsig_count * rrsig_len_size);
|
|
|
+ std::memcpy(&rrsiglen_list[0], &encoded_data[0], rrsig_len_size);
|
|
|
+ encoded_data.assign(encoded_data.begin() + rrsig_len_size,
|
|
|
+ encoded_data.end());
|
|
|
+ }
|
|
|
+
|
|
|
+ // The set of RR types that require additional section processing.
|
|
|
+ // We'll pass it to renderNameField to check the stored attribute matches
|
|
|
+ // our expectation.
|
|
|
+ std::set<RRType> need_additionals;
|
|
|
+ need_additionals.insert(RRType::NS());
|
|
|
+ need_additionals.insert(RRType::MX());
|
|
|
+ need_additionals.insert(RRType::SRV());
|
|
|
+
|
|
|
+ const bool additional_required =
|
|
|
+ (need_additionals.find(rrtype) != need_additionals.end());
|
|
|
+
|
|
|
+ // Create wire-format data from the encoded data
|
|
|
+ foreachRdataField(rrclass, rrtype, rdata_count, encoded_data,
|
|
|
+ varlen_list,
|
|
|
+ boost::bind(renderNameField, &renderer,
|
|
|
+ additional_required, _1, _2),
|
|
|
+ boost::bind(renderDataField, &renderer, _1, _2));
|
|
|
|
|
|
- RdataEncoder encoder_;
|
|
|
- vector<uint8_t> encoded_data_;
|
|
|
- MessageRenderer expected_renderer_;
|
|
|
- MessageRenderer actual_renderer_;
|
|
|
- vector<ConstRdataPtr> rdata_list_;
|
|
|
+ // 2nd dummy name
|
|
|
+ renderer.writeName(dummy_name2);
|
|
|
+ // Finally, dump any RRSIGs in wire format.
|
|
|
+ foreachRRSig(encoded_data, rrsiglen_list,
|
|
|
+ boost::bind(renderDataField, &renderer, _1, _2));
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
+typedef ::testing::Types<ManualDecoderStyle> DecoderStyles;
|
|
|
+TYPED_TEST_CASE(RdataEncodeDecodeTest, DecoderStyles);
|
|
|
|
|
|
void
|
|
|
-RdataEncoderTest::encodeWrapper(size_t data_len) {
|
|
|
+RdataSerializationTest::encodeWrapper(size_t data_len) {
|
|
|
// make sure the data buffer is large enough for the canary
|
|
|
encoded_data_.resize(data_len + 2);
|
|
|
// set the canary data
|
|
@@ -167,12 +254,13 @@ RdataEncoderTest::encodeWrapper(size_t data_len) {
|
|
|
encoded_data_.resize(data_len);
|
|
|
}
|
|
|
|
|
|
+template<class DecoderStyle>
|
|
|
void
|
|
|
-RdataEncoderTest::checkEncode(RRClass rrclass, RRType rrtype,
|
|
|
- const vector<ConstRdataPtr>& rdata_list,
|
|
|
- size_t expected_varlen_fields,
|
|
|
- const vector<ConstRdataPtr>& rrsig_list =
|
|
|
- vector<ConstRdataPtr>())
|
|
|
+RdataEncodeDecodeTest<DecoderStyle>::
|
|
|
+checkEncode(RRClass rrclass, RRType rrtype,
|
|
|
+ const vector<ConstRdataPtr>& rdata_list,
|
|
|
+ size_t expected_varlen_fields,
|
|
|
+ const vector<ConstRdataPtr>& rrsig_list = vector<ConstRdataPtr>())
|
|
|
{
|
|
|
// These two names will be rendered before and after the test RDATA,
|
|
|
// to check in case the RDATA contain a domain name whether it's
|
|
@@ -181,22 +269,11 @@ RdataEncoderTest::checkEncode(RRClass rrclass, RRType rrtype,
|
|
|
// Likewise, dummy_name2 should be able to be fully compressed due to
|
|
|
// the name in the RDATA.
|
|
|
const Name dummy_name("com");
|
|
|
- const Name dummy_name2("example.com");
|
|
|
-
|
|
|
- // The set of RR types that require additional section processing.
|
|
|
- // We'll pass it to renderNameField to check the stored attribute matches
|
|
|
- // our expectation.
|
|
|
- std::set<RRType> need_additionals;
|
|
|
- need_additionals.insert(RRType::NS());
|
|
|
- need_additionals.insert(RRType::MX());
|
|
|
- need_additionals.insert(RRType::SRV());
|
|
|
+
|
|
|
expected_renderer_.clear();
|
|
|
actual_renderer_.clear();
|
|
|
encoded_data_.clear();
|
|
|
|
|
|
- const bool additional_required =
|
|
|
- (need_additionals.find(rrtype) != need_additionals.end());
|
|
|
-
|
|
|
// Build expected wire-format data
|
|
|
expected_renderer_.writeName(dummy_name);
|
|
|
BOOST_FOREACH(const ConstRdataPtr& rdata, rdata_list) {
|
|
@@ -221,54 +298,19 @@ RdataEncoderTest::checkEncode(RRClass rrclass, RRType rrtype,
|
|
|
}
|
|
|
encodeWrapper(encoder_.getStorageLength());
|
|
|
|
|
|
- // If this type of RDATA is expected to contain variable-length fields,
|
|
|
- // we brute force the encoded data, exploiting our knowledge of actual
|
|
|
- // encoding, then adjust the encoded data excluding the list of length
|
|
|
- // fields. This is ugly, but we should be able to eliminate this hack
|
|
|
- // at #2096.
|
|
|
- vector<uint16_t> varlen_list;
|
|
|
- if (expected_varlen_fields > 0) {
|
|
|
- const size_t varlen_list_size =
|
|
|
- rdata_list.size() * expected_varlen_fields * sizeof(uint16_t);
|
|
|
- ASSERT_LE(varlen_list_size, encoded_data_.size());
|
|
|
- varlen_list.resize(rdata_list.size() * expected_varlen_fields);
|
|
|
- std::memcpy(&varlen_list[0], &encoded_data_[0], varlen_list_size);
|
|
|
- encoded_data_.assign(encoded_data_.begin() + varlen_list_size,
|
|
|
- encoded_data_.end());
|
|
|
- }
|
|
|
-
|
|
|
- // If RRSIGs are given, we need to extract the list of the RRSIG lengths
|
|
|
- // and adjust encoded_data_ further (this will be unnecessary at #2096,
|
|
|
- // too).
|
|
|
- vector<uint16_t> rrsiglen_list;
|
|
|
- if (rrsig_list.size() > 0) {
|
|
|
- const size_t rrsig_len_size = rrsig_list.size() * sizeof(uint16_t);
|
|
|
- ASSERT_LE(rrsig_len_size, encoded_data_.size());
|
|
|
- rrsiglen_list.resize(rrsig_list.size() * rrsig_len_size);
|
|
|
- std::memcpy(&rrsiglen_list[0], &encoded_data_[0], rrsig_len_size);
|
|
|
- encoded_data_.assign(encoded_data_.begin() + rrsig_len_size,
|
|
|
- encoded_data_.end());
|
|
|
- }
|
|
|
-
|
|
|
- // Create wire-format data from the encoded data
|
|
|
- foreachRdataField(rrclass, rrtype, rdata_list.size(), encoded_data_,
|
|
|
- varlen_list,
|
|
|
- boost::bind(renderNameField, &actual_renderer_,
|
|
|
- additional_required, _1, _2),
|
|
|
- boost::bind(renderDataField, &actual_renderer_, _1, _2));
|
|
|
- // 2nd dummy name
|
|
|
- actual_renderer_.writeName(dummy_name2);
|
|
|
- // Finally, dump any RRSIGs in wire format.
|
|
|
- foreachRRSig(encoded_data_, rrsiglen_list,
|
|
|
- boost::bind(renderDataField, &actual_renderer_, _1, _2));
|
|
|
+ DecoderStyle::decode(rrclass, rrtype, rdata_list.size(), rrsig_list.size(),
|
|
|
+ expected_varlen_fields, encoded_data_,
|
|
|
+ actual_renderer_);
|
|
|
|
|
|
// Two sets of wire-format data should be identical.
|
|
|
matchWireData(expected_renderer_.getData(), expected_renderer_.getLength(),
|
|
|
actual_renderer_.getData(), actual_renderer_.getLength());
|
|
|
}
|
|
|
|
|
|
+template<class DecoderStyle>
|
|
|
void
|
|
|
-RdataEncoderTest::addRdataCommon(const vector<ConstRdataPtr>& rrsigs) {
|
|
|
+RdataEncodeDecodeTest<DecoderStyle>::
|
|
|
+addRdataCommon(const vector<ConstRdataPtr>& rrsigs) {
|
|
|
// Basic check on the encoded data for (most of) all supported RR types,
|
|
|
// in a comprehensive manner.
|
|
|
for (size_t i = 0; test_rdata_list[i].rrclass != NULL; ++i) {
|
|
@@ -285,18 +327,20 @@ RdataEncoderTest::addRdataCommon(const vector<ConstRdataPtr>& rrsigs) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-TEST_F(RdataEncoderTest, addRdata) {
|
|
|
+TYPED_TEST(RdataEncodeDecodeTest, addRdata) {
|
|
|
vector<ConstRdataPtr> rrsigs;
|
|
|
- addRdataCommon(rrsigs); // basic tests without RRSIGs (empty vector)
|
|
|
+ this->addRdataCommon(rrsigs); // basic tests without RRSIGs (empty vector)
|
|
|
|
|
|
// Test with RRSIGs (covered type doesn't always match, but the encoder
|
|
|
// doesn't check that)
|
|
|
- rrsigs.push_back(rrsig_rdata_);
|
|
|
- addRdataCommon(rrsigs);
|
|
|
+ rrsigs.push_back(this->rrsig_rdata_);
|
|
|
+ this->addRdataCommon(rrsigs);
|
|
|
}
|
|
|
|
|
|
+template<class DecoderStyle>
|
|
|
void
|
|
|
-RdataEncoderTest::addRdataMultiCommon(const vector<ConstRdataPtr>& rrsigs) {
|
|
|
+RdataEncodeDecodeTest<DecoderStyle>::
|
|
|
+addRdataMultiCommon(const vector<ConstRdataPtr>& rrsigs) {
|
|
|
// Similar to addRdata(), but test with multiple RDATAs.
|
|
|
// Four different cases are tested: a single fixed-len RDATA (A),
|
|
|
// fixed-len data + domain name (MX), variable-len data only (TXT),
|
|
@@ -338,7 +382,7 @@ RdataEncoderTest::addRdataMultiCommon(const vector<ConstRdataPtr>& rrsigs) {
|
|
|
checkEncode(RRClass::IN(), RRType::NAPTR(), rdata_list_, 1, rrsigs);
|
|
|
}
|
|
|
|
|
|
-TEST_F(RdataEncoderTest, encodeLargeRdata) {
|
|
|
+TEST_F(RdataSerializationTest, encodeLargeRdata) {
|
|
|
// There should be no reason for a large RDATA to fail in encoding,
|
|
|
// but we check such a case explicitly.
|
|
|
|
|
@@ -357,19 +401,19 @@ TEST_F(RdataEncoderTest, encodeLargeRdata) {
|
|
|
EXPECT_EQ(0, encoded_dhcid.compare(large_dhcid));
|
|
|
}
|
|
|
|
|
|
-TEST_F(RdataEncoderTest, addRdataMulti) {
|
|
|
+TYPED_TEST(RdataEncodeDecodeTest, addRdataMulti) {
|
|
|
vector<ConstRdataPtr> rrsigs;
|
|
|
- addRdataMultiCommon(rrsigs); // test without RRSIGs (empty vector)
|
|
|
+ this->addRdataMultiCommon(rrsigs); // test without RRSIGs (empty vector)
|
|
|
|
|
|
// Tests with two RRSIGs
|
|
|
- rrsigs.push_back(rrsig_rdata_);
|
|
|
+ rrsigs.push_back(this->rrsig_rdata_);
|
|
|
rrsigs.push_back(createRdata(RRType::RRSIG(), RRClass::IN(),
|
|
|
"A 5 2 3600 20120814220826 "
|
|
|
"20120715220826 54321 com. FAKE"));
|
|
|
- addRdataMultiCommon(rrsigs);
|
|
|
+ this->addRdataMultiCommon(rrsigs);
|
|
|
}
|
|
|
|
|
|
-TEST_F(RdataEncoderTest, badAddRdata) {
|
|
|
+TEST_F(RdataSerializationTest, badAddRdata) {
|
|
|
// Some operations must follow start().
|
|
|
EXPECT_THROW(encoder_.addRdata(*a_rdata_), isc::InvalidOperation);
|
|
|
EXPECT_THROW(encoder_.getStorageLength(), isc::InvalidOperation);
|
|
@@ -447,7 +491,7 @@ TEST_F(RdataEncoderTest, badAddRdata) {
|
|
|
isc::BadValue);
|
|
|
}
|
|
|
|
|
|
-TEST_F(RdataEncoderTest, addSIGRdataOnly) {
|
|
|
+TEST_F(RdataSerializationTest, addSIGRdataOnly) {
|
|
|
// Encoded data that only contain RRSIGs. Mostly useless, but can happen
|
|
|
// (in a partially broken zone) and it's accepted.
|
|
|
encoder_.start(RRClass::IN(), RRType::A());
|
|
@@ -461,7 +505,7 @@ TEST_F(RdataEncoderTest, addSIGRdataOnly) {
|
|
|
EXPECT_EQ(0, encoded_sig.compare(*rrsig_rdata_));
|
|
|
}
|
|
|
|
|
|
-TEST_F(RdataEncoderTest, badAddSIGRdata) {
|
|
|
+TEST_F(RdataSerializationTest, badAddSIGRdata) {
|
|
|
// try adding SIG before start
|
|
|
EXPECT_THROW(encoder_.addSIGRdata(*rrsig_rdata_), isc::InvalidOperation);
|
|
|
|