[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