2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-09-06 08:55:13 +00:00
Files
kea/src/lib/dns/labelsequence.cc

316 lines
10 KiB
C++
Raw Normal View History

// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
2012-02-20 23:29:31 +01:00
//
// 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 <dns/labelsequence.h>
#include <dns/name_internal.h>
2012-02-20 23:29:31 +01:00
#include <exceptions/exceptions.h>
#include <boost/functional/hash.hpp>
#include <cstring>
2012-02-20 23:29:31 +01:00
namespace isc {
namespace dns {
LabelSequence::LabelSequence(const uint8_t* data,
const uint8_t* offsets,
size_t offsets_size) : data_(data),
offsets_(offsets),
offsets_size_(offsets_size),
first_label_(0),
last_label_(offsets_size_)
{
if (data == NULL || offsets == NULL) {
isc_throw(BadValue, "Null pointer passed to LabelSequence constructor");
}
if (offsets_size == 0) {
isc_throw(BadValue, "Zero offsets to LabelSequence constructor");
}
if (offsets_size > Name::MAX_LABELS) {
isc_throw(BadValue, "MAX_LABELS exceeded");
}
for (size_t cur_offset = 0; cur_offset < offsets_size; ++cur_offset) {
if (offsets[cur_offset] > Name::MAX_LABELLEN) {
isc_throw(BadValue, "MAX_LABEL_LEN exceeded");
}
if (cur_offset > 0 && offsets[cur_offset] <= offsets[cur_offset - 1]) {
isc_throw(BadValue, "Offset smaller than previous offset");
}
}
}
const uint8_t*
2012-02-20 23:29:31 +01:00
LabelSequence::getData(size_t *len) const {
*len = getDataLength();
return (&data_[offsets_[first_label_]]);
}
void
LabelSequence::getOffsetData(size_t* len,
uint8_t placeholder[Name::MAX_LABELS]) const {
*len = last_label_ - first_label_;
for (size_t i = 0; i < *len; i++) {
placeholder[i] = offsets_[first_label_ + i] - offsets_[first_label_];
}
}
size_t
LabelSequence::getDataLength() const {
// If the labelsequence is absolute, the current last_label_ falls
// out of the vector (since it points to the 'label' after the
// root label, which doesn't exist; in that case, return
// the length for the 'previous' label (the root label) plus
// one (for the root label zero octet)
if (isAbsolute()) {
return (offsets_[last_label_ - 1] -
offsets_[first_label_] + 1);
} else {
return (offsets_[last_label_] - offsets_[first_label_]);
}
2012-02-20 23:29:31 +01:00
}
bool
LabelSequence::equals(const LabelSequence& other, bool case_sensitive) const {
2012-02-21 12:37:07 +01:00
size_t len, other_len;
const uint8_t* data = getData(&len);
const uint8_t* other_data = other.getData(&other_len);
2012-02-21 12:37:07 +01:00
if (len != other_len) {
return (false);
}
2012-02-20 23:29:31 +01:00
if (case_sensitive) {
return (std::memcmp(data, other_data, len) == 0);
2012-02-20 23:29:31 +01:00
}
// 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);
2012-02-20 23:29:31 +01:00
}
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 = last_label_ - first_label_;
int l2 = other.last_label_ - other.first_label_;
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));
}
2012-02-20 23:29:31 +01:00
void
LabelSequence::stripLeft(size_t i) {
if (i >= getLabelCount()) {
isc_throw(OutOfRange, "Cannot strip to zero or less labels; " << i <<
" (labelcount: " << getLabelCount() << ")");
2012-02-28 12:30:04 +01:00
}
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() << ")");
2012-02-20 23:29:31 +01:00
}
last_label_ -= i;
2012-02-20 23:29:31 +01:00
}
bool
LabelSequence::isAbsolute() const {
return (last_label_ == offsets_size_);
}
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 = last_label_ - first_label_;
// 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()));
}
std::ostream&
operator<<(std::ostream& os, const LabelSequence& label_sequence) {
os << label_sequence.toText();
return (os);
}
2012-02-20 23:29:31 +01:00
} // end namespace dns
} // end namespace isc