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
--- /dev/null
+/rdata_reader_bench
#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>
+++ /dev/null
-// 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_encoder.h"
-
-#include <exceptions/exceptions.h>
-
-#include <util/buffer.h>
-
-#include <dns/name.h>
-#include <dns/labelsequence.h>
-#include <dns/messagerenderer.h>
-#include <dns/rdata.h>
-#include <dns/rrclass.h>
-#include <dns/rrtype.h>
-
-#include <cassert>
-#include <cstring>
-#include <vector>
-
-using namespace isc::dns;
-using std::vector;
-
-namespace isc {
-namespace datasrc {
-namespace memory {
-
-namespace {
-
-// This class is a helper for RdataEncoder to divide the content of RDATA
-// fields for encoding by "abusing" the message rendering logic.
-// The idea is to identify domain name fields in the writeName() method,
-// while keeping track of the size and position of other types of data
-// around the names.
-//
-// 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() : last_data_pos_(0), encode_spec_(NULL),
- current_field_(0)
- {}
- virtual ~RdataFieldComposer() {}
- virtual bool isTruncated() const { return (false); }
- virtual size_t getLengthLimit() const { return (65535); }
- virtual CompressMode getCompressMode() const { return (CASE_INSENSITIVE); }
- virtual void setTruncated() {}
- virtual void setLengthLimit(size_t) {}
- virtual void setCompressMode(CompressMode) {}
-
- // Called for each domain name in the RDATA, from the RDATA's toWire()
- // implementation.
- virtual void writeName(const Name& name, bool compress) {
- // First, see if we have other data already stored in the renderer's
- // buffer, and handle it appropriately.
- updateOtherData();
-
- // Then, we should still have a field in the spec, and it must be a
- // domain name field.
- if (current_field_ >= encode_spec_->field_count) {
- isc_throw(BadValue,
- "RDATA encoder encounters an unexpected name data: " <<
- name);
- }
- const RdataFieldSpec& field =
- encode_spec_->fields[current_field_++];
- // Since we know we've passed any prior data field, the next field
- // must be a domain name as long as it exists; otherwise it's a bug
- // in the spec (not a bogus input). So we assert() that condition.
- assert(field.type == RdataFieldSpec::DOMAIN_NAME);
-
- // It would be compressed iff the field has that attribute.
- if (compress !=
- ((field.name_attributes & NAMEATTR_COMPRESSIBLE) != 0)) {
- isc_throw(BadValue, "RDATA encoder error, inconsistent name "
- "compression policy: " << name);
- }
-
- const LabelSequence labels(name);
- labels.serialize(labels_placeholder_, sizeof(labels_placeholder_));
- writeData(labels_placeholder_, labels.getSerializedLength());
-
- last_data_pos_ += labels.getSerializedLength();
- }
- // Clear all internal states and resources for a new set of RDATA.
- void clearLocal(const RdataEncodeSpec* encode_spec) {
- AbstractMessageRenderer::clear();
- encode_spec_ = encode_spec;
- data_lengths_.clear();
- last_data_pos_ = 0;
- }
- // Called at the beginning of an RDATA.
- void startRdata() {
- current_field_ = 0;
- }
- // Called at the end of an RDATA.
- void endRdata() {
- // Handle any remaining data (there should be no more name). Then
- // we should reach the end of the fields.
- updateOtherData();
- if (current_field_ != encode_spec_->field_count) {
- isc_throw(BadValue,
- "RDATA encoder didn't find all expected fields");
- }
- }
-
- // Hold the lengths of variable length fields, in the order of their
- // appearance. For convenience, allow the encoder to refer to it
- // directly.
- vector<uint16_t> data_lengths_;
-
-private:
- // 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, checking consistency with the field spec, and
- // if it contains variable-length field, record its length.
- size_t last_data_pos_;
- void updateOtherData() {
- // If we've reached the end of the fields or we are expecting a
- // domain name, there's nothing to do here.
- if (current_field_ >= encode_spec_->field_count ||
- encode_spec_->fields[current_field_].type ==
- RdataFieldSpec::DOMAIN_NAME) {
- return;
- }
-
- const size_t cur_pos = getLength();
- const size_t data_len = cur_pos - last_data_pos_;
-
- const RdataFieldSpec& field = encode_spec_->fields[current_field_];
- if (field.type == RdataFieldSpec::FIXEDLEN_DATA) {
- // The data length of a fixed length field must be the one
- // specified in the field spec.
- if (data_len != field.fixeddata_len) {
- isc_throw(BadValue,
- "RDATA encoding: available data too short for the "
- "type");
- }
- } else {
- // For encoding purposes, a variable-length data field is
- // a single field covering all data, even if it may
- // consist of multiple fields as DNS RDATA (e.g. TXT).
- if (data_len > 0xffff) {
- isc_throw(RdataEncodingError, "RDATA field is too large: "
- << data_len << " bytes");
- }
- data_lengths_.push_back(data_len);
- }
-
- ++current_field_;
- last_data_pos_ = cur_pos;
- }
-
- // The RDATA field spec of the current session. Set at the beginning of
- // each session.
- const RdataEncodeSpec* encode_spec_;
- // the RDATA field (for encoding) currently handled. Reset to 0 for
- // each RDATA of the session.
- size_t current_field_;
- // Placeholder to convert a name object to a label sequence.
- uint8_t labels_placeholder_[LabelSequence::MAX_SERIALIZED_LENGTH];
-};
-} // end of unnamed namespace
-
-struct RdataEncoder::RdataEncoderImpl {
- RdataEncoderImpl() : encode_spec_(NULL), rrsig_buffer_(0),
- rdata_count_(0)
- {}
-
- const RdataEncodeSpec* encode_spec_; // encode spec of current RDATA set
- RdataFieldComposer field_composer_;
- util::OutputBuffer rrsig_buffer_;
- size_t rdata_count_;
- vector<uint16_t> rrsig_lengths_;
-};
-
-RdataEncoder::RdataEncoder() :
- impl_(new RdataEncoderImpl)
-{}
-
-RdataEncoder::~RdataEncoder() {
- delete impl_;
-}
-
-void
-RdataEncoder::start(RRClass rrclass, RRType rrtype) {
- if (rrtype == RRType::RRSIG()) {
- isc_throw(BadValue, "RRSIG cannot be encoded as main RDATA type");
- }
-
- impl_->encode_spec_ = &getRdataEncodeSpec(rrclass, rrtype);
- impl_->field_composer_.clearLocal(impl_->encode_spec_);
- impl_->rrsig_buffer_.clear();
- impl_->rdata_count_ = 0;
- impl_->rrsig_lengths_.clear();
-}
-
-void
-RdataEncoder::addRdata(const rdata::Rdata& rdata) {
- if (impl_->encode_spec_ == NULL) {
- isc_throw(InvalidOperation,
- "RdataEncoder::addRdata performed before start");
- }
-
- impl_->field_composer_.startRdata();
- rdata.toWire(impl_->field_composer_);
- impl_->field_composer_.endRdata();
- ++impl_->rdata_count_;
-}
-
-void
-RdataEncoder::addSIGRdata(const rdata::Rdata& sig_rdata) {
- if (impl_->encode_spec_ == NULL) {
- isc_throw(InvalidOperation,
- "RdataEncoder::addSIGRdata performed before start");
- }
- const size_t cur_pos = impl_->rrsig_buffer_.getLength();
- sig_rdata.toWire(impl_->rrsig_buffer_);
- const size_t rrsig_datalen = impl_->rrsig_buffer_.getLength() - cur_pos;
- if (rrsig_datalen > 0xffff) {
- isc_throw(RdataEncodingError, "RRSIG is too large: "
- << rrsig_datalen << " bytes");
- }
- impl_->rrsig_lengths_.push_back(rrsig_datalen);
-}
-
-size_t
-RdataEncoder::getStorageLength() const {
- if (impl_->encode_spec_ == NULL) {
- isc_throw(InvalidOperation,
- "RdataEncoder::getStorageLength performed before start");
- }
-
- return (sizeof(uint16_t) * impl_->field_composer_.data_lengths_.size() +
- sizeof(uint16_t) * impl_->rrsig_lengths_.size() +
- impl_->rrsig_buffer_.getLength() +
- impl_->field_composer_.getLength());
-}
-
-void
-RdataEncoder::encode(void* buf, size_t buf_len) const {
- if (impl_->encode_spec_ == NULL) {
- isc_throw(InvalidOperation,
- "RdataEncoder::encode performed before start");
- }
- if (buf == NULL) {
- isc_throw(BadValue,
- "RdataEncoder::encode NULL buffer is given");
- }
- if (getStorageLength() > buf_len) {
- isc_throw(BadValue, "RdataEncoder::encode short buffer given");
- }
-
- uint8_t* const dp_beg = reinterpret_cast<uint8_t*>(buf);
- uint8_t* dp = dp_beg;
- uint16_t* lenp = reinterpret_cast<uint16_t*>(buf);
-
- // Encode list of lengths for variable length fields (if any)
- if (!impl_->field_composer_.data_lengths_.empty()) {
- const size_t varlen_fields_len =
- impl_->field_composer_.data_lengths_.size() * sizeof(uint16_t);
- std::memcpy(lenp, &impl_->field_composer_.data_lengths_[0],
- varlen_fields_len);
- lenp += impl_->field_composer_.data_lengths_.size();
- dp += varlen_fields_len;
- }
- // Encode list of lengths for RRSIGs (if any)
- if (!impl_->rrsig_lengths_.empty()) {
- const size_t rrsigs_len =
- impl_->rrsig_lengths_.size() * sizeof(uint16_t);
- std::memcpy(lenp, &impl_->rrsig_lengths_[0], rrsigs_len);
- dp += rrsigs_len;
- }
- // Encode main RDATA
- std::memcpy(dp, impl_->field_composer_.getData(),
- impl_->field_composer_.getLength());
- dp += impl_->field_composer_.getLength();
- // Encode RRSIGs, if any
- std::memcpy(dp, impl_->rrsig_buffer_.getData(),
- impl_->rrsig_buffer_.getLength());
- dp += impl_->rrsig_buffer_.getLength();
-
- // The validation at the entrance must ensure this
- assert(buf_len >= dp - dp_beg);
-}
-
-
-} // namespace memory
-} // namespace datasrc
-} // datasrc isc
+++ /dev/null
-// 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);
-}
-
-}
-}
-}
+++ /dev/null
-// 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_FIELD
-#define DATASRC_MEMORY_RDATA_FIELD
-
-#include <stdint.h>
-
-/// \file rdata_field.h
-///
-/// This header should be considered private to the implementation and should
-/// not be used included directly.
-///
-/// It is used to share the definition of encoding for RRtypes.
-///
-/// These are types shared by classes in rdata_encoder.h and rdata_reader.h.
-/// The defyne a set of interfaces (classes, types, constants) to
-/// manipulate a given set of RDATA of the same type (normally associated with
-/// an RRset) that may be accompanied with RRSIGs in a memory efficient way.
-///
-/// The entire set of RDATA is stored in a packed form in a contiguous
-/// memory region. It's opaque data, without containing non trivial
-/// data structures, so it can be located anywhere in the memory or even
-/// dumped to a file.
-///
-/// Two main classes are provided: one is
-/// \c isc::datasrc::memory::RdataEncoder, which allows
-/// the application to create encoded data for a set of RDATA;
-/// the isc::datasrc::memory::RdataReader provides an interface to iterate
-/// over encoded set of RDATA for purposes such as data lookups or rendering
-/// the data into the wire format to create a DNS message.
-///
-/// The actual encoding detail is private information to the implementation,
-/// and the application shouldn't assume anything about that except that
-/// each RDATA is considered to consist of one or more generic fields,
-/// and each field is typed as either opaque data or a domain name.
-/// A domain name field has additional attributes
-/// (see \c isc::datasrc::memory::RdataNameAttributes)
-/// so the application can change how the name should be handled in terms
-/// of the DNS protocol (e.g., whether it's subject to name compression).
-///
-/// The following are the current implementation of internal encoding, shown
-/// only for reference. Applications must not assume this particular form
-/// for the encoded data; in fact, it can change in a future version of the
-/// implementation.
-/// \verbatim
-// The encoded data begin with a series of 16-bit length fields (values are
-// stored in the host byte order). The sequence may be empty.
-// uint16_t n1_1: size of 1st variable len field (if any) of 1st RDATA
-// uint16_t n1_2: size of 2nd variable len field of 1st RDATA
-// ...
-// uint16_t nN_M: size of last (Mth) variable len field of last (Nth) RDATA
-// uint16_t ns1: size of 1st RRSIG (if any) data
-// ...
-// uint16_t nsL: size of last (Lth) RRSIG data
-// A sequence of packed data fields follows:
-// uint8_t[]: data field value, length specified by nI_J (in case it's
-// variable-length) or by the per type field spec (in case it's
-// fixed-length).
-// or
-// opaque data, LabelSequence::getSerializedLength() bytes: data for a name
-// uint8_t[ns1]: 1st RRSIG data
-// ...
-// uint8_t[nsL]: last RRSIG data
-// \endverbatim
-///
-/// As described above, this implementation treats RRSIGs as opaque data
-/// that don't contain any domain names. Technically, it has a "signer"
-/// domain name field in the sense of RFC4034. In practice, however, this
-/// field is essentially mere data; it's not subject to name compression,
-/// and since it's very likely to be a subdomain of (or equal to) the
-/// owner name of the corresponding RR (or, if used in a DNS message,
-/// some domain name that already appears before this field), so it won't
-/// be a target of name compression either. By treating the entire RRSIG
-/// as single-field data we can make the implementation simpler, and probably
-/// make it faster in rendering it into a DNS message.
-
-namespace isc {
-namespace dns {
-class RRType;
-class RRClass;
-}
-namespace datasrc {
-namespace memory {
-
-/// \brief Attributes of domain name fields of encoded RDATA.
-///
-/// The enum values define special traits of the name that can affect how
-/// it should be handled in rendering or query processing.
-enum RdataNameAttributes {
- NAMEATTR_NONE = 0, ///< No special attributes
- NAMEATTR_COMPRESSIBLE = 1, ///< Name should be compressed when rendered
- NAMEATTR_ADDITIONAL = (NAMEATTR_COMPRESSIBLE << 1) ///< Name requires
- ///< Additional section
- ///< handling
-};
-
-/// Specification of a single RDATA field in terms of internal encoding.
-struct RdataFieldSpec {
- enum FieldType {
- FIXEDLEN_DATA = 0, // fixed-length data field
- VARLEN_DATA, // variable-length data field
- DOMAIN_NAME // domain name
- };
-
- const FieldType type; // field type
-
- // The length of fixed-length data field. Only valid for FIXEDLEN_DATA.
- // For type DOMAIN_NAME, set it to 0.
- const uint16_t fixeddata_len;
-
- // Attributes of the name. Only valid for DOMAIN_NAME.
- // For type _DATA, set it to NAMEATTR_NONE.
- const RdataNameAttributes name_attributes;
-};
-
-/// Specification of RDATA in terms of internal encoding.
-///
-/// The fields must be a sequence of:
-/// <0 or 1 fixed/var-len data field>,
-/// <1 or more domain name fields>,
-/// <1 fixed/var-len data field>,
-/// <1 or more domain name fields>,
-/// <1 fixed/var-len data field>,
-/// ...and so on.
-/// There must not be more than one consecutive data fields (i.e., without
-/// interleaved by a domain name); it would just be inefficient in terms of
-/// memory footprint and iterating over the fields, and it would break
-/// some assumption within the encoder implementation. For consecutive
-/// data fields in the DNS protocol, if all fields have fixed lengths, they
-/// should be combined into a single fixed-length field (like the last 20
-/// bytes of SOA RDATA). If there's a variable length field, they should be
-/// combined into a single variable-length field (such as DNSKEY, which has
-/// 3 fixed-length fields followed by one variable-length field).
-struct RdataEncodeSpec {
- const uint16_t field_count; // total number of fields (# of fields member)
- const uint16_t name_count; // number of domain name fields
- const uint16_t varlen_count; // number of variable-length data fields
- const RdataFieldSpec* const fields; // list of field specs
-};
-
-/// \brief Get the spec for given class and type
-const RdataEncodeSpec&
-getRdataEncodeSpec(const dns::RRClass& rrclass, const dns::RRType& rrtype);
-
-}
-}
-}
-
-#endif
+++ /dev/null
-// 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);
-}
-
-}
-}
-}
+++ /dev/null
-// 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
- /// use it for deallocation.
- ///
- /// This method only uses the parameters given at the construction of the
- /// object, and does not rely on or modify other mutable states.
- /// In practice, when the caller wants to call this method, that would be
- /// the only purpose of that RdataReader object (although it doesn't have
- /// to be so).
- size_t getSize() const;
-private:
- const NameAction name_action_;
- const DataAction data_action_;
- const RdataEncodeSpec& spec_;
- // Total number of var-length fields, count of signatures
- const size_t var_count_total_, sig_count_, spec_count_;
- // Pointer to the beginning of length fields
- const uint16_t* const lengths_;
- // Pointer to the beginning of the data (after the lengths)
- const uint8_t* const data_;
- // Pointer to the first data signature
- // Will be computed during the normal RR iteration
- const uint8_t* sigs_;
- // The positions in data.
- size_t data_pos_, spec_pos_, length_pos_;
- size_t sig_pos_, sig_data_pos_;
- Boundary nextInternal(const NameAction& name_action,
- const DataAction& data_action);
-};
-
-}
-}
-}
-
-#endif
--- /dev/null
+// 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_serialization.h"
+
+#include <exceptions/exceptions.h>
+
+#include <util/buffer.h>
+
+#include <dns/name.h>
+#include <dns/labelsequence.h>
+#include <dns/messagerenderer.h>
+#include <dns/rdata.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+
+#include <cassert>
+#include <cstring>
+#include <vector>
+#include <boost/static_assert.hpp>
+
+using namespace isc::dns;
+using std::vector;
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+#include "rdata_serialization_priv.cc"
+
+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);
+
+}
+
+/// \brief Get the spec for given class and type
+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);
+}
+
+namespace {
+
+// This class is a helper for RdataEncoder to divide the content of RDATA
+// fields for encoding by "abusing" the message rendering logic.
+// The idea is to identify domain name fields in the writeName() method,
+// while keeping track of the size and position of other types of data
+// around the names.
+//
+// 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() : last_data_pos_(0), encode_spec_(NULL),
+ current_field_(0)
+ {}
+ virtual ~RdataFieldComposer() {}
+ virtual bool isTruncated() const { return (false); }
+ virtual size_t getLengthLimit() const { return (65535); }
+ virtual CompressMode getCompressMode() const { return (CASE_INSENSITIVE); }
+ virtual void setTruncated() {}
+ virtual void setLengthLimit(size_t) {}
+ virtual void setCompressMode(CompressMode) {}
+
+ // Called for each domain name in the RDATA, from the RDATA's toWire()
+ // implementation.
+ virtual void writeName(const Name& name, bool compress) {
+ // First, see if we have other data already stored in the renderer's
+ // buffer, and handle it appropriately.
+ updateOtherData();
+
+ // Then, we should still have a field in the spec, and it must be a
+ // domain name field.
+ if (current_field_ >= encode_spec_->field_count) {
+ isc_throw(BadValue,
+ "RDATA encoder encounters an unexpected name data: " <<
+ name);
+ }
+ const RdataFieldSpec& field =
+ encode_spec_->fields[current_field_++];
+ // Since we know we've passed any prior data field, the next field
+ // must be a domain name as long as it exists; otherwise it's a bug
+ // in the spec (not a bogus input). So we assert() that condition.
+ assert(field.type == RdataFieldSpec::DOMAIN_NAME);
+
+ // It would be compressed iff the field has that attribute.
+ if (compress !=
+ ((field.name_attributes & NAMEATTR_COMPRESSIBLE) != 0)) {
+ isc_throw(BadValue, "RDATA encoder error, inconsistent name "
+ "compression policy: " << name);
+ }
+
+ const LabelSequence labels(name);
+ labels.serialize(labels_placeholder_, sizeof(labels_placeholder_));
+ writeData(labels_placeholder_, labels.getSerializedLength());
+
+ last_data_pos_ += labels.getSerializedLength();
+ }
+ // Clear all internal states and resources for a new set of RDATA.
+ void clearLocal(const RdataEncodeSpec* encode_spec) {
+ AbstractMessageRenderer::clear();
+ encode_spec_ = encode_spec;
+ data_lengths_.clear();
+ last_data_pos_ = 0;
+ }
+ // Called at the beginning of an RDATA.
+ void startRdata() {
+ current_field_ = 0;
+ }
+ // Called at the end of an RDATA.
+ void endRdata() {
+ // Handle any remaining data (there should be no more name). Then
+ // we should reach the end of the fields.
+ updateOtherData();
+ if (current_field_ != encode_spec_->field_count) {
+ isc_throw(BadValue,
+ "RDATA encoder didn't find all expected fields");
+ }
+ }
+
+ // Hold the lengths of variable length fields, in the order of their
+ // appearance. For convenience, allow the encoder to refer to it
+ // directly.
+ vector<uint16_t> data_lengths_;
+
+private:
+ // 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, checking consistency with the field spec, and
+ // if it contains variable-length field, record its length.
+ size_t last_data_pos_;
+ void updateOtherData() {
+ // If we've reached the end of the fields or we are expecting a
+ // domain name, there's nothing to do here.
+ if (current_field_ >= encode_spec_->field_count ||
+ encode_spec_->fields[current_field_].type ==
+ RdataFieldSpec::DOMAIN_NAME) {
+ return;
+ }
+
+ const size_t cur_pos = getLength();
+ const size_t data_len = cur_pos - last_data_pos_;
+
+ const RdataFieldSpec& field = encode_spec_->fields[current_field_];
+ if (field.type == RdataFieldSpec::FIXEDLEN_DATA) {
+ // The data length of a fixed length field must be the one
+ // specified in the field spec.
+ if (data_len != field.fixeddata_len) {
+ isc_throw(BadValue,
+ "RDATA encoding: available data too short for the "
+ "type");
+ }
+ } else {
+ // For encoding purposes, a variable-length data field is
+ // a single field covering all data, even if it may
+ // consist of multiple fields as DNS RDATA (e.g. TXT).
+ if (data_len > 0xffff) {
+ isc_throw(RdataEncodingError, "RDATA field is too large: "
+ << data_len << " bytes");
+ }
+ data_lengths_.push_back(data_len);
+ }
+
+ ++current_field_;
+ last_data_pos_ = cur_pos;
+ }
+
+ // The RDATA field spec of the current session. Set at the beginning of
+ // each session.
+ const RdataEncodeSpec* encode_spec_;
+ // the RDATA field (for encoding) currently handled. Reset to 0 for
+ // each RDATA of the session.
+ size_t current_field_;
+ // Placeholder to convert a name object to a label sequence.
+ uint8_t labels_placeholder_[LabelSequence::MAX_SERIALIZED_LENGTH];
+};
+
+} // end of unnamed namespace
+
+struct RdataEncoder::RdataEncoderImpl {
+ RdataEncoderImpl() : encode_spec_(NULL), rrsig_buffer_(0),
+ rdata_count_(0)
+ {}
+
+ const RdataEncodeSpec* encode_spec_; // encode spec of current RDATA set
+ RdataFieldComposer field_composer_;
+ util::OutputBuffer rrsig_buffer_;
+ size_t rdata_count_;
+ vector<uint16_t> rrsig_lengths_;
+};
+
+RdataEncoder::RdataEncoder() :
+ impl_(new RdataEncoderImpl)
+{}
+
+RdataEncoder::~RdataEncoder() {
+ delete impl_;
+}
+
+void
+RdataEncoder::start(RRClass rrclass, RRType rrtype) {
+ if (rrtype == RRType::RRSIG()) {
+ isc_throw(BadValue, "RRSIG cannot be encoded as main RDATA type");
+ }
+
+ impl_->encode_spec_ = &getRdataEncodeSpec(rrclass, rrtype);
+ impl_->field_composer_.clearLocal(impl_->encode_spec_);
+ impl_->rrsig_buffer_.clear();
+ impl_->rdata_count_ = 0;
+ impl_->rrsig_lengths_.clear();
+}
+
+void
+RdataEncoder::addRdata(const rdata::Rdata& rdata) {
+ if (impl_->encode_spec_ == NULL) {
+ isc_throw(InvalidOperation,
+ "RdataEncoder::addRdata performed before start");
+ }
+
+ impl_->field_composer_.startRdata();
+ rdata.toWire(impl_->field_composer_);
+ impl_->field_composer_.endRdata();
+ ++impl_->rdata_count_;
+}
+
+void
+RdataEncoder::addSIGRdata(const rdata::Rdata& sig_rdata) {
+ if (impl_->encode_spec_ == NULL) {
+ isc_throw(InvalidOperation,
+ "RdataEncoder::addSIGRdata performed before start");
+ }
+ const size_t cur_pos = impl_->rrsig_buffer_.getLength();
+ sig_rdata.toWire(impl_->rrsig_buffer_);
+ const size_t rrsig_datalen = impl_->rrsig_buffer_.getLength() - cur_pos;
+ if (rrsig_datalen > 0xffff) {
+ isc_throw(RdataEncodingError, "RRSIG is too large: "
+ << rrsig_datalen << " bytes");
+ }
+ impl_->rrsig_lengths_.push_back(rrsig_datalen);
+}
+
+size_t
+RdataEncoder::getStorageLength() const {
+ if (impl_->encode_spec_ == NULL) {
+ isc_throw(InvalidOperation,
+ "RdataEncoder::getStorageLength performed before start");
+ }
+
+ return (sizeof(uint16_t) * impl_->field_composer_.data_lengths_.size() +
+ sizeof(uint16_t) * impl_->rrsig_lengths_.size() +
+ impl_->rrsig_buffer_.getLength() +
+ impl_->field_composer_.getLength());
+}
+
+void
+RdataEncoder::encode(void* buf, size_t buf_len) const {
+ if (impl_->encode_spec_ == NULL) {
+ isc_throw(InvalidOperation,
+ "RdataEncoder::encode performed before start");
+ }
+ if (buf == NULL) {
+ isc_throw(BadValue,
+ "RdataEncoder::encode NULL buffer is given");
+ }
+ if (getStorageLength() > buf_len) {
+ isc_throw(BadValue, "RdataEncoder::encode short buffer given");
+ }
+
+ uint8_t* const dp_beg = reinterpret_cast<uint8_t*>(buf);
+ uint8_t* dp = dp_beg;
+ uint16_t* lenp = reinterpret_cast<uint16_t*>(buf);
+
+ // Encode list of lengths for variable length fields (if any)
+ if (!impl_->field_composer_.data_lengths_.empty()) {
+ const size_t varlen_fields_len =
+ impl_->field_composer_.data_lengths_.size() * sizeof(uint16_t);
+ std::memcpy(lenp, &impl_->field_composer_.data_lengths_[0],
+ varlen_fields_len);
+ lenp += impl_->field_composer_.data_lengths_.size();
+ dp += varlen_fields_len;
+ }
+ // Encode list of lengths for RRSIGs (if any)
+ if (!impl_->rrsig_lengths_.empty()) {
+ const size_t rrsigs_len =
+ impl_->rrsig_lengths_.size() * sizeof(uint16_t);
+ std::memcpy(lenp, &impl_->rrsig_lengths_[0], rrsigs_len);
+ dp += rrsigs_len;
+ }
+ // Encode main RDATA
+ std::memcpy(dp, impl_->field_composer_.getData(),
+ impl_->field_composer_.getLength());
+ dp += impl_->field_composer_.getLength();
+ // Encode RRSIGs, if any
+ std::memcpy(dp, impl_->rrsig_buffer_.getData(),
+ impl_->rrsig_buffer_.getLength());
+ dp += impl_->rrsig_buffer_.getLength();
+
+ // The validation at the entrance must ensure this
+ assert(buf_len >= dp - dp_beg);
+}
+
+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);
+}
+
+} // namespace memory
+} // namespace datasrc
+} // datasrc isc
#ifndef DATASRC_MEMORY_RDATA_ENCODER_H
#define DATASRC_MEMORY_RDATA_ENCODER_H 1
-#include <datasrc/memory/rdata_field.h>
-
#include <exceptions/exceptions.h>
#include <dns/labelsequence.h>
RdataEncoderImpl* impl_;
};
+/// \brief Attributes of domain name fields of encoded RDATA.
+///
+/// The enum values define special traits of the name that can affect how
+/// it should be handled in rendering or query processing.
+enum RdataNameAttributes {
+ NAMEATTR_NONE = 0, ///< No special attributes
+ NAMEATTR_COMPRESSIBLE = 1, ///< Name should be compressed when rendered
+ NAMEATTR_ADDITIONAL = (NAMEATTR_COMPRESSIBLE << 1) ///< Name requires
+ ///< Additional section
+ ///< handling
+};
+
+class RdataEncodeSpec;
+
+/// \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
+ /// use it for deallocation.
+ ///
+ /// This method only uses the parameters given at the construction of the
+ /// object, and does not rely on or modify other mutable states.
+ /// In practice, when the caller wants to call this method, that would be
+ /// the only purpose of that RdataReader object (although it doesn't have
+ /// to be so).
+ size_t getSize() const;
+private:
+ const NameAction name_action_;
+ const DataAction data_action_;
+ const RdataEncodeSpec& spec_;
+ // Total number of var-length fields, count of signatures
+ const size_t var_count_total_, sig_count_, spec_count_;
+ // Pointer to the beginning of length fields
+ const uint16_t* const lengths_;
+ // Pointer to the beginning of the data (after the lengths)
+ const uint8_t* const data_;
+ // Pointer to the first data signature
+ // Will be computed during the normal RR iteration
+ const uint8_t* sigs_;
+ // The positions in data.
+ size_t data_pos_, spec_pos_, length_pos_;
+ size_t sig_pos_, sig_data_pos_;
+ Boundary nextInternal(const NameAction& name_action,
+ const DataAction& data_action);
+};
+
} // namespace memory
} // namespace datasrc
} // namespace isc
--- /dev/null
+// 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.
+
+// This file is directly included from the rdata_serialization.cc. It would
+// be part of the file if we didn't need to steal some definitions from here
+// for the tests (which include it too).
+
+struct RdataFieldSpec {
+ enum FieldType {
+ FIXEDLEN_DATA = 0, // fixed-length data field
+ VARLEN_DATA, // variable-length data field
+ DOMAIN_NAME // domain name
+ };
+
+ const FieldType type; // field type
+
+ // The length of fixed-length data field. Only valid for FIXEDLEN_DATA.
+ // For type DOMAIN_NAME, set it to 0.
+ const uint16_t fixeddata_len;
+
+ // Attributes of the name. Only valid for DOMAIN_NAME.
+ // For type _DATA, set it to NAMEATTR_NONE.
+ const RdataNameAttributes name_attributes;
+};
+
+/// Specification of RDATA in terms of internal encoding.
+///
+/// The fields must be a sequence of:
+/// <0 or 1 fixed/var-len data field>,
+/// <1 or more domain name fields>,
+/// <1 fixed/var-len data field>,
+/// <1 or more domain name fields>,
+/// <1 fixed/var-len data field>,
+/// ...and so on.
+/// There must not be more than one consecutive data fields (i.e., without
+/// interleaved by a domain name); it would just be inefficient in terms of
+/// memory footprint and iterating over the fields, and it would break
+/// some assumption within the encoder implementation. For consecutive
+/// data fields in the DNS protocol, if all fields have fixed lengths, they
+/// should be combined into a single fixed-length field (like the last 20
+/// bytes of SOA RDATA). If there's a variable length field, they should be
+/// combined into a single variable-length field (such as DNSKEY, which has
+/// 3 fixed-length fields followed by one variable-length field).
+struct RdataEncodeSpec {
+ const uint16_t field_count; // total number of fields (# of fields member)
+ const uint16_t name_count; // number of domain name fields
+ const uint16_t varlen_count; // number of variable-length data fields
+ const RdataFieldSpec* const fields; // list of field specs
+};
+
+/// \brief Get the spec for given class and type
+const RdataEncodeSpec&
+getRdataEncodeSpec(const RRClass& rrclass, const RRType& rrtype);
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-// Note: This file tests both the rdata_encoder and rdata_reader. They are
-// tested together because they form kind the opposite sides of the same
-// functionality.
-
#include <exceptions/exceptions.h>
#include <util/buffer.h>
#include <dns/rrclass.h>
#include <dns/rrtype.h>
-#include <datasrc/memory/rdata_encoder.h>
-#include <datasrc/memory/rdata_field.h>
-#include <datasrc/memory/rdata_reader.h>
+#include <datasrc/memory/rdata_serialization.h>
#include <util/unittests/wiredata.h>
using std::string;
using std::vector;
+// A trick to steal some private definitions of the implementation we use here
+
+namespace isc {
+namespace datasrc{
+namespace memory {
+
+#include "../rdata_serialization_priv.cc"
+
+}
+}
+}
+
namespace {
// This defines a tuple of test data used in test_rdata_list below.
struct TestRdata {