2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-31 14:05:33 +00:00

- added detaild documentation

- some more tests
- overall cleanup


git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsrrset@917 e5f2f494-b856-4b98-b285-d166d9295462
This commit is contained in:
JINMEI Tatuya
2010-02-23 07:52:57 +00:00
parent ffd4fcba40
commit c4cf50a3af
5 changed files with 690 additions and 110 deletions

View File

@@ -568,7 +568,7 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
INPUT = ../src/lib/cc/cpp
INPUT = ../src/lib/cc/cpp ../src/lib/dns/cpp
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is

View File

@@ -65,6 +65,7 @@ public:
class Rdata;
typedef boost::shared_ptr<Rdata> RdataPtr;
typedef boost::shared_ptr<const Rdata> ConstRdataPtr;
/// Abstract RDATA class
class Rdata {

View File

@@ -46,16 +46,20 @@ AbstractRRset::toText() const
string s;
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);
}
}
namespace {
template <typename T>
@@ -65,9 +69,14 @@ rrsetToWire(const AbstractRRset& rrset, T& output)
unsigned int n = 0;
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
// other options. Details to be considered.
for (it->first(); !it->isLast(); it->next(), ++n) {
do {
rrset.getName().toWire(output);
rrset.getType().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
it->getCurrent().toWire(output);
output.writeUint16At(output.getLength() - pos - sizeof(uint16_t), pos);
}
it->next();
++n;
} while (!it->isLast());
return (n);
}
@@ -102,7 +114,10 @@ operator<<(ostream& os, const AbstractRRset& rrset)
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,
const RRType& rrtype, const RRTTL& ttl) :
name_(name), rrclass_(rrclass), rrtype_(rrtype), ttl_(ttl) {}
@@ -110,7 +125,10 @@ struct BasicRRsetImpl {
RRClass rrclass_;
RRType rrtype_;
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,
@@ -125,11 +143,17 @@ BasicRRset::~BasicRRset()
}
void
BasicRRset::addRdata(const RdataPtr rdata)
BasicRRset::addRdata(ConstRdataPtr rdata)
{
impl_->rdatalist_.push_back(rdata);
}
void
BasicRRset::addRdata(const Rdata& rdata)
{
AbstractRRset::addRdata(rdata);
}
unsigned int
BasicRRset::getRdataCount() const
{
@@ -160,18 +184,42 @@ BasicRRset::getTTL() const
return (impl_->ttl_);
}
void
BasicRRset::setName(const Name& name)
{
impl_->name_ = name;
}
void
BasicRRset::setTTL(const RRTTL& 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 {
class BasicRdataIterator : public RdataIterator {
private:
BasicRdataIterator() {}
public:
BasicRdataIterator(const std::vector<rdata::RdataPtr>& datavector) :
BasicRdataIterator(const std::vector<rdata::ConstRdataPtr>& datavector) :
datavector_(&datavector) {}
~BasicRdataIterator() {}
virtual void first() { it_ = datavector_->begin(); }
@@ -179,8 +227,8 @@ public:
virtual const rdata::Rdata& getCurrent() const { return (**it_); }
virtual bool isLast() const { return (it_ == datavector_->end()); }
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_;
};
}

View File

@@ -22,148 +22,657 @@
#include <boost/shared_ptr.hpp>
#include <exceptions/exceptions.h>
#include "rdata.h"
namespace isc {
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 RRType;
class RRClass;
class RRTTL;
class OututBuffer;
class MessageRenderer;
class AbstractRRset;
typedef boost::shared_ptr<AbstractRRset> RRsetPtr;
class BasicRRset;
typedef BasicRRset RRset;
class RdataIterator;
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;
/// \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;
/// 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?).
// - 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.
/// 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'
/// <a href="http://www.nlnetlabs.nl/projects/ldns/doc/index.html">ldns</a>
/// 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 {
///
/// \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:
/// The destructor.
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;
/// \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;
/// \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;
/// \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;
/// \brief Returns the TTL of the RRset.
///
/// \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.
///
/// once constructed, only TTL and the set of Rdata can be modified,
/// so \c setTTL() is the only explicit setter method.
/// \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;
//@}
///
/// \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.
//@{
/// \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;
//@}
};
/// \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 {
///
/// \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:
/// \brief Destructor
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;
/// \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;
/// \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;
/// \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;
};
/// \brief The \c BasicRRset class is a concrete derived class of
/// \c AbstractRRset that defines a straightforward RRset implementation.
///
/// designed to be as portable as possible. performance is a secondary
/// concern for this class.
///
/// We'd use the default implementations for the 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.
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:
BasicRRset(const BasicRRset& source);
BasicRRset& operator=(const BasicRRset& source);
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,
const RRType& rrtype, const RRTTL& ttl);
/// \brief The destructor.
virtual ~BasicRRset();
//@}
///
/// \name Getter and Setter Methods
///
//@{
/// \brief Returns the number of \c Rdata objects contained in the \c RRset.
///
/// 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.
///
/// This method never throws an exception.
///
/// \return A reference to a \c Name class object corresponding to the
/// \c RRset owner name.
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;
/// \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;
/// \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;
/// \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);
//@}
///
/// \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
///
//@{
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
/// \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);
virtual RdataIteratorPtr getRdataIterator() const = 0;
//@}
};
class RdataIterator {
public:
virtual ~RdataIterator() {}
virtual void first() = 0;
virtual void next() = 0;
virtual const rdata::Rdata& getCurrent() 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.
class BasicRRset : public AbstractRRset {
private:
BasicRRset(const BasicRRset& source);
void operator=(const BasicRRset& source);
public:
explicit BasicRRset(const Name& name, const RRClass& rrclass,
const RRType& rrtype, const RRTTL& ttl);
virtual ~BasicRRset();
/// \brief Return an iterator to go through all RDATA stored in the
/// \c BasicRRset.
///
/// See the note for the base class version.
/// This is a concrete derived implementation of
/// \c AbstractRRset::getRdataIterator().
///
using AbstractRRset::addRdata;
virtual void addRdata(const rdata::RdataPtr rdata);
///
/// 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.
///
/// \name Getter and setter methods
///
//@{
virtual unsigned int getRdataCount() const;
virtual const Name& getName() const;
virtual const RRClass& getClass() const;
virtual const RRType& getType() const;
virtual const RRTTL& getTTL() const;
virtual void setTTL(const RRTTL& ttl);
//@}
/// 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;
//@}
private:
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);
} // end of namespace dns
} // end of namespace isc

View File

@@ -168,6 +168,17 @@ TEST_F(RRsetTest, iterator)
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)
@@ -175,6 +186,9 @@ TEST_F(RRsetTest, toText)
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());
// toText() cannot be performed for an empty RRset.
EXPECT_THROW(rrset_a_empty.toText(), EmptyRRset);
}
TEST_F(RRsetTest, toWireBuffer)
@@ -184,6 +198,10 @@ TEST_F(RRsetTest, toWireBuffer)
UnitTestUtil::readWireData("testdata/rrset_toWire1", wiredata);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
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)
@@ -196,6 +214,10 @@ TEST_F(RRsetTest, toWireRenderer)
UnitTestUtil::readWireData("testdata/rrset_toWire2", wiredata);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
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().