Parcourir la source

[2095] supported RRSIG encoding with basic tests

JINMEI Tatuya il y a 13 ans
Parent
commit
291c79605d

+ 59 - 2
src/lib/datasrc/memory/rdata_encoder.cc

@@ -14,6 +14,8 @@
 
 #include <exceptions/exceptions.h>
 
+#include <util/buffer.h>
+
 #include <dns/name.h>
 #include <dns/labelsequence.h>
 #include <dns/messagerenderer.h>
@@ -389,12 +391,15 @@ private:
 } // end of unnamed namespace
 
 struct RdataEncoder::RdataEncoderImpl {
-    RdataEncoderImpl() : encode_spec_(NULL), rdata_count_(0)
+    RdataEncoderImpl() : encode_spec_(NULL), rrsig_buffer_(0),
+                         rdata_count_(0)
     {}
 
     const RdataEncodeSpec* encode_spec_; // encode spec of current RDATA set
     RdataFieldComposer field_composer_;
+    util::OutputBuffer rrsig_buffer_;
     size_t rdata_count_;
+    vector<uint16_t> rrsig_lengths_;
 };
 
 RdataEncoder::RdataEncoder() :
@@ -413,7 +418,9 @@ RdataEncoder::start(RRClass rrclass, RRType rrtype) {
 
     impl_->encode_spec_ = &getRdataEncodeSpec(rrclass, rrtype);
     impl_->field_composer_.clearLocal(impl_->encode_spec_);
+    impl_->rrsig_buffer_.clear();
     impl_->rdata_count_ = 0;
+    impl_->rrsig_lengths_.clear();
 }
 
 void
@@ -429,6 +436,19 @@ RdataEncoder::addRdata(const rdata::Rdata& rdata) {
     ++impl_->rdata_count_;
 }
 
+void
+RdataEncoder::addSIGRdata(const rdata::Rdata& sig_rdata) {
+    if (impl_->encode_spec_ == NULL) {
+        isc_throw(InvalidOperation,
+                  "RdataEncoder::addSIGRdata performed before start");
+    }
+    const size_t cur_pos = impl_->rrsig_buffer_.getLength();
+    sig_rdata.toWire(impl_->rrsig_buffer_);
+    const size_t rrsig_datalen = impl_->rrsig_buffer_.getLength() - cur_pos;
+    // TBD: too large data
+    impl_->rrsig_lengths_.push_back(rrsig_datalen);
+}
+
 size_t
 RdataEncoder::getStorageLength() const {
     if (impl_->encode_spec_ == NULL) {
@@ -437,6 +457,8 @@ RdataEncoder::getStorageLength() const {
     }
 
     return (sizeof(uint16_t) * impl_->field_composer_.data_lengths_.size() +
+            sizeof(uint16_t) * impl_->rrsig_lengths_.size() +
+            impl_->rrsig_buffer_.getLength() +
             impl_->field_composer_.getLength());
 }
 
@@ -456,17 +478,32 @@ RdataEncoder::encode(void* buf, size_t buf_len) const {
 
     uint8_t* const dp_beg = reinterpret_cast<uint8_t*>(buf);
     uint8_t* dp = dp_beg;
+    uint16_t* lenp = reinterpret_cast<uint16_t*>(buf);
+
+    // Encode list of lengths for variable length fields (if any)
     if (!impl_->field_composer_.data_lengths_.empty()) {
         const size_t varlen_fields_len =
             impl_->field_composer_.data_lengths_.size() * sizeof(uint16_t);
-        uint16_t* const lenp = reinterpret_cast<uint16_t*>(buf);
         memcpy(lenp, &impl_->field_composer_.data_lengths_[0],
                varlen_fields_len);
+        lenp += impl_->field_composer_.data_lengths_.size();
         dp += varlen_fields_len;
     }
+    // Encode list of lengths for RRSIGs (if any)
+    if (!impl_->rrsig_lengths_.empty()) {
+        const size_t rrsigs_len =
+            impl_->rrsig_lengths_.size() * sizeof(uint16_t);
+        memcpy(lenp, &impl_->rrsig_lengths_[0], rrsigs_len);
+        dp += rrsigs_len;
+    }
+    // Encode main RDATA
     memcpy(dp, impl_->field_composer_.getData(),
            impl_->field_composer_.getLength());
     dp += impl_->field_composer_.getLength();
+    // Encode RRSIGs, if any
+    memcpy(dp, impl_->rrsig_buffer_.getData(),
+           impl_->rrsig_buffer_.getLength());
+    dp += impl_->rrsig_buffer_.getLength();
 
     // The validation at the entrance must ensure this
     assert(buf_len >= dp - dp_beg);
@@ -522,6 +559,26 @@ foreachRdataField(RRClass rrclass, RRType rrtype,
     assert(name_count == encode_spec.name_count * rdata_count);
     assert(varlen_count == encode_spec.varlen_count * rdata_count);
 }
+
+void
+foreachRRSig(const vector<uint8_t>& encoded_data,
+             const vector<uint16_t>& rrsiglen_list,
+             DataCallback data_callback)
+{
+    size_t rrsig_totallen = 0;
+    for (vector<uint16_t>::const_iterator it = rrsiglen_list.begin();
+         it != rrsiglen_list.end();
+         ++it) {
+        rrsig_totallen += *it;
+    }
+    assert(encoded_data.size() >= rrsig_totallen);
+
+    const uint8_t* dp = &encoded_data[encoded_data.size() - rrsig_totallen];
+    for (size_t i = 0; i < rrsiglen_list.size(); ++i) {
+        data_callback(dp, rrsiglen_list[i]);
+        dp += rrsiglen_list[i];
+    }
+}
 } // namespace testing
 
 } // namespace memory

+ 11 - 1
src/lib/datasrc/memory/rdata_encoder.h

@@ -99,6 +99,8 @@ public:
     /// \throw std::bad_alloc Internal memory allocation failure.
     void addRdata(const dns::rdata::Rdata& rdata);
 
+    void addSIGRdata(const dns::rdata::Rdata& sig_rdata);
+
     /// \brief TBD
     ///
     /// \throw InvalidOperation called before start().
@@ -131,12 +133,20 @@ typedef boost::function<void(const uint8_t*, size_t)> DataCallback;
 // normal data fields data_callback will be called).  rdata_count is
 // the number of RDATAs.  If the encoded data contain variable-length
 // data fields, varlen_list should store a sequence of their lengths,
-// in the of the appearance.
+// in the order of the appearance.
 void foreachRdataField(dns::RRClass rrclass, dns::RRType rrtype,
                        size_t rdata_count,
                        const std::vector<uint8_t>& encoded_data,
                        const std::vector<uint16_t>& varlen_list,
                        NameCallback name_callback, DataCallback data_callback);
+
+// Iterate over each RRSIG stored in encoded_data, and call the given
+// callback for each.  rrsiglen_list should store a sequence of their lengths,
+// in the order of the appearance.  Its size is the number of RRSIGs.
+// The list can be empty, in which case this function does nothing.
+void foreachRRSig(const std::vector<uint8_t>& encoded_data,
+                  const std::vector<uint16_t>& rrsiglen_list,
+                  DataCallback data_callback);
 }
 
 } // namespace memory

+ 93 - 19
src/lib/datasrc/memory/tests/rdata_encoder_unittest.cc

@@ -116,7 +116,11 @@ protected:
     RdataEncoderTest() : a_rdata_(createRdata(RRType::A(), RRClass::IN(),
                                               "192.0.2.53")),
                          aaaa_rdata_(createRdata(RRType::AAAA(), RRClass::IN(),
-                                                 "2001:db8::53"))
+                                                 "2001:db8::53")),
+                         rrsig_rdata_(createRdata(
+                                          RRType::RRSIG(), RRClass::IN(),
+                                          "A 5 2 3600 20120814220826 "
+                                          "20120715220826 12345 com. FAKE"))
     {}
 
     // This helper test method constructs encodes the given list of RDATAs
@@ -126,11 +130,16 @@ protected:
     // works as intended.
     void checkEncode(RRClass rrclass, RRType rrtype,
                      const vector<ConstRdataPtr>& rdata_list,
-                     size_t expected_varlen_fields);
+                     size_t expected_varlen_fields,
+                     const vector<ConstRdataPtr>& rrsig_list);
+
+    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_;
 
     RdataEncoder encoder_;
     vector<uint8_t> encoded_data_;
@@ -142,7 +151,9 @@ protected:
 void
 RdataEncoderTest::checkEncode(RRClass rrclass, RRType rrtype,
                               const vector<ConstRdataPtr>& rdata_list,
-                              size_t expected_varlen_fields)
+                              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
@@ -160,25 +171,35 @@ RdataEncoderTest::checkEncode(RRClass rrclass, RRType rrtype,
     need_additionals.insert(RRType::NS());
     need_additionals.insert(RRType::MX());
     need_additionals.insert(RRType::SRV());
-
     expected_renderer_.clear();
     actual_renderer_.clear();
-    expected_renderer_.writeName(dummy_name);
-    actual_renderer_.writeName(dummy_name);
     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) {
         rdata->toWire(expected_renderer_);
     }
     expected_renderer_.writeName(dummy_name2);
+    BOOST_FOREACH(const ConstRdataPtr& rdata, rrsig_list) {
+        rdata->toWire(expected_renderer_);
+    }
+
+    // Then build wire format data using the encoded data.
+    // 1st dummy name
+    actual_renderer_.writeName(dummy_name);
 
+    // Create encoded data
     encoder_.start(rrclass, rrtype);
     BOOST_FOREACH(const ConstRdataPtr& rdata, rdata_list) {
         encoder_.addRdata(*rdata);
     }
+    BOOST_FOREACH(const ConstRdataPtr& rdata, rrsig_list) {
+        encoder_.addSIGRdata(*rdata);
+    }
     encoded_data_.resize(encoder_.getStorageLength());
     encoder_.encode(&encoded_data_[0], encoded_data_.size());
 
@@ -197,18 +218,39 @@ RdataEncoderTest::checkEncode(RRClass rrclass, RRType rrtype,
         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));
+
+    // Two sets of wire-format data should be identical.
     matchWireData(expected_renderer_.getData(), expected_renderer_.getLength(),
                   actual_renderer_.getData(), actual_renderer_.getLength());
 }
 
-TEST_F(RdataEncoderTest, addRdata) {
+void
+RdataEncoderTest::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) {
@@ -221,11 +263,22 @@ TEST_F(RdataEncoderTest, addRdata) {
         rdata_list_.clear();
         rdata_list_.push_back(rdata);
         checkEncode(rrclass, rrtype, rdata_list_,
-                    test_rdata_list[i].n_varlen_fields);
+                    test_rdata_list[i].n_varlen_fields, rrsigs);
     }
 }
 
-TEST_F(RdataEncoderTest, addRdataMulti) {
+TEST_F(RdataEncoderTest, addRdata) {
+    vector<ConstRdataPtr> rrsigs;
+    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);
+}
+
+void
+RdataEncoderTest::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),
@@ -235,7 +288,7 @@ TEST_F(RdataEncoderTest, addRdataMulti) {
     rdata_list_.clear();
     rdata_list_.push_back(a_rdata_);
     rdata_list_.push_back(a_rdata2);
-    checkEncode(RRClass::IN(), RRType::A(), rdata_list_, 0);
+    checkEncode(RRClass::IN(), RRType::A(), rdata_list_, 0, rrsigs);
 
     ConstRdataPtr mx_rdata1 = createRdata(RRType::MX(), RRClass::IN(),
                                           "5 mx1.example.com");
@@ -244,7 +297,7 @@ TEST_F(RdataEncoderTest, addRdataMulti) {
     rdata_list_.clear();
     rdata_list_.push_back(mx_rdata1);
     rdata_list_.push_back(mx_rdata2);
-    checkEncode(RRClass::IN(), RRType::MX(), rdata_list_, 0);
+    checkEncode(RRClass::IN(), RRType::MX(), rdata_list_, 0, rrsigs);
 
     ConstRdataPtr txt_rdata1 = createRdata(RRType::TXT(), RRClass::IN(),
                                            "foo bar baz");
@@ -253,7 +306,7 @@ TEST_F(RdataEncoderTest, addRdataMulti) {
     rdata_list_.clear();
     rdata_list_.push_back(txt_rdata1);
     rdata_list_.push_back(txt_rdata2);
-    checkEncode(RRClass::IN(), RRType::TXT(), rdata_list_, 1);
+    checkEncode(RRClass::IN(), RRType::TXT(), rdata_list_, 1, rrsigs);
 
     ConstRdataPtr naptr_rdata1 =
         createRdata(RRType::NAPTR(), RRClass::IN(),
@@ -264,7 +317,19 @@ TEST_F(RdataEncoderTest, addRdataMulti) {
     rdata_list_.clear();
     rdata_list_.push_back(naptr_rdata1);
     rdata_list_.push_back(naptr_rdata2);
-    checkEncode(RRClass::IN(), RRType::NAPTR(), rdata_list_, 1);
+    checkEncode(RRClass::IN(), RRType::NAPTR(), rdata_list_, 1, rrsigs);
+}
+
+TEST_F(RdataEncoderTest, addRdataMulti) {
+    vector<ConstRdataPtr> rrsigs;
+    addRdataMultiCommon(rrsigs); // test without RRSIGs (empty vector)
+
+    // Tests with two RRSIGs
+    rrsigs.push_back(rrsig_rdata_);
+    rrsigs.push_back(createRdata(RRType::RRSIG(), RRClass::IN(),
+                                 "A 5 2 3600 20120814220826 "
+                                 "20120715220826 54321 com. FAKE"));
+    addRdataMultiCommon(rrsigs);
 }
 
 TEST_F(RdataEncoderTest, badAddRdata) {
@@ -342,9 +407,18 @@ TEST_F(RdataEncoderTest, badAddRdata) {
                  isc::BadValue);
 }
 
-// Note: in our implementation RRSIG is treated as opaque data (including
-// the signer name).  We use "com" for signer so it won't be a compress
-// target in the test.
-//{"IN", "RRSIG", "SOA 5 2 3600 20120814220826 20120715220826 12345 "
-//"com. FAKEFAKEFAKE", 1},
+TEST_F(RdataEncoderTest, addSIGRdata) {
+    encoder_.start(RRClass::IN(), RRType::A());
+    encoder_.addRdata(*a_rdata_);
+    encoder_.addSIGRdata(*rrsig_rdata_);
+    // 4-byte A RDATA, 2-byte length field (for the RRSIG), and RRSIG data
+    // (26 bytes).
+    EXPECT_EQ(4 + 2 + 26, encoder_.getStorageLength());
+}
+
+TEST_F(RdataEncoderTest, badAddSIGRdata) {
+    EXPECT_THROW(encoder_.addSIGRdata(*rrsig_rdata_), isc::InvalidOperation);
+
+    // TBD: reject very large RRSIG
+}
 }