// 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 #include #include #include #include #include #include #include #include #include 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& fields, const uint8_t* data, size_t data_length) : allocated_fields_(fields), allocated_data_(data, data + data_length) {} const vector allocated_fields_; const vector 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 fields_; vector& 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 (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(fields)), nfields_(fields_length / sizeof(*fields_)), data_(static_cast(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