summaryrefslogtreecommitdiffstats
path: root/src/lib/dns/labelsequence.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/dns/labelsequence.cc')
-rw-r--r--src/lib/dns/labelsequence.cc472
1 files changed, 472 insertions, 0 deletions
diff --git a/src/lib/dns/labelsequence.cc b/src/lib/dns/labelsequence.cc
new file mode 100644
index 0000000..8e0be43
--- /dev/null
+++ b/src/lib/dns/labelsequence.cc
@@ -0,0 +1,472 @@
+// Copyright (C) 2012-2019 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <dns/labelsequence.h>
+#include <dns/name_internal.h>
+#include <exceptions/exceptions.h>
+#include <exceptions/isc_assert.h>
+
+#include <boost/functional/hash.hpp>
+
+#include <cstring>
+
+namespace isc {
+namespace dns {
+
+LabelSequence::LabelSequence(const void* buf) {
+#ifdef ENABLE_DEBUG
+ // In non-debug mode, dereferencing the NULL pointer further below
+ // will lead to a crash, so disabling this check is not
+ // unsafe. Except for a programming mistake, this case should not
+ // happen.
+ if (buf == NULL) {
+ isc_throw(BadValue,
+ "Null pointer passed to LabelSequence constructor");
+ }
+#endif
+
+ const uint8_t* bp = reinterpret_cast<const uint8_t*>(buf);
+ first_label_ = 0;
+ const uint8_t offsets_len = *bp++;
+
+#ifdef ENABLE_DEBUG
+ if (offsets_len == 0 || offsets_len > Name::MAX_LABELS) {
+ isc_throw(BadValue,
+ "Bad offsets len in serialized LabelSequence data: "
+ << static_cast<unsigned int>(offsets_len));
+ }
+#endif
+
+ last_label_ = offsets_len - 1;
+ offsets_ = bp;
+ data_ = bp + offsets_len;
+
+#ifdef ENABLE_DEBUG
+ // Check the integrity on the offsets and the name data
+ const uint8_t* dp = data_;
+ for (size_t cur_offset = 0; cur_offset < offsets_len; ++cur_offset) {
+ if (dp - data_ != offsets_[cur_offset] || *dp > Name::MAX_LABELLEN) {
+ isc_throw(BadValue,
+ "Broken offset or name data in serialized "
+ "LabelSequence data");
+ }
+ dp += (1 + *dp);
+ }
+#endif
+}
+
+LabelSequence::LabelSequence(const LabelSequence& src,
+ uint8_t buf[MAX_SERIALIZED_LENGTH])
+{
+ size_t data_len;
+ const uint8_t *data = src.getData(&data_len);
+ std::memcpy(buf, data, data_len);
+
+ for (size_t i = 0; i < src.getLabelCount(); ++i) {
+ buf[Name::MAX_WIRE + i] = src.offsets_[i + src.first_label_] -
+ src.offsets_[src.first_label_];
+ }
+
+ first_label_ = 0;
+ last_label_ = src.last_label_ - src.first_label_;
+ data_ = buf;
+ offsets_ = &buf[Name::MAX_WIRE];
+}
+
+
+const uint8_t*
+LabelSequence::getData(size_t *len) const {
+ *len = getDataLength();
+ return (&data_[offsets_[first_label_]]);
+}
+
+size_t
+LabelSequence::getDataLength() const {
+ const size_t last_label_len = data_[offsets_[last_label_]] + 1;
+ return (offsets_[last_label_] - offsets_[first_label_] + last_label_len);
+}
+
+size_t
+LabelSequence::getSerializedLength() const {
+ return (1 + getLabelCount() + getDataLength());
+}
+
+namespace {
+// Check if buf is not in the range of [bp, ep), which means
+// - end of buffer is before bp, or
+// - beginning of buffer is on or after ep
+bool
+isOutOfRange(const uint8_t* bp, const uint8_t* ep,
+ const uint8_t* buf, size_t buf_len)
+{
+ return (bp >= buf + buf_len || // end of buffer is before bp
+ ep <= buf); // beginning of buffer is on or after ep
+}
+}
+
+void
+LabelSequence::serialize(void* buf, size_t buf_len) const {
+ const size_t expected_size = getSerializedLength();
+ if (expected_size > buf_len) {
+ isc_throw(BadValue, "buffer too short for LabelSequence::serialize");
+ }
+
+ const size_t offsets_len = getLabelCount();
+ isc_throw_assert(offsets_len < 256); // should be in the 8-bit range
+
+ // Overridden check. Buffer shouldn't overwrap the offset of name data
+ // regions.
+ uint8_t* bp = reinterpret_cast<uint8_t*>(buf);
+ const size_t ndata_len = getDataLength();
+ if (!isOutOfRange(offsets_, offsets_ + offsets_len, bp, buf_len) ||
+ !isOutOfRange(data_, data_ + ndata_len, bp, buf_len)) {
+ isc_throw(BadValue, "serialize would break the source sequence");
+ }
+
+ *bp++ = offsets_len;
+ for (size_t i = 0; i < offsets_len; ++i) {
+ *bp++ = offsets_[first_label_ + i] - offsets_[first_label_];
+ }
+ std::memcpy(bp, &data_[offsets_[first_label_]], ndata_len);
+ bp += ndata_len;
+
+ isc_throw_assert(bp - reinterpret_cast<const uint8_t*>(buf) == expected_size);
+}
+
+bool
+LabelSequence::equals(const LabelSequence& other, bool case_sensitive) const {
+ size_t len, other_len;
+ const uint8_t* data = getData(&len);
+ const uint8_t* other_data = other.getData(&other_len);
+
+ if (len != other_len) {
+ return (false);
+ }
+ if (case_sensitive) {
+ return (std::memcmp(data, other_data, len) == 0);
+ }
+
+ // As long as the data was originally validated as (part of) a name,
+ // label length must never be a capital ascii character, so we can
+ // simply compare them after converting to lower characters.
+ for (size_t i = 0; i < len; ++i) {
+ const uint8_t ch = data[i];
+ const uint8_t other_ch = other_data[i];
+ if (isc::dns::name::internal::maptolower[ch] !=
+ isc::dns::name::internal::maptolower[other_ch]) {
+ return (false);
+ }
+ }
+ return (true);
+}
+
+NameComparisonResult
+LabelSequence::compare(const LabelSequence& other,
+ bool case_sensitive) const
+{
+ // Determine the relative ordering under the DNSSEC order relation of
+ // 'this' and 'other', and also determine the hierarchical relationship
+ // of the labels.
+
+ unsigned int nlabels = 0;
+ int l1 = getLabelCount();
+ int l2 = other.getLabelCount();
+ const int ldiff = static_cast<int>(l1) - static_cast<int>(l2);
+ unsigned int l = (ldiff < 0) ? l1 : l2;
+
+ while (l > 0) {
+ --l;
+ --l1;
+ --l2;
+ size_t pos1 = offsets_[l1 + first_label_];
+ size_t pos2 = other.offsets_[l2 + other.first_label_];
+ unsigned int count1 = data_[pos1++];
+ unsigned int count2 = other.data_[pos2++];
+
+ // We don't support any extended label types including now-obsolete
+ // bitstring labels.
+ isc_throw_assert(count1 <= Name::MAX_LABELLEN && count2 <= Name::MAX_LABELLEN);
+
+ const int cdiff = static_cast<int>(count1) - static_cast<int>(count2);
+ unsigned int count = (cdiff < 0) ? count1 : count2;
+
+ while (count > 0) {
+ const uint8_t label1 = data_[pos1];
+ const uint8_t label2 = other.data_[pos2];
+ int chdiff;
+
+ if (case_sensitive) {
+ chdiff = static_cast<int>(label1) - static_cast<int>(label2);
+ } else {
+ chdiff = static_cast<int>(
+ isc::dns::name::internal::maptolower[label1]) -
+ static_cast<int>(
+ isc::dns::name::internal::maptolower[label2]);
+ }
+
+ if (chdiff != 0) {
+ return (NameComparisonResult(
+ chdiff, nlabels,
+ nlabels == 0 ? NameComparisonResult::NONE :
+ NameComparisonResult::COMMONANCESTOR));
+ }
+ --count;
+ ++pos1;
+ ++pos2;
+ }
+ if (cdiff != 0) {
+ return (NameComparisonResult(
+ cdiff, nlabels,
+ nlabels == 0 ? NameComparisonResult::NONE :
+ NameComparisonResult::COMMONANCESTOR));
+ }
+ ++nlabels;
+ }
+
+ if (ldiff < 0) {
+ return (NameComparisonResult(ldiff, nlabels,
+ NameComparisonResult::SUPERDOMAIN));
+ } else if (ldiff > 0) {
+ return (NameComparisonResult(ldiff, nlabels,
+ NameComparisonResult::SUBDOMAIN));
+ }
+
+ return (NameComparisonResult(ldiff, nlabels, NameComparisonResult::EQUAL));
+}
+
+void
+LabelSequence::stripLeft(size_t i) {
+ if (i >= getLabelCount()) {
+ isc_throw(OutOfRange, "Cannot strip to zero or less labels; " << i <<
+ " (labelcount: " << getLabelCount() << ")");
+ }
+ first_label_ += i;
+}
+
+void
+LabelSequence::stripRight(size_t i) {
+ if (i >= getLabelCount()) {
+ isc_throw(OutOfRange, "Cannot strip to zero or less labels; " << i <<
+ " (labelcount: " << getLabelCount() << ")");
+ }
+ last_label_ -= i;
+}
+
+bool
+LabelSequence::isAbsolute() const {
+ return (data_[offsets_[last_label_]] == 0);
+}
+
+size_t
+LabelSequence::getHash(bool case_sensitive) const {
+ size_t length;
+ const uint8_t* s = getData(&length);
+ if (length > 16) {
+ length = 16;
+ }
+
+ size_t hash_val = 0;
+ while (length > 0) {
+ const uint8_t c = *s++;
+ boost::hash_combine(hash_val, case_sensitive ? c :
+ isc::dns::name::internal::maptolower[c]);
+ --length;
+ }
+ return (hash_val);
+}
+
+std::string
+LabelSequence::toRawText(bool omit_final_dot) const {
+ const uint8_t* np = &data_[offsets_[first_label_]];
+ const uint8_t* np_end = np + getDataLength();
+
+ // use for integrity check
+ unsigned int labels = getLabelCount();
+ // init with an impossible value to catch error cases in the end:
+ unsigned int count = Name::MAX_LABELLEN + 1;
+
+ // result string: it will roughly have the same length as the wire format
+ // label sequence data. reserve that length to minimize reallocation.
+ std::string result;
+ result.reserve(getDataLength());
+
+ while (np != np_end) {
+ labels--;
+ count = *np++;
+
+ if (count == 0) {
+ // We've reached the "final dot". If we've not dumped any
+ // character, the entire label sequence is the root name.
+ // In that case we don't omit the final dot.
+ if (!omit_final_dot || result.empty()) {
+ result.push_back('.');
+ }
+ break;
+ }
+
+ if (count <= Name::MAX_LABELLEN) {
+ isc_throw_assert(np_end - np >= count);
+
+ if (!result.empty()) {
+ // just after a non-empty label. add a separating dot.
+ result.push_back('.');
+ }
+
+ while (count-- > 0) {
+ const uint8_t c = *np++;
+ result.push_back(c);
+ }
+ } else {
+ isc_throw(BadLabelType, "unknown label type in name data");
+ }
+ }
+
+ // We should be at the end of the data and have consumed all labels.
+ isc_throw_assert(np == np_end);
+ isc_throw_assert(labels == 0);
+
+ return (result);
+}
+
+
+std::string
+LabelSequence::toText(bool omit_final_dot) const {
+ const uint8_t* np = &data_[offsets_[first_label_]];
+ const uint8_t* np_end = np + getDataLength();
+
+ // use for integrity check
+ unsigned int labels = getLabelCount();
+ // init with an impossible value to catch error cases in the end:
+ unsigned int count = Name::MAX_LABELLEN + 1;
+
+ // result string: it will roughly have the same length as the wire format
+ // label sequence data. reserve that length to minimize reallocation.
+ std::string result;
+ result.reserve(getDataLength());
+
+ while (np != np_end) {
+ labels--;
+ count = *np++;
+
+ if (count == 0) {
+ // We've reached the "final dot". If we've not dumped any
+ // character, the entire label sequence is the root name.
+ // In that case we don't omit the final dot.
+ if (!omit_final_dot || result.empty()) {
+ result.push_back('.');
+ }
+ break;
+ }
+
+ if (count <= Name::MAX_LABELLEN) {
+ isc_throw_assert(np_end - np >= count);
+
+ if (!result.empty()) {
+ // just after a non-empty label. add a separating dot.
+ result.push_back('.');
+ }
+
+ while (count-- > 0) {
+ const uint8_t c = *np++;
+ switch (c) {
+ case 0x22: // '"'
+ case 0x28: // '('
+ case 0x29: // ')'
+ case 0x2E: // '.'
+ case 0x3B: // ';'
+ case 0x5C: // '\\'
+ // Special modifiers in zone files.
+ case 0x40: // '@'
+ case 0x24: // '$'
+ result.push_back('\\');
+ result.push_back(c);
+ break;
+ default:
+ if (c > 0x20 && c < 0x7f) {
+ // append printable characters intact
+ result.push_back(c);
+ } else {
+ // encode non-printable characters in the form of \DDD
+ result.push_back(0x5c);
+ result.push_back(0x30 + ((c / 100) % 10));
+ result.push_back(0x30 + ((c / 10) % 10));
+ result.push_back(0x30 + (c % 10));
+ }
+ }
+ }
+ } else {
+ isc_throw(BadLabelType, "unknown label type in name data");
+ }
+ }
+
+ // We should be at the end of the data and have consumed all labels.
+ isc_throw_assert(np == np_end);
+ isc_throw_assert(labels == 0);
+
+ return (result);
+}
+
+std::string
+LabelSequence::toText() const {
+ return (toText(!isAbsolute()));
+}
+
+void
+LabelSequence::extend(const LabelSequence& labels,
+ uint8_t buf[MAX_SERIALIZED_LENGTH])
+{
+ // collect data to perform steps before anything is changed
+ size_t label_count = last_label_ + 1;
+ // Since we may have been stripped, do not use getDataLength(), but
+ // calculate actual data size this labelsequence currently uses
+ size_t data_pos = offsets_[last_label_] + data_[offsets_[last_label_]] + 1;
+
+ // If this labelsequence is absolute, virtually strip the root label.
+ if (isAbsolute()) {
+ data_pos--;
+ label_count--;
+ }
+ const size_t append_label_count = labels.getLabelCount();
+ size_t data_len;
+ const uint8_t *data = labels.getData(&data_len);
+
+ // Sanity checks
+ if (data_ != buf || offsets_ != &buf[Name::MAX_WIRE]) {
+ isc_throw(BadValue,
+ "extend() called with unrelated buffer");
+ }
+ // Check MAX_LABELS before MAX_WIRE or it will be never reached
+ if (label_count + append_label_count > Name::MAX_LABELS) {
+ isc_throw(BadValue,
+ "extend() would exceed maximum number of labels");
+ }
+ if (data_pos + data_len > Name::MAX_WIRE) {
+ isc_throw(BadValue,
+ "extend() would exceed maximum wire length");
+ }
+
+ // All seems to be reasonably ok, let's proceed.
+ std::memmove(&buf[data_pos], data, data_len);
+
+ for (size_t i = 0; i < append_label_count; ++i) {
+ buf[Name::MAX_WIRE + label_count + i] =
+ data_pos +
+ labels.offsets_[i + labels.first_label_] -
+ labels.offsets_[labels.first_label_];
+ }
+ last_label_ = label_count + append_label_count - 1;
+}
+
+std::ostream&
+operator<<(std::ostream& os, const LabelSequence& label_sequence) {
+ os << label_sequence.toText();
+ return (os);
+}
+
+} // end namespace dns
+} // end namespace isc