Browse Source

initial implementation of trac#404: in-memory serialized representation of RDATA

git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac404@3430 e5f2f494-b856-4b98-b285-d166d9295462
JINMEI Tatuya 14 years ago
parent
commit
ff5a80a319
39 changed files with 1223 additions and 105 deletions
  1. 1 0
      src/lib/dns/Makefile.am
  2. 2 2
      src/lib/dns/gen-rdatacode.py.in
  3. 107 73
      src/lib/dns/messagerenderer.h
  4. 1 2
      src/lib/dns/name.cc
  5. 2 2
      src/lib/dns/name.h
  6. 1 1
      src/lib/dns/rdata.cc
  7. 3 3
      src/lib/dns/rdata.h
  8. 1 1
      src/lib/dns/rdata/ch_3/a_1.cc
  9. 1 1
      src/lib/dns/rdata/generic/cname_5.cc
  10. 1 1
      src/lib/dns/rdata/generic/dname_39.cc
  11. 1 1
      src/lib/dns/rdata/generic/dnskey_48.cc
  12. 1 1
      src/lib/dns/rdata/generic/ds_43.cc
  13. 1 1
      src/lib/dns/rdata/generic/mx_15.cc
  14. 1 1
      src/lib/dns/rdata/generic/ns_2.cc
  15. 1 1
      src/lib/dns/rdata/generic/nsec3_50.cc
  16. 1 1
      src/lib/dns/rdata/generic/nsec3param_51.cc
  17. 1 1
      src/lib/dns/rdata/generic/nsec_47.cc
  18. 1 1
      src/lib/dns/rdata/generic/opt_41.cc
  19. 1 1
      src/lib/dns/rdata/generic/ptr_12.cc
  20. 1 1
      src/lib/dns/rdata/generic/rrsig_46.cc
  21. 1 1
      src/lib/dns/rdata/generic/soa_6.cc
  22. 1 1
      src/lib/dns/rdata/generic/txt_16.cc
  23. 1 1
      src/lib/dns/rdata/hs_4/a_1.cc
  24. 1 1
      src/lib/dns/rdata/in_1/a_1.cc
  25. 1 1
      src/lib/dns/rdata/in_1/aaaa_28.cc
  26. 1 1
      src/lib/dns/rdata/template.cc
  27. 249 0
      src/lib/dns/rdatafields.cc
  28. 387 0
      src/lib/dns/rdatafields.h
  29. 2 2
      src/lib/dns/rrtype-placeholder.h
  30. 1 1
      src/lib/dns/rrtype.cc
  31. 1 0
      src/lib/dns/tests/Makefile.am
  32. 379 0
      src/lib/dns/tests/rdatafields_unittest.cc
  33. 4 0
      src/lib/dns/tests/testdata/Makefile.am
  34. 10 0
      src/lib/dns/tests/testdata/rdatafields1.spec
  35. 11 0
      src/lib/dns/tests/testdata/rdatafields2.spec
  36. 11 0
      src/lib/dns/tests/testdata/rdatafields3.spec
  37. 7 0
      src/lib/dns/tests/testdata/rdatafields4.spec
  38. 12 0
      src/lib/dns/tests/testdata/rdatafields5.spec
  39. 13 0
      src/lib/dns/tests/testdata/rdatafields6.spec

+ 1 - 0
src/lib/dns/Makefile.am

@@ -73,6 +73,7 @@ libdns___la_SOURCES += name.h name.cc
 libdns___la_SOURCES += opcode.h opcode.cc
 libdns___la_SOURCES += rcode.h rcode.cc
 libdns___la_SOURCES += rdata.h rdata.cc
+libdns___la_SOURCES += rdatafields.h rdatafields.cc
 libdns___la_SOURCES += rrclass.cc
 libdns___la_SOURCES += rrparamregistry.h
 libdns___la_SOURCES += rrset.h rrset.cc

+ 2 - 2
src/lib/dns/gen-rdatacode.py.in

@@ -109,7 +109,7 @@ def import_classheader(class_txt, type_txt, type_code, file):
             content += '''
 class InputBuffer;
 class OutputBuffer;
-class MessageRenderer;\n\n'''
+class AbstractMessageRenderer;\n\n'''
         if re.match('\s+// BEGIN_COMMON_MEMBERS$', line):
             content += '''
     explicit ''' + type_utxt + '''(const std::string& type_str);
@@ -117,7 +117,7 @@ class MessageRenderer;\n\n'''
     ''' + type_utxt + '''(const ''' + type_utxt + '''& other);
     virtual std::string toText() const;
     virtual void toWire(OutputBuffer& buffer) const;
-    virtual void toWire(MessageRenderer& renderer) const;
+    virtual void toWire(AbstractMessageRenderer& renderer) const;
     virtual int compare(const Rdata& other) const;\n\n'''
     rdata_header.close()
     return content

+ 107 - 73
src/lib/dns/messagerenderer.h

@@ -23,58 +23,46 @@ namespace dns {
 class OutputBuffer;
 class Name;
 
+/// \brief The \c AbstractMessageRenderer class is an abstract base class
+/// that provides common interfaces for rendering a DNS message into a buffer
+/// in wire format.
 ///
-/// \brief The \c MessageRenderer class encapsulates implementation details
-/// of rendering a DNS message into a buffer in wire format.
+/// A specific derived class of \c AbstractMessageRenderer (we call it
+/// a renderer class hereafter) is simply responsible for name compression at
+/// least in the current design.  A renderer class object (conceptually)
+/// manages the positions of names rendered in some sort of buffer and uses
+/// that information to render subsequent names with compression.
 ///
-/// In effect, it's simply responsible for name compression at least in the
-/// current implementation.  A \c MessageRenderer class object manages the
-/// positions of names rendered in a buffer and uses that information to render
-/// subsequent names with compression.
-///
-/// This class is mainly intended to be used as a helper for a more
+/// A renderer class is mainly intended to be used as a helper for a more
 /// comprehensive \c Message class internally; normal applications won't have
-/// 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.
-/// 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
-/// pre-existing portion of the buffer contains DNS names, these names won't
-/// be considered for name compression.
+/// to care about details of this class.
 ///
-/// Once a \c MessageRenderer object is constructed with a buffer, it is
-/// generally expected that all rendering operations are performed via the
-/// \c MessageRenderer object.  If the application modifies the buffer in
-/// parallel with the \c MessageRenderer, the result will be undefined.
+/// Once a renderer class object is constructed with a buffer, it is
+/// generally expected that all rendering operations are performed via that
+/// object.  If the application modifies the buffer in
+/// parallel with the renderer, the result will be undefined.
 ///
 /// Note to developers: we introduced a separate class for name compression
 /// because previous benchmark with BIND9 showed compression affects overall
 /// response performance very much.  By having a separate class dedicated for
 /// this purpose, we'll be able to change the internal implementation of name
 /// compression in the future without affecting other part of the API and
-/// implementation.  For the same reason, we adopt the "pimpl" idiom in the
-/// class definition (i.e., using a pointer to a \c MessageRendererImpl class,
-/// which is defined with the class implementation, not in the header file):
-/// we may want to modify the compression implementation without modifying the
-/// header file thereby requesting rebuild the package.
+/// implementation.
 ///
-/// Furthermore, we may eventually want to allow other developers to develop
-/// and use their own compression implementation.  Should such a case become
-/// realistic, we may want to make the \c MessageRendererImpl class an abstract
-/// base class and let concrete derived classes have their own implementations.
-/// At the moment we don't the strong need for it, so we rather avoid over
-/// abstraction and keep the definition simpler.
-class MessageRenderer {
+/// In addition, by introducing a class hierarchy from
+/// \c AbstractMessageRenderer, we allow an application to use a customized
+/// renderer class for specific purposes.  For example, a high performance
+/// DNS server may want to use an optimized renderer class assuming some
+/// specific underlying data representation.
+class AbstractMessageRenderer {
 public:
     /// \brief Compression mode constants.
     ///
     /// The \c CompressMode enum type represents the name compression mode
-    /// for the \c MessageRenderer.
+    /// for renderer classes.
     /// \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
+    /// By default, a renderer compresses names in case-insensitive
     /// manner.
     /// Compression mode can be dynamically modified by the
     /// \c setCompressMode() method.
@@ -82,7 +70,7 @@ public:
     /// 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()
+    /// If a renderer class object is reinitialized by the \c clear()
     /// method, the compression mode will be reset to the default, which is
     /// \c CASE_INSENSITIVE
     ///
@@ -95,23 +83,18 @@ public:
         CASE_INSENSITIVE,  //!< Compress names case-insensitive manner (default)
         CASE_SENSITIVE     //!< Compress names case-sensitive manner
     };
-public:
+protected:
     ///
     /// \name Constructors and Destructor
     //@{
-    /// \brief Constructor from an output buffer.
+    /// \brief The default constructor.
     ///
-    /// \param buffer An \c OutputBuffer object to which wire format data is
-    /// written.
-    MessageRenderer(OutputBuffer& buffer);
+    /// This is intentionally defined as \c protected as this base class should
+    /// never be instantiated (except as part of a derived class).
+    AbstractMessageRenderer() {}
+public:
     /// \brief The destructor.
-    ///
-    /// The destructor does nothing on the given \c buffer on construction;
-    /// in fact, it is expected that the user will use the resulting buffer
-    /// for some post rendering purposes (e.g., send the data to the network).
-    /// It's the user's responsibility to do any necessary cleanup for the
-    /// \c buffer.
-    ~MessageRenderer();
+    virtual ~AbstractMessageRenderer() {}
     //@}
 
     ///
@@ -123,9 +106,11 @@ public:
     ///
     /// This method works exactly same as the same method of the \c OutputBuffer
     /// class; all notes for \c OutputBuffer apply.
-    const void* getData() const;
+    virtual const void* getData() const = 0;
+
     /// \brief Return the length of data written in the internal buffer.
-    size_t getLength() const;
+    virtual size_t getLength() const = 0;
+
     /// \brief Return whether truncation has occurred while rendering.
     ///
     /// Once the return value of this method is \c true, it doesn't make sense
@@ -135,20 +120,22 @@ public:
     /// This method never throws an exception.
     ///
     /// \return true if truncation has occurred; otherwise \c false.
-    bool isTruncated() const;
+    virtual bool isTruncated() const = 0;
+
     /// \brief Return the maximum length of rendered data that can fit in the
     /// corresponding DNS message without truncation.
     ///
     /// This method never throws an exception.
     ///
     /// \return The maximum length in bytes.
-    size_t getLengthLimit() const;
-    /// \brief Return the compression mode of the \c MessageRenderer.
+    virtual size_t getLengthLimit() const = 0;
+
+    /// \brief Return the compression mode of the renderer class object.
     ///
     /// This method never throws an exception.
     ///
     /// \return The current compression mode.
-    CompressMode getCompressMode() const;
+    virtual CompressMode getCompressMode() const = 0;
     //@}
 
     ///
@@ -159,20 +146,22 @@ public:
     /// rendering.
     ///
     /// This method never throws an exception.
-    void setTruncated();
+    virtual void setTruncated() = 0;
+
     /// \brief Set the maximum length of rendered data that can fit in the
     /// corresponding DNS message without truncation.
     ///
     /// This method never throws an exception.
     ///
     /// \param len The maximum length in bytes.
-    void setLengthLimit(size_t len);
-    /// \brief Set the compression mode of the \c MessageRenderer.
+    virtual void setLengthLimit(size_t len) = 0;
+
+    /// \brief Set the compression mode of the renderer class object.
     ///
     /// This method never throws an exception.
     ///
     /// \param mode A \c CompressMode value representing the compression mode.
-    void setCompressMode(CompressMode mode);
+    virtual void setCompressMode(CompressMode mode) = 0;
     //@}
 
     ///
@@ -186,7 +175,8 @@ public:
     /// that is to be filled in later, e.g, by \ref writeUint16At().
     ///
     /// \param len The length of the gap to be inserted in bytes.
-    void skip(size_t len);
+    virtual void skip(size_t len) = 0;
+
     /// \brief Trim the specified length of data from the end of the internal
     /// buffer.
     ///
@@ -197,21 +187,25 @@ public:
     /// be thrown.
     ///
     /// \param len The length of data that should be trimmed.
-    void trim(size_t len);
+    virtual void trim(size_t len) = 0;
+
     /// \brief Clear the internal buffer and other internal resources.
     ///
     /// This method can be used to re-initialize and reuse the renderer
     /// without constructing a new one.
-    void clear();
+    virtual void clear() = 0;
+
     /// \brief Write an unsigned 8-bit integer into the internal buffer.
     ///
     /// \param data The 8-bit integer to be written into the internal buffer.
-    void writeUint8(uint8_t data);
+    virtual void writeUint8(uint8_t data) = 0;
+
     /// \brief Write an unsigned 16-bit integer in host byte order into the
     /// internal buffer in network byte order.
     ///
     /// \param data The 16-bit integer to be written into the buffer.
-    void writeUint16(uint16_t data);
+    virtual void writeUint16(uint16_t data) = 0;
+
     /// \brief Write an unsigned 16-bit integer in host byte order at the
     /// specified position of the internal buffer in network byte order.
     ///
@@ -223,26 +217,23 @@ public:
     ///
     /// \param data The 16-bit integer to be written into the internal buffer.
     /// \param pos The beginning position in the buffer to write the data.
-    void writeUint16At(uint16_t data, size_t pos);
+    virtual void writeUint16At(uint16_t data, size_t pos) = 0;
+
     /// \brief Write an unsigned 32-bit integer in host byte order into the
     /// internal buffer in network byte order.
     ///
     /// \param data The 32-bit integer to be written into the buffer.
-    void writeUint32(uint32_t data);
+    virtual void writeUint32(uint32_t data) = 0;
+
     /// \brief Copy an arbitrary length of data into the internal buffer
-    /// of the \c MessageRenderer.
+    /// of the renderer object.
     ///
     /// No conversion on the copied data is performed.
     ///
     /// \param data A pointer to the data to be copied into the internal buffer.
     /// \param len The length of the data in bytes.
-    void writeData(const void *data, size_t len);
-    //@}
+    virtual void writeData(const void *data, size_t len) = 0;
 
-    ///
-    /// \name Rendering Methods
-    ///
-    //@{
     /// \brief Write a \c Name object into the internal buffer in wire format,
     /// with or without name compression.
     ///
@@ -257,7 +248,50 @@ public:
     ///
     /// \param name A \c Name object to be written.
     /// \param compress A boolean indicating whether to enable name compression.
-    void writeName(const Name& name, bool compress = true);
+    virtual void writeName(const Name& name, bool compress = true) = 0;
+    //@}
+};
+
+/// The \c MessageRenderer is a concrete derived class of
+/// \c AbstractMessageRenderer as a general purpose implementation of the
+/// renderer interfaces.
+///
+/// A \c MessageRenderer object is constructed with a \c OutputBuffer
+/// 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 renderer object will start rendering from the
+/// end of the buffer at the time of construction.  However, if the
+/// pre-existing portion of the buffer contains DNS names, these names won't
+/// be considered for name compression.
+class MessageRenderer : public AbstractMessageRenderer {
+public:
+    using AbstractMessageRenderer::CASE_INSENSITIVE;
+    using AbstractMessageRenderer::CASE_SENSITIVE;
+
+    /// \brief Constructor from an output buffer.
+    ///
+    /// \param buffer An \c OutputBuffer object to which wire format data is
+    /// written.
+    MessageRenderer(OutputBuffer& buffer);
+
+    virtual ~MessageRenderer();
+    virtual const void* getData() const;
+    virtual size_t getLength() const;
+    virtual bool isTruncated() const;
+    virtual size_t getLengthLimit() const;
+    virtual CompressMode getCompressMode() const;
+    virtual void setTruncated();
+    virtual void setLengthLimit(size_t len);
+    virtual void setCompressMode(CompressMode mode);
+    virtual void skip(size_t len);
+    virtual void trim(size_t len);
+    virtual void clear();
+    virtual void writeUint8(uint8_t data);
+    virtual void writeUint16(uint16_t data);
+    virtual void writeUint16At(uint16_t data, size_t pos);
+    virtual void writeUint32(uint32_t data);
+    virtual void writeData(const void *data, size_t len);
+    virtual void writeName(const Name& name, bool compress = true);
 private:
     struct MessageRendererImpl;
     MessageRendererImpl* impl_;

+ 1 - 2
src/lib/dns/name.cc

@@ -29,7 +29,6 @@
 
 using namespace std;
 using isc::dns::NameComparisonResult;
-using isc::dns::MessageRenderer;
 
 namespace isc {
 namespace dns {
@@ -405,7 +404,7 @@ Name::toWire(OutputBuffer& buffer) const {
 }
 
 void
-Name::toWire(MessageRenderer& renderer) const {
+Name::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeName(*this);
 }
 

+ 2 - 2
src/lib/dns/name.h

@@ -28,7 +28,7 @@ namespace isc {
 namespace dns {
 class InputBuffer;
 class OutputBuffer;
-class MessageRenderer;
+class AbstractMessageRenderer;
 
 ///
 /// \brief A standard DNS module exception that is thrown if the name parser
@@ -349,7 +349,7 @@ public:
     ///
     /// \param renderer DNS message rendering context that encapsulates the
     /// output buffer and name compression information.
-    void toWire(MessageRenderer& renderer) const;
+    void toWire(AbstractMessageRenderer& renderer) const;
 
     /// \brief Render the <code>Name</code> in the wire format without
     /// compression.

+ 1 - 1
src/lib/dns/rdata.cc

@@ -230,7 +230,7 @@ Generic::toWire(OutputBuffer& buffer) const {
 }
 
 void
-Generic::toWire(MessageRenderer& renderer) const {
+Generic::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeData(&impl_->data_[0], impl_->data_.size());
 }
 

+ 3 - 3
src/lib/dns/rdata.h

@@ -27,7 +27,7 @@ namespace isc {
 namespace dns {
 class InputBuffer;
 class OutputBuffer;
-class MessageRenderer;
+class AbstractMessageRenderer;
 class RRType;
 class RRClass;
 class Name;
@@ -180,7 +180,7 @@ public:
     ///
     /// \param renderer DNS message rendering context that encapsulates the
     /// output buffer in which the \c Rdata is to be stored.
-    virtual void toWire(MessageRenderer& renderer) const = 0;
+    virtual void toWire(AbstractMessageRenderer& renderer) const = 0;
     //@}
 
     ///
@@ -329,7 +329,7 @@ public:
     ///
     /// \param renderer DNS message rendering context that encapsulates the
     /// output buffer in which the \c Generic object is to be stored.
-    virtual void toWire(MessageRenderer& renderer) const;
+    virtual void toWire(AbstractMessageRenderer& renderer) const;
     //@}
     ///
     /// \name Comparison method

+ 1 - 1
src/lib/dns/rdata/ch_3/a_1.cc

@@ -48,7 +48,7 @@ A::toWire(OutputBuffer& buffer UNUSED_PARAM) const {
 }
 
 void
-A::toWire(MessageRenderer& renderer UNUSED_PARAM) const {
+A::toWire(AbstractMessageRenderer& renderer UNUSED_PARAM) const {
     // TBD
 }
 

+ 1 - 1
src/lib/dns/rdata/generic/cname_5.cc

@@ -54,7 +54,7 @@ CNAME::toWire(OutputBuffer& buffer) const {
 }
 
 void
-CNAME::toWire(MessageRenderer& renderer) const {
+CNAME::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeName(cname_);
 }
 

+ 1 - 1
src/lib/dns/rdata/generic/dname_39.cc

@@ -54,7 +54,7 @@ DNAME::toWire(OutputBuffer& buffer) const {
 }
 
 void
-DNAME::toWire(MessageRenderer& renderer) const {
+DNAME::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeName(dname_);
 }
 

+ 1 - 1
src/lib/dns/rdata/generic/dnskey_48.cc

@@ -136,7 +136,7 @@ DNSKEY::toWire(OutputBuffer& buffer) const {
 }
 
 void
-DNSKEY::toWire(MessageRenderer& renderer) const {
+DNSKEY::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeUint16(impl_->flags_);
     renderer.writeUint8(impl_->protocol_);
     renderer.writeUint8(impl_->algorithm_);

+ 1 - 1
src/lib/dns/rdata/generic/ds_43.cc

@@ -132,7 +132,7 @@ DS::toWire(OutputBuffer& buffer) const {
 }
 
 void
-DS::toWire(MessageRenderer& renderer) const {
+DS::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeUint16(impl_->tag_);
     renderer.writeUint8(impl_->algorithm_);
     renderer.writeUint8(impl_->digest_type_);

+ 1 - 1
src/lib/dns/rdata/generic/mx_15.cc

@@ -73,7 +73,7 @@ MX::toWire(OutputBuffer& buffer) const {
 }
 
 void
-MX::toWire(MessageRenderer& renderer) const {
+MX::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeUint16(preference_);
     renderer.writeName(mxname_);
 }

+ 1 - 1
src/lib/dns/rdata/generic/ns_2.cc

@@ -50,7 +50,7 @@ NS::toWire(OutputBuffer& buffer) const {
 }
 
 void
-NS::toWire(MessageRenderer& renderer) const {
+NS::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeName(nsname_);
 }
 

+ 1 - 1
src/lib/dns/rdata/generic/nsec3_50.cc

@@ -249,7 +249,7 @@ NSEC3::toWire(OutputBuffer& buffer) const {
 }
 
 void
-NSEC3::toWire(MessageRenderer& renderer) const {
+NSEC3::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeUint8(impl_->hashalg_);
     renderer.writeUint8(impl_->flags_);
     renderer.writeUint16(impl_->iterations_);

+ 1 - 1
src/lib/dns/rdata/generic/nsec3param_51.cc

@@ -136,7 +136,7 @@ NSEC3PARAM::toWire(OutputBuffer& buffer) const {
 }
 
 void
-NSEC3PARAM::toWire(MessageRenderer& renderer) const {
+NSEC3PARAM::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeUint8(impl_->hashalg_);
     renderer.writeUint8(impl_->flags_);
     renderer.writeUint16(impl_->iterations_);

+ 1 - 1
src/lib/dns/rdata/generic/nsec_47.cc

@@ -208,7 +208,7 @@ NSEC::toWire(OutputBuffer& buffer) const {
 }
 
 void
-NSEC::toWire(MessageRenderer& renderer) const {
+NSEC::toWire(AbstractMessageRenderer& renderer) const {
     impl_->nextname_.toWire(renderer);
     renderer.writeData(&impl_->typebits_[0], impl_->typebits_.size());
 }

+ 1 - 1
src/lib/dns/rdata/generic/opt_41.cc

@@ -58,7 +58,7 @@ OPT::toWire(OutputBuffer& buffer UNUSED_PARAM) const {
 }
 
 void
-OPT::toWire(MessageRenderer& renderer UNUSED_PARAM) const {
+OPT::toWire(AbstractMessageRenderer& renderer UNUSED_PARAM) const {
     // nothing to do, as this simple version doesn't support any options.
 }
 

+ 1 - 1
src/lib/dns/rdata/generic/ptr_12.cc

@@ -55,7 +55,7 @@ PTR::toWire(OutputBuffer& buffer) const {
 }
 
 void
-PTR::toWire(MessageRenderer& renderer) const {
+PTR::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeName(ptr_name_);
 }
 

+ 1 - 1
src/lib/dns/rdata/generic/rrsig_46.cc

@@ -187,7 +187,7 @@ RRSIG::toWire(OutputBuffer& buffer) const {
 }
 
 void
-RRSIG::toWire(MessageRenderer& renderer) const {
+RRSIG::toWire(AbstractMessageRenderer& renderer) const {
     impl_->covered_.toWire(renderer);
     renderer.writeUint8(impl_->algorithm_);
     renderer.writeUint8(impl_->labels_);

+ 1 - 1
src/lib/dns/rdata/generic/soa_6.cc

@@ -101,7 +101,7 @@ SOA::toWire(OutputBuffer& buffer) const {
 }
 
 void
-SOA::toWire(MessageRenderer& renderer) const {
+SOA::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeName(mname_);
     renderer.writeName(rname_);
     renderer.writeData(numdata_, sizeof(numdata_));

+ 1 - 1
src/lib/dns/rdata/generic/txt_16.cc

@@ -103,7 +103,7 @@ TXT::toWire(OutputBuffer& buffer) const {
 }
 
 void
-TXT::toWire(MessageRenderer& renderer) const {
+TXT::toWire(AbstractMessageRenderer& renderer) const {
     for (vector<vector<uint8_t> >::const_iterator it = string_list_.begin();
          it != string_list_.end();
          ++it)

+ 1 - 1
src/lib/dns/rdata/hs_4/a_1.cc

@@ -48,7 +48,7 @@ A::toWire(OutputBuffer& buffer UNUSED_PARAM) const {
 }
 
 void
-A::toWire(MessageRenderer& renderer UNUSED_PARAM) const {
+A::toWire(AbstractMessageRenderer& renderer UNUSED_PARAM) const {
     // TBD
 }
 

+ 1 - 1
src/lib/dns/rdata/in_1/a_1.cc

@@ -71,7 +71,7 @@ A::toWire(OutputBuffer& buffer) const {
 }
 
 void
-A::toWire(MessageRenderer& renderer) const {
+A::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeData(&addr_, sizeof(addr_));
 }
 

+ 1 - 1
src/lib/dns/rdata/in_1/aaaa_28.cc

@@ -68,7 +68,7 @@ AAAA::toWire(OutputBuffer& buffer) const {
 }
 
 void
-AAAA::toWire(MessageRenderer& renderer) const {
+AAAA::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeData(&addr_, sizeof(addr_));
 }
 

+ 1 - 1
src/lib/dns/rdata/template.cc

@@ -52,7 +52,7 @@ MyType::toWire(OutputBuffer& buffer) const {
 }
 
 void
-MyType::toWire(MessageRenderer& renderer) const {
+MyType::toWire(AbstractMessageRenderer& renderer) const {
 }
 
 int

+ 249 - 0
src/lib/dns/rdatafields.cc

@@ -0,0 +1,249 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdint.h>
+
+#include <cassert>
+#include <vector>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/buffer.h>
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdatafields.h>
+
+using namespace std;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+namespace isc {
+namespace dns {
+namespace rdata {
+
+/// This is a helper class for \c RdataFields.
+///
+/// It manages a local storage for the data when \c RdataFields is constructed
+/// from an \c Rdata.
+/// The implementation is hidden here to hide non portable details such as
+/// std::vector.
+/// To minimize construction overhead in the other case, an instance of
+/// this class is instantiated only when necessary.
+struct RdataFields::RdataFieldsDetail {
+    RdataFieldsDetail(const vector<FieldSpec>& fields,
+                      const void* data, size_t data_length) :
+        allocated_fields_(fields),
+        allocated_data_(static_cast<const uint8_t*>(data),
+                        static_cast<const uint8_t*>(data) + data_length)
+    {}
+    const vector<FieldSpec> allocated_fields_;
+    const vector<uint8_t> allocated_data_;
+};
+
+namespace {
+// This class is used to divide the content of RDATA into \c RdataField
+// fields via message rendering logic.
+// The idea is to identify domain name fields in the writeName() method,
+// and determine whether they are compressible using the "compress"
+// parameter.
+// Other types of data are simply copied into the internal buffer, and
+// consecutive such fields are combined into a single \c RdataField field.
+//
+// Technically, this use of inheritance may be considered a violation of
+// Liskov Substitution Principle in that it doesn't actually compress domain
+// names, and some of the methods are not expected to be used.
+// In fact, skip() or trim() may not be well defined for the purpose of this
+// class.
+// Nevertheless we keep this idea at the moment.  Since the usage is limited
+// (it's only used within this file, and only used with \c Rdata variants),
+// it's hopefully an acceptable practice.
+class RdataFieldComposer : public AbstractMessageRenderer {
+public:
+    RdataFieldComposer(OutputBuffer& buffer) :
+        buffer_(buffer), truncated_(false), length_limit_(65535),
+        mode_(CASE_INSENSITIVE)
+    {}
+    virtual ~RdataFieldComposer() {}
+    virtual const void* getData() const { return (buffer_.getData()); }
+    virtual size_t getLength() const { return (buffer_.getLength()); }
+    virtual bool isTruncated() const { return (truncated_); }
+    virtual size_t getLengthLimit() const { return (length_limit_); }
+    virtual CompressMode getCompressMode() const { return (mode_); }
+    virtual void setTruncated() { truncated_ = true; }
+    virtual void setLengthLimit(size_t len) { length_limit_ = len; }
+    virtual void setCompressMode(CompressMode mode) { mode_ = mode; }
+    virtual void writeUint8(uint8_t data);
+    virtual void writeUint16(uint16_t data);
+    virtual void writeUint32(uint32_t data);
+    virtual void writeData(const void *data, size_t len);
+    virtual void writeName(const Name& name, bool compress);
+
+    virtual void clear() {
+        isc_throw(Unexpected, "unexpected clear() for RdataFieldComposer");
+    }
+    virtual void skip(size_t) {
+        isc_throw(Unexpected, "unexpected skip() for RdataFieldComposer");
+    }
+    virtual void trim(size_t) {
+        isc_throw(Unexpected, "unexpected trim() for RdataFieldComposer");
+    }
+    virtual void writeUint16At(uint16_t, size_t) {
+        isc_throw(Unexpected,
+                  "unexpected writeUint16At() for RdataFieldComposer");
+    }
+    OutputBuffer buffer_;
+    bool truncated_;
+    size_t length_limit_;
+    CompressMode mode_;
+    vector<RdataFields::FieldSpec> fields_;
+};
+
+void
+RdataFieldComposer::writeUint8(uint8_t data) {
+    buffer_.writeUint8(data);
+    if (fields_.empty() || fields_.back().type != RdataFields::DATA) {
+        fields_.push_back(RdataFields::FieldSpec(RdataFields::DATA, 0));
+    }
+    fields_.back().len += sizeof(data);
+}
+
+void
+RdataFieldComposer::writeUint16(uint16_t data) {
+    buffer_.writeUint16(data);
+    if (fields_.empty() || fields_.back().type != RdataFields::DATA) {
+        fields_.push_back(RdataFields::FieldSpec(RdataFields::DATA, 0));
+    }
+    fields_.back().len += sizeof(data);
+}
+
+void
+RdataFieldComposer::writeUint32(uint32_t data) {
+    buffer_.writeUint32(data);
+    if (fields_.empty() || fields_.back().type != RdataFields::DATA) {
+        fields_.push_back(RdataFields::FieldSpec(RdataFields::DATA, 0));
+    }
+    fields_.back().len += sizeof(data);
+}
+
+void
+RdataFieldComposer::writeData(const void *data, size_t len) {
+    buffer_.writeData(data, len);
+    if (fields_.empty() || fields_.back().type != RdataFields::DATA) {
+        fields_.push_back(RdataFields::FieldSpec(RdataFields::DATA, 0));
+    }
+    fields_.back().len += len;
+}
+
+void
+RdataFieldComposer::writeName(const Name& name, bool compress) {
+    const RdataFields::Type field_type =
+        compress ? RdataFields::COMPRESSIBLE_NAME :
+        RdataFields::INCOMPRESSIBLE_NAME;
+    name.toWire(buffer_);
+    fields_.push_back(RdataFields::FieldSpec(field_type, name.getLength()));
+}
+}
+
+RdataFields::RdataFields(const Rdata& rdata) {
+    OutputBuffer buffer(0);
+    RdataFieldComposer field_composer(buffer);
+    rdata.toWire(field_composer);
+    nfields_ = field_composer.fields_.size();
+    data_length_ = field_composer.getLength();
+    if (nfields_ > 0) {
+        assert(data_length_ > 0);
+        detail_ = new RdataFieldsDetail(field_composer.fields_,
+                                        field_composer.getData(),
+                                        field_composer.getLength());
+        data_ = &detail_->allocated_data_[0];
+        fields_ = &detail_->allocated_fields_[0];
+    } else {
+        assert(data_length_ == 0);
+        detail_ = NULL;
+        data_ = NULL;
+        fields_ = NULL;
+    }
+}
+
+RdataFields::RdataFields(const FieldSpec* fields, const unsigned int nfields,
+                         const uint8_t* data, const size_t data_length) :
+    fields_(fields), nfields_(nfields), data_(data), data_length_(data_length),
+    detail_(NULL)
+{
+    if ((fields_ == NULL && nfields_ > 0) ||
+        (fields_ != NULL && nfields_ == 0)) {
+        isc_throw(InvalidParameter,
+                  "Inconsistent parameters for RdataFields: nfields ("
+                  << nfields_ << ") and fields conflict each other");
+    }
+    if ((data_ == NULL && data_length_ > 0) ||
+        (data_ != NULL && data_length_ == 0)) {
+        isc_throw(InvalidParameter,
+                  "Inconsistent parameters for RdataFields: data length ("
+                  << data_length_ << ") and data conflict each other");
+    }
+
+    size_t total_length = 0;
+    for (int i = 0; i < nfields_; ++i) {
+        total_length += fields_[i].len;
+    }
+    if (total_length != data_length_) {
+        isc_throw(InvalidParameter,
+                  "Inconsistent parameters for RdataFields; "
+                  "fields len: " << total_length <<
+                  " data len: " << data_length_);
+    }
+}
+
+RdataFields::~RdataFields() {
+    delete detail_;
+}
+
+RdataFields::FieldSpec
+RdataFields::getFieldSpec(const unsigned int field_id) const {
+    if (field_id >= nfields_) {
+        isc_throw(OutOfRange, "Rdata field ID is out of range: " << field_id);
+    }
+    return (fields_[field_id]);
+}
+
+void
+RdataFields::toWire(AbstractMessageRenderer& renderer) const {
+    size_t offset = 0;
+
+    for (int i = 0; i < nfields_; ++i) {
+        if (fields_[i].type == DATA) {
+            renderer.writeData(data_ + offset, fields_[i].len);
+        } else {
+            // XXX: this is inefficient.  Even if it's quite likely the
+            // data is a valid wire representation of a name we parse
+            // it to construct the Name object in the generic mode.
+            // This should be improved in a future version.
+            InputBuffer buffer(data_ + offset, fields_[i].len);
+            renderer.writeName(Name(buffer),
+                               fields_[i].type == COMPRESSIBLE_NAME ?
+                               true : false);
+        }   
+        offset += fields_[i].len;
+    }
+}
+
+void
+RdataFields::toWire(OutputBuffer& buffer) const {
+    buffer.writeData(data_, data_length_);
+}
+} // end of namespace rdata
+} // end of namespace dns
+} // end of namespace isc

+ 387 - 0
src/lib/dns/rdatafields.h

@@ -0,0 +1,387 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __RDATAFIELDS_H
+#define __RDATAFIELDS_H 1
+
+#include <stdint.h>
+
+#include <cstddef>
+
+namespace isc {
+namespace dns {
+class OutputBuffer;
+class AbstractMessageRenderer;
+
+namespace rdata {
+class Rdata;
+
+/// A low-level, RR type-independent representation of DNS RDATA.
+///
+/// <b>Purpose of the Class</b>
+///
+/// This class intends to help "serialization" of the content of RDATA
+/// in a space-efficient manner.  Specific derived classes of \c Rdata
+/// focus on the convenience of accessing RDATA fields for RR type-specific
+/// protocol operations, and can be inefficient in terms of space.
+/// For example, a DNS character string may be internally represented as a
+/// \c std::string object with all of the overhead of the richer class.
+/// If an application needs to maintain a very large number of RRs and it
+/// does not have to perform RR specific operation so often, it may make more
+/// sense to store the data in memory in a lower-level but space efficient
+/// form.
+///
+/// Another purpose of this class is to improve rendering performance for
+/// RDATA.  If the only requirement were space efficiency, it would be just
+/// sufficient to convert the \c RDATA into a binary sequence in the wire
+/// format.  However, to render the data in a DNS message, we'd have to
+/// re-construct a corresponding \c Rdata object in the case where name
+/// compression is necessary.  This is not desirable, and this class is
+/// provided to avoid such unnecessary overhead.
+///
+/// <b>Data Format</b>
+///
+/// To meet these goals, this class helps convert an \c Rdata object into
+/// two pieces of information: Wire-format representation of the \c Rdata
+/// and associated meta information for efficient rendering.
+///
+/// Specifically, it maintains the wire-format data as a sequence of typed
+/// fields.  The types are:
+/// - Compressible name: a domain name as an RDATA field that can be compressed
+/// - Incompressible name: a domain name as an RDATA field that cannot be
+///   compressed
+/// - Other data: any other fields of RDATA, which should be treated as opaque
+///
+/// (See also the description of \c RdataFields::Type)
+/// Whether a name can or cannot be compressed is determined according to
+/// RFC3597.
+///
+/// A "other data" field may not always correspond to a single RDATA field.
+/// A \c RdataFields field (of other data) is just a contiguous region of the
+/// wire-format data that does not involve name compression.
+/// For example, the SOA RDATA begins with two "compressible" names followed
+/// by 5 32-bit fields.
+/// In \c RdataFields the last 5 fields would be considered a single 20-byte
+/// field.
+///
+/// Each \c RdataFields field is identified by the \c FieldSpec structure,
+/// which provides the type and length of the field.
+/// An \c RdataFields object internally maintains a sequence of \c FieldSpec
+/// objects in a form of plain C-style array, which can be referenced via
+/// a pointer returned by the \c getFieldSpecData() method.
+/// The \c \c FieldSpec for a specific field can also be retrieved by the
+/// \c getFieldSpec() method.
+///
+/// The following diagram shows the internal memory representation of
+/// an SOA RDATA in the form of \c RdataFields object and how an application
+/// can get access to the memory region.
+/** \verbatim
+accessible via      |0                               getDataLength() bytes
+getData()----------> <MNAME><RNAME><Rest of the data>
+                     <---------- 3 * sizeof(FieldSpec) bytes ------------->
+getFieldSpecData()-> { compressible name { compressible name { other data
+                       len: MNAME-len }    len: RNAME-len }    len: 20    }
+\endverbatim
+ */
+/// where MNAME and RNAME are wire format representations of the MNAME and
+/// RNAME fields of the SOA RDATA, respectively, and "Rest of the data"
+/// encodes the remaining 20 bytes of the RDATA in network byte order.
+///
+/// <b>Usage of the Class</b>
+///
+/// One major and common use case of the \c RdataFields class is to convert
+/// a \c Rdata object (possibly given from a DNS message or some configuration
+/// source such as a zone file) in the serialized format and store a copy of
+/// the data somewhere in memory.  The following code sample implements this
+/// scenario:
+/// \code // assume "rdata" is a reference type to \c Rdata
+/// const RdataFields fields(rdata);
+/// const unsigned int nfields = fields.getFieldCount();
+/// memcpy(some_place, fields.getFieldSpecData(), nfields*sizeof(FieldSpec));
+/// const size_t data_length = fields.getDataLength();
+/// memcpy(other_place, fields.getData(), data_length);
+/// // (nfields and data_length should be stored somewhere, too)
+/// \endcode
+///
+/// Another typical usage is to render the stored data in the wire format
+/// as efficiently as possible.  The following code is an example of such
+/// usage:
+/// \code // assume "renderer" is of type MessageRenderer
+/// // retrieve data_length and nfields from the storage
+/// RdataFields(static_cast<const FieldSpec*>(some_place),
+///             nfields, other_place, data_length).toWire(renderer);
+/// \endcode
+///
+/// <b>Notes to Users</b>
+///
+/// The main purposes of this class is to help efficient operation
+/// for some (limited classes of) performance sensitive application.
+/// For this reason the interface and implementation rely on relatively
+/// lower-level, riskier primitives such as passing around bear pointers.
+///
+/// It is therefore discouraged to use this class for general purpose
+/// applications that do not need to maximize performance in terms of either
+/// memory footprint or rendering speed.
+/// All functionality provided by this class can be achieved via higher level
+/// interfaces such as the \c Rdata class variants.
+/// Normal applications should use those interfaces.
+///
+/// The data format is public information so that an application can examine
+/// and use selected parts of data.  For example, an application may want to
+/// encode domain names in RDATA in a different way while storing the other
+/// data in a separate place.
+/// However, at this moment the format is still in flux, and it may not
+/// be compatible with future versions (see below).
+///
+/// <b>Development Notes</b>
+///
+/// We should conduct benchmark tests to measure rendering performance.
+///
+/// The current implementation needs to re-construct name objects from
+/// compressible and incompressible name fields as wire-format data.
+/// This is not efficient, and we'll probably want to improve this in a
+/// future version.  One possibility is to store offset information as well
+/// as the name data (at the cost of increasing memory footprint), and
+/// to use the pair of data for faster rendering.
+class RdataFields {
+public:
+    /// Types of \c RdataFields fields.
+    ///
+    /// \c COMPRESSIBLE_NAME and \c INCOMPRESSIBLE_NAME represent a domain
+    /// name used as a field of an RDATA that can and cannot be compressed
+    /// per RFC3597.
+    /// \c DATA means all other types of fields.
+    enum Type {
+        DATA,              ///< Plain data.
+        COMPRESSIBLE_NAME, ///< A domain name subject to name compression.
+        INCOMPRESSIBLE_NAME ///< A domain name that shouldn't be compressed.
+    };
+
+    /// Structure that specifies a single \c RdataFields field.
+    ///
+    /// This is a straightforward pair of the type and length of a single
+    /// \c RdataFields field.
+    ///
+    /// In some cases an application may want to do deeper inspection of
+    /// some \c RdataFields field(s).  For example, an application may want
+    /// to construct a \c Name object for each domain name field of an RDATA
+    /// and use it for some special purpose.
+    /// The \c FieldSpec structure provides necessary parameters to get access
+    /// to a specific \c RdataFields field.
+    ///
+    /// The following code snippet implements the above example scenario:
+    /// \code // assume "fields" is of type RdataFields
+    /// size_t offset = 0;
+    /// for (int i = 0; i < fields.getFieldCount(); ++i) {
+    ///     const FieldSpec spec = fields.getFieldSpec(i);
+    ///     if (spec.type == RdataFields::COMPRESSIBLE_NAME ||
+    ///         spec.type == RdataFields::INCOMPRESSIBLE_NAME) {
+    ///         InputBuffer ibuffer(fields.getData() + offset, spec.len);
+    ///         Name name(ibuffer);
+    ///         // do something with name
+    ///     }
+    ///     offset += spec.len;
+    /// } \endcode
+    ///
+    /// Note that the offset is not included in \c FieldSpec.
+    /// This is because such deeper inspection would be a relatively rare
+    /// operation while it is desirable to keep this structure as small as
+    /// possible for the purpose of space efficiency.
+    /// Also, if and when an application wants to look into a specific field,
+    /// it would be quite likely that the application iterates over all fields
+    /// and does something special for selected fields like the above example.
+    /// In that case the application can easily and efficiently identify the
+    /// necessary offset, again, as shown in the above code example.
+    struct FieldSpec {
+        FieldSpec(Type type_param, uint16_t len_param) :
+            type(type_param), len(len_param)
+        {}
+        Type type;              ///< The type of the field.
+        uint16_t len;           ///< The length of the field in bytes.
+    };
+
+    ///
+    /// \name Constructors and Destructor.
+    ///
+    /// \b Note:
+    /// The copy constructor and the assignment operator are intentionally
+    /// defined as private, making this class non copyable.
+    //@{
+private:
+    RdataFields(const RdataFields& source);
+    RdataFields& operator=(const RdataFields& source);
+
+public:
+    /// Constructor from Rdata.
+    ///
+    /// This constructor converts the data of a given \c Rdata object into
+    /// an \c RdataFields object so that the resulting data can be stored
+    /// in memory in a space-efficient way.
+    ///
+    /// It makes a local copy of the original data and dynamically allocates
+    /// necessary memory, so is not very efficient.
+    /// The basic idea is to perform the expensive conversion once and keep
+    /// using the result as long as possible to improve overall performance
+    /// in a longer term.
+    ///
+    /// If the internal resource allocation fails, a corresponding standard
+    /// exception will be thrown.
+    /// The current implementation of this constructor internally calls
+    /// the <code>Rdata::toWire(AbstractMessageRenderer&) const</code> method
+    /// for the conversion.
+    /// If that method throws an exception it will be propagated to the caller
+    /// of this constructor.
+    ///
+    /// \param rdata The RDATA for which the \c RdataFields to be constructed.
+    RdataFields(const Rdata& rdata);
+
+    /// Constructor from field parameters.
+    ///
+    /// The intended usage of this version of constructor is to form a
+    /// structured representation of \c RDATA encoded by the other
+    /// constructor so that the resulting object can be used for subsequent
+    /// operations such as rendering in the wire format.
+    /// This version is intended to be efficient by not making any copy
+    /// of variable length data or expensive data inspection.
+    ///
+    /// This constructor is basically exception free, except against bogus
+    /// input parameters.
+    /// Specifically, the parameters must meet the following conditions;
+    /// otherwise an exception of class \c InvalidParameter will be thrown.
+    /// - \c fields can be \c NULL if and only if \c nfields is 0
+    /// - \c data can be \c NULL if and only if \c data_length is 0
+    /// - the sum of the lengths of \c fields entries must be equal to
+    ///   \c data_length
+    ///
+    /// This constructor assumes that the memory region pointed by \c data (if
+    /// non \c NULL) is encoded as a sequence of valid \c RdataFields fields,
+    /// and does not perform deep inspection on each field.
+    /// In particular, for fields of type \c COMPRESSIBLE_NAME or
+    /// \c INCOMPRESSIBLE_NAME, this constructor assumes the corresponding
+    /// memory region is a valid representation of domain name.
+    /// Otherwise, a subsequent method call such as
+    /// <code>toWire(AbstractMessageRenderer&) const</code>
+    /// may trigger an unexpected exception.
+    ///
+    /// It is the caller's responsibility to ensure this assumption.
+    /// In general, this constructor is expected to be used for serialized data
+    /// generated by the other constructor from a valid \c Rdata.
+    /// The result is not guaranteed if the data is generated in any other
+    /// ways.
+    ///
+    /// The resulting \c RdataFields object does not maintain a copy of
+    /// \c fields or \c data.  It is the caller's responsibility to ensure
+    /// the memory regions pointed to by these parameters are valid and intact
+    /// as long as the \c RdataFields object is used.
+    ///
+    /// \param fields An array of \c FieldSpec entries.  This can be \c NULL.
+    /// \param nfields The number of entries of \c fields.
+    /// \param data A pointer to memory region for the entire RDATA.  This can
+    /// be NULL.
+    /// \param data_length The length of \c data in bytes.
+    RdataFields(const FieldSpec* fields, const unsigned int nfields,
+                const uint8_t* data, const size_t data_length);
+
+    /// The destructor.
+    ~RdataFields();
+    //@}
+
+    ///
+    /// \name Getter Methods
+    ///
+    //@{
+    /// \brief Return the length of the entire RDATA encoded in the
+    /// \c RdataFields in bytes.
+    ///
+    /// This method never throws an exception.
+    unsigned int getDataLength() const { return (data_length_); }
+
+    /// \brief Return a pointer to the RDATA encoded in the \c RdataFields.
+    ///
+    /// This method never throws an exception.
+    const uint8_t* getData() const { return (data_); }
+
+    /// \brief Return the number of fields encoded in the RdataFields.
+    ///
+    /// This method never throws an exception.
+    unsigned int getFieldCount() const { return (nfields_); }
+
+    /// \brief Return a pointer to a sequence of \c FieldSpec for the
+    /// \c RdataFields.
+    ///
+    /// This method never throws an exception.
+    const void* getFieldSpecData() const { return (fields_); } 
+
+    /// \brief Return the specification of the field identified by the given
+    /// index.
+    ///
+    /// \c field_id is the field index, which must be in the range of
+    /// <code>[0, getFieldCount())</code>.  0 means the first field, and
+    /// <code>getFieldCount()-1</code> means the last.
+    ///
+    /// If the given index is not in the valid range, an exception of class
+    /// \c OutOfRange will be thrown.
+    /// This method never throws an exception otherwise.
+    ///
+    /// \param field_id The index of an \c RdataFields field to be returned.
+    /// \return A \c FieldSpec structure that contains the information of
+    /// the \c field_id-th field.
+    FieldSpec getFieldSpec(const unsigned int field_id) const;
+    //@}
+
+    ///
+    /// \name Converter Methods
+    ///
+    //@{
+    /// \brief Render the RdataFields in the wire format with name compression.
+    ///
+    /// This method may require resource allocation in \c renderer.
+    /// If it fails, a corresponding standard exception will be thrown.
+    /// It should not throw any other exception as long as the \c RdataFields
+    /// object was constructed from valid parameters (see the description of
+    /// constructors).  The result is not guaranteed if it's constructed in
+    /// any other ways.
+    ///
+    /// \param renderer DNS message rendering context that encapsulates the
+    /// output buffer and name compression information.
+    void toWire(AbstractMessageRenderer& renderer) const;
+
+    /// \brief Render the RdataFields in the wire format without name
+    /// compression.
+    ///
+    /// This method may require resource allocation in \c buffer.
+    /// If it fails, a corresponding standard exception will be thrown.
+    ///
+    /// \param buffer An output buffer to store the wire data.
+    void toWire(OutputBuffer& buffer) const;
+    //@}
+
+private:
+    const FieldSpec* fields_;
+    unsigned int nfields_;
+    const uint8_t* data_;
+    size_t data_length_;
+
+    // hide further details within the implementation
+    struct RdataFieldsDetail;
+    RdataFieldsDetail* detail_;
+};
+}
+}
+}
+#endif  // __RDATAFIELDS_H
+
+// Local Variables:
+// mode: c++
+// End:

+ 2 - 2
src/lib/dns/rrtype-placeholder.h

@@ -30,7 +30,7 @@ namespace dns {
 // forward declarations
 class InputBuffer;
 class OutputBuffer;
-class MessageRenderer;
+class AbstractMessageRenderer;
 
 ///
 /// \brief A standard DNS module exception that is thrown if an RRType object
@@ -180,7 +180,7 @@ public:
     /// standard exception will be thrown.
     ///
     /// \param buffer An output buffer to store the wire data.
-    void toWire(MessageRenderer& renderer) const;
+    void toWire(AbstractMessageRenderer& renderer) const;
     /// \brief Render the \c RRType in the wire format.
     ///
     /// This method renders the type code in network byte order into the

+ 1 - 1
src/lib/dns/rrtype.cc

@@ -55,7 +55,7 @@ RRType::toWire(OutputBuffer& buffer) const {
 }
 
 void
-RRType::toWire(MessageRenderer& renderer) const {
+RRType::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeUint16(typecode_);
 }
 

+ 1 - 0
src/lib/dns/tests/Makefile.am

@@ -26,6 +26,7 @@ run_unittests_SOURCES += dnssectime_unittest.cc
 run_unittests_SOURCES += opcode_unittest.cc
 run_unittests_SOURCES += rcode_unittest.cc
 run_unittests_SOURCES += rdata_unittest.h rdata_unittest.cc
+run_unittests_SOURCES += rdatafields_unittest.cc
 run_unittests_SOURCES += rdata_in_a_unittest.cc rdata_in_aaaa_unittest.cc
 run_unittests_SOURCES += rdata_ns_unittest.cc rdata_soa_unittest.cc
 run_unittests_SOURCES += rdata_txt_unittest.cc rdata_mx_unittest.cc

+ 379 - 0
src/lib/dns/tests/rdatafields_unittest.cc

@@ -0,0 +1,379 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <vector>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/buffer.h>
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatafields.h>
+#include <dns/tests/unittest_util.h>
+
+#include <gtest/gtest.h>
+
+using isc::UnitTestUtil;
+using namespace std;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+namespace {
+class RdataFieldsTest : public ::testing::Test {
+protected:
+    RdataFieldsTest() : obuffer(0), renderer_buffer(0),
+                        renderer(renderer_buffer),
+                        ns_name("example.com"),
+                        other_name("www.example.com")
+    {}
+    void constructCommonTests(const RdataFields& fields,
+                              const uint8_t* const expected_data,
+                              const size_t expected_data_len);
+    void constructCommonTestsNS(const RdataFields& fields);
+    void constructCommonTestsTXT(const RdataFields& fields);
+    void constructCommonTestsRRSIG(const RdataFields& fields);
+    void constructCommonTestsOPT(const RdataFields& fields);
+    OutputBuffer obuffer;
+    OutputBuffer renderer_buffer;
+    MessageRenderer renderer;
+    const Name ns_name;
+    const Name other_name;
+    vector<unsigned char> expected_wire;
+    vector<unsigned char> fields_wire;
+};
+
+const uint8_t in_a_data[] = { 192, 0, 2, 1 };
+// binary representation of example.com.
+const uint8_t ns_data[] = { 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
+                            0x03, 0x63, 0x6f, 0x6d, 0x00 };
+
+//
+// IN/A RDATA: fixed length, single data field
+//
+void
+RdataFieldsTest::constructCommonTests(const RdataFields& fields,
+                                      const uint8_t* const expected_data,
+                                      const size_t expected_data_len)
+{
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, expected_data,
+                        expected_data_len, fields.getData(),
+                        fields.getDataLength());
+    EXPECT_EQ(1, fields.getFieldCount());
+    EXPECT_EQ(RdataFields::DATA, fields.getFieldSpec(0).type);
+    EXPECT_EQ(4, fields.getFieldSpec(0).len);
+
+    fields.toWire(obuffer);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, expected_data,
+                        expected_data_len, obuffer.getData(),
+                        obuffer.getLength());
+
+    fields.toWire(renderer);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, expected_data,
+                        expected_data_len, renderer.getData(),
+                        renderer.getLength());
+}
+
+TEST_F(RdataFieldsTest, constructFromRdata) {
+    const RdataFields fields(in::A("192.0.2.1"));
+    constructCommonTests(fields, in_a_data, sizeof(in_a_data));
+}
+
+TEST_F(RdataFieldsTest, constructFromParams) {
+    const RdataFields::FieldSpec spec(RdataFields::DATA, 4);
+    const RdataFields fields(&spec, 1, in_a_data, sizeof(in_a_data));
+    constructCommonTests(fields, in_a_data, sizeof(in_a_data));
+}
+
+//
+// NS RDATA: containing a compressible name.
+//
+void
+RdataFieldsTest::constructCommonTestsNS(const RdataFields& fields) {
+    EXPECT_EQ(1, fields.getFieldCount());
+    EXPECT_EQ(RdataFields::COMPRESSIBLE_NAME, fields.getFieldSpec(0).type);
+    EXPECT_EQ(ns_name.getLength(), fields.getFieldSpec(0).len);
+
+    expected_wire.clear();
+    UnitTestUtil::readWireData("rdatafields1.wire", expected_wire);
+    other_name.toWire(obuffer);
+    fields.toWire(obuffer);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &expected_wire[0],
+                        expected_wire.size(), obuffer.getData(),
+                        obuffer.getLength());
+
+    expected_wire.clear();
+    UnitTestUtil::readWireData("rdatafields2.wire", expected_wire);
+    other_name.toWire(renderer);
+    fields.toWire(renderer);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &expected_wire[0],
+                        expected_wire.size(), renderer.getData(),
+                        renderer.getLength());
+}
+
+TEST_F(RdataFieldsTest, constructFromRdataNS) {
+    const RdataFields fields_ns((generic::NS(ns_name)));
+    constructCommonTestsNS(fields_ns);
+}
+
+TEST_F(RdataFieldsTest, constructFromParamsNS) {
+    const RdataFields::FieldSpec spec(RdataFields::COMPRESSIBLE_NAME,
+                                      sizeof(ns_data));
+    const RdataFields fields_ns(&spec, 1, ns_data, sizeof(ns_data));
+    constructCommonTestsNS(fields_ns);
+}
+
+//
+// TXT RDATA: multiple fields, lengths vary
+//
+void
+RdataFieldsTest::constructCommonTestsTXT(const RdataFields& fields) {
+    // Since all fields are plain data, they are handled as a single data
+    // field.
+    EXPECT_EQ(1, fields.getFieldCount());
+    EXPECT_EQ(RdataFields::DATA, fields.getFieldSpec(0).type);
+    EXPECT_EQ(expected_wire.size(), fields.getFieldSpec(0).len);
+
+    fields.toWire(obuffer);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &expected_wire[0],
+                        expected_wire.size(), obuffer.getData(),
+                        obuffer.getLength());
+
+    fields.toWire(renderer);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &expected_wire[0],
+                        expected_wire.size(), renderer.getData(),
+                        renderer.getLength());
+}
+
+TEST_F(RdataFieldsTest, constructFromRdataTXT) {
+    UnitTestUtil::readWireData("rdatafields3.wire", expected_wire);
+    InputBuffer ibuffer(&expected_wire[0], expected_wire.size());
+    const uint16_t rdlen = ibuffer.readUint16();
+    const RdataFields fields(generic::TXT(ibuffer, rdlen));
+
+    // drop the RDLEN part
+    expected_wire.erase(expected_wire.begin(), expected_wire.begin() + 2);
+
+    constructCommonTestsTXT(fields);
+}
+
+TEST_F(RdataFieldsTest, constructFromParamsTXT) {
+    UnitTestUtil::readWireData("rdatafields3.wire", expected_wire);
+    expected_wire.erase(expected_wire.begin(), expected_wire.begin() + 2);
+    const RdataFields::FieldSpec spec(RdataFields::DATA, expected_wire.size());
+    const RdataFields fields(&spec, 1, &expected_wire[0],
+                             expected_wire.size()); 
+    constructCommonTestsTXT(fields);
+}
+
+//
+// RRSIG: multiple fields, with an incompressible name
+//
+void
+RdataFieldsTest::constructCommonTestsRRSIG(const RdataFields& fields) {
+    // In terms of RdataFields RRSIG RDATA consists of 3 fields:
+    // - 18-byte data field (from the "type covered" field to "key tag" field)
+    // - an incompressible name field (for the signer's name field).
+    //   this is a variable length field.  In this test it's a 13-byte field.
+    // - a variable-length data field for the signature.  In this tests
+    //   it's a 15-byte field.
+    ASSERT_EQ(3, fields.getFieldCount());
+    EXPECT_EQ(RdataFields::DATA, fields.getFieldSpec(0).type);
+    EXPECT_EQ(18, fields.getFieldSpec(0).len);
+    EXPECT_EQ(RdataFields::INCOMPRESSIBLE_NAME, fields.getFieldSpec(1).type);
+    EXPECT_EQ(13, fields.getFieldSpec(1).len);
+    EXPECT_EQ(RdataFields::DATA, fields.getFieldSpec(2).type);
+    EXPECT_EQ(15, fields.getFieldSpec(2).len);
+
+    expected_wire.clear();
+    UnitTestUtil::readWireData("rdatafields5.wire", expected_wire);
+    Name("com").toWire(obuffer);
+    obuffer.writeUint16(fields.getDataLength());
+    fields.toWire(obuffer);
+    other_name.toWire(obuffer);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &expected_wire[0],
+                        expected_wire.size(), obuffer.getData(),
+                        obuffer.getLength());
+
+    expected_wire.clear();
+    UnitTestUtil::readWireData("rdatafields6.wire", expected_wire);
+    Name("com").toWire(renderer);
+    renderer.writeUint16(fields.getDataLength());
+    fields.toWire(renderer);    // the signer field won't be compressed
+    other_name.toWire(renderer); // but will be used as a compression target
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &expected_wire[0],
+                        expected_wire.size(), renderer.getData(),
+                        renderer.getLength());
+}
+
+TEST_F(RdataFieldsTest, constructFromRdataRRSIG) {
+    UnitTestUtil::readWireData("rdatafields4.wire", expected_wire);
+    InputBuffer ibuffer(&expected_wire[0], expected_wire.size());
+    const uint16_t rdlen = ibuffer.readUint16();
+    const RdataFields fields(generic::RRSIG(ibuffer, rdlen));
+
+    // drop the RDLEN part
+    expected_wire.erase(expected_wire.begin(), expected_wire.begin() + 2);
+
+    constructCommonTestsRRSIG(fields);
+}
+
+TEST_F(RdataFieldsTest, constructFromParamsRRSIG) {
+    UnitTestUtil::readWireData("rdatafields4.wire", fields_wire);
+    fields_wire.erase(fields_wire.begin(), fields_wire.begin() + 2);
+
+    const RdataFields::FieldSpec specs[] = {
+        RdataFields::FieldSpec(RdataFields::DATA, 18),
+        RdataFields::FieldSpec(RdataFields::INCOMPRESSIBLE_NAME, 13),
+        RdataFields::FieldSpec(RdataFields::DATA, 15)
+    };
+    const RdataFields fields(specs, 3, &fields_wire[0], fields_wire.size());
+    constructCommonTestsRRSIG(fields);
+}
+
+TEST_F(RdataFieldsTest, convertRdatatoParams) {
+    // Confirm we can restore the original data from the serialized data.
+    // We use RRSIG as a relatively complicated field structure.
+    UnitTestUtil::readWireData("rdatafields4.wire", expected_wire);
+    InputBuffer ibuffer(&expected_wire[0], expected_wire.size());
+    const uint16_t rdlen = ibuffer.readUint16();
+    const RdataFields fields(generic::RRSIG(ibuffer, rdlen));
+
+    expected_wire.erase(expected_wire.begin(), expected_wire.begin() + 2);
+
+    // Copy the data in separate storage
+    vector<uint8_t> spec_store(fields.getFieldCount() *
+                               sizeof(RdataFields::FieldSpec));
+    void* cp_spec = &spec_store[0];
+    memcpy(cp_spec, fields.getFieldSpecData(), spec_store.size());
+    vector<uint8_t> data_store(fields.getDataLength());
+    memcpy(&data_store[0], fields.getData(), fields.getDataLength());
+
+    // Restore the data in the form of RdataFields
+    const RdataFields fields_byparams(
+        static_cast<const RdataFields::FieldSpec*>(cp_spec),
+        fields.getFieldCount(), &data_store[0], fields.getDataLength());
+
+    // Check it's valid
+    constructCommonTestsRRSIG(fields_byparams);
+}
+
+//
+// OPT: an empty RDATA
+//
+void
+RdataFieldsTest::constructCommonTestsOPT(const RdataFields& fields) {
+    EXPECT_EQ(0, fields.getFieldCount());
+    EXPECT_EQ(0, fields.getDataLength());
+    EXPECT_EQ(NULL, fields.getData());
+    fields.toWire(obuffer);
+    EXPECT_EQ(0, obuffer.getLength());
+    fields.toWire(renderer);
+    EXPECT_EQ(0, renderer.getLength());
+}
+
+TEST_F(RdataFieldsTest, constructFromRdataOPT) {
+    InputBuffer ibuffer(NULL, 0);
+    const RdataFields fields(generic::OPT(ibuffer, 0));
+    constructCommonTestsOPT(fields);
+}
+
+TEST_F(RdataFieldsTest, constructFromParamsOPT) {
+    const RdataFields fields(NULL, 0, NULL, 0);
+    constructCommonTestsOPT(fields);
+}
+
+// Invalid input to the "from parameter" constructor: sum of the field lengths
+// is not equal to the data length.
+TEST_F(RdataFieldsTest, invalidFieldLength) {
+    UnitTestUtil::readWireData("rdatafields4.wire", fields_wire);
+    fields_wire.erase(fields_wire.begin(), fields_wire.begin() + 2);
+
+    const RdataFields::FieldSpec specs[] = {
+        RdataFields::FieldSpec(RdataFields::DATA, 18),
+        RdataFields::FieldSpec(RdataFields::INCOMPRESSIBLE_NAME, 13),
+        RdataFields::FieldSpec(RdataFields::DATA, 14)
+    };
+    // sum of field len < data len
+    EXPECT_THROW(RdataFields(specs, 3, &fields_wire[0], fields_wire.size()),
+                 isc::InvalidParameter);
+    // sum of field len > data len
+    EXPECT_THROW(RdataFields(specs, 3, &fields_wire[0],
+                             fields_wire.size() - 2),
+                 isc::InvalidParameter);
+}
+
+// Invalid input to the "from parameter" constructor: NULL vs length mismatch
+TEST_F(RdataFieldsTest, mismatchFieldLengthAndData) {
+    const unsigned char dummy_data = 0;
+    const RdataFields::FieldSpec dummy_spec(RdataFields::DATA, 1);
+
+    EXPECT_THROW(RdataFields(NULL, 1, &dummy_data, 1), isc::InvalidParameter);
+    EXPECT_THROW(RdataFields(&dummy_spec, 0, NULL, 0), isc::InvalidParameter);
+    EXPECT_THROW(RdataFields(&dummy_spec, 1, NULL, 1), isc::InvalidParameter);
+    EXPECT_THROW(RdataFields(NULL, 0, &dummy_data, 0), isc::InvalidParameter);
+}
+
+// Bogus input to getFieldSpec()
+TEST_F(RdataFieldsTest, getFieldSpecWithBadFieldId) {
+    const RdataFields fields_in_a(in::A("192.0.2.1"));
+    EXPECT_THROW(fields_in_a.getFieldSpec(1), isc::OutOfRange);
+}
+
+// Tests for unexpected methods in RdataFieldComposerTest.  Confirm
+// a call to these methods triggers an exception.  Expected methods are
+// tested via other tests above.
+class DummyRdata : public Rdata {
+public:
+    enum Mode { CLEAR, SKIP, TRIM, WRITEUINT16AT };
+    explicit DummyRdata(Mode mode) : mode_(mode) {}
+    DummyRdata(const DummyRdata& source) : Rdata(), mode_(source.mode_) {}
+    virtual ~DummyRdata() {}
+    virtual void toWire(AbstractMessageRenderer& renderer) const {
+        // call the unexpected method corresponding to the test mode.
+        // method parameters don't matter.
+        switch (mode_) {
+        case CLEAR:
+            renderer.clear();
+            break;
+        case SKIP:
+            renderer.skip(2);
+            break;
+        case TRIM:
+            renderer.trim(2);
+            break;
+        case WRITEUINT16AT:
+            renderer.writeUint16At(0, 0);
+            break;
+        }
+    }
+    
+    // These are defined only to make the compiler happy.  We don't use them
+    // for the test.
+    virtual string toText() const { return (""); }
+    virtual void toWire(OutputBuffer&) const {}
+    virtual int compare(const Rdata&) const { return (0); }
+private:
+    const int mode_;
+};
+
+TEST(RdataFieldComposerTest, unusedMethods) {
+    EXPECT_THROW(RdataFields(DummyRdata(DummyRdata::CLEAR)), isc::Unexpected);
+    EXPECT_THROW(RdataFields(DummyRdata(DummyRdata::SKIP)), isc::Unexpected);
+    EXPECT_THROW(RdataFields(DummyRdata(DummyRdata::TRIM)), isc::Unexpected);
+    EXPECT_THROW(RdataFields(DummyRdata(DummyRdata::WRITEUINT16AT)),
+                 isc::Unexpected);
+}
+}

+ 4 - 0
src/lib/dns/tests/testdata/Makefile.am

@@ -4,6 +4,8 @@ BUILT_SOURCES = edns_toWire1.wire edns_toWire2.wire edns_toWire3.wire
 BUILT_SOURCES += edns_toWire4.wire
 BUILT_SOURCES += message_fromWire10.wire message_fromWire11.wire
 BUILT_SOURCES += name_toWire5.wire name_toWire6.wire
+BUILT_SOURCES += rdatafields1.wire rdatafields2.wire rdatafields3.wire
+BUILT_SOURCES += rdatafields4.wire rdatafields5.wire rdatafields6.wire
 BUILT_SOURCES += rdata_nsec_fromWire4.wire rdata_nsec_fromWire5.wire
 BUILT_SOURCES += rdata_nsec_fromWire6.wire rdata_nsec_fromWire7.wire
 BUILT_SOURCES += rdata_nsec_fromWire8.wire rdata_nsec_fromWire9.wire
@@ -32,6 +34,8 @@ EXTRA_DIST += name_fromWire13 name_fromWire14
 EXTRA_DIST += name_toWire1 name_toWire2 name_toWire3 name_toWire4
 EXTRA_DIST += name_toWire5.spec name_toWire6.spec
 EXTRA_DIST += question_fromWire question_toWire1 question_toWire2
+EXTRA_DIST += rdatafields1.spec rdatafields2.spec rdatafields3.spec
+EXTRA_DIST += rdatafields4.spec rdatafields5.spec rdatafields6.spec
 EXTRA_DIST += rdata_cname_fromWire rdata_dname_fromWire rdata_dnskey_fromWire
 EXTRA_DIST += rdata_ds_fromWire rdata_in_a_fromWire rdata_in_aaaa_fromWire
 EXTRA_DIST += rdata_mx_fromWire rdata_mx_toWire1 rdata_ns_fromWire

+ 10 - 0
src/lib/dns/tests/testdata/rdatafields1.spec

@@ -0,0 +1,10 @@
+#
+# A sequence of names that could be compressed (but not compressed)
+#
+
+[custom]
+sections: name/1:name/2
+[name/1]
+name: www.example.com
+[name/2]
+name: example.com

+ 11 - 0
src/lib/dns/tests/testdata/rdatafields2.spec

@@ -0,0 +1,11 @@
+#
+# A sequence of names that can be compressed.
+#
+
+[custom]
+sections: name/1:name/2
+[name/1]
+name: www.example.com
+[name/2]
+name: ''
+pointer: 4

+ 11 - 0
src/lib/dns/tests/testdata/rdatafields3.spec

@@ -0,0 +1,11 @@
+#
+# TXT RDATA with multiple character-strings.
+#
+
+[custom]
+sections: txt
+[txt]
+nstring: 3
+string0: 'first string'
+string1: 'second string'
+string2: 'last string'

+ 7 - 0
src/lib/dns/tests/testdata/rdatafields4.spec

@@ -0,0 +1,7 @@
+#
+# Simple form of RRSIG (all fields use the default of generator script)
+#
+
+[custom]
+sections: rrsig
+[rrsig]

+ 12 - 0
src/lib/dns/tests/testdata/rdatafields5.spec

@@ -0,0 +1,12 @@
+#
+# Names and RDATA (RRSIG) with an incompressible name.  All names are
+# rendered without compression.
+#
+
+[custom]
+sections: name/1:rrsig:name/2
+[name/1]
+name: com
+[rrsig]
+[name/2]
+name: www.example.com

+ 13 - 0
src/lib/dns/tests/testdata/rdatafields6.spec

@@ -0,0 +1,13 @@
+#
+# Names and RDATA (RRSIG) with an incompressible name.  The name in RRSIG
+# isn't compressed, but it's used as the compression target.
+#
+
+[custom]
+sections: name/1:rrsig:name/2
+[name/1]
+name: com
+[rrsig]
+[name/2]
+name: www
+pointer: 25