Commit 9beeb21e authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[master] Merge branch 'trac2095' with fixing conflicts.

parents c0f1785c 3b36e5f7
......@@ -573,7 +573,8 @@ WARN_LOGFILE =
# with spaces.
INPUT = ../src/lib/exceptions ../src/lib/cc \
../src/lib/config ../src/lib/cryptolink ../src/lib/dns ../src/lib/datasrc \
../src/lib/config ../src/lib/cryptolink ../src/lib/dns \
../src/lib/datasrc ../src/lib/datasrc/memory \
../src/bin/auth ../src/bin/resolver ../src/lib/bench ../src/lib/log \
../src/lib/log/compiler ../src/lib/asiolink/ ../src/lib/nsas \
../src/lib/testutils ../src/lib/cache ../src/lib/server_common/ \
......
......@@ -12,20 +12,23 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#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/rdataclass.h> // for a test function
#include <dns/rrclass.h>
#include <dns/rrtype.h>
#include <util/buffer.h> // for test functions
#include "rdata_encoder.h"
#include <boost/static_assert.hpp>
#include <cassert>
#include <cstring>
#include <vector>
#include <stdint.h>
......@@ -58,6 +61,23 @@ struct RdataFieldSpec {
};
/// 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
......@@ -243,72 +263,272 @@ getRdataEncodeSpec(RRClass rrclass, RRType rrtype) {
return (generic_data_spec);
}
// A temporary helper of temporary encodeRdata(): it calculates the length
// of the data portion of a NAPTR RDATA (i.e., the RDATA fields before the
// "replacement" name).
// 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
getNAPTRDataLen(const rdata::Rdata& rdata) {
const rdata::generic::NAPTR& naptr_rdata =
dynamic_cast<const rdata::generic::NAPTR&>(rdata);
RdataEncoder::getStorageLength() const {
if (impl_->encode_spec_ == NULL) {
isc_throw(InvalidOperation,
"RdataEncoder::getStorageLength performed before start");
}
util::OutputBuffer buffer(0);
rdata.toWire(buffer);
return (buffer.getLength() - naptr_rdata.getReplacement().getLength());
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());
}
} // end of unnamed namespace
namespace testing {
void
encodeRdata(const rdata::Rdata& rdata, RRClass rrclass, RRType rrtype,
vector<uint8_t>& data_result, vector<uint16_t>& len_result)
{
util::OutputBuffer buffer(0);
rdata.toWire(buffer);
util::InputBuffer ibuffer(buffer.getData(), buffer.getLength());
vector<uint8_t> tmp; // used as temporary placeholder below
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");
}
const RdataEncodeSpec& encode_spec = getRdataEncodeSpec(rrclass, rrtype);
for (size_t i = 0; i < encode_spec.field_count; ++i) {
const RdataFieldSpec& field_spec = encode_spec.fields[i];
switch (field_spec.type) {
case RdataFieldSpec::FIXEDLEN_DATA:
tmp.resize(field_spec.fixeddata_len);
ibuffer.readData(&tmp[0], tmp.size());
data_result.insert(data_result.end(), tmp.begin(), tmp.end());
break;
case RdataFieldSpec::VARLEN_DATA:
{
// In the vast majority cases of our supported RR types,
// variable-length data fields are placed at the end of RDATA,
// so the length of the field should be the remaining length
// of the output buffer. The only exception is NAPTR, for which
// we use an ad hoc workaround (remember this function is for
// initial testing only, and will be cleaned up eventually).
const size_t pos = ibuffer.getPosition();
const size_t data_len = rrtype == RRType::NAPTR() ?
getNAPTRDataLen(rdata) : (ibuffer.getLength() - pos);
tmp.resize(data_len);
ibuffer.readData(&tmp[0], tmp.size());
data_result.insert(data_result.end(), tmp.begin(), tmp.end());
len_result.push_back(data_len);
break;
}
case RdataFieldSpec::DOMAIN_NAME:
{
const Name name(ibuffer);
const LabelSequence labels(name);
uint8_t labels_holder[LabelSequence::MAX_SERIALIZED_LENGTH];
labels.serialize(labels_holder, sizeof(labels_holder));
data_result.insert(data_result.end(), labels_holder,
labels_holder + labels.getSerializedLength());
break;
}
}
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 testing {
void
foreachRdataField(RRClass rrclass, RRType rrtype,
size_t rdata_count,
const vector<uint8_t>& encoded_data,
const vector<uint16_t>& varlen_list,
NameCallback name_callback, DataCallback data_callback)
......@@ -318,40 +538,62 @@ foreachRdataField(RRClass rrclass, RRType rrtype,
size_t off = 0;
size_t varlen_count = 0;
size_t name_count = 0;
for (size_t i = 0; i < encode_spec.field_count; ++i) {
const RdataFieldSpec& field_spec = encode_spec.fields[i];
switch (field_spec.type) {
case RdataFieldSpec::FIXEDLEN_DATA:
if (data_callback) {
data_callback(&encoded_data.at(off), field_spec.fixeddata_len);
for (size_t count = 0; count < rdata_count; ++count) {
for (size_t i = 0; i < encode_spec.field_count; ++i) {
const RdataFieldSpec& field_spec = encode_spec.fields[i];
switch (field_spec.type) {
case RdataFieldSpec::FIXEDLEN_DATA:
if (data_callback) {
data_callback(&encoded_data.at(off),
field_spec.fixeddata_len);
}
off += field_spec.fixeddata_len;
break;
case RdataFieldSpec::VARLEN_DATA:
{
const size_t varlen = varlen_list.at(varlen_count);
if (data_callback && varlen > 0) {
data_callback(&encoded_data.at(off), varlen);
}
off += varlen;
++varlen_count;
break;
}
off += field_spec.fixeddata_len;
break;
case RdataFieldSpec::VARLEN_DATA:
{
const size_t varlen = varlen_list.at(varlen_count);
if (data_callback && varlen > 0) {
data_callback(&encoded_data.at(off), varlen);
case RdataFieldSpec::DOMAIN_NAME:
{
++name_count;
const LabelSequence labels(&encoded_data.at(off));
if (name_callback) {
name_callback(labels, field_spec.name_attributes);
}
off += labels.getSerializedLength();
break;
}
off += varlen;
++varlen_count;
break;
}
case RdataFieldSpec::DOMAIN_NAME:
{
++name_count;
const LabelSequence labels(&encoded_data.at(off));
if (name_callback) {
name_callback(labels, field_spec.name_attributes);
}
off += labels.getSerializedLength();
break;
}
}
}
assert(name_count == encode_spec.name_count * rdata_count);
assert(varlen_count == encode_spec.varlen_count * rdata_count);
}
void
foreachRRSig(const vector<uint8_t>& encoded_data,
const vector<uint16_t>& rrsiglen_list,
DataCallback data_callback)
{
size_t rrsig_totallen = 0;
for (vector<uint16_t>::const_iterator it = rrsiglen_list.begin();
it != rrsiglen_list.end();
++it) {
rrsig_totallen += *it;
}
assert(encoded_data.size() >= rrsig_totallen);
assert(name_count == encode_spec.name_count);
assert(varlen_count == encode_spec.varlen_count);
const uint8_t* dp = &encoded_data[encoded_data.size() - rrsig_totallen];
for (size_t i = 0; i < rrsiglen_list.size(); ++i) {
data_callback(dp, rrsiglen_list[i]);
dp += rrsiglen_list[i];
}
}
} // namespace testing
......
......@@ -15,19 +15,97 @@
#ifndef DATASRC_MEMORY_RDATA_ENCODER_H
#define DATASRC_MEMORY_RDATA_ENCODER_H 1
#include <exceptions/exceptions.h>
#include <dns/labelsequence.h>
#include <dns/rdata.h>
#include <dns/rrclass.h>
#include <dns/rrtype.h>
#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
#include <vector>
/// \file rdata_encoder.h
/// \brief Set of utility classes for encoding RDATA in memory efficient way.
///
/// This file defines 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 other (TBD) 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 datasrc {
namespace memory {
/// \brief General error in RDATA encoding.
///
/// This is thrown when \c RdataEncoder encounters a rare, unsupported
/// situation. a method is called for a name or RRset which
/// is not in or below the zone.
class RdataEncodingError : public Exception {
public:
RdataEncodingError(const char* file, size_t line, const char* what) :
Exception(file, line, what) {}
};
/// \brief Attributes of domain name fields of encoded RDATA.
///
/// The enum values define special traits of the name that can affect how
......@@ -40,42 +118,194 @@ enum RdataNameAttributes {
///< handling
};
// We use the following quick-hack version of encoder and "foreach"
// operator until we implement the complete versions. The plan is to
/// \brief RDATA encoder.
///
/// This class provides interfaces to encode a set of RDATA of a specific
/// RR class and type, possibly with their RRSIG RDATAs, in a memory-efficient
/// format. In many cases these sets of RDATA come from a specific (signed
/// or unsigned) RRset.
///
/// It is expected for a single \c RdataEncoder object to be used multiple
/// times for different sets of RDATA, such as in loading an entire zone
/// into memory. Each encoding session begins with the \c start() method,
/// which sets the context for the specific RR class and type to be encoded.
/// Any number of calls to \c addRdata() or \c addSIGRdata() follow, each
/// of which updates the internal state of the encoder with the encoding
/// information for the given RDATA or RRSIG RDATA, respectively.
/// The \c addRdata() is expected to be called with an
/// \c isc::dns::rdata::Rdata object
/// of the specified class and type, and \c addRdata() checks the consistency
/// for the purpose of encoding (but it's not completely type safe; for
/// example, it wouldn't distinguish TXT RDATA and HINFO RDATA.
/// Likewise, an \c isc::dns::rdata::Rdata given to \c addSIGRdata() is
/// expected to be of RRSIG, but the method does not check the assumption).
///
/// After passing the complete set of RDATA and their RRSIG, the application
/// is expected to call \c getStorageLength() to know the size of storage
/// that is sufficient to store all encoded data. Normally the application
/// would allocate a memory region of that size, and then call \c encode()
/// with the prepared region. The \c encode() method dumps encoded data
/// to the given memory region.