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>
12
13#include <boost/functional/hash.hpp>
14
15#include <cstring>
16
17namespace isc {
18namespace dns {
19
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
81const uint8_t*
82LabelSequence::getData(size_t *len) const {
83 *len = getDataLength();
84 return (&data_[offsets_[first_label_]]);
85}
86
87size_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
93size_t
95 return (1 + getLabelCount() + getDataLength());
96}
97
98namespace {
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
102bool
103isOutOfRange(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
111void
112LabelSequence::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
140bool
141LabelSequence::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
241void
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
250void
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
259bool
261 return (data_[offsets_[last_label_]] == 0);
262}
263
264size_t
265LabelSequence::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
282std::string
283LabelSequence::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
336std::string
337LabelSequence::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
413std::string
415 return (toText(!isAbsolute()));
416}
417
418void
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
464std::ostream&
465operator<<(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
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
A generic exception that is thrown if a parameter given to a method would refer to or modify out-of-r...
A standard DNS module exception that is thrown if the name parser encounters an obsolete or incomplet...
Definition: name.h:62
Light-weight Accessor to Name data.
Definition: labelsequence.h:35
std::string toRawText(bool omit_final_dot) const
Convert the LabelSequence to a string without escape sequences.
size_t getHash(bool case_sensitive) const
Calculate a simple hash for the label sequence.
void serialize(void *buf, size_t buf_len) const
Serialize the LabelSequence object in to a buffer.
NameComparisonResult compare(const LabelSequence &other, bool case_sensitive=false) const
Compares two label sequences.
bool isAbsolute() const
Checks whether the label sequence is absolute.
std::string toText() const
Convert the LabelSequence to a string.
LabelSequence(const Name &name)
Constructs a LabelSequence for the given name.
Definition: labelsequence.h:64
bool equals(const LabelSequence &other, bool case_sensitive=false) const
Compares two label sequences for equality.
size_t getLabelCount() const
Returns the current number of labels for this LabelSequence.
size_t getSerializedLength() const
Return the size of serialized image of the LabelSequence.
void stripLeft(size_t i)
Remove labels from the front of this LabelSequence.
const uint8_t * getData(size_t *len) const
Return the wire-format data for this LabelSequence.
friend std::string Name::toText(bool) const
void stripRight(size_t i)
Remove labels from the end of this LabelSequence.
size_t getDataLength() const
Return the length of the wire-format data of this LabelSequence.
void extend(const LabelSequence &labels, uint8_t buf[MAX_SERIALIZED_LENGTH])
Extend this LabelSequence with the given labelsequence.
This is a supplemental class used only as a return value of Name::compare() and LabelSequence::compar...
Definition: name.h:117
static const size_t MAX_LABELLEN
Max allowable length of labels of a domain name.
Definition: name.h:708
static const size_t MAX_WIRE
Max allowable length of domain names.
Definition: name.h:699
static const size_t MAX_LABELS
Max allowable labels of domain names.
Definition: name.h:705
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
const uint8_t maptolower[]
Definition: name.cc:73
ostream & operator<<(std::ostream &os, const EDNS &edns)
Insert the EDNS as a string into stream.
Definition: edns.cc:172
Defines the logger used by the top-level component of kea-dhcp-ddns.