diff options
Diffstat (limited to 'src/lib/dns/rdatafields.cc')
-rw-r--r-- | src/lib/dns/rdatafields.cc | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/src/lib/dns/rdatafields.cc b/src/lib/dns/rdatafields.cc new file mode 100644 index 0000000..e02ec8f --- /dev/null +++ b/src/lib/dns/rdatafields.cc @@ -0,0 +1,216 @@ +// Copyright (C) 2010-2015,2017 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <stdint.h> + +#include <cassert> +#include <vector> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdatafields.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::dns::rdata; +using isc::util::OutputBuffer; +using isc::util::InputBuffer; + +namespace isc { +namespace dns { +namespace rdata { + +/// This is a helper class for \c RdataFields. +/// +/// It manages a local storage for the data when \c RdataFields is constructed +/// from an \c Rdata. +/// To minimize construction overhead in the other case, an instance of +/// this class is instantiated only when necessary - we don't need the vectors +/// when only rendering. +struct RdataFields::RdataFieldsDetail { + RdataFieldsDetail(const vector<FieldSpec>& fields, + const uint8_t* data, size_t data_length) : + allocated_fields_(fields), + allocated_data_(data, data + data_length) + {} + const vector<FieldSpec> allocated_fields_; + const vector<uint8_t> allocated_data_; +}; + +namespace { +// This class is used to divide the content of RDATA into \c RdataField +// fields via message rendering logic. +// The idea is to identify domain name fields in the writeName() method, +// and determine whether they are compressible using the "compress" +// parameter. +// Other types of data are simply copied into the internal buffer, and +// consecutive such fields are combined into a single \c RdataField field. +// +// Technically, this use of inheritance may be considered a violation of +// Liskov Substitution Principle in that it doesn't actually compress domain +// names, and some of the methods are not expected to be used. +// In fact, skip() or trim() may not be make much sense in this context. +// Nevertheless we keep this idea at the moment. Since the usage is limited +// (it's only used within this file, and only used with \c Rdata variants), +// it's hopefully an acceptable practice. +class RdataFieldComposer : public AbstractMessageRenderer { +public: + RdataFieldComposer() : + truncated_(false), length_limit_(65535), + mode_(CASE_INSENSITIVE), last_data_pos_(0) + {} + virtual ~RdataFieldComposer() {} + virtual bool isTruncated() const { return (truncated_); } + virtual size_t getLengthLimit() const { return (length_limit_); } + virtual CompressMode getCompressMode() const { return (mode_); } + virtual void setTruncated() { truncated_ = true; } + virtual void setLengthLimit(size_t len) { length_limit_ = len; } + virtual void setCompressMode(CompressMode mode) { mode_ = mode; } + virtual void writeName(const LabelSequence&, bool) {} + virtual void writeName(const Name& name, bool compress) { + extendData(); + const RdataFields::Type field_type = + compress ? RdataFields::COMPRESSIBLE_NAME : + RdataFields::INCOMPRESSIBLE_NAME; + // TODO: When we get rid of need for getBuffer, we can output the name + // to a buffer and then write the buffer inside + name.toWire(getBuffer()); + fields_.push_back(RdataFields::FieldSpec(field_type, + name.getLength())); + last_data_pos_ = getLength(); + } + + virtual void clear() { + isc_throw(Unexpected, "unexpected clear() for RdataFieldComposer"); + } + bool truncated_; + size_t length_limit_; + CompressMode mode_; + vector<RdataFields::FieldSpec> fields_; + vector<RdataFields::FieldSpec>& getFields() { + extendData(); + return (fields_); + } + // We use generic write* methods, with the exception of writeName. + // So new data can arrive without us knowing it, this considers all new + // data to be just data and extends the fields to take it into account. + size_t last_data_pos_; + void extendData() { + // No news, return to work + if (getLength() == last_data_pos_) { + return; + } + // The new bytes are just ordinary uninteresting data + if (fields_.empty() || fields_.back().type != RdataFields::DATA) { + fields_.push_back(RdataFields::FieldSpec(RdataFields::DATA, 0)); + } + // We added this much data from last time + fields_.back().len += getLength() - last_data_pos_; + last_data_pos_ = getLength(); + } +}; + +} + +RdataFields::RdataFields(const Rdata& rdata) { + RdataFieldComposer field_composer; + rdata.toWire(field_composer); + nfields_ = field_composer.getFields().size(); + data_length_ = field_composer.getLength(); + if (nfields_ > 0) { + assert(data_length_ > 0); + detail_ = new RdataFieldsDetail(field_composer.getFields(), + static_cast<const uint8_t*> + (field_composer.getData()), + field_composer.getLength()); + data_ = &detail_->allocated_data_[0]; + fields_ = &detail_->allocated_fields_[0]; + } else { + assert(data_length_ == 0); + detail_ = NULL; + data_ = NULL; + fields_ = NULL; + } +} + +RdataFields::RdataFields(const void* fields, const unsigned int fields_length, + const void* data, const size_t data_length) : + fields_(static_cast<const FieldSpec*>(fields)), + nfields_(fields_length / sizeof(*fields_)), + data_(static_cast<const uint8_t*>(data)), + data_length_(data_length), + detail_(NULL) +{ + if ((fields_ == NULL && nfields_ > 0) || + (fields_ != NULL && nfields_ == 0)) { + isc_throw(InvalidParameter, + "Inconsistent parameters for RdataFields: fields_length (" + << fields_length << ") and fields conflict each other"); + } + if ((data_ == NULL && data_length_ > 0) || + (data_ != NULL && data_length_ == 0)) { + isc_throw(InvalidParameter, + "Inconsistent parameters for RdataFields: data length (" + << data_length_ << ") and data conflict each other"); + } + + size_t total_length = 0; + for (unsigned int i = 0; i < nfields_; ++i) { + total_length += fields_[i].len; + } + if (total_length != data_length_) { + isc_throw(InvalidParameter, + "Inconsistent parameters for RdataFields: " + "fields len: " << total_length << + " data len: " << data_length_); + } +} + +RdataFields::~RdataFields() { + delete detail_; +} + +RdataFields::FieldSpec +RdataFields::getFieldSpec(const unsigned int field_id) const { + if (field_id >= nfields_) { + isc_throw(OutOfRange, "Rdata field ID is out of range: " << field_id); + } + return (fields_[field_id]); +} + +void +RdataFields::toWire(AbstractMessageRenderer& renderer) const { + size_t offset = 0; + + for (unsigned int i = 0; i < nfields_; ++i) { + if (fields_[i].type == DATA) { + renderer.writeData(data_ + offset, fields_[i].len); + } else { + // XXX: this is inefficient. Even if it's quite likely the + // data is a valid wire representation of a name we parse + // it to construct the Name object in the generic mode. + // This should be improved in a future version. + InputBuffer buffer(data_ + offset, fields_[i].len); + renderer.writeName(Name(buffer), + fields_[i].type == COMPRESSIBLE_NAME); + } + offset += fields_[i].len; + } +} + +void +RdataFields::toWire(OutputBuffer& buffer) const { + buffer.writeData(data_, data_length_); +} +} // end of namespace rdata +} // end of namespace dns +} // end of namespace isc |