summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/lib/dns/messagerenderer.cc51
-rw-r--r--src/lib/dns/messagerenderer.h44
-rw-r--r--src/lib/dns/tests/messagerenderer_unittest.cc45
-rw-r--r--src/lib/dns/tests/testdata/name_toWire512
-rw-r--r--src/lib/dns/tests/testdata/name_toWire5.spec19
-rw-r--r--src/lib/dns/tests/testdata/name_toWire612
-rw-r--r--src/lib/dns/tests/testdata/name_toWire6.spec19
7 files changed, 190 insertions, 12 deletions
diff --git a/src/lib/dns/messagerenderer.cc b/src/lib/dns/messagerenderer.cc
index 3b0b6246b8..a5bff63afe 100644
--- a/src/lib/dns/messagerenderer.cc
+++ b/src/lib/dns/messagerenderer.cc
@@ -34,9 +34,14 @@ namespace { // hide internal-only names from the public namespaces
/// 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 @@ struct NameCompare : public std::binary_function<NameCompressNode,
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 @@ struct NameCompare : public std::binary_function<NameCompressNode,
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 @@ private:
/// 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 @@ struct MessageRendererImpl {
/// 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 @@ MessageRenderer::clear() {
impl_->nodeset_.clear();
impl_->msglength_limit_ = 512;
impl_->truncated_ = false;
+ impl_->compress_mode_ = CASE_INSENSITIVE;
}
void
@@ -238,6 +258,16 @@ 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
MessageRenderer::writeName(const Name& name, const bool compress) {
impl_->nbuffer_.clear();
@@ -254,7 +284,7 @@ MessageRenderer::writeName(const Name& name, const bool compress) {
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 @@ MessageRenderer::writeName(const Name& name, const bool compress) {
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));
}
diff --git a/src/lib/dns/messagerenderer.h b/src/lib/dns/messagerenderer.h
index f83293792a..bdcf663c02 100644
--- a/src/lib/dns/messagerenderer.h
+++ b/src/lib/dns/messagerenderer.h
@@ -22,7 +22,6 @@ namespace dns {
// forward declarations
class OutputBuffer;
class Name;
-class MessageRendererImpl;
///
/// \brief The \c MessageRenderer class encapsulates implementation details
@@ -38,7 +37,7 @@ class MessageRendererImpl;
/// 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 @@ class MessageRendererImpl;
/// 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 @@ public:
///
/// \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 @@ public:
///
/// \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 @@ public:
/// \param compress A boolean indicating whether to enable name compression.
void writeName(const Name& name, bool compress = true);
private:
+ struct MessageRendererImpl;
MessageRendererImpl* impl_;
};
}
diff --git a/src/lib/dns/tests/messagerenderer_unittest.cc b/src/lib/dns/tests/messagerenderer_unittest.cc
index 197ee7c9e8..cc42f7125f 100644
--- a/src/lib/dns/tests/messagerenderer_unittest.cc
+++ b/src/lib/dns/tests/messagerenderer_unittest.cc
@@ -98,7 +98,25 @@ TEST_F(MessageRendererTest, writeNamePointerChain) {
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:
@@ -108,6 +126,33 @@ TEST_F(MessageRendererTest, writeNameCaseCompress) {
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());
+}
+
TEST_F(MessageRendererTest, writeRootName) {
// root name is special: it never causes compression or can (reasonably)
// be a compression pointer. So it makes sense to check this case
diff --git a/src/lib/dns/tests/testdata/name_toWire5 b/src/lib/dns/tests/testdata/name_toWire5
new file mode 100644
index 0000000000..22cb686a5b
--- /dev/null
+++ b/src/lib/dns/tests/testdata/name_toWire5
@@ -0,0 +1,12 @@
+###
+### This data file was auto-generated from name_toWire5.spec
+###
+
+# DNS Name: a.example.com
+0161076578616d706c6503636f6d00
+
+# DNS Name: b.eXample + compression pointer: 10
+0162076558616d706c65 c00a
+
+# DNS Name: c + compression pointer: 17
+0163 c011
diff --git a/src/lib/dns/tests/testdata/name_toWire5.spec b/src/lib/dns/tests/testdata/name_toWire5.spec
new file mode 100644
index 0000000000..87e140d43a
--- /dev/null
+++ b/src/lib/dns/tests/testdata/name_toWire5.spec
@@ -0,0 +1,19 @@
+#
+# A sequence of names that would be compressed case-sensitive manner.
+# First name: "a.example.com"
+# Second name: "b.eXample.com". Due to case-sensitive comparison only "com"
+# can be compressed.
+# Third name: "c.eXample.com". "eXample.com" part matches that of the second
+# name and can be compressed.
+#
+
+[custom]
+sections: name/1:name/2:name/3
+[name/1]
+name: a.example.com
+[name/2]
+name: b.eXample
+pointer: 10
+[name/3]
+name: c
+pointer: 17
diff --git a/src/lib/dns/tests/testdata/name_toWire6 b/src/lib/dns/tests/testdata/name_toWire6
new file mode 100644
index 0000000000..49db876c6e
--- /dev/null
+++ b/src/lib/dns/tests/testdata/name_toWire6
@@ -0,0 +1,12 @@
+###
+### This data file was auto-generated from name_toWire6.spec
+###
+
+# DNS Name: a.example.com
+0161076578616d706c6503636f6d00
+
+# DNS Name: b.eXample + compression pointer: 10
+0162076558616d706c65 c00a
+
+# DNS Name: c + compression pointer: 15
+0163 c00f
diff --git a/src/lib/dns/tests/testdata/name_toWire6.spec b/src/lib/dns/tests/testdata/name_toWire6.spec
new file mode 100644
index 0000000000..a536f5da19
--- /dev/null
+++ b/src/lib/dns/tests/testdata/name_toWire6.spec
@@ -0,0 +1,19 @@
+#
+# A sequence of names that would be compressed both case-sensitive and
+# case-insensitive manner (unusual, but allowed).
+# First and second name: see name_toWire5.spec.
+# Third name: "c.b.EXAMPLE.com". This is rendered with case-insensitive
+# compression, so "b.EXAMPLE.com" part of the name matches that of the
+# second name.
+#
+
+[custom]
+sections: name/1:name/2:name/3
+[name/1]
+name: a.example.com
+[name/2]
+name: b.eXample
+pointer: 10
+[name/3]
+name: c
+pointer: 15