From: Michal 'vorner' Vaner Date: Fri, 24 Aug 2012 09:45:40 +0000 (+0200) Subject: [2096] Reader and encoder merged to the same file X-Git-Tag: trac2351_base~114^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b97f714d59f1990b62a01e5ca0c5f1c95834ed7c;p=thirdparty%2Fkea.git [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). --- diff --git a/src/lib/datasrc/memory/Makefile.am b/src/lib/datasrc/memory/Makefile.am index 0ec2add0bb..16e7fd0751 100644 --- a/src/lib/datasrc/memory/Makefile.am +++ b/src/lib/datasrc/memory/Makefile.am @@ -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 diff --git a/src/lib/datasrc/memory/benchmarks/.gitignore b/src/lib/datasrc/memory/benchmarks/.gitignore new file mode 100644 index 0000000000..ea243a4fd8 --- /dev/null +++ b/src/lib/datasrc/memory/benchmarks/.gitignore @@ -0,0 +1 @@ +/rdata_reader_bench diff --git a/src/lib/datasrc/memory/benchmarks/rdata_reader_bench.cc b/src/lib/datasrc/memory/benchmarks/rdata_reader_bench.cc index e3fb39675c..4e6af56142 100644 --- a/src/lib/datasrc/memory/benchmarks/rdata_reader_bench.cc +++ b/src/lib/datasrc/memory/benchmarks/rdata_reader_bench.cc @@ -22,8 +22,7 @@ #include #include -#include -#include +#include #include diff --git a/src/lib/datasrc/memory/rdata_encoder.cc b/src/lib/datasrc/memory/rdata_encoder.cc deleted file mode 100644 index 9b47b675c2..0000000000 --- a/src/lib/datasrc/memory/rdata_encoder.cc +++ /dev/null @@ -1,306 +0,0 @@ -// 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 - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -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 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 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(buf); - uint8_t* dp = dp_beg; - uint16_t* lenp = reinterpret_cast(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 diff --git a/src/lib/datasrc/memory/rdata_field.cc b/src/lib/datasrc/memory/rdata_field.cc deleted file mode 100644 index bc48a52a74..0000000000 --- a/src/lib/datasrc/memory/rdata_field.cc +++ /dev/null @@ -1,211 +0,0 @@ -// 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 -#include - -#include - -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( - static_cast(NAMEATTR_COMPRESSIBLE) | - static_cast(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( - static_cast(NAMEATTR_COMPRESSIBLE) | - static_cast(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); -} - -} -} -} diff --git a/src/lib/datasrc/memory/rdata_field.h b/src/lib/datasrc/memory/rdata_field.h deleted file mode 100644 index d9ab4159a4..0000000000 --- a/src/lib/datasrc/memory/rdata_field.h +++ /dev/null @@ -1,161 +0,0 @@ -// 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 - -/// \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 diff --git a/src/lib/datasrc/memory/rdata_reader.cc b/src/lib/datasrc/memory/rdata_reader.cc deleted file mode 100644 index 5429fb9b22..0000000000 --- a/src/lib/datasrc/memory/rdata_reader.cc +++ /dev/null @@ -1,169 +0,0 @@ -// 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(data)), - // And the data just after all the lengths - data_(reinterpret_cast(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); -} - -} -} -} diff --git a/src/lib/datasrc/memory/rdata_reader.h b/src/lib/datasrc/memory/rdata_reader.h deleted file mode 100644 index 8f502253fd..0000000000 --- a/src/lib/datasrc/memory/rdata_reader.h +++ /dev/null @@ -1,237 +0,0 @@ -// 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 - -#include -#include - -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 NameAction; - /// \brief Function called on each data field in the data. - typedef boost::function 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 diff --git a/src/lib/datasrc/memory/rdata_serialization.cc b/src/lib/datasrc/memory/rdata_serialization.cc new file mode 100644 index 0000000000..9c020b20b6 --- /dev/null +++ b/src/lib/datasrc/memory/rdata_serialization.cc @@ -0,0 +1,635 @@ +// 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 + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +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( + static_cast(NAMEATTR_COMPRESSIBLE) | + static_cast(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( + static_cast(NAMEATTR_COMPRESSIBLE) | + static_cast(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 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 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(buf); + uint8_t* dp = dp_beg; + uint16_t* lenp = reinterpret_cast(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(data)), + // And the data just after all the lengths + data_(reinterpret_cast(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 diff --git a/src/lib/datasrc/memory/rdata_encoder.h b/src/lib/datasrc/memory/rdata_serialization.h similarity index 51% rename from src/lib/datasrc/memory/rdata_encoder.h rename to src/lib/datasrc/memory/rdata_serialization.h index 585a50048b..4f6597183e 100644 --- a/src/lib/datasrc/memory/rdata_encoder.h +++ b/src/lib/datasrc/memory/rdata_serialization.h @@ -15,8 +15,6 @@ #ifndef DATASRC_MEMORY_RDATA_ENCODER_H #define DATASRC_MEMORY_RDATA_ENCODER_H 1 -#include - #include #include @@ -202,6 +200,218 @@ private: 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 NameAction; + /// \brief Function called on each data field in the data. + typedef boost::function 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 diff --git a/src/lib/datasrc/memory/rdata_serialization_priv.cc b/src/lib/datasrc/memory/rdata_serialization_priv.cc new file mode 100644 index 0000000000..23bdf53fcd --- /dev/null +++ b/src/lib/datasrc/memory/rdata_serialization_priv.cc @@ -0,0 +1,64 @@ +// 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); diff --git a/src/lib/datasrc/memory/tests/rdata_serialization_unittest.cc b/src/lib/datasrc/memory/tests/rdata_serialization_unittest.cc index b7a13d94e8..12b613a8ec 100644 --- a/src/lib/datasrc/memory/tests/rdata_serialization_unittest.cc +++ b/src/lib/datasrc/memory/tests/rdata_serialization_unittest.cc @@ -12,10 +12,6 @@ // 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 #include @@ -28,9 +24,7 @@ #include #include -#include -#include -#include +#include #include @@ -52,6 +46,18 @@ using isc::util::unittests::matchWireData; 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 {