Commit b97f714d authored by Michal 'vorner' Vaner's avatar Michal 'vorner' Vaner
Browse files

[2096] Reader and encoder merged to the same file

They share some bit of private stuff, so we get rid of the private
header. We still have a bit of the code included from outside, but this
is only for tests (and it's not a header).
parent d9e1e79f
......@@ -11,7 +11,6 @@ CLEANFILES = *.gcno *.gcda datasrc_messages.h datasrc_messages.cc
noinst_LTLIBRARIES = libdatasrc_memory.la
libdatasrc_memory_la_SOURCES = \
rdata_encoder.h rdata_encoder.cc \
rdata_field.h rdata_field.cc \
rdata_reader.h rdata_reader.cc \
rdata_serialization.h rdata_serialization.cc \
domaintree.h
EXTRA_DIST = rdata_serialization_priv.cc
......@@ -22,8 +22,7 @@
#include <dns/rrclass.h>
#include <dns/masterload.h>
#include <datasrc/memory/rdata_encoder.h>
#include <datasrc/memory/rdata_reader.h>
#include <datasrc/memory/rdata_serialization.h>
#include <boost/bind.hpp>
......
// Copyright (C) 2012 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 "rdata_field.h"
#include <dns/rrclass.h>
#include <dns/rrtype.h>
#include <boost/static_assert.hpp>
using namespace isc::dns;
namespace isc {
namespace datasrc {
namespace memory {
namespace {
// Many types of RDATA can be treated as a single-field, variable length
// field (in terms of our encoding). The following define such most general
// form of field spec.
const RdataFieldSpec generic_data_fields[] = {
{RdataFieldSpec::VARLEN_DATA, 0, NAMEATTR_NONE}
};
const uint16_t n_generic_data_fields =
sizeof(generic_data_fields) / sizeof(RdataFieldSpec);
const RdataEncodeSpec generic_data_spec = {
n_generic_data_fields, 0, 1, generic_data_fields
};
// RDATA consist of a single IPv4 address field.
const RdataFieldSpec single_ipv4_fields[] = {
{RdataFieldSpec::FIXEDLEN_DATA, sizeof(uint32_t), NAMEATTR_NONE}
};
const uint16_t n_ipv4_fields =
sizeof(single_ipv4_fields) / sizeof(RdataFieldSpec);
// RDATA consist of a single IPv6 address field.
const RdataFieldSpec single_ipv6_fields[] = {
{RdataFieldSpec::FIXEDLEN_DATA, 16, NAMEATTR_NONE} // 128bits = 16 bytes
};
const uint16_t n_ipv6_fields =
sizeof(single_ipv6_fields) / sizeof(RdataFieldSpec);
// There are several RR types that consist of a single domain name.
const RdataFieldSpec single_noattr_name_fields[] = {
{RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE}
};
const RdataFieldSpec single_compressible_name_fields[] = {
{RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_COMPRESSIBLE}
};
const RdataFieldSpec single_compadditional_name_fields[] = {
{RdataFieldSpec::DOMAIN_NAME, 0,
static_cast<RdataNameAttributes>(
static_cast<unsigned int>(NAMEATTR_COMPRESSIBLE) |
static_cast<unsigned int>(NAMEATTR_ADDITIONAL))}
};
const uint16_t n_single_name_fields =
sizeof(single_noattr_name_fields) / sizeof(RdataFieldSpec);
// RDATA consisting of two names. There are some of this type.
const RdataFieldSpec double_compressible_name_fields[] = {
{RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_COMPRESSIBLE},
{RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_COMPRESSIBLE}
};
const RdataFieldSpec double_noattr_name_fields[] = {
{RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE},
{RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE}
};
const uint16_t n_double_name_fields =
sizeof(double_compressible_name_fields) / sizeof(RdataFieldSpec);
// SOA specific: two compressible names + 5*32-bit data
const RdataFieldSpec soa_fields[] = {
{RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_COMPRESSIBLE},
{RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_COMPRESSIBLE},
{RdataFieldSpec::FIXEDLEN_DATA, sizeof(uint32_t) * 5, NAMEATTR_NONE}
};
const uint16_t n_soa_fields = sizeof(soa_fields) / sizeof(RdataFieldSpec);
// MX specific: 16-bit data + compressible/additional name
const RdataFieldSpec mx_fields[] = {
{RdataFieldSpec::FIXEDLEN_DATA, sizeof(uint16_t), NAMEATTR_NONE},
{RdataFieldSpec::DOMAIN_NAME, 0,
static_cast<RdataNameAttributes>(
static_cast<unsigned int>(NAMEATTR_COMPRESSIBLE) |
static_cast<unsigned int>(NAMEATTR_ADDITIONAL))}
};
const uint16_t n_mx_fields = sizeof(mx_fields) / sizeof(RdataFieldSpec);
// AFSDB specific: 16-bit data + no-attribute name
const RdataFieldSpec afsdb_fields[] = {
{RdataFieldSpec::FIXEDLEN_DATA, sizeof(uint16_t), NAMEATTR_NONE},
{RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE}
};
const uint16_t n_afsdb_fields = sizeof(afsdb_fields) / sizeof(RdataFieldSpec);
// SRV specific: 3*16-bit data + additional name
const RdataFieldSpec srv_fields[] = {
{RdataFieldSpec::FIXEDLEN_DATA, sizeof(uint16_t) * 3, NAMEATTR_NONE},
{RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_ADDITIONAL}
};
const uint16_t n_srv_fields = sizeof(srv_fields) / sizeof(RdataFieldSpec);
// NAPTR specific: (multi-field) variable data + (additional) name
// NAPTR requires complicated additional section handling; for now, we skip
// the additional handling completely.
const RdataFieldSpec naptr_fields[] = {
{RdataFieldSpec::VARLEN_DATA, 0, NAMEATTR_NONE},
{RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE}
};
const uint16_t n_naptr_fields = sizeof(naptr_fields) / sizeof(RdataFieldSpec);
// NSEC specific: no-attribute name + varlen data
const RdataFieldSpec nsec_fields[] = {
{RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE},
{RdataFieldSpec::VARLEN_DATA, 0, NAMEATTR_NONE}
};
const uint16_t n_nsec_fields = sizeof(nsec_fields) / sizeof(RdataFieldSpec);
// Class IN encode specs. This gives a shortcut to the encode spec for
// some well-known types of RDATA specific to class IN (most of which are
// generic and can be used for other classes). The array index is the
// RR type code.
const RdataEncodeSpec encode_spec_list_in[] = {
generic_data_spec, // #0: (NONE)
{n_ipv4_fields, 0, 0, single_ipv4_fields}, // #1: A
{n_single_name_fields, 1, 0, single_compadditional_name_fields}, // #2: NS
generic_data_spec, // #3
generic_data_spec, // #4
{n_single_name_fields, 1, 0, single_compressible_name_fields}, // #5: CNAME
{n_soa_fields, 2, 0, soa_fields}, // #6: SOA
generic_data_spec, // #7
generic_data_spec, // #8
generic_data_spec, // #9
generic_data_spec, // #10
generic_data_spec, // #11
{n_single_name_fields, 1, 0, single_compressible_name_fields}, // #12: PTR
generic_data_spec, // #13: HINFO
{n_double_name_fields, 2, 0, double_compressible_name_fields}, // #14:HINFO
{n_mx_fields, 1, 0, mx_fields}, // #15: MX
generic_data_spec, // #16: TXT
{n_double_name_fields, 2, 0, double_noattr_name_fields}, // 17: RP
{n_afsdb_fields, 1, 0, afsdb_fields}, // #18: AFSDB
// #19-#26
generic_data_spec, generic_data_spec, generic_data_spec, generic_data_spec,
generic_data_spec, generic_data_spec, generic_data_spec, generic_data_spec,
generic_data_spec, // #27
{n_ipv6_fields, 0, 0, single_ipv6_fields}, // #28: AAAA
// #29-#32
generic_data_spec, generic_data_spec, generic_data_spec, generic_data_spec,
{n_srv_fields, 1, 0, srv_fields}, // #33: SRV
generic_data_spec, // #34
{n_naptr_fields, 1, 1, naptr_fields}, // #35: NAPTR
generic_data_spec, // #36
generic_data_spec, // #37
generic_data_spec, // #38
{n_single_name_fields, 1, 0, single_noattr_name_fields}, // #39 DNAME
generic_data_spec, // #40
generic_data_spec, // #41 (OPT)
generic_data_spec, // #42
generic_data_spec, // #43: DS (this is opaque for encoding purposes)
generic_data_spec, // #44: SSHFP (this is opaque for encoding purposes)
generic_data_spec, // #45
generic_data_spec, // #46: RRSIG (this is opaque for encoding purposes)
{n_nsec_fields, 1, 1, nsec_fields} // #47: NSEC
// All others can be treated as single-field variable length data, at
// least for currently supported RR types.
};
// # of entries in encode_spec_list_in
const size_t encode_spec_list_in_size =
sizeof(encode_spec_list_in) / sizeof(encode_spec_list_in[0]);
BOOST_STATIC_ASSERT(encode_spec_list_in_size == 48);
}
const RdataEncodeSpec&
getRdataEncodeSpec(const RRClass& rrclass, const RRType& rrtype) {
// Special case: for classes other than IN, we treat RDATA of RR types
// that are class-IN specific as generic opaque data.
if (rrclass != RRClass::IN() &&
(rrtype == RRType::A() || rrtype == RRType::AAAA() ||
rrtype == RRType::SRV())) {
return (generic_data_spec);
}
// Otherwise, if the type is in the pre-defined range, we use the defined
// spec; otherwise we treat it as opaque data.
const uint16_t typecode = rrtype.getCode();
if (typecode < encode_spec_list_in_size) {
return (encode_spec_list_in[rrtype.getCode()]);
}
return (generic_data_spec);
}
}
}
}
// Copyright (C) 2012 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 "rdata_reader.h"
using namespace isc::dns;
namespace isc {
namespace datasrc {
namespace memory {
RdataReader::RdataReader(const RRClass& rrclass, const RRType& rrtype,
const void* data,
size_t rdata_count, size_t sig_count,
const NameAction& name_action,
const DataAction& data_action) :
name_action_(name_action),
data_action_(data_action),
spec_(getRdataEncodeSpec(rrclass, rrtype)),
var_count_total_(spec_.varlen_count * rdata_count),
sig_count_(sig_count),
spec_count_(spec_.field_count * rdata_count),
// The lenghts are stored first
lengths_(reinterpret_cast<const uint16_t*>(data)),
// And the data just after all the lengths
data_(reinterpret_cast<const uint8_t*>(data) +
(var_count_total_ + sig_count_) * sizeof(uint16_t)),
sigs_(NULL)
{
rewind();
}
void
RdataReader::rewind() {
data_pos_ = 0;
spec_pos_ = 0;
length_pos_ = 0;
sig_data_pos_ = 0;
sig_pos_ = 0;
}
RdataReader::Boundary
RdataReader::nextInternal(const NameAction& name_action,
const DataAction& data_action)
{
if (spec_pos_ < spec_count_) {
const RdataFieldSpec& spec(spec_.fields[(spec_pos_++) %
spec_.field_count]);
if (spec.type == RdataFieldSpec::DOMAIN_NAME) {
const LabelSequence sequence(data_ + data_pos_);
data_pos_ += sequence.getSerializedLength();
name_action(sequence, spec.name_attributes);
} else {
const size_t length(spec.type == RdataFieldSpec::FIXEDLEN_DATA ?
spec.fixeddata_len : lengths_[length_pos_++]);
const uint8_t* const pos = data_ + data_pos_;
data_pos_ += length;
data_action(pos, length);
}
return (spec_pos_ % spec_.field_count == 0 ?
RDATA_BOUNDARY : NO_BOUNDARY);
} else {
sigs_ = data_ + data_pos_;
return (RRSET_BOUNDARY);
}
}
RdataReader::Boundary
RdataReader::next() {
return (nextInternal(name_action_, data_action_));
}
namespace {
void
emptyNameAction(const LabelSequence&, unsigned) {
// Do nothing here.
}
void
emptyDataAction(const void*, size_t) {
// Do nothing here.
}
}
RdataReader::Boundary
RdataReader::nextSig() {
if (sig_pos_ < sig_count_) {
if (sigs_ == NULL) {
// We didn't find where the signatures start yet. We do it
// by iterating the whole data and then returning the state
// back.
const size_t data_pos = data_pos_;
const size_t spec_pos = spec_pos_;
const size_t length_pos = length_pos_;
// When the next() gets to the last item, it sets the sigs_
while (nextInternal(emptyNameAction, emptyDataAction) !=
RRSET_BOUNDARY) {}
assert(sigs_ != NULL);
// Return the state
data_pos_ = data_pos;
spec_pos_ = spec_pos;
length_pos_ = length_pos;
}
// Extract the result
const size_t length = lengths_[var_count_total_ + sig_pos_];
const uint8_t* const pos = sigs_ + sig_data_pos_;
// Move the position of iterator.
sig_data_pos_ += lengths_[var_count_total_ + sig_pos_];
++sig_pos_;
// Call the callback
data_action_(pos, length);
return (RDATA_BOUNDARY);
} else {
return (RRSET_BOUNDARY);
}
}
size_t
RdataReader::getSize() const {
size_t storage_size = 0; // this will be the end result
size_t data_pos = 0;
size_t length_pos = 0;
// Go over all data fields, adding their lengths to storage_size
for (size_t spec_pos = 0; spec_pos < spec_count_; ++spec_pos) {
const RdataFieldSpec& spec =
spec_.fields[spec_pos % spec_.field_count];
if (spec.type == RdataFieldSpec::DOMAIN_NAME) {
const size_t seq_len =
LabelSequence(data_ + data_pos).getSerializedLength();
data_pos += seq_len;
storage_size += seq_len;
} else {
const size_t data_len =
(spec.type == RdataFieldSpec::FIXEDLEN_DATA ?
spec.fixeddata_len : lengths_[length_pos++]);
data_pos += data_len;
storage_size += data_len;
}
}
// Same for all RRSIG data
for (size_t sig_pos = 0; sig_pos < sig_count_; ++sig_pos) {
const size_t sig_data_len = lengths_[length_pos++];
storage_size += sig_data_len;
}
// Finally, add the size for 16-bit length fields
storage_size += (var_count_total_ * sizeof(uint16_t) +
sig_count_ * sizeof(uint16_t));
return (storage_size);
}
}
}
}
// Copyright (C) 2012 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 DATASRC_MEMORY_RDATA_READER_H
#define DATASRC_MEMORY_RDATA_READER_H 1
#include "rdata_field.h"
#include <boost/function.hpp>
#include <dns/labelsequence.h>
#include <dns/name.h>
namespace isc {
// Some forward declarations
namespace dns{
class RRClass;
class RRType;
}
namespace datasrc {
namespace memory {
/// \brief Class to read serialized rdata
///
/// This class allows you to read the data encoded by RdataEncoder.
/// It is rather low-level -- it provides sequence of data fields.
/// Each field is either opaque data, passed as a pointer and length,
/// or a name, in the form of dns::LabelSequence (which is always
/// absolute) and attributes.
///
/// Conceptually, these fields correspond to consecutive regions in
/// wire-format representation of the RDATA, varying the type of above
/// two cases depending on whether the region corresponds to a domain
/// name or other data. For example, for an MX RDATA the field
/// sequence will be
/// - 2 bytes of opaque data (which corresponds to the MX preference)
/// - a domain name (which corresponds to the MX name)
///
/// If the encoded data contain multiple MX RDATAs, the same type of
/// sequence continues for the number of RDATAs. Note that the opaque
/// data field does not always corresponds to a specific RDATA field
/// as is the 2-byte preference field of MX. For example, the field
/// sequence for an SOA RDATA in terms of RdataEncoder will be:
/// - a domain name (which corresponds to the SOA MNAME)
/// - a domain name (which corresponds to the SOA RNAME)
/// - 20 bytes of opaque data (for the rest of fields)
///
/// So, if you want to construct a general purpose dns::Rdata object
/// from the field sequence, you'll need to build the complete
/// wire-format data, and then construct a dns::Rdata object from it.
///
/// To use it, contstruct it with the data you got from RDataEncoder,
/// provide it with callbacks and then iterate through the data.
/// The callbacks are called with the data fields contained in the
/// data.
///
/// \code
/// void handleName(const dns::LabelSequence& labels, unsigned int flags) {
/// ...
/// }
/// void handleData(const void* data, size_t size) {
/// ...
/// }
///
/// RdataReader reader(RRClass::IN(), RRType::AAAA(), size, data,
/// &handleName, &handleData);
/// reader.iterate();
/// \endcode
///
/// \note It is caller's responsibility to pass valid data here. This means
/// the data returned by RdataEncoder and the corresponding class and type.
/// If this is not the case, all the kinds of pointer hell might get loose.
class RdataReader {
public:
/// \brief Function called on each name encountered in the data.
typedef boost::function<void(const dns::LabelSequence&,
RdataNameAttributes)> NameAction;
/// \brief Function called on each data field in the data.
typedef boost::function<void(const void*, size_t)> DataAction;
/// \brief Constructor
///
/// This constructs the reader on top of some serialized data.
/// It does not copy the data, you have to make sure the data
/// is valid for the whole life of this object and that they
/// don't change.
///
/// \param rrclass The class the encoded rdata belongs to.
/// \param rrtype The type of the encode rdata.
/// \param data The actual data.
/// \param rdata_count The number of Rdata encoded in the data.
/// \param sig_count The number of RRSig rdata bundled with the data.
/// \param name_action The callback to be called on each encountered name.
/// \param data_action The callback to be called on each data chunk.
RdataReader(const dns::RRClass& rrclass, const dns::RRType& rrtype,
const void* data, size_t rdata_count, size_t sig_count,
const NameAction& name_action, const DataAction& data_action);
/// \brief Result of next() and nextSig()
///
/// This specifies if there's any boundary in the data at the
/// place where the corresponding call to next() or nextSig()
/// finished.
enum Boundary {
NO_BOUNDARY, ///< It is in the middle of Rdata
RDATA_BOUNDARY, ///< At the end of single Rdata
RRSET_BOUNDARY ///< At the end of the RRset (past the end)
};
/// \brief Step to next data field.
///
/// Iterate over the next field and call appropriate hook (name_action
/// or data_action, depending on the type) as passed to the constructor.
///
/// \return It returns NO_BOUNDARY if the next call to next() will process
/// data of the same rdata as this one. RDATA_BOUNDARY is returned when
/// this field is the last of the current rdata. If there are no more
/// data to process, no hook is called and RRSET_BOUNDARY is returned.
/// Therefore, at the end of the whole data, once it processes the last
/// field and returns RDATA_BOUNDARY and then it returns RRSET_BOUNDARY
/// on the next call.
Boundary next();
/// \brief Call next() until the end.
///
/// This is just convenience method to iterate through all the data.
/// It calls next until it reaches the end (it does not rewind beforehand,
/// therefore if you already called next() yourself, it does not start
/// at the beginning).
void iterate() {
while (next() != RRSET_BOUNDARY) {}
}
/// \brief Call next() until the end of current rdata.
///
/// This is a convenience method to iterate until the end of current
/// rdata. Notice this may cause more than one field being processed,
/// as some rrtypes are more complex.
///
/// \return If there was Rdata to iterate through.
bool iterateRdata() {
while (true) {
switch (next()) {
case NO_BOUNDARY: break;
case RDATA_BOUNDARY: return (true);
case RRSET_BOUNDARY: return (false);
}
}
}
/// \brief Step to next field of RRSig data.
///
/// This is almost the same as next(), but it iterates through the
/// associated RRSig data, not the data for the given RRType.
Boundary nextSig();
/// \brief Iterate through all RRSig data.
///
/// This is almost the same as iterate(), but it iterates through the
/// RRSig data instead.
void iterateAllSigs() {
while (nextSig() != RRSET_BOUNDARY) {}
}
/// \brief Iterate through the current RRSig Rdata.
///
/// This is almote the same as iterateRdata, except it is for single
/// signature Rdata.
///
/// In practice, this should process one DATA field.
bool iterateSingleSig() {
while (true) {
switch (nextSig()) {
case NO_BOUNDARY: break;
case RDATA_BOUNDARY: return (true);
case RRSET_BOUNDARY: return (false);
}
}
}
/// \brief Rewind the iterator to the beginnig of data.
///
/// The following next() and nextSig() will start iterating from the
/// beginning again.
void rewind();
/// \brief Returns the size of associated data.
///
/// This should be the same as the return value of
/// RdataEncoder::getStorageLength() for the same set of data.
/// The intended use of this method is to tell the caller the size of
/// data that were possibly dynamically allocated so that the caller can
</