diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 11:36:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 11:36:04 +0000 |
commit | 040eee1aa49b49df4698d83a05af57c220127fd1 (patch) | |
tree | f635435954e6ccde5eee9893889e24f30ca68346 /src/lib/dns/rdata.cc | |
parent | Initial commit. (diff) | |
download | isc-kea-upstream.tar.xz isc-kea-upstream.zip |
Adding upstream version 2.2.0.upstream/2.2.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib/dns/rdata.cc')
-rw-r--r-- | src/lib/dns/rdata.cc | 408 |
1 files changed, 408 insertions, 0 deletions
diff --git a/src/lib/dns/rdata.cc b/src/lib/dns/rdata.cc new file mode 100644 index 0000000..357ccc7 --- /dev/null +++ b/src/lib/dns/rdata.cc @@ -0,0 +1,408 @@ +// Copyright (C) 2010-2016 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 <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <util/encode/hex.h> + +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/master_lexer.h> +#include <dns/rdata.h> +#include <dns/rrparamregistry.h> +#include <dns/rrtype.h> + +#include <boost/lexical_cast.hpp> +#include <boost/shared_ptr.hpp> + +#include <algorithm> +#include <cctype> +#include <string> +#include <sstream> +#include <iomanip> +#include <ios> +#include <ostream> +#include <vector> + +#include <stdint.h> +#include <string.h> + +using namespace std; +using boost::lexical_cast; +using namespace isc::util; + +namespace isc { +namespace dns { +namespace rdata { + +uint16_t +Rdata::getLength() const { + OutputBuffer obuffer(0); + + toWire(obuffer); + + return (obuffer.getLength()); +} + +// XXX: we need to specify std:: for string to help doxygen match the +// function signature with that given in the header file. +RdataPtr +createRdata(const RRType& rrtype, const RRClass& rrclass, + const std::string& rdata_string) +{ + return (RRParamRegistry::getRegistry().createRdata(rrtype, rrclass, + rdata_string)); +} + +RdataPtr +createRdata(const RRType& rrtype, const RRClass& rrclass, + isc::util::InputBuffer& buffer, size_t len) +{ + if (len > MAX_RDLENGTH) { + isc_throw(InvalidRdataLength, "RDLENGTH too large"); + } + + size_t old_pos = buffer.getPosition(); + + RdataPtr rdata = + RRParamRegistry::getRegistry().createRdata(rrtype, rrclass, buffer, + len); + + if (buffer.getPosition() - old_pos != len) { + isc_throw(InvalidRdataLength, "RDLENGTH mismatch: " << + buffer.getPosition() - old_pos << " != " << len); + } + + return (rdata); +} + +RdataPtr +createRdata(const RRType& rrtype, const RRClass& rrclass, const Rdata& source) +{ + return (RRParamRegistry::getRegistry().createRdata(rrtype, rrclass, + source)); +} + +namespace { +void +fromtextError(bool& error_issued, const MasterLexer& lexer, + MasterLoaderCallbacks& callbacks, + const MasterToken* token, const char* reason) +{ + // Don't be too noisy if there are many issues for single RDATA + if (error_issued) { + return; + } + error_issued = true; + + if (token == NULL) { + callbacks.error(lexer.getSourceName(), lexer.getSourceLine(), + "createRdata from text failed: " + string(reason)); + return; + } + + switch (token->getType()) { + case MasterToken::STRING: + case MasterToken::QSTRING: + callbacks.error(lexer.getSourceName(), lexer.getSourceLine(), + "createRdata from text failed near '" + + token->getString() + "': " + string(reason)); + break; + case MasterToken::ERROR: + callbacks.error(lexer.getSourceName(), lexer.getSourceLine(), + "createRdata from text failed: " + + token->getErrorText()); + break; + default: + // This case shouldn't happen based on how we use MasterLexer in + // createRdata(), so we could assert() that here. But since it + // depends on detailed behavior of other classes, we treat the case + // in a bit less harsh way. + isc_throw(Unexpected, "bug: createRdata() saw unexpected token type"); + } +} +} + +RdataPtr +createRdata(const RRType& rrtype, const RRClass& rrclass, + MasterLexer& lexer, const Name* origin, + MasterLoader::Options options, + MasterLoaderCallbacks& callbacks) +{ + RdataPtr rdata; + + bool error_issued = false; + try { + rdata = RRParamRegistry::getRegistry().createRdata( + rrtype, rrclass, lexer, origin, options, callbacks); + } catch (const MasterLexer::LexerError& error) { + fromtextError(error_issued, lexer, callbacks, &error.token_, ""); + } catch (const Exception& ex) { + // Catching all isc::Exception is too broad, but right now we don't + // have better granularity. When we complete #2518 we can make this + // finer. + fromtextError(error_issued, lexer, callbacks, NULL, ex.what()); + } + // Other exceptions mean a serious implementation bug or fatal system + // error; it doesn't make sense to catch and try to recover from them + // here. Just propagate. + + // Consume to end of line / file. + // Call callback via fromtextError once if there was an error. + do { + const MasterToken& token = lexer.getNextToken(); + switch (token.getType()) { + case MasterToken::END_OF_LINE: + return (rdata); + case MasterToken::END_OF_FILE: + callbacks.warning(lexer.getSourceName(), lexer.getSourceLine(), + "file does not end with newline"); + return (rdata); + default: + rdata.reset(); // we'll return NULL + fromtextError(error_issued, lexer, callbacks, &token, + "extra input text"); + // Continue until we see EOL or EOF + } + } while (true); + + // We shouldn't reach here + assert(false); + return (RdataPtr()); // add explicit return to silence some compilers +} + +int +compareNames(const Name& n1, const Name& n2) { + size_t len1 = n1.getLength(); + size_t len2 = n2.getLength(); + size_t cmplen = min(len1, len2); + + for (size_t i = 0; i < cmplen; ++i) { + uint8_t c1 = tolower(n1.at(i)); + uint8_t c2 = tolower(n2.at(i)); + if (c1 < c2) { + return (-1); + } else if (c1 > c2) { + return (1); + } + } + + return ((len1 == len2) ? 0 : (len1 < len2) ? -1 : 1); +} + +namespace generic { +struct GenericImpl { + GenericImpl(const vector<uint8_t>& data) : data_(data) {} + vector<uint8_t> data_; +}; + +Generic::Generic(isc::util::InputBuffer& buffer, size_t rdata_len) { + if (rdata_len > MAX_RDLENGTH) { + isc_throw(InvalidRdataLength, "RDLENGTH too large"); + } + + vector<uint8_t> data(rdata_len); + if (rdata_len > 0) { + buffer.readData(&data[0], rdata_len); + } + + impl_ = new GenericImpl(data); +} + +GenericImpl* +Generic::constructFromLexer(MasterLexer& lexer) { + const MasterToken& token = lexer.getNextToken(MasterToken::STRING); + if (token.getString() != "\\#") { + isc_throw(InvalidRdataText, + "Missing the special token (\\#) for " + "unknown RDATA encoding"); + } + + // Initialize with an absurd value. + uint32_t rdlen = 65536; + + try { + rdlen = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + } catch (const MasterLexer::LexerError&) { + isc_throw(InvalidRdataLength, + "Unknown RDATA length is invalid"); + } + + if (rdlen > 65535) { + isc_throw(InvalidRdataLength, + "Unknown RDATA length is out of range: " << rdlen); + } + + vector<uint8_t> data; + + if (rdlen > 0) { + string hex_txt; + string hex_part; + // Whitespace is allowed within hex data, so read to the end of input. + while (true) { + const MasterToken& token = + lexer.getNextToken(MasterToken::STRING, true); + if ((token.getType() == MasterToken::END_OF_FILE) || + (token.getType() == MasterToken::END_OF_LINE)) { + // Unget the last read token as createRdata() expects us + // to leave it at the end-of-line or end-of-file when we + // return. + lexer.ungetToken(); + break; + } + token.getString(hex_part); + hex_txt.append(hex_part); + } + + try { + isc::util::encode::decodeHex(hex_txt, data); + } catch (const isc::BadValue& ex) { + isc_throw(InvalidRdataText, + "Invalid hex encoding of generic RDATA: " << ex.what()); + } + } + + if (data.size() != rdlen) { + isc_throw(InvalidRdataLength, + "Size of unknown RDATA hex data doesn't match RDLENGTH: " + << data.size() << " vs. " << rdlen); + } + + return (new GenericImpl(data)); +} + +Generic::Generic(const std::string& rdata_string) : + impl_(NULL) +{ + // We use unique_ptr here because if there is an exception in this + // constructor, the destructor is not called and there could be a + // leak of the GenericImpl that constructFromLexer() returns. + std::unique_ptr<GenericImpl> impl_ptr; + + try { + std::istringstream ss(rdata_string); + MasterLexer lexer; + lexer.pushSource(ss); + + impl_ptr.reset(constructFromLexer(lexer)); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for unknown RDATA: " + << rdata_string); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct unknown RDATA " + "from '" << rdata_string << "': " << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +Generic::Generic(MasterLexer& lexer, const Name*, + MasterLoader::Options, + MasterLoaderCallbacks&) : + impl_(constructFromLexer(lexer)) +{ +} + +Generic::~Generic() { + delete impl_; +} + +Generic::Generic(const Generic& source) : + Rdata(), impl_(new GenericImpl(*source.impl_)) +{} + +Generic& +// Our check is better than the usual if (this == &source), +// but cppcheck doesn't recognize it. +// cppcheck-suppress operatorEqToSelf +Generic::operator=(const Generic& source) { + if (impl_ == source.impl_) { + return (*this); + } + + GenericImpl* newimpl = new GenericImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +namespace { +class UnknownRdataDumper { +public: + UnknownRdataDumper(ostringstream& oss) : oss_(&oss) {} + void operator()(const unsigned char d) + { + *oss_ << setw(2) << static_cast<unsigned int>(d); + } +private: + ostringstream* oss_; +}; +} + +string +Generic::toText() const { + ostringstream oss; + + oss << "\\# " << impl_->data_.size() << " "; + oss.fill('0'); + oss << right << hex; + for_each(impl_->data_.begin(), impl_->data_.end(), UnknownRdataDumper(oss)); + + return (oss.str()); +} + +void +Generic::toWire(isc::util::OutputBuffer& buffer) const { + buffer.writeData(&impl_->data_[0], impl_->data_.size()); +} + +void +Generic::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeData(&impl_->data_[0], impl_->data_.size()); +} + +namespace { +inline int +compare_internal(const GenericImpl& lhs, const GenericImpl& rhs) { + size_t this_len = lhs.data_.size(); + size_t other_len = rhs.data_.size(); + size_t len = (this_len < other_len) ? this_len : other_len; + int cmp; + + // TODO: is there a need to check len - should we just assert? + // (Depends if it is possible for rdata to have zero length) + if ((len != 0) && + ((cmp = memcmp(&lhs.data_[0], &rhs.data_[0], len)) != 0)) { + return (cmp); + } else { + return ((this_len == other_len) ? 0 : + (this_len < other_len) ? -1 : 1); + } +} +} + +int +Generic::compare(const Rdata& other) const { + const Generic& other_rdata = dynamic_cast<const Generic&>(other); + + return (compare_internal(*impl_, *other_rdata.impl_)); +} + +std::ostream& +operator<<(std::ostream& os, const Generic& rdata) { + return (os << rdata.toText()); +} +} // end of namespace generic + +} // end of namespace rdata +} +} |