Kea  2.1.7-git
rdatafields.cc
Go to the documentation of this file.
1 // Copyright (C) 2010-2015,2017 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
9 #include <stdint.h>
10 
11 #include <cassert>
12 #include <vector>
13 
14 #include <exceptions/exceptions.h>
15 
16 #include <util/buffer.h>
17 #include <dns/messagerenderer.h>
18 #include <dns/name.h>
19 #include <dns/rdata.h>
20 #include <dns/rdatafields.h>
21 
22 using namespace std;
23 using namespace isc::dns;
24 using namespace isc::dns::rdata;
27 
28 namespace isc {
29 namespace dns {
30 namespace rdata {
31 
40  RdataFieldsDetail(const vector<FieldSpec>& fields,
41  const uint8_t* data, size_t data_length) :
42  allocated_fields_(fields),
43  allocated_data_(data, data + data_length)
44  {}
45  const vector<FieldSpec> allocated_fields_;
46  const vector<uint8_t> allocated_data_;
47 };
48 
49 namespace {
50 // This class is used to divide the content of RDATA into \c RdataField
51 // fields via message rendering logic.
52 // The idea is to identify domain name fields in the writeName() method,
53 // and determine whether they are compressible using the "compress"
54 // parameter.
55 // Other types of data are simply copied into the internal buffer, and
56 // consecutive such fields are combined into a single \c RdataField field.
57 //
58 // Technically, this use of inheritance may be considered a violation of
59 // Liskov Substitution Principle in that it doesn't actually compress domain
60 // names, and some of the methods are not expected to be used.
61 // In fact, skip() or trim() may not be make much sense in this context.
62 // Nevertheless we keep this idea at the moment. Since the usage is limited
63 // (it's only used within this file, and only used with \c Rdata variants),
64 // it's hopefully an acceptable practice.
65 class RdataFieldComposer : public AbstractMessageRenderer {
66 public:
67  RdataFieldComposer() :
68  truncated_(false), length_limit_(65535),
69  mode_(CASE_INSENSITIVE), last_data_pos_(0)
70  {}
71  virtual ~RdataFieldComposer() {}
72  virtual bool isTruncated() const { return (truncated_); }
73  virtual size_t getLengthLimit() const { return (length_limit_); }
74  virtual CompressMode getCompressMode() const { return (mode_); }
75  virtual void setTruncated() { truncated_ = true; }
76  virtual void setLengthLimit(size_t len) { length_limit_ = len; }
77  virtual void setCompressMode(CompressMode mode) { mode_ = mode; }
78  virtual void writeName(const LabelSequence&, bool) {}
79  virtual void writeName(const Name& name, bool compress) {
80  extendData();
81  const RdataFields::Type field_type =
82  compress ? RdataFields::COMPRESSIBLE_NAME :
83  RdataFields::INCOMPRESSIBLE_NAME;
84  // TODO: When we get rid of need for getBuffer, we can output the name
85  // to a buffer and then write the buffer inside
86  name.toWire(getBuffer());
87  fields_.push_back(RdataFields::FieldSpec(field_type,
88  name.getLength()));
89  last_data_pos_ = getLength();
90  }
91 
92  virtual void clear() {
93  isc_throw(Unexpected, "unexpected clear() for RdataFieldComposer");
94  }
95  bool truncated_;
96  size_t length_limit_;
97  CompressMode mode_;
98  vector<RdataFields::FieldSpec> fields_;
99  vector<RdataFields::FieldSpec>& getFields() {
100  extendData();
101  return (fields_);
102  }
103  // We use generic write* methods, with the exception of writeName.
104  // So new data can arrive without us knowing it, this considers all new
105  // data to be just data and extends the fields to take it into account.
107  void extendData() {
108  // No news, return to work
109  if (getLength() == last_data_pos_) {
110  return;
111  }
112  // The new bytes are just ordinary uninteresting data
113  if (fields_.empty() || fields_.back().type != RdataFields::DATA) {
114  fields_.push_back(RdataFields::FieldSpec(RdataFields::DATA, 0));
115  }
116  // We added this much data from last time
117  fields_.back().len += getLength() - last_data_pos_;
118  last_data_pos_ = getLength();
119  }
120 };
121 
122 }
123 
124 RdataFields::RdataFields(const Rdata& rdata) {
125  RdataFieldComposer field_composer;
126  rdata.toWire(field_composer);
127  nfields_ = field_composer.getFields().size();
128  data_length_ = field_composer.getLength();
129  if (nfields_ > 0) {
130  assert(data_length_ > 0);
131  detail_ = new RdataFieldsDetail(field_composer.getFields(),
132  static_cast<const uint8_t*>
133  (field_composer.getData()),
134  field_composer.getLength());
135  data_ = &detail_->allocated_data_[0];
136  fields_ = &detail_->allocated_fields_[0];
137  } else {
138  assert(data_length_ == 0);
139  detail_ = NULL;
140  data_ = NULL;
141  fields_ = NULL;
142  }
143 }
144 
145 RdataFields::RdataFields(const void* fields, const unsigned int fields_length,
146  const void* data, const size_t data_length) :
147  fields_(static_cast<const FieldSpec*>(fields)),
148  nfields_(fields_length / sizeof(*fields_)),
149  data_(static_cast<const uint8_t*>(data)),
150  data_length_(data_length),
151  detail_(NULL)
152 {
153  if ((fields_ == NULL && nfields_ > 0) ||
154  (fields_ != NULL && nfields_ == 0)) {
156  "Inconsistent parameters for RdataFields: fields_length ("
157  << fields_length << ") and fields conflict each other");
158  }
159  if ((data_ == NULL && data_length_ > 0) ||
160  (data_ != NULL && data_length_ == 0)) {
162  "Inconsistent parameters for RdataFields: data length ("
163  << data_length_ << ") and data conflict each other");
164  }
165 
166  size_t total_length = 0;
167  for (unsigned int i = 0; i < nfields_; ++i) {
168  total_length += fields_[i].len;
169  }
170  if (total_length != data_length_) {
172  "Inconsistent parameters for RdataFields: "
173  "fields len: " << total_length <<
174  " data len: " << data_length_);
175  }
176 }
177 
179  delete detail_;
180 }
181 
183 RdataFields::getFieldSpec(const unsigned int field_id) const {
184  if (field_id >= nfields_) {
185  isc_throw(OutOfRange, "Rdata field ID is out of range: " << field_id);
186  }
187  return (fields_[field_id]);
188 }
189 
190 void
192  size_t offset = 0;
193 
194  for (unsigned int i = 0; i < nfields_; ++i) {
195  if (fields_[i].type == DATA) {
196  renderer.writeData(data_ + offset, fields_[i].len);
197  } else {
198  // XXX: this is inefficient. Even if it's quite likely the
199  // data is a valid wire representation of a name we parse
200  // it to construct the Name object in the generic mode.
201  // This should be improved in a future version.
202  InputBuffer buffer(data_ + offset, fields_[i].len);
203  renderer.writeName(Name(buffer),
204  fields_[i].type == COMPRESSIBLE_NAME);
205  }
206  offset += fields_[i].len;
207  }
208 }
209 
210 void
212  buffer.writeData(data_, data_length_);
213 }
214 } // end of namespace rdata
215 } // end of namespace dns
216 } // end of namespace isc
The Name class encapsulates DNS names.
Definition: name.h:223
virtual void writeName(const Name &name, bool compress=true)=0
Write a Name object into the internal buffer in wire format, with or without name compression...
CompressMode mode_
Definition: rdatafields.cc:97
Type
Types of RdataFields fields.
Definition: rdatafields.h:159
A generic exception that is thrown if a parameter given to a method or function is considered invalid...
The Rdata class is an abstract base class that provides a set of common interfaces to manipulate conc...
Definition: rdata.h:123
STL namespace.
size_t length_limit_
Definition: rdatafields.cc:96
virtual void toWire(isc::util::OutputBuffer &buffer) const =0
Render the Rdata in the wire format into a buffer.
void writeData(const void *data, size_t len)
Copy an arbitrary length of data into the buffer.
Definition: buffer.h:550
size_t last_data_pos_
Definition: rdatafields.cc:106
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
The AbstractMessageRenderer class is an abstract base class that provides common interfaces for rende...
Structure that specifies a single RdataFields field.
Definition: rdatafields.h:206
A generic exception that is thrown when an unexpected error condition occurs.
void writeData(const void *data, size_t len)
Copy an arbitrary length of data into the internal buffer of the renderer object. ...
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
size_t getLength() const
Gets the length of the Name in its wire format.
Definition: name.h:360
void toWire(AbstractMessageRenderer &renderer) const
Render the Name in the wire format with compression.
Definition: name.cc:502
Defines the logger used by the top-level component of kea-lfc.
void toWire(AbstractMessageRenderer &renderer) const
Render the RdataFields in the wire format with name compression.
Definition: rdatafields.cc:191
FieldSpec getFieldSpec(const unsigned int field_id) const
Return the specification of the field identified by the given index.
Definition: rdatafields.cc:183
bool truncated_
Definition: rdatafields.cc:95
A domain name subject to name compression.
Definition: rdatafields.h:161
vector< RdataFields::FieldSpec > fields_
Definition: rdatafields.cc:98
RdataFieldsDetail(const vector< FieldSpec > &fields, const uint8_t *data, size_t data_length)
Definition: rdatafields.cc:40
The InputBuffer class is a buffer abstraction for manipulating read-only data.
Definition: buffer.h:81
A generic exception that is thrown if a parameter given to a method would refer to or modify out-of-r...
uint16_t len
The length of the field in bytes.
Definition: rdatafields.h:211
~RdataFields()
The destructor.
Definition: rdatafields.cc:178
Light-weight Accessor to Name data.
Definition: labelsequence.h:35
This is a helper class for RdataFields.
Definition: rdatafields.cc:39