summaryrefslogtreecommitdiffstats
path: root/src/lib/dns/rdatafields.cc
blob: e02ec8ff87c59bd70e94ae28378b1c68d68ec9a0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
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