Kea  1.5.0
labelsequence.cc
Go to the documentation of this file.
1 // Copyright (C) 2012-2018 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 <dns/labelsequence.h>
10 #include <dns/name_internal.h>
11 #include <exceptions/exceptions.h>
12 
13 #include <boost/functional/hash.hpp>
14 
15 #include <cstring>
16 
17 namespace isc {
18 namespace dns {
19 
20 LabelSequence::LabelSequence(const void* buf) {
21 #ifdef ENABLE_DEBUG
22  // In non-debug mode, dereferencing the NULL pointer further below
23  // will lead to a crash, so disabling this check is not
24  // unsafe. Except for a programming mistake, this case should not
25  // happen.
26  if (buf == NULL) {
28  "Null pointer passed to LabelSequence constructor");
29  }
30 #endif
31 
32  const uint8_t* bp = reinterpret_cast<const uint8_t*>(buf);
33  first_label_ = 0;
34  const uint8_t offsets_len = *bp++;
35 
36 #ifdef ENABLE_DEBUG
37  if (offsets_len == 0 || offsets_len > Name::MAX_LABELS) {
39  "Bad offsets len in serialized LabelSequence data: "
40  << static_cast<unsigned int>(offsets_len));
41  }
42 #endif
43 
44  last_label_ = offsets_len - 1;
45  offsets_ = bp;
46  data_ = bp + offsets_len;
47 
48 #ifdef ENABLE_DEBUG
49  // Check the integrity on the offsets and the name data
50  const uint8_t* dp = data_;
51  for (size_t cur_offset = 0; cur_offset < offsets_len; ++cur_offset) {
52  if (dp - data_ != offsets_[cur_offset] || *dp > Name::MAX_LABELLEN) {
54  "Broken offset or name data in serialized "
55  "LabelSequence data");
56  }
57  dp += (1 + *dp);
58  }
59 #endif
60 }
61 
63  uint8_t buf[MAX_SERIALIZED_LENGTH])
64 {
65  size_t data_len;
66  const uint8_t *data = src.getData(&data_len);
67  std::memcpy(buf, data, data_len);
68 
69  for (size_t i = 0; i < src.getLabelCount(); ++i) {
70  buf[Name::MAX_WIRE + i] = src.offsets_[i + src.first_label_] -
71  src.offsets_[src.first_label_];
72  }
73 
74  first_label_ = 0;
75  last_label_ = src.last_label_ - src.first_label_;
76  data_ = buf;
77  offsets_ = &buf[Name::MAX_WIRE];
78 }
79 
80 
81 const uint8_t*
82 LabelSequence::getData(size_t *len) const {
83  *len = getDataLength();
84  return (&data_[offsets_[first_label_]]);
85 }
86 
87 size_t
89  const size_t last_label_len = data_[offsets_[last_label_]] + 1;
90  return (offsets_[last_label_] - offsets_[first_label_] + last_label_len);
91 }
92 
93 size_t
95  return (1 + getLabelCount() + getDataLength());
96 }
97 
98 namespace {
99 // Check if buf is not in the range of [bp, ep), which means
100 // - end of buffer is before bp, or
101 // - beginning of buffer is on or after ep
102 bool
103 isOutOfRange(const uint8_t* bp, const uint8_t* ep,
104  const uint8_t* buf, size_t buf_len)
105 {
106  return (bp >= buf + buf_len || // end of buffer is before bp
107  ep <= buf); // beginning of buffer is on or after ep
108 }
109 }
110 
111 void
112 LabelSequence::serialize(void* buf, size_t buf_len) const {
113  const size_t expected_size = getSerializedLength();
114  if (expected_size > buf_len) {
115  isc_throw(BadValue, "buffer too short for LabelSequence::serialize");
116  }
117 
118  const size_t offsets_len = getLabelCount();
119  assert(offsets_len < 256); // should be in the 8-bit range
120 
121  // Overridden check. Buffer shouldn't overwrap the offset of name data
122  // regions.
123  uint8_t* bp = reinterpret_cast<uint8_t*>(buf);
124  const size_t ndata_len = getDataLength();
125  if (!isOutOfRange(offsets_, offsets_ + offsets_len, bp, buf_len) ||
126  !isOutOfRange(data_, data_ + ndata_len, bp, buf_len)) {
127  isc_throw(BadValue, "serialize would break the source sequence");
128  }
129 
130  *bp++ = offsets_len;
131  for (size_t i = 0; i < offsets_len; ++i) {
132  *bp++ = offsets_[first_label_ + i] - offsets_[first_label_];
133  }
134  std::memcpy(bp, &data_[offsets_[first_label_]], ndata_len);
135  bp += ndata_len;
136 
137  assert(bp - reinterpret_cast<const uint8_t*>(buf) == expected_size);
138 }
139 
140 bool
141 LabelSequence::equals(const LabelSequence& other, bool case_sensitive) const {
142  size_t len, other_len;
143  const uint8_t* data = getData(&len);
144  const uint8_t* other_data = other.getData(&other_len);
145 
146  if (len != other_len) {
147  return (false);
148  }
149  if (case_sensitive) {
150  return (std::memcmp(data, other_data, len) == 0);
151  }
152 
153  // As long as the data was originally validated as (part of) a name,
154  // label length must never be a capital ascii character, so we can
155  // simply compare them after converting to lower characters.
156  for (size_t i = 0; i < len; ++i) {
157  const uint8_t ch = data[i];
158  const uint8_t other_ch = other_data[i];
161  return (false);
162  }
163  }
164  return (true);
165 }
166 
169  bool case_sensitive) const
170 {
171  // Determine the relative ordering under the DNSSEC order relation of
172  // 'this' and 'other', and also determine the hierarchical relationship
173  // of the labels.
174 
175  unsigned int nlabels = 0;
176  int l1 = getLabelCount();
177  int l2 = other.getLabelCount();
178  const int ldiff = static_cast<int>(l1) - static_cast<int>(l2);
179  unsigned int l = (ldiff < 0) ? l1 : l2;
180 
181  while (l > 0) {
182  --l;
183  --l1;
184  --l2;
185  size_t pos1 = offsets_[l1 + first_label_];
186  size_t pos2 = other.offsets_[l2 + other.first_label_];
187  unsigned int count1 = data_[pos1++];
188  unsigned int count2 = other.data_[pos2++];
189 
190  // We don't support any extended label types including now-obsolete
191  // bitstring labels.
192  assert(count1 <= Name::MAX_LABELLEN && count2 <= Name::MAX_LABELLEN);
193 
194  const int cdiff = static_cast<int>(count1) - static_cast<int>(count2);
195  unsigned int count = (cdiff < 0) ? count1 : count2;
196 
197  while (count > 0) {
198  const uint8_t label1 = data_[pos1];
199  const uint8_t label2 = other.data_[pos2];
200  int chdiff;
201 
202  if (case_sensitive) {
203  chdiff = static_cast<int>(label1) - static_cast<int>(label2);
204  } else {
205  chdiff = static_cast<int>(
207  static_cast<int>(
209  }
210 
211  if (chdiff != 0) {
212  return (NameComparisonResult(
213  chdiff, nlabels,
214  nlabels == 0 ? NameComparisonResult::NONE :
216  }
217  --count;
218  ++pos1;
219  ++pos2;
220  }
221  if (cdiff != 0) {
222  return (NameComparisonResult(
223  cdiff, nlabels,
224  nlabels == 0 ? NameComparisonResult::NONE :
226  }
227  ++nlabels;
228  }
229 
230  if (ldiff < 0) {
231  return (NameComparisonResult(ldiff, nlabels,
233  } else if (ldiff > 0) {
234  return (NameComparisonResult(ldiff, nlabels,
236  }
237 
238  return (NameComparisonResult(ldiff, nlabels, NameComparisonResult::EQUAL));
239 }
240 
241 void
243  if (i >= getLabelCount()) {
244  isc_throw(OutOfRange, "Cannot strip to zero or less labels; " << i <<
245  " (labelcount: " << getLabelCount() << ")");
246  }
247  first_label_ += i;
248 }
249 
250 void
252  if (i >= getLabelCount()) {
253  isc_throw(OutOfRange, "Cannot strip to zero or less labels; " << i <<
254  " (labelcount: " << getLabelCount() << ")");
255  }
256  last_label_ -= i;
257 }
258 
259 bool
261  return (data_[offsets_[last_label_]] == 0);
262 }
263 
264 size_t
265 LabelSequence::getHash(bool case_sensitive) const {
266  size_t length;
267  const uint8_t* s = getData(&length);
268  if (length > 16) {
269  length = 16;
270  }
271 
272  size_t hash_val = 0;
273  while (length > 0) {
274  const uint8_t c = *s++;
275  boost::hash_combine(hash_val, case_sensitive ? c :
277  --length;
278  }
279  return (hash_val);
280 }
281 
282 std::string
283 LabelSequence::toRawText(bool omit_final_dot) const {
284  const uint8_t* np = &data_[offsets_[first_label_]];
285  const uint8_t* np_end = np + getDataLength();
286 
287  // use for integrity check
288  unsigned int labels = getLabelCount();
289  // init with an impossible value to catch error cases in the end:
290  unsigned int count = Name::MAX_LABELLEN + 1;
291 
292  // result string: it will roughly have the same length as the wire format
293  // label sequence data. reserve that length to minimize reallocation.
294  std::string result;
295  result.reserve(getDataLength());
296 
297  while (np != np_end) {
298  labels--;
299  count = *np++;
300 
301  if (count == 0) {
302  // We've reached the "final dot". If we've not dumped any
303  // character, the entire label sequence is the root name.
304  // In that case we don't omit the final dot.
305  if (!omit_final_dot || result.empty()) {
306  result.push_back('.');
307  }
308  break;
309  }
310 
311  if (count <= Name::MAX_LABELLEN) {
312  assert(np_end - np >= count);
313 
314  if (!result.empty()) {
315  // just after a non-empty label. add a separating dot.
316  result.push_back('.');
317  }
318 
319  while (count-- > 0) {
320  const uint8_t c = *np++;
321  result.push_back(c);
322  }
323  } else {
324  isc_throw(BadLabelType, "unknown label type in name data");
325  }
326  }
327 
328  // We should be at the end of the data and have consumed all labels.
329  assert(np == np_end);
330  assert(labels == 0);
331 
332  return (result);
333 }
334 
335 
336 std::string
337 LabelSequence::toText(bool omit_final_dot) const {
338  const uint8_t* np = &data_[offsets_[first_label_]];
339  const uint8_t* np_end = np + getDataLength();
340 
341  // use for integrity check
342  unsigned int labels = getLabelCount();
343  // init with an impossible value to catch error cases in the end:
344  unsigned int count = Name::MAX_LABELLEN + 1;
345 
346  // result string: it will roughly have the same length as the wire format
347  // label sequence data. reserve that length to minimize reallocation.
348  std::string result;
349  result.reserve(getDataLength());
350 
351  while (np != np_end) {
352  labels--;
353  count = *np++;
354 
355  if (count == 0) {
356  // We've reached the "final dot". If we've not dumped any
357  // character, the entire label sequence is the root name.
358  // In that case we don't omit the final dot.
359  if (!omit_final_dot || result.empty()) {
360  result.push_back('.');
361  }
362  break;
363  }
364 
365  if (count <= Name::MAX_LABELLEN) {
366  assert(np_end - np >= count);
367 
368  if (!result.empty()) {
369  // just after a non-empty label. add a separating dot.
370  result.push_back('.');
371  }
372 
373  while (count-- > 0) {
374  const uint8_t c = *np++;
375  switch (c) {
376  case 0x22: // '"'
377  case 0x28: // '('
378  case 0x29: // ')'
379  case 0x2E: // '.'
380  case 0x3B: // ';'
381  case 0x5C: // '\\'
382  // Special modifiers in zone files.
383  case 0x40: // '@'
384  case 0x24: // '$'
385  result.push_back('\\');
386  result.push_back(c);
387  break;
388  default:
389  if (c > 0x20 && c < 0x7f) {
390  // append printable characters intact
391  result.push_back(c);
392  } else {
393  // encode non-printable characters in the form of \DDD
394  result.push_back(0x5c);
395  result.push_back(0x30 + ((c / 100) % 10));
396  result.push_back(0x30 + ((c / 10) % 10));
397  result.push_back(0x30 + (c % 10));
398  }
399  }
400  }
401  } else {
402  isc_throw(BadLabelType, "unknown label type in name data");
403  }
404  }
405 
406  // We should be at the end of the data and have consumed all labels.
407  assert(np == np_end);
408  assert(labels == 0);
409 
410  return (result);
411 }
412 
413 std::string
415  return (toText(!isAbsolute()));
416 }
417 
418 void
420  uint8_t buf[MAX_SERIALIZED_LENGTH])
421 {
422  // collect data to perform steps before anything is changed
423  size_t label_count = last_label_ + 1;
424  // Since we may have been stripped, do not use getDataLength(), but
425  // calculate actual data size this labelsequence currently uses
426  size_t data_pos = offsets_[last_label_] + data_[offsets_[last_label_]] + 1;
427 
428  // If this labelsequence is absolute, virtually strip the root label.
429  if (isAbsolute()) {
430  data_pos--;
431  label_count--;
432  }
433  const size_t append_label_count = labels.getLabelCount();
434  size_t data_len;
435  const uint8_t *data = labels.getData(&data_len);
436 
437  // Sanity checks
438  if (data_ != buf || offsets_ != &buf[Name::MAX_WIRE]) {
440  "extend() called with unrelated buffer");
441  }
442  // Check MAX_LABELS before MAX_WIRE or it will be never reached
443  if (label_count + append_label_count > Name::MAX_LABELS) {
445  "extend() would exceed maximum number of labels");
446  }
447  if (data_pos + data_len > Name::MAX_WIRE) {
449  "extend() would exceed maximum wire length");
450  }
451 
452  // All seems to be reasonably ok, let's proceed.
453  std::memmove(&buf[data_pos], data, data_len);
454 
455  for (size_t i = 0; i < append_label_count; ++i) {
456  buf[Name::MAX_WIRE + label_count + i] =
457  data_pos +
458  labels.offsets_[i + labels.first_label_] -
459  labels.offsets_[labels.first_label_];
460  }
461  last_label_ = label_count + append_label_count - 1;
462 }
463 
464 std::ostream&
465 operator<<(std::ostream& os, const LabelSequence& label_sequence) {
466  os << label_sequence.toText();
467  return (os);
468 }
469 
470 } // end namespace dns
471 } // end namespace isc
LabelSequence(const Name &name)
Constructs a LabelSequence for the given name.
Definition: labelsequence.h:64
ostream & operator<<(std::ostream &os, const EDNS &edns)
Insert the EDNS as a string into stream.
Definition: edns.cc:172
size_t getLabelCount() const
Returns the current number of labels for this LabelSequence.
void serialize(void *buf, size_t buf_len) const
Serialize the LabelSequence object in to a buffer.
size_t getHash(bool case_sensitive) const
Calculate a simple hash for the label sequence.
size_t getDataLength() const
Return the length of the wire-format data of this LabelSequence.
This is a supplemental class used only as a return value of Name::compare() and LabelSequence::compar...
Definition: name.h:117
void extend(const LabelSequence &labels, uint8_t buf[MAX_SERIALIZED_LENGTH])
Extend this LabelSequence with the given labelsequence.
static const size_t MAX_WIRE
Max allowable length of domain names.
Definition: name.h:699
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
const uint8_t * getData(size_t *len) const
Return the wire-format data for this LabelSequence.
void stripRight(size_t i)
Remove labels from the end of this LabelSequence.
std::string toText() const
Convert the LabelSequence to a string.
NameComparisonResult compare(const LabelSequence &other, bool case_sensitive=false) const
Compares two label sequences.
A standard DNS module exception that is thrown if the name parser encounters an obsolete or incomplet...
Definition: name.h:62
Defines the logger used by the top-level component of kea-dhcp-ddns.
bool equals(const LabelSequence &other, bool case_sensitive=false) const
Compares two label sequences for equality.
size_t getSerializedLength() const
Return the size of serialized image of the LabelSequence.
std::string toRawText(bool omit_final_dot) const
Convert the LabelSequence to a string without escape sequences.
static const size_t MAX_LABELLEN
Max allowable length of labels of a domain name.
Definition: name.h:708
static const size_t MAX_LABELS
Max allowable labels of domain names.
Definition: name.h:705
A generic exception that is thrown if a parameter given to a method would refer to or modify out-of-r...
bool isAbsolute() const
Checks whether the label sequence is absolute.
void stripLeft(size_t i)
Remove labels from the front of this LabelSequence.
Light-weight Accessor to Name data.
Definition: labelsequence.h:35
const uint8_t maptolower[]
Definition: name.cc:73