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

Merge branch 'work/rdatafields'

Conflicts:
	src/lib/dns/gen-rdatacode.py.in
	src/lib/dns/messagerenderer.cc
	src/lib/dns/messagerenderer.h
	src/lib/dns/name.h
	src/lib/dns/rdata.h
	src/lib/dns/rrtype-placeholder.h
This commit is contained in:
Michal 'vorner' Vaner
2011-04-19 23:29:25 +02:00
50 changed files with 1580 additions and 171 deletions

View File

@@ -682,6 +682,7 @@ AC_CONFIG_FILES([Makefile
src/lib/dns/tests/testdata/Makefile
src/lib/dns/python/Makefile
src/lib/dns/python/tests/Makefile
src/lib/dns/benchmarks/Makefile
src/lib/exceptions/Makefile
src/lib/exceptions/tests/Makefile
src/lib/datasrc/Makefile

View File

@@ -1,4 +1,4 @@
SUBDIRS = . tests python
SUBDIRS = . tests python benchmarks
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
@@ -72,6 +72,7 @@ libdns___la_SOURCES += name.h name.cc
libdns___la_SOURCES += opcode.h opcode.cc
libdns___la_SOURCES += rcode.h rcode.cc
libdns___la_SOURCES += rdata.h rdata.cc
libdns___la_SOURCES += rdatafields.h rdatafields.cc
libdns___la_SOURCES += rrclass.cc
libdns___la_SOURCES += rrparamregistry.h
libdns___la_SOURCES += rrset.h rrset.cc

View File

@@ -0,0 +1,17 @@
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CXXFLAGS = $(B10_CXXFLAGS)
if USE_STATIC_LINK
AM_LDFLAGS = -static
endif
CLEANFILES = *.gcno *.gcda
noinst_PROGRAMS = rdatarender_bench
rdatarender_bench_SOURCES = rdatarender_bench.cc
rdatarender_bench_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
rdatarender_bench_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
rdatarender_bench_LDADD += $(SQLITE_LIBS)

View File

@@ -0,0 +1,10 @@
- rdatarender_bench
This is a benchmark for RDATA rendering performance comparing the basic
Rdata objects and RdataField objects. It takes a command line argument
that specifies an input data file. Each line of the data file should
specify a single RDATA with its RR class and type, e.g.
IN A 192.0.2.1
IN NS ns.example.com.
Lines beginning with '#' and empty lines will be ignored. Sample input
files can be found in benchmarkdata/rdatarender_*.

View File

@@ -0,0 +1,32 @@
# This is sample input data for rdatarender_bench.
# These are RDATA in the authority and additional sections in a response from
# a root server for a query domain under COM.
IN NS g.gtld-servers.net.
IN NS a.gtld-servers.net.
IN NS k.gtld-servers.net.
IN NS c.gtld-servers.net.
IN NS d.gtld-servers.net.
IN NS m.gtld-servers.net.
IN NS h.gtld-servers.net.
IN NS b.gtld-servers.net.
IN NS j.gtld-servers.net.
IN NS l.gtld-servers.net.
IN NS e.gtld-servers.net.
IN NS f.gtld-servers.net.
IN NS i.gtld-servers.net.
IN A 192.5.6.30
IN A 192.33.14.30
IN A 192.26.92.30
IN A 192.31.80.30
IN A 192.12.94.30
IN A 192.35.51.30
IN A 192.42.93.30
IN A 192.54.112.30
IN A 192.43.172.30
IN A 192.48.79.30
IN A 192.52.178.30
IN A 192.41.162.30
IN A 192.55.83.30
IN AAAA 2001:503:a83e::2:30
IN AAAA 2001:503:231d::2:30

View File

@@ -0,0 +1,10 @@
# This is sample input data for rdatarender_bench.
# These are RDATA in the authority section in a response from
# a root server for a non existent query domain (with DNSSEC).
IN SOA a.root-servers.net. nstld.verisign-grs.com. 2010110301 1800 900 604800 86400
IN RRSIG SOA 8 0 86400 20101110000000 20101102230000 40288 . WtvYyX2nIsaqjWqkIG1WHFE5PnJ6eno0KqF6azU/MFJ/t1JpKWQ1P4rA 61rnoq0p252fg7wT4XzEz9UDxmpB5pvF2VApe2w9LvSWxsWIIOg8ue5u e9NAAYdzjd0rsYObQQ6msf7WchyAUbnmrqKvf8/CK6+s1xFihXp5DpYL 6K0=
IN NSEC ac. NS SOA RRSIG NSEC DNSKEY
IN RRSIG NSEC 8 0 86400 20101110000000 20101102230000 40288 . rWfgg4YUDFAjhiUOT+niJy/qbaIbydqoXg5oB/5j//ZjNFy4hqU8DvdM xJr9UybQpEvu7pvmKQ0jRYO98Fw/UTlY5KiKbhVBJ1t8AE93cbU+s5gX d3Q6+wRcFX5MjZyIe+f30llKrYOZHjRyEFALCkLt4XEmr0xsua+ztAFY 65k=
IN NSEC np. NS RRSIG NSEC
IN RRSIG NSEC 8 1 86400 20101110000000 20101102230000 40288 . G32LGynsGA2fyDnesyeCtBCoM3ERMgGS4touDUuoBYW1NrZub76kz5fc z93p8VZfoYWAW7LuC8vJ1jl2sUgBNns4zN4RsfFeopcYjjFnGbGuoZnO NmTU+NKO53Ub7uIcCSeqV+COAaL8XqDfyk1FmVdQvtrBaOW/PWpRahVq 7E8=

View File

@@ -0,0 +1,22 @@
# This is sample input data for rdatarender_bench.
# These are RDATA in the authority and additional sections in a response from
# a root server for a query domain under ORG.
IN NS b0.org.afilias-nst.org.
IN NS a2.org.afilias-nst.info.
IN NS a0.org.afilias-nst.info.
IN NS c0.org.afilias-nst.info.
IN NS d0.org.afilias-nst.org.
IN NS b2.org.afilias-nst.org.
IN A 199.19.56.1
IN A 199.249.112.1
IN A 199.19.54.1
IN A 199.249.120.1
IN A 199.19.53.1
IN A 199.19.57.1
IN AAAA 2001:500:e::1
IN AAAA 2001:500:40::1
IN AAAA 2001:500:c::1
IN AAAA 2001:500:48::1
IN AAAA 2001:500:b::1
IN AAAA 2001:500:f::1

View File

@@ -0,0 +1,188 @@
// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <boost/shared_ptr.hpp>
#include <bench/benchmark.h>
#include <util/buffer.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdatafields.h>
#include <dns/rrclass.h>
#include <dns/rrtype.h>
using namespace std;
using namespace isc::bench;
using namespace isc::dns;
using namespace isc::dns::rdata;
using isc::util::OutputBuffer;
namespace {
// This templated benchmark class is constructed with a vector of Rdata-like
// (pointer) objects which should have a "toWire()" method. In its run(),
// it calls toWire() for each element of the vector.
template <typename T>
class RdataRenderBenchMark {
public:
RdataRenderBenchMark(const vector<T>& dataset) :
dataset_(dataset), buffer_(4096), renderer_(buffer_)
{}
unsigned int run() {
typename vector<T>::const_iterator data;
typename vector<T>::const_iterator data_end = dataset_.end();
for (data = dataset_.begin(); data != data_end; ++data) {
renderer_.clear();
(*data)->toWire(renderer_);
}
return (dataset_.size());
}
private:
const vector<T>& dataset_;
OutputBuffer buffer_;
MessageRenderer renderer_;
};
// This supplemental class emulates an RRset like class that internally
// uses RdataFields. On construction it stores RDATA information in the
// form of RdataFields fields. Its toWire() method restores the data as
// an RdataFields object for the rendering.
class RdataFieldsStore {
public:
RdataFieldsStore(ConstRdataPtr rdata) {
const RdataFields fields(*rdata);
spec_size_ = fields.getFieldSpecDataSize();
spec_store_.resize(spec_size_);
void* cp_spec = &spec_store_[0];
memcpy(cp_spec, fields.getFieldSpecData(), spec_store_.size());
spec_ptr_ = cp_spec;
data_length_ = fields.getDataLength();
data_store_.resize(data_length_);
void* cp_data = &data_store_[0];
memcpy(cp_data, fields.getData(), data_store_.size());
// Vector guarantees that the elements are stored in continuous array
// in memory, so this is actually correct by the standard
data_ptr_ = cp_data;
}
void toWire(MessageRenderer& renderer) const {
RdataFields(spec_ptr_, spec_size_,
data_ptr_, data_length_).toWire(renderer);
}
private:
vector<unsigned char> spec_store_;
vector<unsigned char> data_store_;
const void* spec_ptr_;
const void* data_ptr_;
unsigned int spec_size_;
size_t data_length_;
};
// We wouldn't necessarily have to use a shared pointer, but it's easier
// to use pointer-like values to adjust them with the RdataRenderBenchMark
// template.
typedef boost::shared_ptr<const RdataFieldsStore> ConstRdataFieldsStorePtr;
void
readInputFile(const char* const input_file, vector<ConstRdataPtr>& rdata_sets,
vector<ConstRdataFieldsStorePtr>& fields_sets)
{
ifstream ifs;
ifs.open(input_file, ios_base::in);
if ((ifs.rdstate() & istream::failbit) != 0) {
cerr << "Failed to read input file: " << input_file << endl;
exit (1);
}
string line;
unsigned int linenum = 0;
while (getline(ifs, line), !ifs.eof()) {
++linenum;
if (ifs.bad() || ifs.fail()) {
cerr << "Unexpected input at line " << linenum << endl;
exit (1);
}
if (line.empty() || line[0] == '#') {
continue; // skip comment and blank lines
}
istringstream iss(line);
string rrclass_string, rrtype_string;
stringbuf rdatabuf;
iss >> rrclass_string >> rrtype_string >> &rdatabuf;
if (iss.bad() || iss.fail()) {
cerr << "Unexpected input at line " << linenum << endl;
exit (1);
}
ConstRdataPtr rdata = createRdata(RRType(rrtype_string),
RRClass(rrclass_string),
rdatabuf.str());
rdata_sets.push_back(rdata);
fields_sets.push_back(ConstRdataFieldsStorePtr(
new RdataFieldsStore(rdata)));
}
ifs.close();
}
void
usage() {
cerr << "Usage: rdatafields_bench [-n iterations] input_file" << endl;
exit (1);
}
}
int
main(int argc, char* argv[]) {
int ch;
int iteration = 10000;
while ((ch = getopt(argc, argv, "n:")) != -1) {
switch (ch) {
case 'n':
iteration = atoi(optarg);
break;
case '?':
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc < 1) {
usage();
}
const char* const input_file = argv[0];
vector<ConstRdataPtr> rdata_sets;
vector<ConstRdataFieldsStorePtr> fields_sets;
readInputFile(input_file, rdata_sets, fields_sets);
cout << "Parameters:" << endl;
cout << " Iterations: " << iteration << endl;
cout << " Input File: " << input_file << endl;
typedef RdataRenderBenchMark<ConstRdataPtr> RdataBenchMark;
cout << "Benchmark for rendering with standard Rdata" << endl;
BenchMark<RdataBenchMark>(iteration, RdataBenchMark(rdata_sets));
typedef RdataRenderBenchMark<ConstRdataFieldsStorePtr> FieldsBenchMark;
cout << "Benchmark for rendering with RdataFields" << endl;
BenchMark<FieldsBenchMark>(iteration, FieldsBenchMark(fields_sets));
return (0);
}

View File

@@ -110,7 +110,7 @@ class OutputBuffer;\n'''
content += line
if re.match('// BEGIN_COMMON_DECLARATIONS', line):
content += '''
class MessageRenderer;\n\n'''
class AbstractMessageRenderer;\n\n'''
if re.match('\s+// BEGIN_COMMON_MEMBERS$', line):
content += '''
explicit ''' + type_utxt + '''(const std::string& type_str);
@@ -118,7 +118,7 @@ class MessageRenderer;\n\n'''
''' + type_utxt + '''(const ''' + type_utxt + '''& other);
virtual std::string toText() const;
virtual void toWire(isc::util::OutputBuffer& buffer) const;
virtual void toWire(MessageRenderer& renderer) const;
virtual void toWire(AbstractMessageRenderer& renderer) const;
virtual int compare(const Rdata& other) const;\n\n'''
rdata_header.close()
return content

View File

@@ -152,12 +152,10 @@ struct MessageRenderer::MessageRendererImpl {
///
/// \param buffer An \c OutputBuffer object to which wire format data is
/// written.
MessageRendererImpl(OutputBuffer& buffer) :
buffer_(buffer), nbuffer_(Name::MAX_WIRE), msglength_limit_(512),
MessageRendererImpl() :
nbuffer_(Name::MAX_WIRE), msglength_limit_(512),
truncated_(false), compress_mode_(MessageRenderer::CASE_INSENSITIVE)
{}
/// The buffer that holds the entire DNS message.
OutputBuffer& buffer_;
/// A local working buffer to convert each given name into wire format.
/// This could be a local variable of the \c writeName() method, but
/// we keep it in the class so that we can reuse it and avoid construction
@@ -176,26 +174,17 @@ struct MessageRenderer::MessageRendererImpl {
};
MessageRenderer::MessageRenderer(OutputBuffer& buffer) :
impl_(new MessageRendererImpl(buffer))
AbstractMessageRenderer(buffer),
impl_(new MessageRendererImpl)
{}
MessageRenderer::~MessageRenderer() {
delete impl_;
}
void
MessageRenderer::skip(const size_t len) {
impl_->buffer_.skip(len);
}
void
MessageRenderer::trim(const size_t len) {
impl_->buffer_.trim(len);
}
void
MessageRenderer::clear() {
impl_->buffer_.clear();
AbstractMessageRenderer::clear();
impl_->nbuffer_.clear();
impl_->nodeset_.clear();
impl_->msglength_limit_ = 512;
@@ -203,41 +192,6 @@ MessageRenderer::clear() {
impl_->compress_mode_ = CASE_INSENSITIVE;
}
void
MessageRenderer::writeUint8(const uint8_t data) {
impl_->buffer_.writeUint8(data);
}
void
MessageRenderer::writeUint16(const uint16_t data) {
impl_->buffer_.writeUint16(data);
}
void
MessageRenderer::writeUint16At(const uint16_t data, const size_t pos) {
impl_->buffer_.writeUint16At(data, pos);
}
void
MessageRenderer::writeUint32(const uint32_t data) {
impl_->buffer_.writeUint32(data);
}
void
MessageRenderer::writeData(const void* const data, const size_t len) {
impl_->buffer_.writeData(data, len);
}
const void*
MessageRenderer::getData() const {
return (impl_->buffer_.getData());
}
size_t
MessageRenderer::getLength() const {
return (impl_->buffer_.getLength());
}
size_t
MessageRenderer::getLengthLimit() const {
return (impl_->msglength_limit_);
@@ -293,15 +247,15 @@ MessageRenderer::writeName(const Name& name, const bool compress) {
}
// Record the current offset before extending the buffer.
const size_t offset = impl_->buffer_.getLength();
const size_t offset = getLength();
// Write uncompress part...
impl_->buffer_.writeData(impl_->nbuffer_.getData(),
compress ? i : impl_->nbuffer_.getLength());
writeData(impl_->nbuffer_.getData(),
compress ? i : impl_->nbuffer_.getLength());
if (compress && n != notfound) {
// ...and compression pointer if available.
uint16_t pointer = (*n).pos_;
pointer |= Name::COMPRESS_POINTER_MARK16;
impl_->buffer_.writeUint16(pointer);
writeUint16(pointer);
}
// Finally, add to the set the newly rendered name and its ancestors that
@@ -313,11 +267,17 @@ MessageRenderer::writeName(const Name& name, const bool compress) {
if (offset + j > Name::MAX_COMPRESS_POINTER) {
break;
}
impl_->nodeset_.insert(NameCompressNode(*this, impl_->buffer_,
impl_->nodeset_.insert(NameCompressNode(*this, getBuffer(),
offset + j,
impl_->nbuffer_.getLength() -
j));
}
}
void
AbstractMessageRenderer::clear() {
buffer_.clear();
}
}
}

View File

@@ -15,67 +15,64 @@
#ifndef __MESSAGERENDERER_H
#define __MESSAGERENDERER_H 1
#include <util/buffer.h>
namespace isc {
namespace util {
class OutputBuffer;
}
namespace dns {
// forward declarations
class Name;
/// \brief The \c AbstractMessageRenderer class is an abstract base class
/// that provides common interfaces for rendering a DNS message into a buffer
/// in wire format.
///
/// \brief The \c MessageRenderer class encapsulates implementation details
/// of rendering a DNS message into a buffer in wire format.
/// A specific derived class of \c AbstractMessageRenderer (we call it
/// a renderer class hereafter) is simply responsible for name compression at
/// least in the current design. A renderer class object (conceptually)
/// manages the positions of names rendered in some sort of buffer and uses
/// that information to render subsequent names with compression.
///
/// In effect, it's simply responsible for name compression at least in the
/// current implementation. A \c MessageRenderer class object manages the
/// positions of names rendered in a buffer and uses that information to render
/// subsequent names with compression.
///
/// This class is mainly intended to be used as a helper for a more
/// A renderer class is mainly intended to be used as a helper for a more
/// comprehensive \c Message class internally; normal applications won't have
/// to care about this class.
/// to care about details of this class.
///
/// A \c MessageRenderer class object is constructed with a \c OutputBuffer
/// object, which is the buffer into which the rendered %data will be written.
/// Normally the buffer is expected to be empty on construction, but it doesn't
/// have to be so; the \c MessageRenderer object will start rendering from the
/// end of the buffer at the time of construction. However, if the
/// pre-existing portion of the buffer contains DNS names, these names won't
/// be considered for name compression.
///
/// Once a \c MessageRenderer object is constructed with a buffer, it is
/// generally expected that all rendering operations are performed via the
/// \c MessageRenderer object. If the application modifies the buffer in
/// parallel with the \c MessageRenderer, the result will be undefined.
/// Once a renderer class object is constructed with a buffer, it is
/// generally expected that all rendering operations are performed via that
/// object. If the application modifies the buffer in
/// parallel with the renderer, the result will be undefined.
///
/// Note to developers: we introduced a separate class for name compression
/// because previous benchmark with BIND9 showed compression affects overall
/// response performance very much. By having a separate class dedicated for
/// this purpose, we'll be able to change the internal implementation of name
/// compression in the future without affecting other part of the API and
/// implementation. For the same reason, we adopt the "pimpl" idiom in the
/// class definition (i.e., using a pointer to a \c MessageRendererImpl class,
/// which is defined with the class implementation, not in the header file):
/// we may want to modify the compression implementation without modifying the
/// header file thereby requesting rebuild the package.
/// implementation.
///
/// Furthermore, we may eventually want to allow other developers to develop
/// and use their own compression implementation. Should such a case become
/// realistic, we may want to make the \c MessageRendererImpl class an abstract
/// base class and let concrete derived classes have their own implementations.
/// At the moment we don't the strong need for it, so we rather avoid over
/// abstraction and keep the definition simpler.
class MessageRenderer {
/// In addition, by introducing a class hierarchy from
/// \c AbstractMessageRenderer, we allow an application to use a customized
/// renderer class for specific purposes. For example, a high performance
/// DNS server may want to use an optimized renderer class assuming some
/// specific underlying data representation.
///
/// \note Some functions (like writeUint8) are not virtual. It is because
/// it is hard to imagine any version of message renderer that would
/// do anything else than just putting the data into a buffer, so we
/// provide a default implementation and having them virtual would only
/// hurt the performance with no real gain. If it would happen a different
/// implementation is really needed, we can make them virtual in future.
/// The only one that is virtual is writeName and it's because this
/// function is much more complicated, therefore there's a lot of space
/// for different implementations or behaviours.
class AbstractMessageRenderer {
public:
/// \brief Compression mode constants.
///
/// The \c CompressMode enum type represents the name compression mode
/// for the \c MessageRenderer.
/// for renderer classes.
/// \c CASE_INSENSITIVE means compress names in case-insensitive manner;
/// \c CASE_SENSITIVE means compress names in case-sensitive manner.
/// By default, \c MessageRenderer compresses names in case-insensitive
/// By default, a renderer compresses names in case-insensitive
/// manner.
/// Compression mode can be dynamically modified by the
/// \c setCompressMode() method.
@@ -83,7 +80,7 @@ public:
/// is not an intended usage. In this case the names already compressed
/// are intact; only names being compressed after the mode change are
/// affected by the change.
/// If the internal \c MessageRenderer is reinitialized by the \c clear()
/// If a renderer class object is reinitialized by the \c clear()
/// method, the compression mode will be reset to the default, which is
/// \c CASE_INSENSITIVE
///
@@ -96,25 +93,39 @@ public:
CASE_INSENSITIVE, //!< Compress names case-insensitive manner (default)
CASE_SENSITIVE //!< Compress names case-sensitive manner
};
public:
protected:
///
/// \name Constructors and Destructor
//@{
/// \brief Constructor from an output buffer.
/// \brief The default constructor.
///
/// \param buffer An \c OutputBuffer object to which wire format data is
/// written.
MessageRenderer(isc::util::OutputBuffer& buffer);
/// This is intentionally defined as \c protected as this base class should
/// never be instantiated (except as part of a derived class).
/// \param buffer The buffer where the data should be rendered into.
/// \todo We might want to revisit this API at some point and remove the
/// buffer parameter. In that case it would create it's own buffer and
/// a function to extract the data would be available instead. It seems
/// like a cleaner design, but it's left undone until we would actually
/// benefit from the change.
AbstractMessageRenderer(isc::util::OutputBuffer& buffer) :
buffer_(buffer)
{}
public:
/// \brief The destructor.
///
/// The destructor does nothing on the given \c buffer on construction;
/// in fact, it is expected that the user will use the resulting buffer
/// for some post rendering purposes (e.g., send the data to the network).
/// It's the user's responsibility to do any necessary cleanup for the
/// \c buffer.
~MessageRenderer();
virtual ~AbstractMessageRenderer() {}
//@}
protected:
/// \brief Return the output buffer we render into.
const isc::util::OutputBuffer& getBuffer() const { return (buffer_); }
isc::util::OutputBuffer& getBuffer() { return (buffer_); }
private:
/// \short Buffer to store data
///
/// It was decided that there's no need to have this in every subclass,
/// at least not now, and this reduces code size and gives compiler a better
/// chance to optimise.
isc::util::OutputBuffer& buffer_;
public:
///
/// \name Getter Methods
///
@@ -124,9 +135,15 @@ public:
///
/// This method works exactly same as the same method of the \c OutputBuffer
/// class; all notes for \c OutputBuffer apply.
const void* getData() const;
const void* getData() const {
return (buffer_.getData());
}
/// \brief Return the length of data written in the internal buffer.
size_t getLength() const;
size_t getLength() const {
return (buffer_.getLength());
}
/// \brief Return whether truncation has occurred while rendering.
///
/// Once the return value of this method is \c true, it doesn't make sense
@@ -136,20 +153,22 @@ public:
/// This method never throws an exception.
///
/// \return true if truncation has occurred; otherwise \c false.
bool isTruncated() const;
virtual bool isTruncated() const = 0;
/// \brief Return the maximum length of rendered data that can fit in the
/// corresponding DNS message without truncation.
///
/// This method never throws an exception.
///
/// \return The maximum length in bytes.
size_t getLengthLimit() const;
/// \brief Return the compression mode of the \c MessageRenderer.
virtual size_t getLengthLimit() const = 0;
/// \brief Return the compression mode of the renderer class object.
///
/// This method never throws an exception.
///
/// \return The current compression mode.
CompressMode getCompressMode() const;
virtual CompressMode getCompressMode() const = 0;
//@}
///
@@ -160,20 +179,22 @@ public:
/// rendering.
///
/// This method never throws an exception.
void setTruncated();
virtual void setTruncated() = 0;
/// \brief Set the maximum length of rendered data that can fit in the
/// corresponding DNS message without truncation.
///
/// This method never throws an exception.
///
/// \param len The maximum length in bytes.
void setLengthLimit(size_t len);
/// \brief Set the compression mode of the \c MessageRenderer.
virtual void setLengthLimit(size_t len) = 0;
/// \brief Set the compression mode of the renderer class object.
///
/// This method never throws an exception.
///
/// \param mode A \c CompressMode value representing the compression mode.
void setCompressMode(CompressMode mode);
virtual void setCompressMode(CompressMode mode) = 0;
//@}
///
@@ -187,7 +208,10 @@ public:
/// that is to be filled in later, e.g, by \ref writeUint16At().
///
/// \param len The length of the gap to be inserted in bytes.
void skip(size_t len);
void skip(size_t len) {
buffer_.skip(len);
}
/// \brief Trim the specified length of data from the end of the internal
/// buffer.
///
@@ -198,21 +222,31 @@ public:
/// be thrown.
///
/// \param len The length of data that should be trimmed.
void trim(size_t len);
void trim(size_t len) {
buffer_.trim(len);
}
/// \brief Clear the internal buffer and other internal resources.
///
/// This method can be used to re-initialize and reuse the renderer
/// without constructing a new one.
void clear();
virtual void clear();
/// \brief Write an unsigned 8-bit integer into the internal buffer.
///
/// \param data The 8-bit integer to be written into the internal buffer.
void writeUint8(uint8_t data);
void writeUint8(const uint8_t data) {
buffer_.writeUint8(data);
}
/// \brief Write an unsigned 16-bit integer in host byte order into the
/// internal buffer in network byte order.
///
/// \param data The 16-bit integer to be written into the buffer.
void writeUint16(uint16_t data);
void writeUint16(uint16_t data) {
buffer_.writeUint16(data);
}
/// \brief Write an unsigned 16-bit integer in host byte order at the
/// specified position of the internal buffer in network byte order.
///
@@ -224,26 +258,29 @@ public:
///
/// \param data The 16-bit integer to be written into the internal buffer.
/// \param pos The beginning position in the buffer to write the data.
void writeUint16At(uint16_t data, size_t pos);
void writeUint16At(uint16_t data, size_t pos) {
buffer_.writeUint16At(data, pos);
}
/// \brief Write an unsigned 32-bit integer in host byte order into the
/// internal buffer in network byte order.
///
/// \param data The 32-bit integer to be written into the buffer.
void writeUint32(uint32_t data);
void writeUint32(uint32_t data) {
buffer_.writeUint32(data);
}
/// \brief Copy an arbitrary length of data into the internal buffer
/// of the \c MessageRenderer.
/// of the renderer object.
///
/// No conversion on the copied data is performed.
///
/// \param data A pointer to the data to be copied into the internal buffer.
/// \param len The length of the data in bytes.
void writeData(const void *data, size_t len);
//@}
void writeData(const void *data, size_t len) {
buffer_.writeData(data, len);
}
///
/// \name Rendering Methods
///
//@{
/// \brief Write a \c Name object into the internal buffer in wire format,
/// with or without name compression.
///
@@ -258,8 +295,41 @@ public:
///
/// \param name A \c Name object to be written.
/// \param compress A boolean indicating whether to enable name compression.
void writeName(const Name& name, bool compress = true);
virtual void writeName(const Name& name, bool compress = true) = 0;
//@}
};
/// The \c MessageRenderer is a concrete derived class of
/// \c AbstractMessageRenderer as a general purpose implementation of the
/// renderer interfaces.
///
/// A \c MessageRenderer object is constructed with a \c OutputBuffer
/// object, which is the buffer into which the rendered %data will be written.
/// Normally the buffer is expected to be empty on construction, but it doesn't
/// have to be so; the renderer object will start rendering from the
/// end of the buffer at the time of construction. However, if the
/// pre-existing portion of the buffer contains DNS names, these names won't
/// be considered for name compression.
class MessageRenderer : public AbstractMessageRenderer {
public:
using AbstractMessageRenderer::CASE_INSENSITIVE;
using AbstractMessageRenderer::CASE_SENSITIVE;
/// \brief Constructor from an output buffer.
///
/// \param buffer An \c OutputBuffer object to which wire format data is
/// written.
MessageRenderer(isc::util::OutputBuffer& buffer);
virtual ~MessageRenderer();
virtual bool isTruncated() const;
virtual size_t getLengthLimit() const;
virtual CompressMode getCompressMode() const;
virtual void setTruncated();
virtual void setLengthLimit(size_t len);
virtual void setCompressMode(CompressMode mode);
virtual void clear();
virtual void writeName(const Name& name, bool compress = true);
private:
struct MessageRendererImpl;
MessageRendererImpl* impl_;

View File

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

View File

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

View File

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

View File

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

View File

@@ -380,9 +380,9 @@ TSIG::toWire(OutputBuffer& buffer) const {
/// \param renderer DNS message rendering context that encapsulates the
/// output buffer and name compression information.
void
TSIG::toWire(MessageRenderer& renderer) const {
TSIG::toWire(AbstractMessageRenderer& renderer) const {
renderer.writeName(impl_->algorithm_, false);
impl_->toWireCommon<MessageRenderer>(renderer);
impl_->toWireCommon<AbstractMessageRenderer>(renderer);
}
// A helper function commonly used for TSIG::compare().

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -103,7 +103,7 @@ RP::toWire(OutputBuffer& buffer) const {
}
void
RP::toWire(MessageRenderer& renderer) const {
RP::toWire(AbstractMessageRenderer& renderer) const {
// Type RP is not "well-known", and name compression must be disabled
// per RFC3597.
renderer.writeName(mailbox_, false);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

223
src/lib/dns/rdatafields.cc Normal file
View File

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

427
src/lib/dns/rdatafields.h Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -125,7 +125,7 @@ public:
virtual void toWire(OutputBuffer& buffer) const;
/// \brief render the \Rdata in the wire format to a \c MessageRenderer
virtual void toWire(MessageRenderer& renderer) const;
virtual void toWire(AbstractMessageRenderer& renderer) const;
/// \brief Comparison Method
virtual int compare(const Rdata& other) const;
@@ -141,7 +141,7 @@ void RdataTest<T>::toWire(OutputBuffer&) const {
}
template <typename T>
void RdataTest<T>::toWire(MessageRenderer&) const {
void RdataTest<T>::toWire(AbstractMessageRenderer&) const {
}
template <typename T>