Branch data Line data Source code
1 : : // Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
2 : : //
3 : : // Permission to use, copy, modify, and/or distribute this software for any
4 : : // purpose with or without fee is hereby granted, provided that the above
5 : : // copyright notice and this permission notice appear in all copies.
6 : : //
7 : : // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
8 : : // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
9 : : // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
10 : : // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11 : : // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
12 : : // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13 : : // PERFORMANCE OF THIS SOFTWARE.
14 : :
15 : : #include <stdint.h>
16 : :
17 : : #include <cassert>
18 : : #include <vector>
19 : :
20 : : #include <exceptions/exceptions.h>
21 : :
22 : : #include <util/buffer.h>
23 : : #include <dns/messagerenderer.h>
24 : : #include <dns/name.h>
25 : : #include <dns/rdata.h>
26 : : #include <dns/rdatafields.h>
27 : :
28 : : using namespace std;
29 : : using namespace isc::dns;
30 : : using namespace isc::dns::rdata;
31 : : using isc::util::OutputBuffer;
32 : : using isc::util::InputBuffer;
33 : :
34 : : namespace isc {
35 : : namespace dns {
36 : : namespace rdata {
37 : :
38 : : /// This is a helper class for \c RdataFields.
39 : : ///
40 : : /// It manages a local storage for the data when \c RdataFields is constructed
41 : : /// from an \c Rdata.
42 : : /// To minimize construction overhead in the other case, an instance of
43 : : /// this class is instantiated only when necessary - we don't need the vectors
44 : : /// when only rendering.
45 : 12 : struct RdataFields::RdataFieldsDetail {
46 : : RdataFieldsDetail(const vector<FieldSpec>& fields,
47 : : const uint8_t* data, size_t data_length) :
48 : : allocated_fields_(fields),
49 : 12 : allocated_data_(data, data + data_length)
50 : : {}
51 : : const vector<FieldSpec> allocated_fields_;
52 : : const vector<uint8_t> allocated_data_;
53 : : };
54 : :
55 : : namespace {
56 : : // This class is used to divide the content of RDATA into \c RdataField
57 : : // fields via message rendering logic.
58 : : // The idea is to identify domain name fields in the writeName() method,
59 : : // and determine whether they are compressible using the "compress"
60 : : // parameter.
61 : : // Other types of data are simply copied into the internal buffer, and
62 : : // consecutive such fields are combined into a single \c RdataField field.
63 : : //
64 : : // Technically, this use of inheritance may be considered a violation of
65 : : // Liskov Substitution Principle in that it doesn't actually compress domain
66 : : // names, and some of the methods are not expected to be used.
67 : : // In fact, skip() or trim() may not be make much sense in this context.
68 : : // Nevertheless we keep this idea at the moment. Since the usage is limited
69 : : // (it's only used within this file, and only used with \c Rdata variants),
70 : : // it's hopefully an acceptable practice.
71 : : class RdataFieldComposer : public AbstractMessageRenderer {
72 : : public:
73 : : RdataFieldComposer() :
74 : : truncated_(false), length_limit_(65535),
75 : 8 : mode_(CASE_INSENSITIVE), last_data_pos_(0)
76 : : {}
77 : 8 : virtual ~RdataFieldComposer() {}
78 : 0 : virtual bool isTruncated() const { return (truncated_); }
79 : 0 : virtual size_t getLengthLimit() const { return (length_limit_); }
80 : 0 : virtual CompressMode getCompressMode() const { return (mode_); }
81 : 0 : virtual void setTruncated() { truncated_ = true; }
82 : 0 : virtual void setLengthLimit(size_t len) { length_limit_ = len; }
83 : 0 : virtual void setCompressMode(CompressMode mode) { mode_ = mode; }
84 : 3 : virtual void writeName(const Name& name, bool compress) {
85 : 3 : extendData();
86 : : const RdataFields::Type field_type =
87 : : compress ? RdataFields::COMPRESSIBLE_NAME :
88 [ + + ]: 3 : RdataFields::INCOMPRESSIBLE_NAME;
89 : : // TODO: When we get rid of need for getBuffer, we can output the name
90 : : // to a buffer and then write the buffer inside
91 : 3 : name.toWire(getBuffer());
92 : : fields_.push_back(RdataFields::FieldSpec(field_type,
93 : 3 : name.getLength()));
94 : 3 : last_data_pos_ = getLength();
95 : 3 : }
96 : :
97 : 1 : virtual void clear() {
98 [ + - ]: 2 : isc_throw(Unexpected, "unexpected clear() for RdataFieldComposer");
99 : : }
100 : : bool truncated_;
101 : : size_t length_limit_;
102 : : CompressMode mode_;
103 : : vector<RdataFields::FieldSpec> fields_;
104 : : vector<RdataFields::FieldSpec>& getFields() {
105 [ + - ][ + - ]: 13 : extendData();
106 : : return (fields_);
107 : : }
108 : : // We use generict write* methods, with the exception of writeName.
109 : : // So new data can arrive without us knowing it, this considers all new
110 : : // data to be just data and extends the fields to take it into account.
111 : : size_t last_data_pos_;
112 : 16 : void extendData() {
113 : : // No news, return to work
114 [ + + ]: 16 : if (getLength() == last_data_pos_) {
115 : 16 : return;
116 : : }
117 : : // The new bytes are just ordinary uninteresting data
118 [ + + ][ - + ]: 7 : if (fields_.empty() || fields_.back().type != RdataFields::DATA) {
[ + - ]
119 : 7 : fields_.push_back(RdataFields::FieldSpec(RdataFields::DATA, 0));
120 : : }
121 : : // We added this much data from last time
122 : 21 : fields_.back().len += getLength() - last_data_pos_;
123 : 7 : last_data_pos_ = getLength();
124 : : }
125 : : };
126 : :
127 : : }
128 : :
129 : 8 : RdataFields::RdataFields(const Rdata& rdata) {
130 : 8 : RdataFieldComposer field_composer;
131 [ + + ]: 8 : rdata.toWire(field_composer);
132 : 7 : nfields_ = field_composer.getFields().size();
133 : 14 : data_length_ = field_composer.getLength();
134 [ + + ]: 7 : if (nfields_ > 0) {
135 [ - + ]: 6 : assert(data_length_ > 0);
136 : : detail_ = new RdataFieldsDetail(field_composer.getFields(),
137 : : static_cast<const uint8_t*>
138 : 6 : (field_composer.getData()),
139 [ + - ]: 6 : field_composer.getLength());
140 : 12 : data_ = &detail_->allocated_data_[0];
141 : 6 : fields_ = &detail_->allocated_fields_[0];
142 : : } else {
143 [ - + ]: 1 : assert(data_length_ == 0);
144 : 1 : detail_ = NULL;
145 : 1 : data_ = NULL;
146 : 1 : fields_ = NULL;
147 : : }
148 : 7 : }
149 : :
150 : 12 : RdataFields::RdataFields(const void* fields, const unsigned int fields_length,
151 : : const void* data, const size_t data_length) :
152 : : fields_(static_cast<const FieldSpec*>(fields)),
153 : : nfields_(fields_length / sizeof(*fields_)),
154 : : data_(static_cast<const uint8_t*>(data)),
155 : : data_length_(data_length),
156 : 12 : detail_(NULL)
157 : : {
158 [ + + ][ + - ]: 12 : if ((fields_ == NULL && nfields_ > 0) ||
[ + + ][ + + ]
159 : : (fields_ != NULL && nfields_ == 0)) {
160 [ + - ][ + - ]: 12 : isc_throw(InvalidParameter,
161 : : "Inconsistent parameters for RdataFields: fields_length ("
162 : : << fields_length << ") and fields conflict each other");
163 : : }
164 [ + + ][ + - ]: 8 : if ((data_ == NULL && data_length_ > 0) ||
[ + + ][ + + ]
165 : : (data_ != NULL && data_length_ == 0)) {
166 [ + - ][ + - ]: 3 : isc_throw(InvalidParameter,
167 : : "Inconsistent parameters for RdataFields: data length ("
168 : : << data_length_ << ") and data conflict each other");
169 : : }
170 : :
171 : : size_t total_length = 0;
172 [ + + ]: 16 : for (unsigned int i = 0; i < nfields_; ++i) {
173 : 9 : total_length += fields_[i].len;
174 : : }
175 [ + + ]: 7 : if (total_length != data_length_) {
176 [ + - ][ + - ]: 4 : isc_throw(InvalidParameter,
177 : : "Inconsistent parameters for RdataFields; "
178 : : "fields len: " << total_length <<
179 : : " data len: " << data_length_);
180 : : }
181 : 6 : }
182 : :
183 : 13 : RdataFields::~RdataFields() {
184 [ + + ]: 19 : delete detail_;
185 : 13 : }
186 : :
187 : : RdataFields::FieldSpec
188 : 31 : RdataFields::getFieldSpec(const unsigned int field_id) const {
189 [ + + ]: 31 : if (field_id >= nfields_) {
190 [ + - ]: 3 : isc_throw(OutOfRange, "Rdata field ID is out of range: " << field_id);
191 : : }
192 : 30 : return (fields_[field_id]);
193 : : }
194 : :
195 : : void
196 : 11 : RdataFields::toWire(AbstractMessageRenderer& renderer) const {
197 : 11 : size_t offset = 0;
198 : :
199 [ + + ]: 26 : for (unsigned int i = 0; i < nfields_; ++i) {
200 [ + + ]: 15 : if (fields_[i].type == DATA) {
201 : 10 : renderer.writeData(data_ + offset, fields_[i].len);
202 : : } else {
203 : : // XXX: this is inefficient. Even if it's quite likely the
204 : : // data is a valid wire representation of a name we parse
205 : : // it to construct the Name object in the generic mode.
206 : : // This should be improved in a future version.
207 : 5 : InputBuffer buffer(data_ + offset, fields_[i].len);
208 : 5 : renderer.writeName(Name(buffer),
209 [ + - ]: 5 : fields_[i].type == COMPRESSIBLE_NAME);
210 : : }
211 : 15 : offset += fields_[i].len;
212 : : }
213 : 11 : }
214 : :
215 : : void
216 : 11 : RdataFields::toWire(OutputBuffer& buffer) const {
217 : 11 : buffer.writeData(data_, data_length_);
218 : 11 : }
219 : : } // end of namespace rdata
220 : : } // end of namespace dns
221 : 229 : } // end of namespace isc
|