rdatafields.cc 7.76 KB
Newer Older
1
// Copyright (C) 2010-2015,2017 Internet Systems Consortium, Inc. ("ISC")
2
//
3 4 5
// 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/.
6 7 8 9 10 11 12 13

#include <stdint.h>

#include <cassert>
#include <vector>

#include <exceptions/exceptions.h>

14
#include <util/buffer.h>
15 16 17 18 19 20 21 22
#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;
23 24
using isc::util::OutputBuffer;
using isc::util::InputBuffer;
25 26 27 28 29 30 31 32 33 34

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
35 36
/// this class is instantiated only when necessary - we don't need the vectors
/// when only rendering.
37 38
struct RdataFields::RdataFieldsDetail {
    RdataFieldsDetail(const vector<FieldSpec>& fields,
39
                      const uint8_t* data, size_t data_length) :
40
        allocated_fields_(fields),
41
        allocated_data_(data, data + data_length)
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
    {}
    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.
59
// In fact, skip() or trim() may not be make much sense in this context.
60 61 62 63 64
// 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:
65
    RdataFieldComposer() :
66 67
        truncated_(false), length_limit_(65535),
        mode_(CASE_INSENSITIVE), last_data_pos_(0)
68 69 70 71 72 73 74 75
    {}
    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; }
76
    virtual void writeName(const LabelSequence&, bool) {}
77
    virtual void writeName(const Name& name, bool compress) {
78
        extendData();
79 80 81
        const RdataFields::Type field_type =
            compress ? RdataFields::COMPRESSIBLE_NAME :
            RdataFields::INCOMPRESSIBLE_NAME;
82 83
        // TODO: When we get rid of need for getBuffer, we can output the name
        // to a buffer and then write the buffer inside
84
        name.toWire(getBuffer());
85 86
        fields_.push_back(RdataFields::FieldSpec(field_type,
                                                 name.getLength()));
87
        last_data_pos_ = getLength();
88
    }
89 90 91 92 93 94 95 96

    virtual void clear() {
        isc_throw(Unexpected, "unexpected clear() for RdataFieldComposer");
    }
    bool truncated_;
    size_t length_limit_;
    CompressMode mode_;
    vector<RdataFields::FieldSpec> fields_;
97 98 99 100
    vector<RdataFields::FieldSpec>& getFields() {
        extendData();
        return (fields_);
    }
101
    // We use generic write* methods, with the exception of writeName.
102
    // So new data can arrive without us knowing it, this considers all new
103 104 105 106
    // 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
107
        if (getLength() == last_data_pos_) {
108 109 110 111 112 113 114
            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
115 116
        fields_.back().len += getLength() - last_data_pos_;
        last_data_pos_ = getLength();
117
    }
118 119 120 121 122
};

}

RdataFields::RdataFields(const Rdata& rdata) {
123
    RdataFieldComposer field_composer;
124
    rdata.toWire(field_composer);
125
    nfields_ = field_composer.getFields().size();
126 127 128
    data_length_ = field_composer.getLength();
    if (nfields_ > 0) {
        assert(data_length_ > 0);
129
        detail_ = new RdataFieldsDetail(field_composer.getFields(),
130 131
                                        static_cast<const uint8_t*>
                                        (field_composer.getData()),
132 133 134 135 136 137 138 139 140 141 142
                                        field_composer.getLength());
        data_ = &detail_->allocated_data_[0];
        fields_ = &detail_->allocated_fields_[0];
    } else {
        assert(data_length_ == 0);
        detail_ = NULL;
        data_ = NULL;
        fields_ = NULL;
    }
}

143 144 145 146 147 148
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),
149 150 151 152 153
    detail_(NULL)
{
    if ((fields_ == NULL && nfields_ > 0) ||
        (fields_ != NULL && nfields_ == 0)) {
        isc_throw(InvalidParameter,
154 155
                  "Inconsistent parameters for RdataFields: fields_length ("
                  << fields_length << ") and fields conflict each other");
156 157 158 159 160 161 162 163 164
    }
    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;
165
    for (unsigned int i = 0; i < nfields_; ++i) {
166 167 168 169
        total_length += fields_[i].len;
    }
    if (total_length != data_length_) {
        isc_throw(InvalidParameter,
Francis Dupont's avatar
Francis Dupont committed
170
                  "Inconsistent parameters for RdataFields: "
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
                  "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;

192
    for (unsigned int i = 0; i < nfields_; ++i) {
193 194 195 196 197 198 199 200 201
        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),
202
                               fields_[i].type == COMPRESSIBLE_NAME);
203
        }
204 205 206 207 208 209 210 211 212 213 214
        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