123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424 |
- // Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC")
- //
- // Permission to use, copy, modify, and/or distribute this software for any
- // purpose with or without fee is hereby granted, provided that the above
- // copyright notice and this permission notice appear in all copies.
- //
- // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- // PERFORMANCE OF THIS SOFTWARE.
- #include <config.h>
- #include <dns/labelsequence.h>
- #include <dns/name_internal.h>
- #include <exceptions/exceptions.h>
- #include <boost/functional/hash.hpp>
- #include <cstring>
- namespace isc {
- namespace dns {
- LabelSequence::LabelSequence(const void* buf) {
- #ifdef ENABLE_DEBUG
- // In non-debug mode, derefencing 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();
- 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;
- 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.
- 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::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) {
- 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.
- assert(np == np_end);
- 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");
- }
- if (data_pos + data_len > Name::MAX_WIRE) {
- isc_throw(BadValue,
- "extend() would exceed maximum wire length");
- }
- if (label_count + append_label_count > Name::MAX_LABELS) {
- isc_throw(BadValue,
- "extend() would exceed maximum number of labels");
- }
- // 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
|