Browse Source

sync with turnk

git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsrdata2@971 e5f2f494-b856-4b98-b285-d166d9295462
JINMEI Tatuya 15 years ago
parent
commit
1a324c8333

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

@@ -15,6 +15,7 @@ libdns_la_SOURCES += rrclass.h rrclass.cc rrtype.h rrtype.cc rrttl.h rrttl.cc
 libdns_la_SOURCES += rdata.h rdata.cc
 libdns_la_SOURCES += rdata.h rdata.cc
 libdns_la_SOURCES += rdataclass.h rdataclass.cc
 libdns_la_SOURCES += rdataclass.h rdataclass.cc
 libdns_la_SOURCES += rrset.h rrset.cc
 libdns_la_SOURCES += rrset.h rrset.cc
+libdns_la_SOURCES += rrsetlist.h rrsetlist.cc
 libdns_la_SOURCES += question.h question.cc
 libdns_la_SOURCES += question.h question.cc
 libdns_la_SOURCES += message.h message.cc
 libdns_la_SOURCES += message.h message.cc
 libdns_la_SOURCES += base64.h base64.cc
 libdns_la_SOURCES += base64.h base64.cc

+ 0 - 8
src/lib/dns/cpp/question.cc

@@ -25,7 +25,6 @@
 #include "rrtype.h"
 #include "rrtype.h"
 
 
 using namespace std;
 using namespace std;
-using namespace isc::dns;
 
 
 namespace isc {
 namespace isc {
 namespace dns {
 namespace dns {
@@ -69,13 +68,6 @@ Question::toWire(MessageRenderer& renderer) const
     return (1);                 // number of "entries"
     return (1);                 // number of "entries"
 }
 }
 
 
-bool
-Question::operator==(const Question& other) const
-{
-    return (name_ == other.name_ && rrtype_ == other.rrtype_ &&
-            rrclass_ == other.rrclass_);
-}
-
 ostream&
 ostream&
 operator<<(std::ostream& os, const Question& question)
 operator<<(std::ostream& os, const Question& question)
 {
 {

+ 192 - 12
src/lib/dns/cpp/question.h

@@ -18,6 +18,7 @@
 #define __QUESTION_H 1
 #define __QUESTION_H 1
 
 
 #include <iostream>
 #include <iostream>
+#include <string>
 
 
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_ptr.hpp>
 
 
@@ -29,46 +30,225 @@ namespace isc {
 namespace dns {
 namespace dns {
 
 
 class InputBuffer;
 class InputBuffer;
+class MessageRenderer;
 class Question;
 class Question;
+
+/// \brief A pointer-like type pointing to an \c Question object.
 typedef boost::shared_ptr<Question> QuestionPtr;
 typedef boost::shared_ptr<Question> QuestionPtr;
 
 
+/// \brief A pointer-like type pointing to an (immutable) \c Question object.
+typedef boost::shared_ptr<const Question> ConstQuestionPtr;
+
+/// \brief The \c Question class encapsulates the common search key of DNS
+/// lookup, consisting of owner name, RR type and RR class.
+///
+/// The primarily intended use case of this class is an entry of the question
+/// section of DNS messages.
+/// This could also be used as a general purpose lookup key, e.g., in a
+/// custom implementation of DNS database.
+///
+/// In this initial implementation, the \c Question class is defined as
+/// a <em>concrete class</em>; it's not expected to be inherited by
+/// a user-defined customized class.
+/// It may be worth noting that it's different from the design of the
+/// RRset classes (\c AbstractRRset and its derived classes).
+/// The RRset classes form an inheritance hierarchy from the base abstract
+/// class.
+/// This may look odd in that an "RRset" and "Question" are similar from the
+/// protocol point of view: Both are used as a semantics unit of DNS messages;
+/// both share the same set of components, name, RR type and RR class.
+///
+/// In fact, BIND9 didn't introduce a separate data structure for Questions,
+/// and use the same \c "rdataset" structure for both RRsets and Questions.
+/// We could take the same approach, but chose to adopt the different design.
+/// One reason for that is because a Question and an RRset are still
+/// different, and a Question might not be cleanly defined if (e.g.) it were
+/// a derived class of some "RRset-like" class.
+/// For example, we couldn't give a reasonable semantics for \c %getTTL() or
+/// \c %setTTL() methods for a Question, since it's not associated with the
+/// TTL.
+/// In fact, the BIND9 implementation ended up often separating the case where
+/// a \c "rdataset" is from the Question section of a DNS message and the
+/// case where it comes from other sections.
+/// If we cannot treat them completely transparently anyway, separating the
+/// class (type) would make more sense because we can exploit compilation
+/// time type checks.
+///
+/// On the other hand, we do not expect a strong need for customizing the
+/// \c Question class, unlike the RRset.
+/// Handling the Question section of a DNS message is relatively a
+/// simple work comparing to RRset-involved operations, so a unified
+/// straightforward implementation should suffice for any use cases
+/// including performance sensitive ones.
 ///
 ///
-/// This is a straightforward implementation of a "DNS question", modeling
-/// an entry of the question section of DNS messages.
-/// This could also be used as an input parameter for lookups into internal
-/// data sources.
+/// We may, however, still want to have customized version of Question
+/// for, e.g, highly optimized behavior, and may revisit this design choice
+/// as we have more experiences with this implementation.
 ///
 ///
-/// Open issue: we may want to introduce an abstract base class so that we
-/// can have multiple concrete implementations (e.g. for better performance)
-/// of the base class in a polymorphic way.
+/// One disadvantage of defining RRsets and Questions as unrelated classes
+/// is that we cannot handle them in a polymorphic way.
+/// For example, we might want to iterate over DNS message sections and
+/// render the data in the wire format, whether it's an RRset or a Question.
+/// If a \c Question were a derived class of some common RRset-like class,
+/// we could do this by calling <code>rrset_or_question->%toWire()</code>.
+/// But the actual design doesn't allow this approach, which may require
+/// duplicate code for almost the same operation.
+/// To mitigate this problem, we intentionally used the same names
+/// with the same signature for some common methods of \c Question and
+/// \c AbstractRRset such as \c %getName() or \c %toWire().
+/// So the user class may use a template function that is applicable to both
+/// \c Question and \c RRset to avoid writing duplicate code logic.
 class Question {
 class Question {
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// We use the default versions of destructor, copy constructor,
+    /// and assignment operator.
+    ///
+    /// The default constructor is hidden as a result of defining the other
+    /// constructors.  This is intentional; we don't want to allow a
+    /// \c Question object to be constructed with an invalid state.
+    //@{
 public:
 public:
+    /// \brief Constructor from wire-format data.
+    ///
+    /// It simply constructs a set of \c Name, \c RRType, and \c RRClass
+    /// object from the \c buffer in this order, and constructs a
+    /// \c Question object in a straightforward way.
+    ///
+    /// It may throw an exception if the construction of these component
+    /// classes fails.
+    ///
+    /// \param buffer A buffer storing the wire format data.
     Question(InputBuffer& buffer);
     Question(InputBuffer& buffer);
+
+    /// \brief Constructor from fixed parameters of the \c Question.
+    ///
+    /// This constructor is basically expected to be exception free, but
+    /// copying the name may involve resource allocation, and if it fails
+    /// the corresponding standard exception will be thrown.
+    ///
+    /// \param name The owner name of the \c Question.
+    /// \param rrclass The RR class of the \c Question.
+    /// \param rrtype The RR type of the \c Question.
     Question(const Name& name, const RRClass& rrclass, const RRType& rrtype) :
     Question(const Name& name, const RRClass& rrclass, const RRType& rrtype) :
         name_(name), rrtype_(rrtype), rrclass_(rrclass)
         name_(name), rrtype_(rrtype), rrclass_(rrclass)
     {}
     {}
+    //@}
 
 
     ///
     ///
-    /// \name Getter methods
+    /// \name Getter Methods
+    ///
     //@{
     //@{
+    /// \brief Returns the owner name of the \c Question.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return A reference to a \c Name class object corresponding to the
+    /// \c Question owner name.
     const Name& getName() const { return (name_); }
     const Name& getName() const { return (name_); }
+
+    /// \brief Returns the RR Class of the \c Question.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return A reference to a \c RRClass class object corresponding to the
+    /// RR class of the \c Question.
     const RRType& getType() const { return (rrtype_); }
     const RRType& getType() const { return (rrtype_); }
+
+    /// \brief Returns the RR Type of the \c Question.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return A reference to a \c RRType class object corresponding to the
+    /// RR type of the \c Question.
     const RRClass& getClass() const { return (rrclass_); }
     const RRClass& getClass() const { return (rrclass_); }
     //@}
     //@}
 
 
+    ///
+    /// \name Converter Methods
+    ///
+    //@{
+    /// \brief Convert the Question to a string.
+    ///
+    /// Unlike other similar methods of this library, this method terminates
+    /// the resulting string with a trailing newline character
+    /// (following the BIND9 convention).
+    ///
+    /// This method simply calls the \c %toText() methods of the corresponding
+    /// \c Name, \c RRType and \c RRClass classes for this \c Question, and
+    /// these methods may throw an exception.
+    /// In particular, if resource allocation fails, a corresponding standard
+    /// exception will be thrown.
+    ///
+    /// \return A string representation of the \c Question.
     std::string toText() const;
     std::string toText() const;
-    unsigned int toWire(OutputBuffer& buffer) const;
+
+    /// \brief Render the Question in the wire format with name compression.
+    ///
+    /// This method simply calls the \c %toWire() methods of the corresponding
+    /// \c Name, \c RRType and \c RRClass classes for this \c Question, and
+    /// these methods may throw an exception.
+    /// In particular, if resource allocation fails, a corresponding standard
+    /// exception will be thrown.
+    ///
+    /// This method returns 1, which is the number of "questions" contained
+    /// in the \c Question.
+    /// This is a meaningless value, but is provided to be consistent with
+    /// the corresponding method of \c AbstractRRset (see the detailed
+    /// class description).
+    ///
+    /// The owner name will be compressed if possible, although it's an
+    /// unlikely event in practice because the %Question section a DNS
+    /// message normally doesn't contain multiple question entries and
+    /// it's located right after the Header section.
+    /// Nevertheless, \c renderer records the information of the owner name
+    /// so that it can be pointed by other RRs in other sections (which is
+    /// more likely to happen).
+    ///
+    /// In theory, an attempt to render a Question may cause truncation
+    /// (when the Question section contains a large number of entries),
+    /// but this implementation doesn't catch that situation.
+    /// It would make the code unnecessarily complicated (though perhaps
+    /// slightly) for almost impossible case in practice.
+    /// An upper layer will handle the pathological case as a general error.
+    ///
+    /// \param renderer DNS message rendering context that encapsulates the
+    /// output buffer and name compression information.
+    /// \return 1
     unsigned int toWire(MessageRenderer& renderer) const;
     unsigned int toWire(MessageRenderer& renderer) const;
 
 
-    bool operator==(const Question& other) const;
-    bool operator!=(const Question& other) const
-    { return !(*this == other); }
+    /// \brief Render the Question in the wire format without name compression.
+    ///
+    /// This method behaves like the render version except it doesn't compress
+    /// the owner name.
+    /// See \c toWire(MessageRenderer& renderer)const.
+    ///
+    /// \param buffer An output buffer to store the wire data.
+    /// \return 1
+    unsigned int toWire(OutputBuffer& buffer) const;
+    //@}
+
 private:
 private:
     Name name_;
     Name name_;
     RRType rrtype_;
     RRType rrtype_;
     RRClass rrclass_;
     RRClass rrclass_;
 };
 };
 
 
+/// \brief Insert the \c Question as a string into stream.
+///
+/// This method convert the \c question into a string and inserts it into the
+/// output stream \c os.
+///
+/// This function overloads the global \c operator<< to behave as described in
+/// \c %ostream::%operator<< but applied to Question objects.
+///
+/// \param os A \c std::ostream object on which the insertion operation is
+/// performed.
+/// \param question A reference to a \c Question object output by the
+/// operation.
+/// \return A reference to the same \c std::ostream object referenced by
+/// parameter \c os after the insertion operation.
 std::ostream& operator<<(std::ostream& os, const Question& question);
 std::ostream& operator<<(std::ostream& os, const Question& question);
 } // end of namespace dns
 } // end of namespace dns
 } // end of namespace isc
 } // end of namespace isc

+ 1 - 0
src/lib/dns/cpp/rdata.h

@@ -71,6 +71,7 @@ class Rdata;
 /// object of some concrete derived class of \c Rdata.
 /// object of some concrete derived class of \c Rdata.
 ///
 ///
 typedef boost::shared_ptr<Rdata> RdataPtr;
 typedef boost::shared_ptr<Rdata> RdataPtr;
+typedef boost::shared_ptr<const Rdata> ConstRdataPtr;
 
 
 /// \brief Possible maximum length of RDATA, which is the maximum unsigned
 /// \brief Possible maximum length of RDATA, which is the maximum unsigned
 /// 16 bit value.
 /// 16 bit value.

+ 28 - 3
src/lib/dns/cpp/rdata/generic/soa_6.cc

@@ -39,10 +39,35 @@ SOA::SOA(InputBuffer& buffer, size_t rdata_len) :
     buffer.readData(numdata_, sizeof(numdata_));
     buffer.readData(numdata_, sizeof(numdata_));
 }
 }
 
 
-SOA::SOA(const std::string& soastr) :
-    mname_("."), rname_(".")
+SOA::SOA(const string& soastr) :
+    mname_("."), rname_(".")    // quick hack workaround
 {
 {
-    dns_throw(InvalidRdataText, "Not implemented yet");
+    istringstream iss(soastr);
+    string token;
+
+    iss >> token;
+    if (iss.bad() || iss.fail()) {
+        dns_throw(InvalidRdataText, "Invalid SOA MNAME");
+    }
+    mname_ = Name(token);
+    iss >> token;
+    if (iss.bad() || iss.fail()) {
+        dns_throw(InvalidRdataText, "Invalid SOA RNAME");
+    }
+    rname_ = Name(token);
+
+    uint32_t serial, refresh, retry, expire, minimum;
+    iss >> serial >> refresh >> retry >> expire >> minimum;
+    if (iss.rdstate() != ios::eofbit) {
+        dns_throw(InvalidRdataText, "Invalid SOA format");
+    }
+    OutputBuffer buffer(20);
+    buffer.writeUint32(serial);
+    buffer.writeUint32(refresh);
+    buffer.writeUint32(retry);
+    buffer.writeUint32(expire);
+    buffer.writeUint32(minimum);
+    memcpy(numdata_,  buffer.getData(), buffer.getLength());
 }
 }
 
 
 SOA::SOA(const Name& mname, const Name& rname, uint32_t serial,
 SOA::SOA(const Name& mname, const Name& rname, uint32_t serial,

+ 63 - 15
src/lib/dns/cpp/rrset.cc

@@ -46,16 +46,20 @@ AbstractRRset::toText() const
     string s;
     string s;
     RdataIteratorPtr it = getRdataIterator();
     RdataIteratorPtr it = getRdataIterator();
 
 
-    for (it->first(); !it->isLast(); it->next()) {
-        s += getName().toText() + " " +
-            getTTL().toText() + " " +
-            getClass().toText() + " " +
-            getType().toText() + " " +
-            it->getCurrent().toText() + "\n";
+    it->first();
+    if (it->isLast()) {
+        dns_throw(EmptyRRset, "ToText() is attempted for an empty RRset");
     }
     }
 
 
+    do {
+        s += getName().toText() + " " + getTTL().toText() + " " +
+            getClass().toText() + " " + getType().toText() + " " +
+            it->getCurrent().toText() + "\n";
+        it->next();
+    } while (!it->isLast());
+
     return (s);
     return (s);
-}
+}   
 
 
 namespace {
 namespace {
 template <typename T>
 template <typename T>
@@ -65,9 +69,14 @@ rrsetToWire(const AbstractRRset& rrset, T& output)
     unsigned int n = 0;
     unsigned int n = 0;
     RdataIteratorPtr it = rrset.getRdataIterator();
     RdataIteratorPtr it = rrset.getRdataIterator();
 
 
+    it->first();
+    if (it->isLast()) {
+        dns_throw(EmptyRRset, "ToWire() is attempted for an empty RRset");
+    }
+
     // sort the set of Rdata based on rrset-order and sortlist, and possible
     // sort the set of Rdata based on rrset-order and sortlist, and possible
     // other options.  Details to be considered.
     // other options.  Details to be considered.
-    for (it->first(); !it->isLast(); it->next(), ++n) {
+    do {
         rrset.getName().toWire(output);
         rrset.getName().toWire(output);
         rrset.getType().toWire(output);
         rrset.getType().toWire(output);
         rrset.getClass().toWire(output);
         rrset.getClass().toWire(output);
@@ -77,7 +86,10 @@ rrsetToWire(const AbstractRRset& rrset, T& output)
         output.skip(sizeof(uint16_t)); // leave the space for RDLENGTH
         output.skip(sizeof(uint16_t)); // leave the space for RDLENGTH
         it->getCurrent().toWire(output);
         it->getCurrent().toWire(output);
         output.writeUint16At(output.getLength() - pos - sizeof(uint16_t), pos);
         output.writeUint16At(output.getLength() - pos - sizeof(uint16_t), pos);
-    }
+
+        it->next();
+        ++n;
+    } while (!it->isLast());
 
 
     return (n);
     return (n);
 }
 }
@@ -102,7 +114,10 @@ operator<<(ostream& os, const AbstractRRset& rrset)
     return (os);
     return (os);
 }
 }
 
 
-struct BasicRRsetImpl {
+/// \brief This encapsulates the actual implementation of the \c BasicRRset
+/// class.  It's hidden from applications.
+class BasicRRsetImpl {
+public:
     BasicRRsetImpl(const Name& name, const RRClass& rrclass,
     BasicRRsetImpl(const Name& name, const RRClass& rrclass,
                    const RRType& rrtype, const RRTTL& ttl) :
                    const RRType& rrtype, const RRTTL& ttl) :
         name_(name), rrclass_(rrclass), rrtype_(rrtype), ttl_(ttl) {}
         name_(name), rrclass_(rrclass), rrtype_(rrtype), ttl_(ttl) {}
@@ -110,7 +125,10 @@ struct BasicRRsetImpl {
     RRClass rrclass_;
     RRClass rrclass_;
     RRType rrtype_;
     RRType rrtype_;
     RRTTL ttl_;
     RRTTL ttl_;
-    vector<RdataPtr> rdatalist_;
+    // XXX: "list" is not a good name: It in fact isn't a list; more conceptual
+    // name than a data structure name is generally better.  But since this
+    // is only used in the internal implementation we'll live with it.
+    vector<ConstRdataPtr> rdatalist_;
 };
 };
 
 
 BasicRRset::BasicRRset(const Name& name, const RRClass& rrclass,
 BasicRRset::BasicRRset(const Name& name, const RRClass& rrclass,
@@ -125,11 +143,17 @@ BasicRRset::~BasicRRset()
 }
 }
 
 
 void
 void
-BasicRRset::addRdata(const RdataPtr rdata)
+BasicRRset::addRdata(ConstRdataPtr rdata)
 {
 {
     impl_->rdatalist_.push_back(rdata);
     impl_->rdatalist_.push_back(rdata);
 }
 }
 
 
+void
+BasicRRset::addRdata(const Rdata& rdata)
+{
+    AbstractRRset::addRdata(rdata);
+}
+
 unsigned int
 unsigned int
 BasicRRset::getRdataCount() const
 BasicRRset::getRdataCount() const
 {
 {
@@ -161,17 +185,41 @@ BasicRRset::getTTL() const
 }
 }
 
 
 void
 void
+BasicRRset::setName(const Name& name)
+{
+    impl_->name_ = name;
+}
+
+void
 BasicRRset::setTTL(const RRTTL& ttl)
 BasicRRset::setTTL(const RRTTL& ttl)
 {
 {
     impl_->ttl_ = ttl;
     impl_->ttl_ = ttl;
 }
 }
 
 
+string
+BasicRRset::toText() const
+{
+    return (AbstractRRset::toText());
+}
+
+unsigned int
+BasicRRset::toWire(OutputBuffer& buffer) const
+{
+    return (AbstractRRset::toWire(buffer));
+}
+
+unsigned int
+BasicRRset::toWire(MessageRenderer& renderer) const
+{
+    return (AbstractRRset::toWire(renderer));
+}
+
 namespace {
 namespace {
 class BasicRdataIterator : public RdataIterator {
 class BasicRdataIterator : public RdataIterator {
 private:
 private:
     BasicRdataIterator() {}
     BasicRdataIterator() {}
 public:
 public:
-    BasicRdataIterator(const std::vector<rdata::RdataPtr>& datavector) :
+    BasicRdataIterator(const std::vector<rdata::ConstRdataPtr>& datavector) :
         datavector_(&datavector) {}
         datavector_(&datavector) {}
     ~BasicRdataIterator() {}
     ~BasicRdataIterator() {}
     virtual void first() { it_ = datavector_->begin(); }
     virtual void first() { it_ = datavector_->begin(); }
@@ -179,8 +227,8 @@ public:
     virtual const rdata::Rdata& getCurrent() const { return (**it_); }
     virtual const rdata::Rdata& getCurrent() const { return (**it_); }
     virtual bool isLast() const { return (it_ == datavector_->end()); }
     virtual bool isLast() const { return (it_ == datavector_->end()); }
 private:
 private:
-    const std::vector<rdata::RdataPtr>* datavector_;
-    std::vector<rdata::RdataPtr>::const_iterator it_;
+    const std::vector<rdata::ConstRdataPtr>* datavector_;
+    std::vector<rdata::ConstRdataPtr>::const_iterator it_;
 };
 };
 }
 }
 
 

+ 580 - 69
src/lib/dns/cpp/rrset.h

@@ -19,152 +19,663 @@
 
 
 #include <iostream>
 #include <iostream>
 #include <string>
 #include <string>
-#include <vector>
 
 
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_ptr.hpp>
 
 
+#include <exceptions/exceptions.h>
+
 #include "rdata.h"
 #include "rdata.h"
 
 
 namespace isc {
 namespace isc {
 namespace dns {
 namespace dns {
 
 
+///
+/// \brief A standard DNS module exception that is thrown if an RRset object
+/// does not contain any RDATA where required.
+///
+class EmptyRRset : public Exception {
+public:
+    EmptyRRset(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+// forward declarations
 class Name;
 class Name;
 class RRType;
 class RRType;
 class RRClass;
 class RRClass;
 class RRTTL;
 class RRTTL;
 class OututBuffer;
 class OututBuffer;
 class MessageRenderer;
 class MessageRenderer;
-
 class AbstractRRset;
 class AbstractRRset;
-typedef boost::shared_ptr<AbstractRRset> RRsetPtr;
 class BasicRRset;
 class BasicRRset;
-typedef BasicRRset RRset;
+class RdataIterator;
 class BasicRRsetImpl;
 class BasicRRsetImpl;
 
 
-class RdataIterator;
+/// \brief A pointer-like type pointing to an \c AbstractRRset object.
+///
+/// This type is commonly used as an argument of various functions defined
+/// in this library in order to handle RRsets in a polymorphic manner.
+typedef boost::shared_ptr<AbstractRRset> RRsetPtr;
+
+/// \brief A pointer-like type pointing to an (immutable) \c AbstractRRset
+/// object.
+///
+/// This type is commonly used as an argument of various functions defined
+/// in this library in order to handle RRsets in a polymorphic manner.
+typedef boost::shared_ptr<const AbstractRRset> ConstRRsetPtr;
+
+/// \brief A convenient abbreviation for the most generic derived RRset class.
+typedef BasicRRset RRset;
+
+/// \brief A pointer-like type point to an \c RdataIterator object.
 typedef boost::shared_ptr<RdataIterator> RdataIteratorPtr;
 typedef boost::shared_ptr<RdataIterator> RdataIteratorPtr;
 
 
-/// \brief TBD
+/// \brief The \c AbstractRRset class is an abstract base class that
+/// models a DNS RRset.
 ///
 ///
-/// An \c RRset object models an RRset as described in the DNS standard:
-/// a set of DNS resource records (RRs) of the same type and class.
+/// An object of (a specific derived class of) \c AbstractRRset
+/// models an RRset as described in the DNS standard:
+/// A set of DNS resource records (RRs) of the same type and class.
 /// The standard requires the TTL of all RRs in an RRset be the same;
 /// The standard requires the TTL of all RRs in an RRset be the same;
 /// this class follows that requirement.
 /// this class follows that requirement.
-/// Note about duplication: we probably don't enforce the RDATA is unique
-/// as a class responsibility - but may revisit the decision.
+
+/// Note about duplicate RDATA: RFC2181 states that it's meaningless that an
+/// RRset contains two identical RRs and that name servers should suppress
+/// such duplicates.
+/// This class is not responsible for ensuring this requirement: For example,
+/// \c addRdata() method doesn't check if there's already RDATA identical
+/// to the one being added.
+/// This is because such checks can be expensive, and it's often easy to
+/// ensure the uniqueness requirement at the %data preparation phase
+/// (e.g. when loading a zone).
+/// When parsing an incoming DNS message, the uniqueness may not be guaranteed,
+/// so the application needs to detect and ignore any duplicate RDATA
+/// (the \c Message class of this library should provide this responsibility).
 ///
 ///
-// This is a primary class internally used in our major software such as name
-// servers.
-//
-// Note about terminology: there has been a discussion at the IETF namedroppers
-// ML about RRset vs RRSet (case of "s").  While RFC2181 uses the latter,
-// many other RFCs use the former, and most of the list members who showed
-// their opinion seem to prefer RRset.  We follow that preference in this
-// implementation.
-//
-// Open Issues:
-//   - add more set-like operations, e.g, merge?
-//   - add a "sort" method?  "search(find)" method?
-//   - BIND9 libdns has some special DNSSEC-related methods
-//     such as addnoqname(), addclosest().  do we need these?
-//   - need to check duplicate rdata in addrdata()?
-//   - need a comparison method?  if so, should it compare
-//     rdata's as a set or as a list (compare each rdata one
-//     by one)?  ldns has ldns_rr_list_compare(), which takes
-//     the latter approach (assuming the caller sorts the lists
-//     beforehand?).
+/// Another point to note is that \c AbstractRRset and its derived classes
+/// allow an object to have an empty set of RDATA.
+/// Even though there's no corresponding notion in the protocol specification,
+/// it would be more intuitive for a container-like %data structure
+/// to allow an empty set.
+///
+/// Since \c AbstractRRset is an abstract class, it is generally used
+/// via a pointer (or pointer like object) or a reference.
+/// In particular, \c RRsetPtr, a pointer like type for \c AbstractRRset,
+/// is used for polymorphic RRset operations throughout this library.
+///
+/// The \c AbstractRRset class is also intended to be a major customization
+/// point.  For example, a high performance server implementation may want
+/// to define an optimized "pre-compiled" RRset and provide an optimized
+/// implementation of the \c toWire() method.
+///
+/// Note about design choice: In BIND9, a set of RDATA with a common tuple
+/// of RR class, RR type, and TTL was represented in a structure named
+/// \c rdataset.  Unlike the RRset classes, an \c rdataset did not contain
+/// the information of the owner name.
+/// This might be advantageous if we want to handle "RRsets", that is,
+/// a set of different types of RRset for the same owner name, because
+/// a single "name" structure can be used for multiple RRsets, minimizing
+/// %data copy and memory footprint.
+/// On the other hand, it's inconvenient for API users since in many cases
+/// a pair of name and an \c rdataset must be maintained.  It's also counter
+/// intuitive in implementing protocol operations as an RRset is often used
+/// as an atomic entity in DNS protocols while an \c rdataset is a component
+/// of an RRset.
+///
+/// We have therefore defined the notion of RRset explicitly in our initial
+/// API design.  We believe memory footprint is not a big concern because
+/// RRsets are generally expected to be used as temporary objects, e.g.
+/// while parsing or constructing a DNS message, or searching a DNS %data
+/// source; for longer term purposes such as in-memory %data source entries,
+/// the corresponding %data would be represented in a different, memory
+/// optimized format.  As for the concern about %data copy, we believe
+/// it can be mitigated by using copy-efficient implementation for the
+/// \c Name class implementation, such as reference counted objects.
+/// Later, We plan to perform benchmark tests later to see if this assumption
+/// is valid and to revisit the design if necessary.
+///
+/// Note about terminology: there has been a discussion at the IETF
+/// namedroppers ML about RRset vs RRSet (case of "s")
+/// [http://ops.ietf.org/lists/namedroppers/namedroppers.2009/msg02737.html].
+/// While RFC2181 uses the latter, many other RFCs use the former,
+/// and most of the list members who showed their opinion seem to prefer
+/// "RRset".  We follow that preference in this implementation.
+///
+/// The current design of \c AbstractRRset is still in flux.
+/// There are many open questions in design details:
+///   - support more set-like operations, e.g, merge two RRsets of the same
+///     type?
+///   - more convenient methods or non member utility functions, e.g.
+///      "sort" and "search(find)" method?
+///   - what about comparing two RRsets of the same type?  If we need this,
+///     should it compare rdata's as a set or as a list (i.e. compare
+///     each %rdata one by one or as a whole)?  c.f. NLnet Labs' ldns
+///     (http://www.nlnetlabs.nl/projects/ldns/doc/index.html)
+///     has \c ldns_rr_list_compare(), which takes the latter approach
+///     (seemingly assuming the caller sorts the lists beforehand).
+///   - BIND9 libdns has some special DNSSEC-related methods
+///     such as \c addnoqname() or \c addclosest().  Do we need these?
+///     (Probably not.  We wouldn't want to make the class design too
+///     monolithic.)
+///   - Do we need to allow the user to remove specific Rdata?
+///     Probably not, according to the current usage of the BIND9 code.
 class AbstractRRset {
 class AbstractRRset {
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// Note: The copy constructor and the assignment operator are intentionally
+    /// defined as private to make it explicit that this is a pure base class.
+    //@{
+private:
+    AbstractRRset(const AbstractRRset& source);
+    AbstractRRset& operator=(const AbstractRRset& source);
+protected:
+    /// \brief The default constructor.
+    ///
+    /// This is intentionally defined as \c protected as this base class should
+    /// never be instantiated (except as part of a derived class).
+    AbstractRRset() {}
 public:
 public:
+    /// The destructor.
     virtual ~AbstractRRset() {}
     virtual ~AbstractRRset() {}
-    virtual std::string toText() const;
-    /// Note: perhaps we may want to add more arguments to convey optional
-    /// information such as an "rrset-order" policy.
-    /// no name compression, no truncation
-    virtual unsigned int toWire(OutputBuffer& buffer) const;
-    /// name compression when necessary, taking into account truncation
-    virtual unsigned int toWire(MessageRenderer& renderer) const;
+    //@}
+
+    ///
+    /// \name Getter and Setter Methods
+    ///
+    /// These methods are generally expected to be exception free, but it's
+    /// not guaranteed at the interface level;
+    /// for example, some performance optimized derived class may manage
+    /// the information corresponding to the class "attributes" to get or set,
+    /// and may require dynamic memory allocation to execute the method.
+    /// Consult the derived class description to see if a specific derived
+    /// \c RRset class may throw an exception from these methods.
+    ///
+    /// Note that setter methods are not provided for \c RRClass and
+    /// \c RRType.  This is intentional.  Since the format and semantics of
+    /// \c Rdata are dependent on the RR type (and RR class for some RR types),
+    /// allowing dynamically modify these attributes can easily lead to a
+    /// bug where the RDATA and type and/or class become inconsistent.
+    /// We want to avoid that situation by restricting the access.
+    //@{
+    /// \brief Returns the number of \c Rdata objects contained in the \c RRset.
+    ///
+    /// Note that an \c RRset with an empty set of \c Rdata can exist, so
+    /// this method may return 0.
+    ///
+    /// \return The number of \c Rdata objects contained.
     virtual unsigned int getRdataCount() const = 0;
     virtual unsigned int getRdataCount() const = 0;
+
+    /// \brief Returns the owner name of the \c RRset.
+    ///
+    /// \return A reference to a \c Name class object corresponding to the
+    /// \c RRset owner name.
     virtual const Name& getName() const = 0;
     virtual const Name& getName() const = 0;
+
+    /// \brief Returns the RR Class of the \c RRset.
+    ///
+    /// \return A reference to a \c RRClass class object corresponding to the
+    /// RR class of the \c RRset.
     virtual const RRClass& getClass() const = 0;
     virtual const RRClass& getClass() const = 0;
+
+    /// \brief Returns the RR Type of the \c RRset.
+    ///
+    /// \return A reference to a \c RRType class object corresponding to the
+    /// RR type of the \c RRset.
     virtual const RRType& getType() const = 0;
     virtual const RRType& getType() const = 0;
-    virtual const RRTTL& getTTL() const = 0;
+
+    /// \brief Returns the TTL of the RRset.
     ///
     ///
-    /// once constructed, only TTL and the set of Rdata can be modified,
-    /// so \c setTTL() is the only explicit setter method.
+    /// \return A reference to a \c RRTTL class object corresponding to the
+    /// TTL of the \c RRset.
+    virtual const RRTTL& getTTL() const = 0;
+
+    /// \brief Updates the owner name of the \c RRset.
     ///
     ///
-    /// Do we need to allow the user to remove specific Rdata?
-    /// Looking at the BIND9 code, don't see the strong need for this at the
-    /// moment.
+    /// \param name A reference to a \c RRTTL class object to be copied as the
+    /// new TTL.
+    virtual void setName(const Name& name) = 0;
+
+    /// \brief Updates the TTL of the \c RRset.
     ///
     ///
+    /// \param ttl A reference to a \c RRTTL class object to be copied as the
+    /// new TTL.
     virtual void setTTL(const RRTTL& ttl) = 0;
     virtual void setTTL(const RRTTL& ttl) = 0;
+    //@}
 
 
     ///
     ///
-    /// \name RDATA manipulation methods
+    /// \name Converter Methods
     ///
     ///
+    /// These methods have the default implementation that can be reused by
+    /// derived classes.
+    /// Since they are defined as pure virtual, derived classes
+    /// that want to reuse the default implementation must explicitly
+    /// invoke their base class version (see the description for
+    /// <code>addRdata(const rdata::Rdata&)</code>).
+    ///
+    /// Design Note: the default implementations are defined only using
+    /// other public methods of the \c AbstractRRset class, and could be
+    /// implemented as non member functions (as some C++ textbooks suggest).
+    /// However, since derived classes may want to provide customized versions
+    /// (especially of the \c toWire() method for performance reasons)
+    /// we chose to define them as virtual functions, and, as a result,
+    /// member functions.
     //@{
     //@{
-    virtual void addRdata(const rdata::RdataPtr rdata) = 0;
-    /// This method has the default implementation.
-    /// Note: since concrete classes would define the pure virtual version
-    /// of \c addRdata(), they'll need to declare the use of this method
-    /// to avoid name hiding (unless they redefine this method):
-    /// \code class CustomizedRRset : public AbstractRRset {
-    /// public:
-    ///     using AbstractRRset::addRdata;
-    ///     ...
-    /// }; \endcode
-    virtual void addRdata(const rdata::Rdata& rdata);
+    /// \brief Convert the RRset to a string.
+    ///
+    /// Unlike other similar methods of this library, this method terminates
+    /// the resulting string with a trailing newline character.
+    /// (following the BIND9 convention)
+    ///
+    /// The RRset must contain some RDATA; otherwise, an exception of class
+    /// \c EmptyRRset will be thrown.
+    /// If resource allocation fails, a corresponding standard exception
+    /// will be thrown.
+    /// The default implementation may throw other exceptions if the
+    /// \c toText() method of the RDATA objects throws.
+    /// If a derived class of \c AbstractRRset overrides the default
+    /// implementation, the derived version may throw its own exceptions.
+    ///
+    /// Open issue: We may want to support multiple output formats as
+    /// BIND9 does.  For example, we might want to allow omitting the owner
+    /// name when possible in the context of zone dump.  This is a future
+    /// TODO item.
+    ///
+    /// \param rrset A reference to a (derived class of) \c AbstractRRset object
+    /// whose content is to be converted.
+    /// \return A string representation of the RRset.
+    virtual std::string toText() const = 0;
+
+    /// \brief Render the RRset in the wire format with name compression and
+    /// truncation handling.
+    ///
+    /// This method compresses the owner name of the RRset and domain names
+    /// used in RDATA that should be compressed.
+    /// In addition, this method detects the case where rendering the entire
+    /// RRset would cause truncation, and handles the case appropriately
+    /// (this is a TODO item, and not implemented in this version).
+    ///
+    /// Note: perhaps we may want to add more arguments to convey optional
+    /// information such as an "rrset-order" policy or how to handle truncation
+    /// case.  This is a TODO item.
+    ///
+    /// If resource allocation fails, a corresponding standard exception
+    /// will be thrown.
+    /// The RRset must contain some RDATA; otherwise, an exception of class
+    /// \c EmptyRRset will be thrown.
+    /// The default implementation may throw other exceptions if the
+    /// \c toWire() method of the RDATA objects throws.
+    /// If a derived class of \c AbstractRRset overrides the default
+    /// implementation, the derived version may throw its own exceptions.
+    ///
+    /// \param renderer DNS message rendering context that encapsulates the
+    /// output buffer and name compression information.
+    /// \return The number of RRs rendered.  If the truncation is necessary
+    /// this value may be different from the number of RDATA objects contained
+    /// in the RRset.
+    virtual unsigned int toWire(MessageRenderer& renderer) const = 0;
+
+    /// \brief Render the RRset in the wire format without any compression.
+    ///
+    /// See the other toWire() description about possible exceptions.
+    ///
+    /// \param buffer An output buffer to store the wire data.
+    /// \return The number of RRs rendered.
+    virtual unsigned int toWire(OutputBuffer& buffer) const = 0;
+    //@}
+
+    ///
+    /// \name RDATA Manipulation Methods
+    ///
+    //@{
+    /// \brief Add an RDATA to the RRset (pointer version).
+    ///
+    /// This method adds the given RDATA (as a pointer-like type to a
+    /// derived class object of \c rdata::Rdata) to the \c RRset.
+    ///
+    /// \param rdata A pointer (like) type of \c rdata::RdataPtr to be added
+    /// to the \c RRset.
+    virtual void addRdata(rdata::ConstRdataPtr rdata) = 0;
+
+    /// \brief Add an RDATA to the RRset (reference version).
+    ///
+    /// This method adds the given RDATA (as a reference to a
+    /// derived class object of \c rdata::Rdata) to the \c RRset.
+    ///
+    /// This method has the default implementation that can be reused by
+    /// derived classes.
+    /// Since this method is defined as pure virtual, derived classes
+    /// that want to reuse the default implementation must explicitly
+    /// invoke this base class version.
+    /// For example, if the class \c CustomizedRRset, a derived class of
+    /// \c AbstractRRset, wants to reuse the default implementation of
+    /// \c %addRdata() (reference version), it would be defined as follows:
+    /// \code void
+    /// CustomizedRRset::addRdata(const rdata::Rdata& rdata)
+    /// {
+    ///     AbstractRRset::addRdata(rdata);
+    /// }
+    /// \endcode
+    ///
+    /// This method is more strictly typed than the pointer version:
+    /// If \c %rdata does not refer to the appropriate derived
+    /// \c Rdata class
+    /// for the \c RRType for this \c RRset, it throws an exception of class
+    /// \c std::bad_cast.
+    /// If resource allocation fails, a corresponding standard exception
+    /// will be thrown.
+    /// The RRset must contain some RDATA; otherwise, an exception of class
+    /// \c EmptyRRset will be thrown.
+    /// The default implementation may throw other exceptions if the
+    /// \c toWire() method of the RDATA objects throws.
+    /// If a derived class of \c AbstractRRset overrides the default
+    /// implementation, the derived version may throw its own exceptions.
+    ///
+    /// The default implementation simply constructs an \c rdata::RdataPtr
+    /// object from a newly allocated RDATA object copying from parameter
+    /// \c rdata, and calls the other version of
+    /// \c addRdata(const rdata::RdataPtr).
+    /// So it is inherently less efficient than the other version.
+    /// Still, this version would offer a more intuitive interface and is
+    /// provided as such.
+    ///
+    /// \param rdata A reference to a \c rdata::RdataPtr (derived) class
+    /// object, a copy of which is to be added to the \c RRset.
+    virtual void addRdata(const rdata::Rdata& rdata) = 0;
+
+    /// \brief Return an iterator to go through all RDATA stored in the
+    /// \c RRset.
+    ///
+    /// Using the design pattern terminology, \c getRdataIterator()
+    /// is an example of a <em>factory method</em>.
+    ///
+    /// Whether this method throws an exception depends on the actual
+    /// implementation of the derived \c AbstractRRset class, but in general
+    /// it will involve resource allocation and can throw a standard exception
+    /// if it fails.
+    ///
+    /// \return A pointer-like object pointing to the derived \c RdataIterator
+    /// object.
     virtual RdataIteratorPtr getRdataIterator() const = 0;
     virtual RdataIteratorPtr getRdataIterator() const = 0;
     //@}
     //@}
 };
 };
 
 
+/// \brief The \c RdataIterator class is an abstract base class that
+/// provides an interface for accessing RDATA objects stored in an RRset.
+///
+/// While different derived classes of \c AbstractRRset may maintain the RDATA
+/// objects in different ways, the \c RdataIterator class provides a
+/// unified interface to iterate over the RDATA objects in a polymorphic
+/// manner.
+///
+/// Each derived class of \c AbstractRRset is expected to provide a concrete
+/// derived class of \c RdataIterator, and each derived \c RdataIterator
+/// class implements the unified interface in a way specific to the
+/// implementation of the corresponding derived \c AbstractRRset class.
+/// Using the design pattern terminology, this is a typical example of
+/// the \e Iterator pattern.
+///
+/// The RDATA objects stored in the \c RRset are considered to form
+/// a unidirectional list from the \c RdataIterator point of view (while
+/// the actual implementation in the derived \c RRset may not use a list).
+/// We call this unidirectional list the <em>%rdata list</em>.
+///
+/// An \c RdataIterator object internally (and conceptually) holds a
+/// <em>%rdata cursor</em>, which points to a specific item of the %rdata list.
+///
+/// Note about design choice: as is clear from the interface, \c RdataIterator
+/// is not compatible with the standard iterator classes.
+/// Although it would be useful (for example, we could then use STL algorithms)
+/// and is not necessarily impossible, it would make the iterator implementation
+/// much more complicated.
+/// For instance, any standard iterator must be assignable and
+/// copy-constructible.
+/// So we'd need to implement \c RdataIterator::operator=() in a polymorphic
+/// way.  This will require non-trivial implementation tricks.
+/// We believe the simplified iterator interface as provided by the
+/// \c RdataIterator class is sufficient in practice:
+/// Most applications will simply go through the RDATA objects contained in
+/// an RRset, examining (and possibly using) each object, as one path
+/// operation.
 class RdataIterator {
 class RdataIterator {
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// Note: The copy constructor and the assignment operator are intentionally
+    /// defined as private to make it explicit that this is a pure base class.
+    //@{
+protected:
+    /// \brief The default constructor.
+    ///
+    /// This is intentionally defined as \c protected as this base class should
+    /// never be instantiated (except as part of a derived class).
+    RdataIterator() {}
 public:
 public:
+    /// \brief Destructor
     virtual ~RdataIterator() {}
     virtual ~RdataIterator() {}
+private:
+    RdataIterator(const RdataIterator& source);
+    RdataIterator& operator=(const RdataIterator& source);
+    //@}
+
+public:
+    /// \brief Move the %rdata cursor to the first RDATA in the %rdata list
+    /// (if any).
+    ///
+    /// This method can safely be called multiple times, even after moving
+    /// the %rdata cursor forward by the \c next() method.
+    ///
+    /// This method should never throw an exception.
     virtual void first() = 0;
     virtual void first() = 0;
+
+    /// \brief Move the %rdata cursor to the next RDATA in the %rdata list
+    /// (if any).
+    ///
+    /// This method should never throw an exception.
     virtual void next() = 0;
     virtual void next() = 0;
+
+    /// \brief Return the current \c Rdata corresponding to the %rdata cursor.
+    ///
+    /// \return A reference to an \c rdata::::Rdata object corresponding
+    /// to the %rdata cursor.
     virtual const rdata::Rdata& getCurrent() const = 0;
     virtual const rdata::Rdata& getCurrent() const = 0;
+
+    /// \brief Return true iff the %rdata cursor has reached the end of the
+    /// %rdata list.
+    ///
+    /// Once this method returns \c true, the behavior of any subsequent
+    /// call to \c next() or \c getCurrent() is undefined.
+    /// Likewise, the result of \c isLast() call followed by such undefined
+    /// operations is also undefined.
+    ///
+    /// This method should never throw an exception.
+    ///
+    /// \return \c true if the %rdata cursor has reached the end of the
+    /// %rdata list; otherwise \c false.
     virtual bool isLast() const = 0;
     virtual bool isLast() const = 0;
 };
 };
 
 
-/// Straightforward RRset implementation.
-/// designed to be as portable as possible.  performance is a secondary
-/// concern for this class.
+/// \brief The \c BasicRRset class is a concrete derived class of
+/// \c AbstractRRset that defines a straightforward RRset implementation.
+///
+/// This class is designed to be as portable as possible, and so it adopts
+/// the Pimpl idiom to hide as many details as possible.
+/// Performance is a secondary concern for this class.
+///
+/// This class is intended to be used by applications that only need
+/// moderate level of performance with full functionality provided by
+/// the \c AbstractRRset interfaces.
+/// Highly performance-sensitive applications, such as a large scale
+/// authoritative or caching name servers will implement and use a customized
+/// version of derived \c AbstractRRset class.
 class BasicRRset : public AbstractRRset {
 class BasicRRset : public AbstractRRset {
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// Note: The copy constructor and the assignment operator are intentionally
+    /// defined as private.  The intended use case wouldn't require copies of
+    /// a \c BasicRRset object; once created, it would normally be used
+    /// as a \c const object (via references).
+    //@{
 private:
 private:
     BasicRRset(const BasicRRset& source);
     BasicRRset(const BasicRRset& source);
-    void operator=(const BasicRRset& source);
+    BasicRRset& operator=(const BasicRRset& source);
 public:
 public:
+    /// \brief Constructor from (mostly) fixed parameters of the RRset.
+    ///
+    /// This constructor is normally expected to be exception free, but
+    /// copying the name may involve resource allocation, and if it fails
+    /// the corresponding standard exception will be thrown.
+    ///
+    /// \param name The owner name of the RRset.
+    /// \param rrclass The RR class of the RRset.
+    /// \param rrtype The RR type of the RRset.
+    /// \param ttl The TTL of the RRset.
     explicit BasicRRset(const Name& name, const RRClass& rrclass,
     explicit BasicRRset(const Name& name, const RRClass& rrclass,
                         const RRType& rrtype, const RRTTL& ttl);
                         const RRType& rrtype, const RRTTL& ttl);
+    /// \brief The destructor.
     virtual ~BasicRRset();
     virtual ~BasicRRset();
+    //@}
+
     ///
     ///
-    /// See the note for the base class version.
+    /// \name Getter and Setter Methods
     ///
     ///
-    using AbstractRRset::addRdata;
-    virtual void addRdata(const rdata::RdataPtr rdata);
+    //@{
+    /// \brief Returns the number of \c Rdata objects contained in the \c RRset.
     ///
     ///
-    /// We'd use the default implementations for toText() and toWire()
-    /// variants as defined in the base class.  These are not fully optimized
-    /// for performance, but, again, it's a secondary goal for this generic
-    /// class.
+    /// This method never throws an exception.
+    ///
+    /// \return The number of \c Rdata objects contained.
+    virtual unsigned int getRdataCount() const;
 
 
+    /// \brief Returns the owner name of the \c RRset.
     ///
     ///
-    /// \name Getter and setter methods
+    /// This method never throws an exception.
     ///
     ///
-    //@{
-    virtual unsigned int getRdataCount() const;
+    /// \return A reference to a \c Name class object corresponding to the
+    /// \c RRset owner name.
     virtual const Name& getName() const;
     virtual const Name& getName() const;
+
+    /// \brief Returns the RR Class of the \c RRset.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return A reference to a \c RRClass class object corresponding to the
+    /// RR class of the \c RRset.
     virtual const RRClass& getClass() const;
     virtual const RRClass& getClass() const;
+
+    /// \brief Returns the RR Type of the \c RRset.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return A reference to a \c RRType class object corresponding to the
+    /// RR type of the \c RRset.
     virtual const RRType& getType() const;
     virtual const RRType& getType() const;
+
+    /// \brief Returns the TTL of the \c RRset.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return A reference to a \c RRTTL class object corresponding to the
+    /// TTL of the \c RRset.
     virtual const RRTTL& getTTL() const;
     virtual const RRTTL& getTTL() const;
+
+    /// \brief Updates the owner name of the \c RRset.
+    ///
+    /// This method normally does not throw an exception, but could throw
+    /// some standard exception on resource allocation failure if the
+    /// internal copy of the \c name involves resource allocation and it
+    /// fails.
+    ///
+    /// \param name A reference to a \c RRTTL class object to be copied as the
+    /// new TTL.
+    virtual void setName(const Name& name);
+
+    /// \brief Updates the TTL of the \c RRset.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \param ttl A reference to a \c RRTTL class object to be copied as the
+    /// new TTL.
     virtual void setTTL(const RRTTL& ttl);
     virtual void setTTL(const RRTTL& ttl);
     //@}
     //@}
+
+    ///
+    /// \name Converter Methods
+    ///
+    //@{
+    /// \brief Convert the RRset to a string.
+    ///
+    /// This method simply uses the default implementation.
+    /// See \c AbstractRRset::toText().
+    virtual std::string toText() const;
+
+    /// \brief Render the RRset in the wire format with name compression and
+    /// truncation handling.
+    ///
+    /// This method simply uses the default implementation.
+    /// See \c AbstractRRset::toWire(MessageRenderer&)const.
+    virtual unsigned int toWire(MessageRenderer& renderer) const;
+
+    /// \brief Render the RRset in the wire format without any compression.
+    ///
+    /// This method simply uses the default implementation.
+    /// See \c AbstractRRset::toWire(OutputBuffer&)const.
+    virtual unsigned int toWire(OutputBuffer& buffer) const;
+    //@}
+
     ///
     ///
+    /// \name RDATA manipulation methods
+    ///
+    //@{
+    /// \brief Add an RDATA to the RRset (pointer version).
+    ///
+    /// This method is normally expected to be exception free, but it may
+    /// involve resource allocation, and if it fails the corresponding
+    /// standard exception will be thrown.
+    ///
+    /// \param rdata A pointer (like) type of \c rdata::RdataPtr to be added
+    /// to the \c BasicRRset.
+    virtual void addRdata(rdata::ConstRdataPtr rdata);
+
+    /// \brief Add an RDATA to the RRset (reference version).
+    ///
+    /// This method simply uses the default implementation.
+    /// See \c AbstractRRset::addRdata(const rdata::Rdata&).
+    virtual void addRdata(const rdata::Rdata& rdata);
+
+    /// \brief Return an iterator to go through all RDATA stored in the
+    /// \c BasicRRset.
+    ///
+    /// This is a concrete derived implementation of
+    /// \c AbstractRRset::getRdataIterator().
+    ///
+    /// This method dynamically allocates resources.  If it fails it will
+    /// throw the corresponding standard exception.
+    /// The iterator methods for the \c BasicRRset class are exception free.
+    ///
+    /// \return A pointer-like object pointing to the derived \c RdataIterator
+    /// object for the \c BasicRRset class.
     virtual RdataIteratorPtr getRdataIterator() const;
     virtual RdataIteratorPtr getRdataIterator() const;
+    //@}
 private:
 private:
     BasicRRsetImpl* impl_;
     BasicRRsetImpl* impl_;
 };
 };
 
 
+/// \brief Insert the \c RRset as a string into stream.
+///
+/// This method convert the \c rrset into a string and inserts it into the
+/// output stream \c os.
+///
+/// This function overloads the global \c operator<< to behave as described in
+/// \c %ostream::%operator<< but applied to RRset objects.
+///
+/// \param os A \c std::ostream object on which the insertion operation is
+/// performed.
+/// \param rrset A reference to a (derived class of) \c AbstractRRset object
+/// output by the operation.
+/// \return A reference to the same \c std::ostream object referenced by
+/// parameter \c os after the insertion operation.
 std::ostream& operator<<(std::ostream& os, const AbstractRRset& rrset);
 std::ostream& operator<<(std::ostream& os, const AbstractRRset& rrset);
 } // end of namespace dns
 } // end of namespace dns
 } // end of namespace isc
 } // end of namespace isc

+ 67 - 0
src/lib/dns/cpp/rrsetlist.cc

@@ -0,0 +1,67 @@
+// 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.
+
+// $Id$
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/foreach.hpp>
+
+#include "rrsetlist.h"
+
+using namespace std;
+using namespace isc::dns;
+
+namespace isc {
+namespace dns {
+
+void
+RRsetList::addRRset(const RRsetPtr rrsetptr)
+{
+    const RRsetPtr rrset_found = findRRset(rrsetptr->getType(),
+                                           rrsetptr->getClass());
+    if (rrset_found) {
+        dns_throw(DuplicateRRset, "");
+    }
+    rrsets_.push_back(rrsetptr);
+}
+
+const RRsetPtr
+RRsetList::findRRset(const RRsetPtr rrsetptr)
+{
+    BOOST_FOREACH(const RRsetPtr t, rrsets_) {
+        if (rrsetptr == t) {
+            return rrsetptr;
+        }
+    }
+    return RRsetPtr();
+}
+
+const RRsetPtr
+RRsetList::findRRset(const RRType& rrtype, const RRClass& rrclass)
+{
+    BOOST_FOREACH(const RRsetPtr rrsetptr, rrsets_) {
+        const AbstractRRset* rrset = rrsetptr.get();
+        if ((rrset->getClass() == rrclass) && (rrset->getType() == rrtype)) {
+            return rrsetptr;
+        }
+    }
+    return RRsetPtr();
+}
+
+}
+}

+ 67 - 0
src/lib/dns/cpp/rrsetlist.h

@@ -0,0 +1,67 @@
+// 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.
+
+// $Id$
+
+#ifndef __RRSETLIST_H
+#define __RRSETLIST_H 1
+
+#include <iostream>
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+
+#include "rrset.h"
+#include "rrclass.h"
+#include "rrtype.h"
+
+namespace isc {
+namespace dns {
+
+class DuplicateRRset : public Exception {
+public:
+    DuplicateRRset(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+class RRsetList {
+public:
+    void addRRset(const RRsetPtr new_rrsetptr);
+    const RRsetPtr findRRset(const RRType& rrtype,
+                             const RRClass& rrclass = RRClass::IN());
+    const RRsetPtr findRRset(const RRsetPtr);
+
+    const RRsetPtr operator[](RRType t) { return (this->findRRset(t)); }
+
+    typedef std::vector<RRsetPtr>::const_iterator const_iterator;
+    const_iterator begin() const { return (rrsets_.begin()); }
+    const_iterator end() const { return (rrsets_.end)(); }
+
+    typedef std::vector<RRsetPtr>::iterator iterator;
+    iterator begin() { return (rrsets_.begin()); }
+    iterator end() { return (rrsets_.end)(); }
+
+    size_t size() const { return (rrsets_.size()); }
+
+private:
+    std::vector<RRsetPtr> rrsets_;
+};
+
+} // end of namespace dns
+} // end of namespace isc
+#endif  // __RRSETLIST_H
+
+// Local Variables: 
+// mode: c++
+// End: 

+ 177 - 0
src/lib/dns/cpp/rrsetlist_unittest.cc

@@ -0,0 +1,177 @@
+// 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.
+
+// $Id: rrtype_unittest.cc 476 2010-01-19 00:29:28Z jinmei $
+
+#include <vector>
+#include <boost/foreach.hpp>
+
+#include "rdata.h"
+#include "rdataclass.h"
+#include "rrclass.h"
+#include "rrtype.h"
+#include "rrsetlist.h"
+#include "rrset.h"
+#include "rrttl.h"
+
+#include <gtest/gtest.h>
+
+#include "unittest_util.h"
+
+using isc::UnitTestUtil;
+using namespace std;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+namespace {
+class RRsetListTest : public ::testing::Test {
+protected:
+    RRsetListTest() {}
+    void setupList(RRsetList& list);
+    static const in::A rdata_in_a;
+    static const in::AAAA rdata_in_aaaa;
+    static const generic::NS rdata_ns;
+    static const generic::SOA rdata_soa;
+    static const generic::CNAME rdata_cname;
+};
+
+const in::A RRsetListTest::rdata_in_a("192.0.2.1");
+const in::AAAA RRsetListTest::rdata_in_aaaa("2001:db8::1234");
+const generic::NS RRsetListTest::rdata_ns("ns.example.com");
+const generic::SOA RRsetListTest::rdata_soa(Name("ns.example.com"),
+                                            Name("root.example.com"),
+                                            2010012601, 3600, 300,
+                                            3600000, 1200);
+const generic::CNAME RRsetListTest::rdata_cname("target.example.com");
+
+void
+RRsetListTest::setupList(RRsetList& list) {
+    RRsetPtr a(new RRset(Name("example.com"), RRClass::IN(),
+                         RRType::A(), RRTTL(3600)));
+    RRsetPtr aaaa(new RRset(Name("example.com"), RRClass::IN(),
+                            RRType::AAAA(), RRTTL(3600)));
+    RRsetPtr ns(new RRset(Name("example.com"), RRClass::IN(),
+                          RRType::NS(), RRTTL(3600)));
+    RRsetPtr soa(new RRset(Name("example.com"), RRClass::IN(),
+                           RRType::SOA(), RRTTL(3600)));
+    RRsetPtr cname(new RRset(Name("example.com"), RRClass::IN(),
+                             RRType::CNAME(), RRTTL(3600)));
+
+    a->addRdata(rdata_in_a);
+    aaaa->addRdata(rdata_in_aaaa);
+    ns->addRdata(rdata_ns);
+    soa->addRdata(rdata_soa);
+    cname->addRdata(rdata_cname);
+
+    list.addRRset(a);
+    list.addRRset(aaaa);
+    list.addRRset(ns);
+    list.addRRset(soa);
+    list.addRRset(cname);
+}
+
+TEST_F(RRsetListTest, emptyOnInitialCreate) {
+    RRsetList list;
+    EXPECT_EQ(list.size(), 0);
+}
+
+TEST_F(RRsetListTest, addRRsets) {
+    RRsetList list;
+    setupList(list);
+    EXPECT_EQ(list.size(), 5);
+}
+
+TEST_F(RRsetListTest, extraRRset) {
+    RRsetList list;
+    setupList(list);
+    RRsetPtr cname(new RRset(Name("another.example.com"), RRClass::IN(),
+                             RRType::CNAME(), RRTTL(3600)));
+    EXPECT_THROW(list.addRRset(cname), DuplicateRRset);
+}
+
+TEST_F(RRsetListTest, randomAccess) {
+    RRsetList list;
+    setupList(list);
+
+    RRsetPtr p;
+
+    p = list[RRType::CNAME()];
+    EXPECT_TRUE(p->getType() == RRType::CNAME());
+
+    p = list[RRType::AAAA()];
+    EXPECT_TRUE(p->getType() == RRType::AAAA());
+
+    p = list[RRType::NS()];
+    EXPECT_TRUE(p->getType() == RRType::NS());
+
+    p = list[RRType::A()];
+    EXPECT_TRUE(p->getType() == RRType::A());
+
+    p = list[RRType::SOA()];
+    EXPECT_TRUE(p->getType() == RRType::SOA());
+}
+
+TEST_F(RRsetListTest, findRRset) {
+    RRsetList list;
+    setupList(list);
+    EXPECT_EQ(list[RRType::A()], list.findRRset(RRType::A(), RRClass::IN()));
+}
+
+TEST_F(RRsetListTest, checkData) {
+    RRsetList list;
+    RRsetPtr a(new RRset(Name("example.com"), RRClass::IN(),
+                         RRType::A(), RRTTL(3600)));
+    a->addRdata(rdata_in_a);
+    list.addRRset(a);
+
+    RdataIteratorPtr it = list[RRType::A()]->getRdataIterator();
+    it->first();
+    EXPECT_FALSE(it->isLast());
+    EXPECT_EQ("192.0.2.1", it->getCurrent().toText());
+}
+
+TEST_F(RRsetListTest, iterate) {
+    RRsetList list;
+    setupList(list);
+
+    bool has_a, has_aaaa, has_ns, has_soa, has_cname;
+    int i = 0;
+    BOOST_FOREACH(RRsetPtr rrset, list) {
+        if (rrset->getType() == RRType::A()) {
+            has_a = true;
+        }
+        if (rrset->getType() == RRType::AAAA()) {
+            has_aaaa = true;
+        }
+        if (rrset->getType() == RRType::NS()) {
+            has_ns = true;
+        }
+        if (rrset->getType() == RRType::SOA()) {
+            has_soa = true;
+        }
+        if (rrset->getType() == RRType::CNAME()) {
+            has_cname = true;
+        }
+        ++i;
+    }
+    EXPECT_TRUE(has_a);
+    EXPECT_TRUE(has_aaaa);
+    EXPECT_TRUE(has_ns);
+    EXPECT_TRUE(has_soa);
+    EXPECT_TRUE(has_cname);
+    EXPECT_TRUE(i == 5);
+}
+
+}
+

+ 70 - 15
src/lib/dns/cpp/tests/question_unittest.cc

@@ -14,6 +14,11 @@
 
 
 // $Id$
 // $Id$
 
 
+#include <vector>
+#include <sstream>
+
+#include <exceptions/exceptions.h>
+
 #include <dns/buffer.h>
 #include <dns/buffer.h>
 #include <dns/messagerenderer.h>
 #include <dns/messagerenderer.h>
 #include <dns/name.h>
 #include <dns/name.h>
@@ -33,36 +38,86 @@ namespace {
 class QuestionTest : public ::testing::Test {
 class QuestionTest : public ::testing::Test {
 protected:
 protected:
     QuestionTest() : obuffer(0), renderer(obuffer),
     QuestionTest() : obuffer(0), renderer(obuffer),
-                     test_question(Name("example.com"), RRClass::IN(),
-                                   RRType::NS()) 
+                     example_name1(Name("foo.example.com")),
+                     example_name2(Name("bar.example.com")),
+                     test_question1(example_name1, RRClass::IN(),
+                                    RRType::NS()),
+                     test_question2(example_name2, RRClass::CH(),
+                                    RRType::A())
     {}
     {}
     OutputBuffer obuffer;
     OutputBuffer obuffer;
     MessageRenderer renderer;
     MessageRenderer renderer;
-    Question test_question;
-    static const uint8_t wiredata[];
+    Name example_name1;
+    Name example_name2;
+    Question test_question1;
+    Question test_question2;
+    vector<unsigned char> wiredata;
 };
 };
 
 
-// wire-format representation of "example.com. NS IN"
-const uint8_t QuestionTest::wiredata[] = { 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70,
-                                           0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d,
-                                           0x00, 0x00, 0x02, 0x00, 0x01 };
+Question
+questionFromWire(const char* datafile, size_t position = 0)
+{
+    vector<unsigned char> data;
+    UnitTestUtil::readWireData(datafile, data);
+
+    InputBuffer buffer(&data[0], data.size());
+    buffer.setPosition(position);
+
+    return (Question(buffer));
+}
 
 
 TEST_F(QuestionTest, fromWire)
 TEST_F(QuestionTest, fromWire)
 {
 {
-    InputBuffer ibuffer(wiredata, sizeof(wiredata));
-    Question q(ibuffer);
-    EXPECT_EQ(test_question, q);
+    Question q = questionFromWire("testdata/question_fromWire");
+
+    EXPECT_EQ(example_name1, q.getName());
+    EXPECT_EQ(RRClass::IN(), q.getClass());
+    EXPECT_EQ(RRType::NS(), q.getType());
+
+    // owner name of the second Question is compressed.  It's uncommon
+    // (to have multiple questions), but isn't prohibited by the protocol.
+    q = questionFromWire("testdata/question_fromWire", 21);
+    EXPECT_EQ(example_name2, q.getName());
+    EXPECT_EQ(RRClass::CH(), q.getClass());
+    EXPECT_EQ(RRType::A(), q.getType());
+
+    // Pathological cases: Corresponding exceptions will be thrown from
+    // the underlying parser.
+    EXPECT_THROW(questionFromWire("testdata/question_fromWire", 31),
+                 BadPointer);
+    EXPECT_THROW(questionFromWire("testdata/question_fromWire", 36),
+                 IncompleteRRClass);
 }
 }
 
 
 TEST_F(QuestionTest, toText)
 TEST_F(QuestionTest, toText)
 {
 {
-    EXPECT_EQ("example.com. IN NS\n", test_question.toText());
+    EXPECT_EQ("foo.example.com. IN NS\n", test_question1.toText());
+    EXPECT_EQ("bar.example.com. CH A\n", test_question2.toText());
+}
+
+TEST_F(QuestionTest, toWireBuffer)
+{
+    test_question1.toWire(obuffer);
+    test_question2.toWire(obuffer);
+    UnitTestUtil::readWireData("testdata/question_toWire1", wiredata);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
+                        obuffer.getLength(), &wiredata[0], wiredata.size());
 }
 }
 
 
-TEST_F(QuestionTest, toWire)
+TEST_F(QuestionTest, toWireRenderer)
 {
 {
-    test_question.toWire(obuffer);
+    test_question1.toWire(renderer);
+    test_question2.toWire(renderer);
+    UnitTestUtil::readWireData("testdata/question_toWire2", wiredata);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
-                        obuffer.getLength(), wiredata, sizeof(wiredata));
+                        obuffer.getLength(), &wiredata[0], wiredata.size());
+}
+
+// test operator<<.  We simply confirm it appends the result of toText().
+TEST_F(QuestionTest, LeftShiftOperator)
+{
+    ostringstream oss;
+    oss << test_question1;
+    EXPECT_EQ(test_question1.toText(), oss.str());
 }
 }
 }
 }

+ 146 - 13
src/lib/dns/cpp/tests/rrset_unittest.cc

@@ -14,6 +14,8 @@
 
 
 // $Id$
 // $Id$
 
 
+#include <stdexcept>
+
 #include <dns/buffer.h>
 #include <dns/buffer.h>
 #include <dns/messagerenderer.h>
 #include <dns/messagerenderer.h>
 #include <dns/name.h>
 #include <dns/name.h>
@@ -42,9 +44,16 @@ protected:
                   test_domain("example.com"),
                   test_domain("example.com"),
                   test_nsname("ns.example.com"),
                   test_nsname("ns.example.com"),
                   rrset_a(test_name, RRClass::IN(), RRType::A(), RRTTL(3600)),
                   rrset_a(test_name, RRClass::IN(), RRType::A(), RRTTL(3600)),
+                  rrset_a_empty(test_name, RRClass::IN(), RRType::A(),
+                                RRTTL(3600)),
                   rrset_ns(test_domain, RRClass::IN(), RRType::NS(),
                   rrset_ns(test_domain, RRClass::IN(), RRType::NS(),
-                           RRTTL(86400))
-    {}
+                           RRTTL(86400)),
+                  rrset_ch_txt(test_domain, RRClass::CH(), RRType::TXT(),
+                               RRTTL(0))
+    {
+        rrset_a.addRdata(in::A("192.0.2.1"));
+        rrset_a.addRdata(in::A("192.0.2.2"));
+    }
 
 
     OutputBuffer buffer;
     OutputBuffer buffer;
     MessageRenderer renderer;
     MessageRenderer renderer;
@@ -52,44 +61,157 @@ protected:
     Name test_domain;
     Name test_domain;
     Name test_nsname;
     Name test_nsname;
     RRset rrset_a;
     RRset rrset_a;
+    RRset rrset_a_empty;
     RRset rrset_ns;
     RRset rrset_ns;
+    RRset rrset_ch_txt;
     std::vector<unsigned char> wiredata;
     std::vector<unsigned char> wiredata;
+
+    // max number of Rdata objects added to a test RRset object.
+    // this is an arbitrary chosen limit, but should be sufficiently large
+    // in practice and reasonable even as an extreme test case.
+    static const int MAX_RDATA_COUNT = 100;
 };
 };
 
 
-TEST_F(RRsetTest, addRdata)
+TEST_F(RRsetTest, getRdataCount)
+{
+    for (int i = 0; i < MAX_RDATA_COUNT; ++i) {
+        EXPECT_EQ(i, rrset_a_empty.getRdataCount());
+        rrset_a_empty.addRdata(in::A("192.0.2.1"));
+    }
+}
+
+TEST_F(RRsetTest, getName)
 {
 {
-    rrset_a.addRdata(in::A("192.0.2.1"));
-    rrset_a.addRdata(in::A("192.0.2.2"));
+    EXPECT_EQ(test_name, rrset_a.getName());
+    EXPECT_EQ(test_domain, rrset_ns.getName());
+}
+
+TEST_F(RRsetTest, getClass)
+{
+    EXPECT_EQ(RRClass("IN"), rrset_a.getClass());
+    EXPECT_EQ(RRClass("CH"), rrset_ch_txt.getClass());
+}
 
 
-    RdataIteratorPtr it = rrset_a.getRdataIterator();
+TEST_F(RRsetTest, getType)
+{
+    EXPECT_EQ(RRType("A"), rrset_a.getType());
+    EXPECT_EQ(RRType("NS"), rrset_ns.getType());
+    EXPECT_EQ(RRType("TXT"), rrset_ch_txt.getType());
+}
+
+TEST_F(RRsetTest, getTTL)
+{
+    EXPECT_EQ(RRTTL(3600), rrset_a.getTTL());
+    EXPECT_EQ(RRTTL(86400), rrset_ns.getTTL());
+    EXPECT_EQ(RRTTL(0), rrset_ch_txt.getTTL());
+}
+
+TEST_F(RRsetTest, setTTL)
+{
+    rrset_a.setTTL(RRTTL(86400));
+    EXPECT_EQ(RRTTL(86400), rrset_a.getTTL());
+    rrset_a.setTTL(RRTTL(0));
+    EXPECT_EQ(RRTTL(0), rrset_a.getTTL());
+}
+
+TEST_F(RRsetTest, setName)
+{
+    rrset_a.setName(test_nsname);
+    EXPECT_EQ(test_nsname, rrset_a.getName());
+}
+
+void
+addRdataTestCommon(const RRset& rrset)
+{
+    EXPECT_EQ(2, rrset.getRdataCount());
+
+    RdataIteratorPtr it = rrset.getRdataIterator();
     it->first();
     it->first();
     EXPECT_FALSE(it->isLast());
     EXPECT_FALSE(it->isLast());
-    EXPECT_EQ("192.0.2.1", it->getCurrent().toText());
+    EXPECT_EQ(0, it->getCurrent().compare(in::A("192.0.2.1")));
     it->next();
     it->next();
     EXPECT_FALSE(it->isLast());
     EXPECT_FALSE(it->isLast());
-    EXPECT_EQ("192.0.2.2", it->getCurrent().toText());
+    EXPECT_EQ(0, it->getCurrent().compare(in::A("192.0.2.2")));
     it->next();
     it->next();
     EXPECT_TRUE(it->isLast());
     EXPECT_TRUE(it->isLast());
+}
+
+TEST_F(RRsetTest, addRdata)
+{
+    addRdataTestCommon(rrset_a);
+
+    // Reference version of addRdata() doesn't allow to add a different
+    // type of Rdata.
+    EXPECT_THROW(rrset_a.addRdata(generic::NS(test_nsname)), std::bad_cast);
+}
+
+TEST_F(RRsetTest, addRdataPtr)
+{
+    rrset_a_empty.addRdata(createRdata(rrset_a_empty.getType(),
+                                       rrset_a_empty.getClass(),
+                                       "192.0.2.1"));
+    rrset_a_empty.addRdata(createRdata(rrset_a_empty.getType(),
+                                       rrset_a_empty.getClass(),
+                                       "192.0.2.2"));
+
+    addRdataTestCommon(rrset_a);
+
+    // Pointer version of addRdata() doesn't type check and does allow to
+    //add a different type of Rdata as a result.
+    rrset_a_empty.addRdata(createRdata(RRType::NS(), RRClass::IN(),
+                                       "ns.example.com"));
+    EXPECT_EQ(3, rrset_a_empty.getRdataCount());
+}
+
+TEST_F(RRsetTest, iterator)
+{
+    // Iterator for an empty RRset.
+    RdataIteratorPtr it = rrset_a_empty.getRdataIterator();
+    it->first();
+    EXPECT_TRUE(it->isLast());
 
 
+    // Normal case (already tested, but do it again just in case)
+    rrset_a_empty.addRdata(in::A("192.0.2.1"));
+    rrset_a_empty.addRdata(in::A("192.0.2.2"));
+    addRdataTestCommon(rrset_a_empty);
+
+    // Rewind test: should be repeat the iteration by calling first().
+    for (int i = 0; i < 2; ++i) {
+        it = rrset_a_empty.getRdataIterator();
+        it->first();
+        EXPECT_FALSE(it->isLast());
+        it->next();
+        EXPECT_FALSE(it->isLast());
+        it->next();
+        EXPECT_TRUE(it->isLast());
+    }
+}
+
+TEST_F(RRsetTest, toText)
+{
     EXPECT_EQ("test.example.com. 3600 IN A 192.0.2.1\n"
     EXPECT_EQ("test.example.com. 3600 IN A 192.0.2.1\n"
-              "test.example.com. 3600 IN A 192.0.2.2\n", rrset_a.toText());
+              "test.example.com. 3600 IN A 192.0.2.2\n",
+              rrset_a.toText());
+
+    // toText() cannot be performed for an empty RRset.
+    EXPECT_THROW(rrset_a_empty.toText(), EmptyRRset);
 }
 }
 
 
 TEST_F(RRsetTest, toWireBuffer)
 TEST_F(RRsetTest, toWireBuffer)
 {
 {
-    rrset_a.addRdata(in::A("192.0.2.1"));
-    rrset_a.addRdata(in::A("192.0.2.2"));
     rrset_a.toWire(buffer);
     rrset_a.toWire(buffer);
 
 
     UnitTestUtil::readWireData("testdata/rrset_toWire1", wiredata);
     UnitTestUtil::readWireData("testdata/rrset_toWire1", wiredata);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
                         buffer.getLength(), &wiredata[0], wiredata.size());
                         buffer.getLength(), &wiredata[0], wiredata.size());
+
+    // toWire() cannot be performed for an empty RRset.
+    buffer.clear();
+    EXPECT_THROW(rrset_a_empty.toWire(buffer), EmptyRRset);
 }
 }
 
 
 TEST_F(RRsetTest, toWireRenderer)
 TEST_F(RRsetTest, toWireRenderer)
 {
 {
-    rrset_a.addRdata(in::A("192.0.2.1"));
-    rrset_a.addRdata(in::A("192.0.2.2"));
     rrset_ns.addRdata(generic::NS(test_nsname));
     rrset_ns.addRdata(generic::NS(test_nsname));
 
 
     rrset_a.toWire(renderer);
     rrset_a.toWire(renderer);
@@ -98,6 +220,17 @@ TEST_F(RRsetTest, toWireRenderer)
     UnitTestUtil::readWireData("testdata/rrset_toWire2", wiredata);
     UnitTestUtil::readWireData("testdata/rrset_toWire2", wiredata);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
                         buffer.getLength(), &wiredata[0], wiredata.size());
                         buffer.getLength(), &wiredata[0], wiredata.size());
+
+    // toWire() cannot be performed for an empty RRset.
+    renderer.clear();
+    EXPECT_THROW(rrset_a_empty.toWire(renderer), EmptyRRset);
 }
 }
 
 
+// test operator<<.  We simply confirm it appends the result of toText().
+TEST_F(RRsetTest, LeftShiftOperator)
+{
+    ostringstream oss;
+    oss << rrset_a;
+    EXPECT_EQ(rrset_a.toText(), oss.str());
+}
 }
 }