Browse Source

[2098] supported truncation case

also cleaned up the code by unifying the common rendering logic for main
and RRSIG RRs.
JINMEI Tatuya 12 years ago
parent
commit
acd2393951

+ 98 - 36
src/lib/datasrc/memory/tests/treenode_rrset_unittest.cc

@@ -92,21 +92,24 @@ protected:
 };
 
 TEST_F(TreeNodeRRsetTest, create) {
-    const TreeNodeRRset rrset1(RRClass::IN(), www_node_, a_rdataset_, true);
-    EXPECT_EQ(RRClass::IN(), rrset1.getClass());
+    const TreeNodeRRset rrset1(rrclass_, www_node_, a_rdataset_, true);
+    EXPECT_EQ(rrclass_, rrset1.getClass());
     EXPECT_EQ(RRType::A(), rrset1.getType());
     EXPECT_EQ(www_name_, rrset1.getName());
     EXPECT_EQ(2, rrset1.getRdataCount());
     EXPECT_EQ(1, rrset1.getRRsigDataCount());
 
-    const TreeNodeRRset rrset2(RRClass::IN(), www_node_, a_rdataset_, false);
-    EXPECT_EQ(RRClass::IN(), rrset2.getClass());
+    const TreeNodeRRset rrset2(rrclass_, www_node_, a_rdataset_, false);
+    EXPECT_EQ(rrclass_, rrset2.getClass());
     EXPECT_EQ(RRType::A(), rrset2.getType());
     EXPECT_EQ(www_name_, rrset2.getName());
     EXPECT_EQ(2, rrset2.getRdataCount());
     EXPECT_EQ(0, rrset2.getRRsigDataCount());
 }
 
+// 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,
@@ -140,38 +143,97 @@ checkToWireResult(OutputType& expected_output, OutputType& actual_output,
 
 TEST_F(TreeNodeRRsetTest, toWire) {
     MessageRenderer expected_renderer, actual_renderer;
-    //OutputBuffer expected_buffer(0), actual_buffer(0);
-
-    // 1. with RRSIG, DNSSEC OK
-    const TreeNodeRRset rrset1(RRClass::IN(), www_node_, a_rdataset_, true);
-    checkToWireResult(expected_renderer, actual_renderer, rrset1, www_name_,
-                      a_rrset_, rrsig_rrset_, true);
-#ifdef notyet
-    checkToWireResult(expected_buffer, actual_buffer, rrset1,  www_name_,
-                      a_rrset_, rrsig_rrset_, true);
-#endif
-
-    // 2. with RRSIG, DNSSEC not OK
-    const TreeNodeRRset rrset2(rrclass_, www_node_, a_rdataset_, false);
-    checkToWireResult(expected_renderer, actual_renderer, rrset2, www_name_,
-                      a_rrset_, rrsig_rrset_, false);
-
-    // 3. 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);
-
-    // 4. 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 RR shouldn't be compressed.  Prepending "example.org"
-    // will check that.
-    const TreeNodeRRset rrset5(rrclass_, origin_node_, dname_rdataset_, false);
-    checkToWireResult(expected_renderer, actual_renderer, rrset5,
-                      Name("example.org"), dname_rrset_, ConstRRsetPtr(),
-                      false);
+
+    {
+        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_, rrsig_rrset_, true);
+        EXPECT_FALSE(actual_renderer.isTruncated());
+    }
+
+    {
+        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_, rrsig_rrset_, false);
+        EXPECT_FALSE(actual_renderer.isTruncated());
+    }
+
+    {
+        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);
+        EXPECT_FALSE(actual_renderer.isTruncated());
+    }
+
+    {
+        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);
+        EXPECT_FALSE(actual_renderer.isTruncated());
+    }
+
+    {
+        // 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);
+        EXPECT_FALSE(actual_renderer.isTruncated());
+    }
+}
+
+void
+checkTruncationResult(MessageRenderer& expected_renderer,
+                      MessageRenderer& actual_renderer,
+                      const TreeNodeRRset& 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_, rrsig_rrset_, true,
+                          www_name_.getLength() + 14,
+                          1);
+
+    // The first normal 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_, rrsig_rrset_, true,
+                          www_name_.getLength() + 14 + 2 + 14, 2);
 }
 
 void

+ 47 - 36
src/lib/datasrc/memory/treenode_rrset.cc

@@ -88,6 +88,40 @@ renderData(const void* data, size_t data_len,
 {
     renderer->writeData(data, data_len);
 }
+
+// Common code logic for rendering a single (either main or RRSIG) RRset.
+size_t
+writeRRs(AbstractMessageRenderer& renderer, size_t rr_count,
+         const LabelSequence& name_labels, const RRType& rrtype,
+         const RRClass& rrclass, const void* ttl_data,
+         RdataReader& reader, bool (RdataReader::* rdata_iterate_fn)())
+{
+    for (size_t i = 0; i < rr_count; ++i) {
+        const size_t pos0 = renderer.getLength();
+
+        // Name, type, class, TTL
+        renderer.writeName(name_labels, true);
+        rrtype.toWire(renderer);
+        rrclass.toWire(renderer);
+        renderer.writeData(ttl_data, sizeof(uint32_t));
+
+        // RDLEN and RDATA
+        const size_t pos = renderer.getLength();
+        renderer.skip(sizeof(uint16_t)); // leave the space for RDLENGTH
+        const bool rendered = (reader.*rdata_iterate_fn)();
+        assert(rendered == true);
+        renderer.writeUint16At(renderer.getLength() - pos - sizeof(uint16_t),
+                               pos);
+
+        // Check if truncation would happen
+        if (renderer.getLength() > renderer.getLengthLimit()) {
+            renderer.trim(renderer.getLength() - pos0);
+            renderer.setTruncated();
+            return (i);
+        }
+    }
+    return (rr_count);
+}
 }
 
 unsigned int
@@ -101,47 +135,24 @@ TreeNodeRRset::toWire(AbstractMessageRenderer& renderer) const {
     uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
     const LabelSequence node_labels = node_->getAbsoluteLabels(labels_buf);
 
-    size_t i = 0;
-    for (; i < rdataset_->getRdataCount(); ++i) {
-        //const size_t pos0 = output.getLength();
-
-        renderer.writeName(node_labels, true);
-        rdataset_->type.toWire(renderer);
-        rrclass_.toWire(renderer);
-        renderer.writeData(rdataset_->getTTLData(), sizeof(uint32_t));
-
-        const size_t pos = renderer.getLength();
-        renderer.skip(sizeof(uint16_t)); // leave the space for RDLENGTH
-        const bool rendered = reader.iterateRdata();
-        assert(rendered == true);
-        renderer.writeUint16At(renderer.getLength() - pos - sizeof(uint16_t),
-                               pos);
-
-        // for truncation processing
+    // Render the main (non RRSIG) RRs
+    const size_t rendered_rdata_count =
+        writeRRs(renderer, rdataset_->getRdataCount(), node_labels,
+                 rdataset_->type, rrclass_, rdataset_->getTTLData(), reader,
+                 &RdataReader::iterateRdata);
+    if (renderer.isTruncated()) {
+        return (rendered_rdata_count);
     }
     const bool rendered = reader.iterateRdata();
     assert(rendered == false); // we should've reached the end
 
-    size_t j = 0;
-    if (dnssec_ok_) {
-        for (; j < rrsig_count_; ++j) {
-            // TBD: truncation consideration
-
-            renderer.writeName(node_labels, true);
-            RRType::RRSIG().toWire(renderer);
-            rrclass_.toWire(renderer);
-            renderer.writeData(rdataset_->getTTLData(), sizeof(uint32_t));
-
-            const size_t pos = renderer.getLength();
-            renderer.skip(sizeof(uint16_t)); // leave the space for RDLENGTH
-            assert(reader.iterateSingleSig() == true);
-            renderer.writeUint16At(renderer.getLength() - pos -
-                                   sizeof(uint16_t), pos);
-        }
-        assert(reader.iterateSingleSig() == false);
-    }
+    // Render any RRSIGs, if we supposed to do so
+    const size_t rendered_rrsig_count = dnssec_ok_ ?
+        writeRRs(renderer, rrsig_count_, node_labels, RRType::RRSIG(),
+                 rrclass_, rdataset_->getTTLData(), reader,
+                 &RdataReader::iterateSingleSig) : 0;
 
-    return (i + j);
+    return (rendered_rdata_count + rendered_rrsig_count);
 }
 
 unsigned int