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