summaryrefslogtreecommitdiffstats
path: root/src/lib/dns/rdatafields.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/dns/rdatafields.cc')
-rw-r--r--src/lib/dns/rdatafields.cc216
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