[svn] commit: r1704 - in /trunk/src/lib/dns: messagerenderer.cc messagerenderer.h tests/messagerenderer_unittest.cc tests/testdata/name_toWire5 tests/testdata/name_toWire5.spec tests/testdata/name_toWire6 tests/testdata/name_toWire6.spec

BIND 10 source code commits bind10-changes at lists.isc.org
Sat Apr 10 00:11:20 UTC 2010


Author: jinmei
Date: Sat Apr 10 00:11:20 2010
New Revision: 1704

Log:
supported case-sensitive name compression in MessageRenderer.
test cases and documentation were also fully provided.

(this changeset includes a minor unrelated cleanup: move the pimpl structure
 inside the MessageRenderer class declaration to clarify the intent)

Added:
    trunk/src/lib/dns/tests/testdata/name_toWire5
    trunk/src/lib/dns/tests/testdata/name_toWire5.spec
    trunk/src/lib/dns/tests/testdata/name_toWire6
    trunk/src/lib/dns/tests/testdata/name_toWire6.spec
Modified:
    trunk/src/lib/dns/messagerenderer.cc
    trunk/src/lib/dns/messagerenderer.h
    trunk/src/lib/dns/tests/messagerenderer_unittest.cc

Modified: trunk/src/lib/dns/messagerenderer.cc
==============================================================================
--- trunk/src/lib/dns/messagerenderer.cc (original)
+++ trunk/src/lib/dns/messagerenderer.cc Sat Apr 10 00:11:20 2010
@@ -34,9 +34,14 @@
 /// objects, and searches the set for the position of the longest match
 /// (ancestor) name against each new name to be rendered into the buffer.
 struct NameCompressNode {
-    NameCompressNode(const OutputBuffer& buffer, const size_t pos,
+    NameCompressNode(const MessageRenderer& renderer,
+                     const OutputBuffer& buffer, const size_t pos,
                      const size_t len) :
-        buffer_(buffer), pos_(pos), len_(len) {}
+        renderer_(renderer), buffer_(buffer), pos_(pos), len_(len) {}
+    /// The renderer that performs name compression using the node.
+    /// This is kept in each node to detect the compression mode
+    /// (case-sensitive or not) in the comparison functor (\c NameCompare).
+    const MessageRenderer& renderer_;
     /// The buffer in which the corresponding name is rendered.
     const OutputBuffer& buffer_;
     /// The position (offset from the beginning) in the buffer where the
@@ -78,6 +83,9 @@
             return (false);
         }
 
+        const bool case_sensitive =
+            (n1.renderer_.getCompressMode() == MessageRenderer::CASE_SENSITIVE);
+
         uint16_t pos1 = n1.pos_;
         uint16_t pos2 = n2.pos_;
         uint16_t l1 = 0;
@@ -85,10 +93,19 @@
         for (uint16_t i = 0; i < n1.len_; i++, pos1++, pos2++) {
             pos1 = nextPosition(n1.buffer_, pos1, l1);
             pos2 = nextPosition(n2.buffer_, pos2, l2);
-            if (tolower(n1.buffer_[pos1]) < tolower(n2.buffer_[pos2])) {
-                return (true);
-            } else if (tolower(n1.buffer_[pos1]) > tolower(n2.buffer_[pos2])) {
-                return (false);
+            if (case_sensitive) {
+                if (n1.buffer_[pos1] < n2.buffer_[pos2]) {
+                    return (true);
+                } else if (n1.buffer_[pos1] > n2.buffer_[pos2]) {
+                    return (false);
+                }
+            } else {
+                if (tolower(n1.buffer_[pos1]) < tolower(n2.buffer_[pos2])) {
+                    return (true);
+                } else if (tolower(n1.buffer_[pos1]) >
+                           tolower(n2.buffer_[pos2])) {
+                    return (false);
+                }
             }
         }
 
@@ -130,14 +147,14 @@
 /// The implementation is hidden from applications.  We can refer to specific
 /// members of this class only within the implementation source file.
 ///
-struct MessageRendererImpl {
+struct MessageRenderer::MessageRendererImpl {
     /// \brief Constructor from an output buffer.
     ///
     /// \param buffer An \c OutputBuffer object to which wire format data is
     /// written.
     MessageRendererImpl(OutputBuffer& buffer) :
         buffer_(buffer), nbuffer_(Name::MAX_WIRE), msglength_limit_(512),
-        truncated_(false)
+        truncated_(false), compress_mode_(MessageRenderer::CASE_INSENSITIVE)
     {}
     /// The buffer that holds the entire DNS message.
     OutputBuffer& buffer_;
@@ -154,6 +171,8 @@
     /// A boolean flag that indicates truncation has occurred while rendering
     /// the data.
     bool truncated_;
+    /// The name compression mode.
+    CompressMode compress_mode_;
 };
 
 MessageRenderer::MessageRenderer(OutputBuffer& buffer) :
@@ -181,6 +200,7 @@
     impl_->nodeset_.clear();
     impl_->msglength_limit_ = 512;
     impl_->truncated_ = false;
+    impl_->compress_mode_ = CASE_INSENSITIVE;
 }
 
 void
@@ -236,6 +256,16 @@
 void
 MessageRenderer::setTruncated() {
     impl_->truncated_ = true;
+}
+
+MessageRenderer::CompressMode
+MessageRenderer::getCompressMode() const {
+    return (impl_->compress_mode_);
+}
+
+void
+MessageRenderer::setCompressMode(const CompressMode mode) {
+    impl_->compress_mode_ = mode;
 }
 
 void
@@ -254,7 +284,7 @@
         if (impl_->nbuffer_[i] == 0) {
             continue;
         }
-        n = impl_->nodeset_.find(NameCompressNode(impl_->nbuffer_, i,
+        n = impl_->nodeset_.find(NameCompressNode(*this, impl_->nbuffer_, i,
                                                   impl_->nbuffer_.getLength() -
                                                   i));
         if (n != notfound) {
@@ -283,7 +313,8 @@
         if (offset + j > Name::MAX_COMPRESS_POINTER) {
             break;
         }
-        impl_->nodeset_.insert(NameCompressNode(impl_->buffer_, offset + j,
+        impl_->nodeset_.insert(NameCompressNode(*this, impl_->buffer_,
+                                                offset + j,
                                                 impl_->nbuffer_.getLength() -
                                                 j));
     }

Modified: trunk/src/lib/dns/messagerenderer.h
==============================================================================
--- trunk/src/lib/dns/messagerenderer.h (original)
+++ trunk/src/lib/dns/messagerenderer.h Sat Apr 10 00:11:20 2010
@@ -22,7 +22,6 @@
 // forward declarations
 class OutputBuffer;
 class Name;
-class MessageRendererImpl;
 
 ///
 /// \brief The \c MessageRenderer class encapsulates implementation details
@@ -38,7 +37,7 @@
 /// to care about this class.
 ///
 /// A \c MessageRenderer class object is constructed with a \c OutputBuffer
-/// object, which is the buffer into which the rendered data will be written.
+/// object, which is the buffer into which the rendered %data will be written.
 /// Normally the buffer is expected to be empty on construction, but it doesn't
 /// have to be so; the \c MessageRenderer object will start rendering from the
 /// end of the buffer at the time of construction.  However, if the
@@ -69,6 +68,34 @@
 /// abstraction and keep the definition simpler.
 class MessageRenderer {
 public:
+    /// \brief Compression mode constants.
+    ///
+    /// The \c CompressMode enum type represents the name compression mode
+    /// for the \c MessageRenderer.
+    /// \c CASE_INSENSITIVE means compress names in case-insensitive manner;
+    /// \c CASE_SENSITIVE means compress names in case-sensitive manner.
+    /// By default, \c MessageRenderer compresses names in case-insensitive
+    /// manner.
+    /// Compression mode can be dynamically modified by the
+    /// \c setCompressMode() method.
+    /// The mode can be changed even in the middle of rendering, although this
+    /// is not an intended usage.  In this case the names already compressed
+    /// are intact; only names being compressed after the mode change are
+    /// affected by the change.
+    /// If the internal \c MessageRenderer is reinitialized by the \c clear()
+    /// method, the compression mode will be reset to the default, which is
+    /// \c CASE_INSENSITIVE
+    ///
+    /// One specific case where case-sensitive compression is required is
+    /// AXFR as described in draft-ietf-dnsext-axfr-clarify.  A primary
+    /// authoritative DNS server implementation using this API would specify
+    /// \c CASE_SENSITIVE before rendering outgoing AXFR messages.
+    ///
+    enum CompressMode {
+        CASE_INSENSITIVE,  //!< Compress names case-insensitive manner (default)
+        CASE_SENSITIVE     //!< Compress names case-sensitive manner
+    };
+public:
     ///
     /// \name Constructors and Destructor
     //@{
@@ -116,6 +143,12 @@
     ///
     /// \return The maximum length in bytes.
     size_t getLengthLimit() const;
+    /// \brief Return the compression mode of the \c MessageRenderer.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return The current compression mode.
+    CompressMode getCompressMode() const;
     //@}
 
     ///
@@ -134,6 +167,12 @@
     ///
     /// \param len The maximum length in bytes.
     void setLengthLimit(size_t len);
+    /// \brief Set the compression mode of the \c MessageRenderer.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \param mode A \c CompressMode value representing the compression mode.
+    void setCompressMode(CompressMode mode);
     //@}
 
     ///
@@ -220,6 +259,7 @@
     /// \param compress A boolean indicating whether to enable name compression.
     void writeName(const Name& name, bool compress = true);
 private:
+    struct MessageRendererImpl;
     MessageRendererImpl* impl_;
 };
 }

Modified: trunk/src/lib/dns/tests/messagerenderer_unittest.cc
==============================================================================
--- trunk/src/lib/dns/tests/messagerenderer_unittest.cc (original)
+++ trunk/src/lib/dns/tests/messagerenderer_unittest.cc Sat Apr 10 00:11:20 2010
@@ -98,12 +98,57 @@
                         buffer.getLength(), &data[0], data.size());
 }
 
+TEST_F(MessageRendererTest, compressMode) {
+    // By default the render performs case insensitive compression.
+    EXPECT_EQ(MessageRenderer::CASE_INSENSITIVE, renderer.getCompressMode());
+
+    // The mode can be explicitly changed.
+    renderer.setCompressMode(MessageRenderer::CASE_SENSITIVE);
+    EXPECT_EQ(MessageRenderer::CASE_SENSITIVE, renderer.getCompressMode());
+    renderer.setCompressMode(MessageRenderer::CASE_INSENSITIVE);
+    EXPECT_EQ(MessageRenderer::CASE_INSENSITIVE, renderer.getCompressMode());
+
+    // The clear() method resets the mode to the default.
+    renderer.setCompressMode(MessageRenderer::CASE_SENSITIVE);
+    renderer.clear();
+    EXPECT_EQ(MessageRenderer::CASE_INSENSITIVE, renderer.getCompressMode());
+}
+
 TEST_F(MessageRendererTest, writeNameCaseCompress) {
+    // By default MessageRenderer performs case insensitive compression.
+
     UnitTestUtil::readWireData("testdata/name_toWire1", data);
     renderer.writeName(Name("a.example.com."));
     // this should match the first name in terms of compression:
     renderer.writeName(Name("b.exAmple.CoM."));
     renderer.writeName(Name("a.example.org."));
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
+                        buffer.getLength(), &data[0], data.size());
+}
+
+TEST_F(MessageRendererTest, writeNameCaseSensitiveCompress) {
+    // name compression in case sensitive manner.  See the data file
+    // description for details.
+    renderer.setCompressMode(MessageRenderer::CASE_SENSITIVE);
+    UnitTestUtil::readWireData("testdata/name_toWire5", data);
+    renderer.writeName(Name("a.example.com."));
+    renderer.writeName(Name("b.eXample.com."));
+    renderer.writeName(Name("c.eXample.com."));
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
+                        buffer.getLength(), &data[0], data.size());
+}
+
+TEST_F(MessageRendererTest, writeNameMixedCaseCompress) {
+    renderer.setCompressMode(MessageRenderer::CASE_SENSITIVE);
+    UnitTestUtil::readWireData("testdata/name_toWire6", data);
+    renderer.writeName(Name("a.example.com."));
+    renderer.writeName(Name("b.eXample.com."));
+
+    // Change the compression mode in the middle of rendering.  This is an
+    // unusual operation and is unlikely to happen in practice, but is still
+    // allowed in this API.
+    renderer.setCompressMode(MessageRenderer::CASE_INSENSITIVE);
+    renderer.writeName(Name("c.b.EXAMPLE.com."));
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
                         buffer.getLength(), &data[0], data.size());
 }




More information about the bind10-changes mailing list