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:
@@ -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
|
||||
|
@@ -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
|
||||
|
17
src/lib/dns/benchmarks/Makefile.am
Normal file
17
src/lib/dns/benchmarks/Makefile.am
Normal 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)
|
10
src/lib/dns/benchmarks/README
Normal file
10
src/lib/dns/benchmarks/README
Normal 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_*.
|
32
src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_com
Normal file
32
src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_com
Normal 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
|
@@ -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=
|
22
src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_org
Normal file
22
src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_org
Normal 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
|
188
src/lib/dns/benchmarks/rdatarender_bench.cc
Normal file
188
src/lib/dns/benchmarks/rdatarender_bench.cc
Normal 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);
|
||||
}
|
@@ -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
|
||||
|
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -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_;
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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.
|
||||
|
@@ -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());
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
//@}
|
||||
|
||||
///
|
||||
|
@@ -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().
|
||||
|
@@ -45,7 +45,7 @@ A::toWire(OutputBuffer&) const {
|
||||
}
|
||||
|
||||
void
|
||||
A::toWire(MessageRenderer&) const {
|
||||
A::toWire(AbstractMessageRenderer&) const {
|
||||
// TBD
|
||||
}
|
||||
|
||||
|
@@ -53,7 +53,7 @@ CNAME::toWire(OutputBuffer& buffer) const {
|
||||
}
|
||||
|
||||
void
|
||||
CNAME::toWire(MessageRenderer& renderer) const {
|
||||
CNAME::toWire(AbstractMessageRenderer& renderer) const {
|
||||
renderer.writeName(cname_);
|
||||
}
|
||||
|
||||
|
@@ -53,7 +53,7 @@ DNAME::toWire(OutputBuffer& buffer) const {
|
||||
}
|
||||
|
||||
void
|
||||
DNAME::toWire(MessageRenderer& renderer) const {
|
||||
DNAME::toWire(AbstractMessageRenderer& renderer) const {
|
||||
renderer.writeName(dname_);
|
||||
}
|
||||
|
||||
|
@@ -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_);
|
||||
|
@@ -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_);
|
||||
|
@@ -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_);
|
||||
}
|
||||
|
@@ -49,7 +49,7 @@ NS::toWire(OutputBuffer& buffer) const {
|
||||
}
|
||||
|
||||
void
|
||||
NS::toWire(MessageRenderer& renderer) const {
|
||||
NS::toWire(AbstractMessageRenderer& renderer) const {
|
||||
renderer.writeName(nsname_);
|
||||
}
|
||||
|
||||
|
@@ -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_);
|
||||
|
@@ -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_);
|
||||
|
@@ -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());
|
||||
}
|
||||
|
@@ -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.
|
||||
}
|
||||
|
||||
|
@@ -54,7 +54,7 @@ PTR::toWire(OutputBuffer& buffer) const {
|
||||
}
|
||||
|
||||
void
|
||||
PTR::toWire(MessageRenderer& renderer) const {
|
||||
PTR::toWire(AbstractMessageRenderer& renderer) const {
|
||||
renderer.writeName(ptr_name_);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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_);
|
||||
|
@@ -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_));
|
||||
|
@@ -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)
|
||||
|
@@ -45,7 +45,7 @@ A::toWire(OutputBuffer&) const {
|
||||
}
|
||||
|
||||
void
|
||||
A::toWire(MessageRenderer&) const {
|
||||
A::toWire(AbstractMessageRenderer&) const {
|
||||
// TBD
|
||||
}
|
||||
|
||||
|
@@ -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_));
|
||||
}
|
||||
|
||||
|
@@ -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_));
|
||||
}
|
||||
|
||||
|
@@ -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
223
src/lib/dns/rdatafields.cc
Normal 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
427
src/lib/dns/rdatafields.h
Normal 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:
|
@@ -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
|
||||
|
@@ -53,7 +53,7 @@ RRType::toWire(OutputBuffer& buffer) const {
|
||||
}
|
||||
|
||||
void
|
||||
RRType::toWire(MessageRenderer& renderer) const {
|
||||
RRType::toWire(AbstractMessageRenderer& renderer) const {
|
||||
renderer.writeUint16(typecode_);
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
380
src/lib/dns/tests/rdatafields_unittest.cc
Normal file
380
src/lib/dns/tests/rdatafields_unittest.cc
Normal 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);
|
||||
}
|
||||
}
|
4
src/lib/dns/tests/testdata/Makefile.am
vendored
4
src/lib/dns/tests/testdata/Makefile.am
vendored
@@ -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
|
||||
|
10
src/lib/dns/tests/testdata/rdatafields1.spec
vendored
Normal file
10
src/lib/dns/tests/testdata/rdatafields1.spec
vendored
Normal 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
|
11
src/lib/dns/tests/testdata/rdatafields2.spec
vendored
Normal file
11
src/lib/dns/tests/testdata/rdatafields2.spec
vendored
Normal 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
|
11
src/lib/dns/tests/testdata/rdatafields3.spec
vendored
Normal file
11
src/lib/dns/tests/testdata/rdatafields3.spec
vendored
Normal 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'
|
7
src/lib/dns/tests/testdata/rdatafields4.spec
vendored
Normal file
7
src/lib/dns/tests/testdata/rdatafields4.spec
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
#
|
||||
# Simple form of RRSIG (all fields use the default of generator script)
|
||||
#
|
||||
|
||||
[custom]
|
||||
sections: rrsig
|
||||
[rrsig]
|
12
src/lib/dns/tests/testdata/rdatafields5.spec
vendored
Normal file
12
src/lib/dns/tests/testdata/rdatafields5.spec
vendored
Normal 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
|
13
src/lib/dns/tests/testdata/rdatafields6.spec
vendored
Normal file
13
src/lib/dns/tests/testdata/rdatafields6.spec
vendored
Normal 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
|
@@ -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>
|
||||
|
Reference in New Issue
Block a user