From f5f56e1a1c4d9e9496fcb9d81131066a964ccd23 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 13 Apr 2024 14:15:43 +0200 Subject: Adding upstream version 2.4.1. Signed-off-by: Daniel Baumann --- src/lib/dns/rdata/generic/detail/char_string.cc | 264 ++++++++++++++++++++ src/lib/dns/rdata/generic/detail/char_string.h | 140 +++++++++++ src/lib/dns/rdata/generic/detail/ds_like.h | 277 +++++++++++++++++++++ src/lib/dns/rdata/generic/detail/lexer_util.h | 62 +++++ .../dns/rdata/generic/detail/nsec3param_common.cc | 115 +++++++++ .../dns/rdata/generic/detail/nsec3param_common.h | 123 +++++++++ src/lib/dns/rdata/generic/detail/nsec_bitmap.cc | 169 +++++++++++++ src/lib/dns/rdata/generic/detail/nsec_bitmap.h | 103 ++++++++ src/lib/dns/rdata/generic/detail/txt_like.h | 237 ++++++++++++++++++ 9 files changed, 1490 insertions(+) create mode 100644 src/lib/dns/rdata/generic/detail/char_string.cc create mode 100644 src/lib/dns/rdata/generic/detail/char_string.h create mode 100644 src/lib/dns/rdata/generic/detail/ds_like.h create mode 100644 src/lib/dns/rdata/generic/detail/lexer_util.h create mode 100644 src/lib/dns/rdata/generic/detail/nsec3param_common.cc create mode 100644 src/lib/dns/rdata/generic/detail/nsec3param_common.h create mode 100644 src/lib/dns/rdata/generic/detail/nsec_bitmap.cc create mode 100644 src/lib/dns/rdata/generic/detail/nsec_bitmap.h create mode 100644 src/lib/dns/rdata/generic/detail/txt_like.h (limited to 'src/lib/dns/rdata/generic/detail') diff --git a/src/lib/dns/rdata/generic/detail/char_string.cc b/src/lib/dns/rdata/generic/detail/char_string.cc new file mode 100644 index 0000000..8eee8c0 --- /dev/null +++ b/src/lib/dns/rdata/generic/detail/char_string.cc @@ -0,0 +1,264 @@ +// Copyright (C) 2012-2015 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 + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { +namespace detail { + +namespace { +// Convert a DDD form to the corresponding integer +int +decimalToNumber(const char* s, const char* s_end) { + if (s_end - s < 3) { + isc_throw(InvalidRdataText, "Escaped digits too short"); + } + + const std::string num_str(s, s + 3); + try { + const int i = boost::lexical_cast(num_str); + if (i > 255) { + isc_throw(InvalidRdataText, "Escaped digits too large: " + << num_str); + } + return (i); + } catch (const boost::bad_lexical_cast&) { + isc_throw(InvalidRdataText, + "Invalid form for escaped digits: " << num_str); + } +} +} + +void +stringToCharString(const MasterToken::StringRegion& str_region, + CharString& result) +{ + // make a space for the 1-byte length field; filled in at the end + result.push_back(0); + + bool escape = false; + const char* s = str_region.beg; + const char* const s_end = str_region.beg + str_region.len; + + for (size_t n = str_region.len; n != 0; --n, ++s) { + int c = (*s & 0xff); + if (escape && std::isdigit(c) != 0) { + c = decimalToNumber(s, s_end); + assert(n >= 3); + n -= 2; + s += 2; + } else if (!escape && c == '\\') { + escape = true; + continue; + } + escape = false; + result.push_back(c); + } + if (escape) { // terminated by non-escaped '\' + isc_throw(InvalidRdataText, "character-string ends with '\\'"); + } + if (result.size() > MAX_CHARSTRING_LEN + 1) { // '+ 1' due to the len field + isc_throw(CharStringTooLong, "character-string is too long: " << + (result.size() - 1) << "(+1) characters"); + } + result[0] = result.size() - 1; +} + +void +stringToCharStringData(const MasterToken::StringRegion& str_region, + CharStringData& result) +{ + bool escape = false; + const char* s = str_region.beg; + const char* const s_end = str_region.beg + str_region.len; + + for (size_t n = str_region.len; n != 0; --n, ++s) { + int c = (*s & 0xff); + if (escape && std::isdigit(c) != 0) { + c = decimalToNumber(s, s_end); + // decimalToNumber() already throws if (s_end - s) is less + // than 3, so the following assertion is unnecessary. But we + // assert it anyway. 'n' is an unsigned type (size_t) and + // can underflow. + assert(n >= 3); + // 'n' and 's' are also updated by 1 in the for statement's + // expression, so we update them by 2 instead of 3 here. + n -= 2; + s += 2; + } else if (!escape && c == '\\') { + escape = true; + continue; + } + escape = false; + result.push_back(c); + } + if (escape) { // terminated by non-escaped '\' + isc_throw(InvalidRdataText, "character-string ends with '\\'"); + } +} + +std::string +charStringToString(const CharString& char_string) { + std::string s; + for (CharString::const_iterator it = char_string.begin() + 1; + it != char_string.end(); ++it) { + const uint8_t ch = *it; + if ((ch < 0x20) || (ch >= 0x7f)) { + // convert to escaped \xxx (decimal) format + s.push_back('\\'); + s.push_back('0' + ((ch / 100) % 10)); + s.push_back('0' + ((ch / 10) % 10)); + s.push_back('0' + (ch % 10)); + continue; + } + if ((ch == '"') || (ch == ';') || (ch == '\\')) { + s.push_back('\\'); + } + s.push_back(ch); + } + + return (s); +} + +std::string +charStringDataToString(const CharStringData& char_string) { + std::string s; + for (CharString::const_iterator it = char_string.begin(); + it != char_string.end(); ++it) { + const uint8_t ch = *it; + if ((ch < 0x20) || (ch >= 0x7f)) { + // convert to escaped \xxx (decimal) format + s.push_back('\\'); + s.push_back('0' + ((ch / 100) % 10)); + s.push_back('0' + ((ch / 10) % 10)); + s.push_back('0' + (ch % 10)); + continue; + } + if ((ch == '"') || (ch == ';') || (ch == '\\')) { + s.push_back('\\'); + } + s.push_back(ch); + } + + return (s); +} + +int compareCharStrings(const detail::CharString& self, + const detail::CharString& other) { + if (self.size() == 0 && other.size() == 0) { + return (0); + } + if (self.size() == 0) { + return (-1); + } + if (other.size() == 0) { + return (1); + } + const size_t self_len = self[0]; + const size_t other_len = other[0]; + const size_t cmp_len = std::min(self_len, other_len); + if (cmp_len == 0) { + if (self_len < other_len) { + return (-1); + } else if (self_len > other_len) { + return (1); + } else { + return (0); + } + } + const int cmp = std::memcmp(&self[1], &other[1], cmp_len); + if (cmp < 0) { + return (-1); + } else if (cmp > 0) { + return (1); + } else if (self_len < other_len) { + return (-1); + } else if (self_len > other_len) { + return (1); + } else { + return (0); + } +} + +int compareCharStringDatas(const detail::CharStringData& self, + const detail::CharStringData& other) { + if (self.size() == 0 && other.size() == 0) { + return (0); + } + if (self.size() == 0) { + return (-1); + } + if (other.size() == 0) { + return (1); + } + const size_t self_len = self.size(); + const size_t other_len = other.size(); + const size_t cmp_len = std::min(self_len, other_len); + const int cmp = std::memcmp(&self[0], &other[0], cmp_len); + if (cmp < 0) { + return (-1); + } else if (cmp > 0) { + return (1); + } else if (self_len < other_len) { + return (-1); + } else if (self_len > other_len) { + return (1); + } else { + return (0); + } +} + +size_t +bufferToCharString(isc::util::InputBuffer& buffer, size_t rdata_len, + CharString& target) { + if (rdata_len < 1 || buffer.getLength() - buffer.getPosition() < 1) { + isc_throw(isc::dns::DNSMessageFORMERR, + "insufficient data to read character-string length"); + } + const uint8_t len = buffer.readUint8(); + if (rdata_len < len + 1) { + isc_throw(isc::dns::DNSMessageFORMERR, + "character string length is too large: " << + static_cast(len)); + } + if (buffer.getLength() - buffer.getPosition() < len) { + isc_throw(isc::dns::DNSMessageFORMERR, + "not enough data in buffer to read character-string of len" + << static_cast(len)); + } + + target.resize(len + 1); + target[0] = len; + buffer.readData(&target[0] + 1, len); + + return (len + 1); +} + +} // end of detail +} // end of generic +} // end of rdata +} // end of dns +} // end of isc diff --git a/src/lib/dns/rdata/generic/detail/char_string.h b/src/lib/dns/rdata/generic/detail/char_string.h new file mode 100644 index 0000000..2ad12fb --- /dev/null +++ b/src/lib/dns/rdata/generic/detail/char_string.h @@ -0,0 +1,140 @@ +// Copyright (C) 2012-2015 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/. + +#ifndef DNS_RDATA_CHARSTRING_H +#define DNS_RDATA_CHARSTRING_H 1 + +#include + +#include +#include +#include +#include + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { +namespace detail { + +/// \brief Type for DNS character string. +/// +/// A character string can contain any unsigned 8-bit value, so this cannot +/// be the bare char basis. +typedef std::vector CharString; + +/// \brief Type for DNS character string without the length prefix. +typedef std::vector CharStringData; + +/// \brief Convert a DNS character-string into corresponding binary data. +/// +/// This helper function takes a string object that is expected to be a +/// textual representation of a valid DNS character-string, and dumps +/// the corresponding binary sequence in the given placeholder (passed +/// via the \c result parameter). It handles escape notations of +/// character-strings with a backslash ('\'), and checks the length +/// restriction. +/// +/// \throw CharStringTooLong The resulting binary data are too large for a +/// valid character-string. +/// \throw InvalidRdataText Other syntax errors. +/// +/// \brief str_region A string that represents a character-string. +/// \brief result A placeholder vector where the resulting data are to be +/// stored. Expected to be empty, but it's not checked. +void stringToCharString(const MasterToken::StringRegion& str_region, + CharString& result); + +/// \brief Convert a DNS character-string into corresponding binary data. +/// +/// This method functions similar to \c stringToCharString() except it +/// does not include the 1-octet length prefix in the \c result, and the +/// result is not limited to MAX_CHARSTRING_LEN octets. +/// +/// \throw InvalidRdataText Upon syntax errors. +/// +/// \brief str_region A string that represents a character-string. +/// \brief result A placeholder vector where the resulting data are to be +/// stored. Expected to be empty, but it's not checked. +void stringToCharStringData(const MasterToken::StringRegion& str_region, + CharStringData& result); + +/// \brief Convert a CharString into a textual DNS character-string. +/// +/// This method converts a binary 8-bit representation of a DNS +/// character string into a textual string representation, escaping any +/// special characters in the process. For example, characters like +/// double-quotes, semi-colon and backspace are prefixed with backspace +/// character, and characters not in the printable range of [0x20, 0x7e] +/// (inclusive) are converted to the \xxx 3-digit decimal +/// representation. +/// +/// \param char_string The \c CharString to convert. +/// \return A string representation of \c char_string. +std::string charStringToString(const CharString& char_string); + +/// \brief Convert a CharStringData into a textual DNS character-string. +/// +/// Reverse of \c stringToCharStringData(). See \c stringToCharString() +/// vs. \c stringToCharStringData(). +/// +/// \param char_string The \c CharStringData to convert. +/// \return A string representation of \c char_string. +std::string charStringDataToString(const CharStringData& char_string); + +/// \brief Compare two CharString objects +/// +/// \param self The CharString field to compare +/// \param other The CharString field to compare to +/// +/// \return -1 if \c self would be sorted before \c other +/// 1 if \c self would be sorted after \c other +/// 0 if \c self and \c other are equal +int compareCharStrings(const CharString& self, const CharString& other); + +/// \brief Compare two CharStringData objects +/// +/// \param self The CharStringData field to compare +/// \param other The CharStringData field to compare to +/// +/// \return -1 if \c self would be sorted before \c other +/// 1 if \c self would be sorted after \c other +/// 0 if \c self and \c other are equal +int compareCharStringDatas(const CharStringData& self, + const CharStringData& other); + +/// \brief Convert a buffer containing a character-string to CharString +/// +/// This method reads one character-string from the given buffer (in wire +/// format) and places the result in the given \c CharString object. +/// Since this is expected to be used in message parsing, the exception it +/// raises is of that type. +/// +/// On success, the buffer position is advanced to the end of the char-string, +/// and the number of bytes read is returned. +/// +/// \param buffer The buffer to read from. +/// \param rdata_len The total size of the rr's rdata currently being read +/// (used for integrity checks in the wire data) +/// \param target The \c CharString where the result will be stored. Any +/// existing data in the target will be overwritten. +/// \throw DNSMessageFORMERR If the available data is not enough to read +/// the character-string, or if the character-string length is out of bounds +/// \return The number of bytes read +size_t bufferToCharString(isc::util::InputBuffer& buffer, size_t rdata_len, + CharString& target); + + +} // namespace detail +} // namespace generic +} // namespace rdata +} // namespace dns +} // namespace isc +#endif // DNS_RDATA_CHARSTRING_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/detail/ds_like.h b/src/lib/dns/rdata/generic/detail/ds_like.h new file mode 100644 index 0000000..4d8c2ea --- /dev/null +++ b/src/lib/dns/rdata/generic/detail/ds_like.h @@ -0,0 +1,277 @@ +// Copyright (C) 2011-2015 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/. + +#ifndef DS_LIKE_H +#define DS_LIKE_H 1 + +#include + +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { +namespace detail { + +/// \brief \c rdata::DSLikeImpl class represents the DS-like RDATA for DS +/// and DLV types. +/// +/// This class implements the basic interfaces inherited by the DS and DLV +/// classes from the abstract \c rdata::Rdata class, and provides trivial +/// accessors to DS-like RDATA. +template class DSLikeImpl { + // Common sequence of toWire() operations used for the two versions of + // toWire(). + template + void + toWireCommon(Output& output) const { + output.writeUint16(tag_); + output.writeUint8(algorithm_); + output.writeUint8(digest_type_); + output.writeData(&digest_[0], digest_.size()); + } + +public: + /// \brief Constructor from string. + /// + /// The given string must represent a valid DS-like RDATA. There + /// can be extra space characters at the beginning or end of the + /// text (which are simply ignored), but other extra text, including + /// a new line, will make the construction fail with an exception. + /// + /// The tag field must be a valid decimal representation of an + /// unsigned 16-bit integer. The protocol and algorithm fields must + /// be valid decimal representations of unsigned 8-bit integers + /// respectively. The digest field may contain whitespace. + /// + /// \throw InvalidRdataText if any fields are out of their valid range. + /// + /// \param ds_str A string containing the RDATA to be created + DSLikeImpl(const std::string& ds_str) { + try { + std::istringstream ss(ds_str); + MasterLexer lexer; + lexer.pushSource(ss); + + constructFromLexer(lexer); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, + "Extra input text for " << RRType(typeCode) << ": " + << ds_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, + "Failed to construct " << RRType(typeCode) << " from '" << + ds_str << "': " << ex.what()); + } + } + + /// \brief Constructor with a context of MasterLexer. + /// + /// The \c lexer should point to the beginning of valid textual + /// representation of a DS-like RDATA. + /// + /// The tag field must be a valid decimal representation of an + /// unsigned 16-bit integer. The protocol and algorithm fields must + /// be valid decimal representations of unsigned 8-bit integers + /// respectively. + /// + /// \throw MasterLexer::LexerError General parsing error such as + /// missing field. + /// \throw InvalidRdataText if any fields are out of their valid range. + /// + /// \param lexer A \c MasterLexer object parsing a master file for the + /// RDATA to be created + DSLikeImpl(MasterLexer& lexer, const Name*, MasterLoader::Options, + MasterLoaderCallbacks&) + { + constructFromLexer(lexer); + } + +private: + void constructFromLexer(MasterLexer& lexer) { + const uint32_t tag = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (tag > 0xffff) { + isc_throw(InvalidRdataText, + "Invalid " << RRType(typeCode) << " tag: " << tag); + } + + const uint32_t algorithm = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (algorithm > 0xff) { + isc_throw(InvalidRdataText, + "Invalid " << RRType(typeCode) << " algorithm: " + << algorithm); + } + + const uint32_t digest_type = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (digest_type > 0xff) { + isc_throw(InvalidRdataText, + "Invalid " << RRType(typeCode) << " digest type: " + << digest_type); + } + + std::string digest; + while (true) { + const MasterToken& token = lexer.getNextToken(); + if (token.getType() != MasterToken::STRING) { + break; + } + digest.append(token.getString()); + } + + lexer.ungetToken(); + + if (digest.size() == 0) { + isc_throw(InvalidRdataText, + "Missing " << RRType(typeCode) << " digest"); + } + + tag_ = tag; + algorithm_ = algorithm; + digest_type_ = digest_type; + decodeHex(digest, digest_); + } + +public: + /// \brief Constructor from wire-format data. + /// + /// \param buffer A buffer storing the wire format data. + /// \param rdata_len The length of the RDATA in bytes, normally expected + /// to be the value of the RDLENGTH field of the corresponding RR. + /// + /// Exceptions + /// + /// \c InvalidRdataLength is thrown if the input data is too short for the + /// type. + DSLikeImpl(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len < 4) { + isc_throw(InvalidRdataLength, RRType(typeCode) << " too short"); + } + + tag_ = buffer.readUint16(); + algorithm_ = buffer.readUint8(); + digest_type_ = buffer.readUint8(); + + rdata_len -= 4; + digest_.resize(rdata_len); + buffer.readData(&digest_[0], rdata_len); + } + + /// \brief The copy constructor. + /// + /// Trivial for now, we could've used the default one. + DSLikeImpl(const DSLikeImpl& source) : + tag_(source.tag_), + algorithm_(source.algorithm_), + digest_type_(source.digest_type_), + digest_(source.digest_) + {} + + /// \brief Convert the DS-like data to a string. + /// + /// \return A \c string object that represents the DS-like data. + std::string + toText() const { + using namespace boost; + return (lexical_cast(static_cast(tag_)) + + " " + lexical_cast(static_cast(algorithm_)) + + " " + lexical_cast(static_cast(digest_type_)) + + " " + encodeHex(digest_)); + } + + /// \brief Render the DS-like data in the wire format to an OutputBuffer + /// object. + /// + /// \param buffer An output buffer to store the wire data. + void + toWire(OutputBuffer& buffer) const { + toWireCommon(buffer); + } + + /// \brief Render the DS-like data in the wire format to an + /// AbstractMessageRenderer object. + /// + /// \param renderer A renderer object to send the wire data to. + void + toWire(AbstractMessageRenderer& renderer) const { + toWireCommon(renderer); + } + + /// \brief Compare two instances of DS-like RDATA. + /// + /// It is up to the caller to make sure that \c other is an object of the + /// same \c DSLikeImpl class. + /// + /// \param other the right-hand operand to compare against. + /// \return < 0 if \c this would be sorted before \c other. + /// \return 0 if \c this is identical to \c other in terms of sorting + /// order. + /// \return > 0 if \c this would be sorted after \c other. + int + compare(const DSLikeImpl& other_ds) const { + if (tag_ != other_ds.tag_) { + return (tag_ < other_ds.tag_ ? -1 : 1); + } + if (algorithm_ != other_ds.algorithm_) { + return (algorithm_ < other_ds.algorithm_ ? -1 : 1); + } + if (digest_type_ != other_ds.digest_type_) { + return (digest_type_ < other_ds.digest_type_ ? -1 : 1); + } + + size_t this_len = digest_.size(); + size_t other_len = other_ds.digest_.size(); + size_t cmplen = min(this_len, other_len); + int cmp = memcmp(&digest_[0], &other_ds.digest_[0], cmplen); + if (cmp != 0) { + return (cmp); + } else { + return ((this_len == other_len) + ? 0 : (this_len < other_len) ? -1 : 1); + } + } + + /// \brief Accessors + uint16_t + getTag() const { + return (tag_); + } + +private: + // straightforward representation of DS RDATA fields + uint16_t tag_; + uint8_t algorithm_; + uint8_t digest_type_; + std::vector digest_; +}; + +} +} +} +} +} +#endif // DS_LIKE_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/detail/lexer_util.h b/src/lib/dns/rdata/generic/detail/lexer_util.h new file mode 100644 index 0000000..29b6c31 --- /dev/null +++ b/src/lib/dns/rdata/generic/detail/lexer_util.h @@ -0,0 +1,62 @@ +// Copyright (C) 2013-2015 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/. + +#ifndef DNS_RDATA_LEXER_UTIL_H +#define DNS_RDATA_LEXER_UTIL_H 1 + +#include +#include + +/// \file lexer_util.h +/// \brief Utilities for extracting RDATA fields from lexer. +/// +/// This file intends to define convenient small routines that can be +/// commonly used in the RDATA implementation to build RDATA fields from +/// a \c MasterLexer. + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { +namespace detail { + +/// \brief Construct a Name object using a master lexer and optional origin. +/// +/// This is a convenient shortcut of commonly used code pattern that would +/// be used to build RDATA that contain a domain name field. +/// +/// Note that this function throws an exception against invalid input. +/// The (direct or indirect) caller's responsibility needs to expect and +/// handle exceptions appropriately. +/// +/// \throw MasterLexer::LexerError The next token from lexer is not string. +/// \throw Other Exceptions from the \c Name class constructor if the next +/// string token from the lexer does not represent a valid name. +/// +/// \param lexer A \c MasterLexer object. Its next token is expected to be +/// a string that represent a domain name. +/// \param origin If non NULL, specifies the origin of the name to be +/// constructed. +/// +/// \return A new Name object that corresponds to the next string token of +/// the \c lexer. +inline Name +createNameFromLexer(MasterLexer& lexer, const Name* origin) { + const MasterToken::StringRegion& str_region = + lexer.getNextToken(MasterToken::STRING).getStringRegion(); + return (Name(str_region.beg, str_region.len, origin)); +} + +} // namespace detail +} // namespace generic +} // namespace rdata +} // namespace dns +} // namespace isc +#endif // DNS_RDATA_LEXER_UTIL_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/detail/nsec3param_common.cc b/src/lib/dns/rdata/generic/detail/nsec3param_common.cc new file mode 100644 index 0000000..efe488a --- /dev/null +++ b/src/lib/dns/rdata/generic/detail/nsec3param_common.cc @@ -0,0 +1,115 @@ +// Copyright (C) 2012-2015 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 + +#include + +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +using namespace std; +using namespace isc::util; +using namespace isc::util::encode; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { +namespace detail { +namespace nsec3 { + +ParseNSEC3ParamResult +parseNSEC3ParamFromLexer(const char* const rrtype_name, + MasterLexer& lexer, vector& salt) +{ + const uint32_t hashalg = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (hashalg > 0xff) { + isc_throw(InvalidRdataText, rrtype_name << + " hash algorithm out of range: " << hashalg); + } + + const uint32_t flags = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (flags > 0xff) { + isc_throw(InvalidRdataText, rrtype_name << " flags out of range: " << + flags); + } + + const uint32_t iterations = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (iterations > 0xffff) { + isc_throw(InvalidRdataText, rrtype_name << + " iterations out of range: " << iterations); + } + + const string salthex = + lexer.getNextToken(MasterToken::STRING).getString(); + + // Salt is up to 255 bytes, and space is not allowed in the HEX encoding, + // so the encoded string cannot be longer than the double of max length + // of the actual salt. + if (salthex.size() > 255 * 2) { + isc_throw(InvalidRdataText, rrtype_name << " salt is too long: " + << salthex.size() << " (encoded) bytes"); + } + if (salthex != "-") { // "-" means a 0-length salt + decodeHex(salthex, salt); + } + + return (ParseNSEC3ParamResult(hashalg, flags, iterations)); +} + +ParseNSEC3ParamResult +parseNSEC3ParamWire(const char* const rrtype_name, + InputBuffer& buffer, + size_t& rdata_len, std::vector& salt) +{ + // NSEC3/NSEC3PARAM RR must have at least 5 octets: + // hash algorithm(1), flags(1), iteration(2), saltlen(1) + if (rdata_len < 5) { + isc_throw(DNSMessageFORMERR, rrtype_name << " too short, length: " + << rdata_len); + } + + const uint8_t hashalg = buffer.readUint8(); + const uint8_t flags = buffer.readUint8(); + const uint16_t iterations = buffer.readUint16(); + + const uint8_t saltlen = buffer.readUint8(); + rdata_len -= 5; + if (rdata_len < saltlen) { + isc_throw(DNSMessageFORMERR, rrtype_name << + " salt length is too large: " << + static_cast(saltlen)); + } + + salt.resize(saltlen); + if (saltlen > 0) { + buffer.readData(&salt[0], saltlen); + rdata_len -= saltlen; + } + + return (ParseNSEC3ParamResult(hashalg, flags, iterations)); +} + +} // end of nsec3 +} // end of detail +} // end of generic +} // end of rdata +} // end of dns +} // end of isc diff --git a/src/lib/dns/rdata/generic/detail/nsec3param_common.h b/src/lib/dns/rdata/generic/detail/nsec3param_common.h new file mode 100644 index 0000000..89b2596 --- /dev/null +++ b/src/lib/dns/rdata/generic/detail/nsec3param_common.h @@ -0,0 +1,123 @@ +// Copyright (C) 2012-2015 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/. + +#ifndef NSEC3PARAM_COMMON_H +#define NSEC3PARAM_COMMON_H 1 + +#include + +#include + +#include +#include + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { +namespace detail { +namespace nsec3 { + +/// \file +/// +/// This helper module provides some utilities that handle NSEC3 and +/// NSEC3PARAM RDATA. They share the first few fields, and some operations +/// on these fields are sufficiently complicated, so it would make sense to +/// consolidate the processing logic into a single implementation module. +/// +/// The functions defined here are essentially private and are only expected +/// to be called from the \c NSEC3 and \c NSEC3PARAM class implementations. + +/// \brief Result values of the utilities. +/// +/// This structure encapsulates a tuple of NSEC3/NSEC3PARAM algorithm, +/// flags and iterations field values. This is used as the return value +/// of the utility functions defined in this module so the caller can +/// use it for constructing the corresponding RDATA. +struct ParseNSEC3ParamResult { + ParseNSEC3ParamResult(uint8_t param_algorithm, uint8_t param_flags, + uint16_t param_iterations) : + algorithm(param_algorithm), flags(param_flags), + iterations(param_iterations) + {} + const uint8_t algorithm; + const uint8_t flags; + const uint16_t iterations; +}; + +/// \brief Convert textual representation of NSEC3 parameters. +/// +/// This function takes an input MasterLexer that points at a complete +/// textual representation of an NSEC3 or NSEC3PARAM RDATA and parses it +/// extracting the hash algorithm, flags, iterations, and salt fields. +/// +/// The first three fields are returned as the return value of this function. +/// The salt will be stored in the given vector. The vector is expected +/// to be empty, but if not, the existing content will be overridden. +/// +/// On successful return the given MasterLexer will reach the end of the +/// salt field. +/// +/// \exception isc::BadValue The salt is not a valid hex string. +/// \exception InvalidRdataText The given RDATA is otherwise invalid for +/// NSEC3 or NSEC3PARAM fields. +/// \exception MasterLexer::LexerError There was a syntax error reading +/// a field from the MasterLexer. +/// +/// \param rrtype_name Either "NSEC3" or "NSEC3PARAM"; used as part of +/// exception messages. +/// \param lexer The MasterLexer to read NSEC3 parameter fields from. +/// \param salt A placeholder for the salt field value of the RDATA. +/// Expected to be empty, but it's not checked (and will be overridden). +/// +/// \return The hash algorithm, flags, iterations in the form of +/// ParseNSEC3ParamResult. +ParseNSEC3ParamResult parseNSEC3ParamFromLexer(const char* const rrtype_name, + isc::dns::MasterLexer& lexer, + std::vector& salt); + +/// \brief Extract NSEC3 parameters from wire-format data. +/// +/// This function takes an input buffer that stores wire-format NSEC3 or +/// NSEC3PARAM RDATA and parses it extracting the hash algorithm, flags, +/// iterations, and salt fields. +/// +/// The first three fields are returned as the return value of this function. +/// The salt will be stored in the given vector. The vector is expected +/// to be empty, but if not, the existing content will be overridden. +/// +/// On successful return the input buffer will point to the end of the +/// salt field; rdata_len will be the length of the rest of RDATA +/// (in the case of a valid NSEC3PARAM, it should be 0). +/// +/// \exception DNSMessageFORMERR The wire data is invalid. +/// +/// \param rrtype_name Either "NSEC3" or "NSEC3PARAM"; used as part of +/// exception messages. +/// \param buffer An input buffer that stores wire-format RDATA. It must +/// point to the beginning of the data. +/// \param rdata_len The total length of the RDATA. +/// \param salt A placeholder for the salt field value of the RDATA. +/// Expected to be empty, but it's not checked (and will be overridden). +/// +/// \return The hash algorithm, flags, iterations in the form of +/// ParseNSEC3ParamResult. +ParseNSEC3ParamResult parseNSEC3ParamWire(const char* const rrtype_name, + isc::util::InputBuffer& buffer, + size_t& rdata_len, + std::vector& salt); +} +} +} +} +} +} + +#endif // NSEC3PARAM_COMMON_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/detail/nsec_bitmap.cc b/src/lib/dns/rdata/generic/detail/nsec_bitmap.cc new file mode 100644 index 0000000..d02c11d --- /dev/null +++ b/src/lib/dns/rdata/generic/detail/nsec_bitmap.cc @@ -0,0 +1,169 @@ +// Copyright (C) 2011-2015 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 + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace std; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { +namespace detail { +namespace nsec { +void +checkRRTypeBitmaps(const char* const rrtype_name, + const vector& typebits) +{ + bool first = true; + unsigned int lastblock = 0; + const size_t total_len = typebits.size(); + size_t i = 0; + + while (i < total_len) { + if (i + 2 > total_len) { + isc_throw(DNSMessageFORMERR, rrtype_name << + " RDATA from wire: incomplete bit map field"); + } + const unsigned int block = typebits[i]; + const size_t len = typebits[i + 1]; + // Check that bitmap window blocks are in the correct order. + if (!first && block <= lastblock) { + isc_throw(DNSMessageFORMERR, rrtype_name << + " RDATA from wire: Disordered window blocks found: " + << lastblock << " then " << block); + } + // Check for legal length + if (len < 1 || len > 32) { + isc_throw(DNSMessageFORMERR, rrtype_name << + " RDATA from wire: Invalid bitmap length: " << len); + } + // Check for overflow. + i += 2; + if (i + len > total_len) { + isc_throw(DNSMessageFORMERR, rrtype_name << + " RDATA from wire: bitmap length too large: " << len); + } + // The last octet of the bitmap must be non zero. + if (typebits[i + len - 1] == 0) { + isc_throw(DNSMessageFORMERR, rrtype_name << + " RDATA from wire: bitmap ending an all-zero byte"); + } + + i += len; + lastblock = block; + first = false; + } +} + +void +buildBitmapsFromLexer(const char* const rrtype_name, + MasterLexer& lexer, vector& typebits, + bool allow_empty) +{ + uint8_t bitmap[8 * 1024]; // 64k bits + memset(bitmap, 0, sizeof(bitmap)); + + bool have_rrtypes = false; + std::string type_str; + while (true) { + const MasterToken& token = + lexer.getNextToken(MasterToken::STRING, true); + if ((token.getType() == MasterToken::END_OF_FILE) || + (token.getType() == MasterToken::END_OF_LINE)) { + break; + } + + // token is now assured to be of type STRING. + + have_rrtypes = true; + token.getString(type_str); + try { + const int code = RRType(type_str).getCode(); + bitmap[code / 8] |= (0x80 >> (code % 8)); + } catch (const InvalidRRType&) { + isc_throw(InvalidRdataText, "Invalid RRtype in " + << rrtype_name << " bitmap: " << type_str); + } + } + + lexer.ungetToken(); + + if (!have_rrtypes) { + if (allow_empty) { + return; + } + isc_throw(InvalidRdataText, + rrtype_name << + " record does not end with RR type mnemonic"); + } + + for (int window = 0; window < 256; ++window) { + int octet; + for (octet = 31; octet >= 0; octet--) { + if (bitmap[window * 32 + octet] != 0) { + break; + } + } + if (octet < 0) { + continue; + } + typebits.push_back(window); + typebits.push_back(octet + 1); + for (int i = 0; i <= octet; ++i) { + typebits.push_back(bitmap[window * 32 + i]); + } + } +} + +void +bitmapsToText(const vector& typebits, ostringstream& oss) { + // In the following loop we use string::at() rather than operator[]. + // Since the index calculation is a bit complicated, it will be safer + // and easier to find a bug (if any). Note that this conversion method + // is generally not expected to be very efficient, so the slight overhead + // of at() should be acceptable. + const size_t typebits_len = typebits.size(); + size_t len = 0; + for (size_t i = 0; i < typebits_len; i += len) { + assert(i + 2 <= typebits.size()); + const unsigned int block = typebits.at(i); + len = typebits.at(i + 1); + assert(len > 0 && len <= 32); + i += 2; + for (size_t j = 0; j < len; ++j) { + if (typebits.at(i + j) == 0) { + continue; + } + for (size_t k = 0; k < 8; ++k) { + if ((typebits.at(i + j) & (0x80 >> k)) == 0) { + continue; + } + const unsigned int t = block * 256 + j * 8 + k; + assert(t < 65536); + oss << " " << RRType(t); + } + } + } +} +} +} +} +} +} +} diff --git a/src/lib/dns/rdata/generic/detail/nsec_bitmap.h b/src/lib/dns/rdata/generic/detail/nsec_bitmap.h new file mode 100644 index 0000000..4e073a0 --- /dev/null +++ b/src/lib/dns/rdata/generic/detail/nsec_bitmap.h @@ -0,0 +1,103 @@ +// Copyright (C) 2011-2015 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/. + +#ifndef NSECBITMAP_H +#define NSECBITMAP_H 1 + +#include + +#include + +#include +#include + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { +namespace detail { +namespace nsec { + +/// \file +/// +/// This helper module provides some utilities that handle NSEC and NSEC3 +/// type bitmaps. The format and processing of the type bitmaps are generally +/// the same for these two RRs, so it would make sense to consolidate +/// the processing logic into a single implementation module. +/// +/// The functions defined here are essentially private and are only expected +/// to be called from the \c NSEC and \c NSEC3 class implementations. + +/// \brief Check if a given "type bitmap" for NSEC/NSEC3 is valid. +/// +/// This function checks given wire format data (stored in a +/// \c std::vector) is a valid type bitmaps used for the NSEC and NSEC3 RRs +/// according to RFC4034 and RFC5155. +/// +/// \exception DNSMessageFORMERR The bitmap is not valid. +/// +/// \param rrtype_name Either "NSEC" or "NSEC3"; used as part of exception +/// messages. +/// \param typebits The type bitmaps in wire format. The size of vector +/// is the total length of the bitmaps. +void checkRRTypeBitmaps(const char* const rrtype_name, + const std::vector& typebits); + +/// \brief Convert textual sequence of RR types read from a lexer into +/// type bitmaps. +/// +/// See the other variant above for description. If \c allow_empty is +/// true and there are no mnemonics, \c typebits is left untouched. +/// +/// \exception InvalidRdataText Data read from the given lexer does not +/// meet the assumption (e.g. including invalid form of RR type, not +/// ending with an RR type string). +/// +/// \param rrtype_name Either "NSEC" or "NSEC3"; used as part of exception +/// messages. +/// \param lexer MasterLexer that provides consists of a complete +/// sequence of textual lexemes of RR types for which the corresponding +/// bits are set. +/// \param typebits A placeholder for the resulting bitmaps. Expected to be +/// empty, but it's not checked. +/// \param allow_empty If true, the function simply returns if no RR +/// type mnemonics are found. Otherwise, it throws an exception if no RR +/// type mnemonics are found. +void buildBitmapsFromLexer(const char* const rrtype_name, + isc::dns::MasterLexer& lexer, + std::vector& typebits, + bool allow_empty = false); + +/// \brief Convert type bitmaps to textual sequence of RR types. +/// +/// This function converts wire-format data of type bitmaps for NSEC/NSEC3 +/// into a sequence of corresponding RR type strings, and inserts them +/// into the given output stream with separating them by a single space +/// character. +/// +/// This function assumes the given bitmaps are valid in terms of RFC4034 +/// and RFC5155 (in practice, it assumes it's from a validly constructed +/// NSEC or NSEC3 object); if it detects a format error, it aborts the +/// program with assert(). +/// +/// \param typebits The type bitmaps in wire format. The size of vector +/// is the total length of the bitmaps. +/// \param oss The output stream to which the converted RR type sequence +/// are to be inserted. +void bitmapsToText(const std::vector& typebits, + std::ostringstream& oss); +} +} +} +} +} +} + +#endif // NSECBITMAP_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/detail/txt_like.h b/src/lib/dns/rdata/generic/detail/txt_like.h new file mode 100644 index 0000000..5801b09 --- /dev/null +++ b/src/lib/dns/rdata/generic/detail/txt_like.h @@ -0,0 +1,237 @@ +// Copyright (C) 2011-2015,2021 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/. + +#ifndef TXT_LIKE_H +#define TXT_LIKE_H 1 + +#include +#include + +#include + +#include +#include +#include + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { +namespace detail { + +/// \brief \c rdata::TXTLikeImpl class represents the TXT-like RDATA for TXT +/// and SPF types. +/// +/// This class implements the basic interfaces inherited by the TXT and SPF +/// classes from the abstract \c rdata::Rdata class, and provides trivial +/// accessors to TXT-like RDATA. +templateclass TXTLikeImpl { +public: + /// \brief Constructor from wire-format data. + /// + /// \param buffer A buffer storing the wire format data. + /// \param rdata_len The length of the RDATA in bytes, normally expected + /// to be the value of the RDLENGTH field of the corresponding RR. + /// + /// Exceptions + /// + /// \c InvalidRdataLength is thrown if rdata_len exceeds the maximum. + /// \c DNSMessageFORMERR is thrown if the RR is malformed. + TXTLikeImpl(util::InputBuffer& buffer, size_t rdata_len) { + if (rdata_len > MAX_RDLENGTH) { + isc_throw(InvalidRdataLength, "RDLENGTH too large: " << rdata_len); + } + + if (rdata_len == 0) { // note that this couldn't happen in the loop. + isc_throw(DNSMessageFORMERR, "Error in parsing " << + RRType(typeCode) << " RDATA: 0-length character string"); + } + + do { + const uint8_t len = buffer.readUint8(); + if (rdata_len < len + 1) { + isc_throw(DNSMessageFORMERR, "Error in parsing " << + RRType(typeCode) << + " RDATA: character string length is too large: " << + static_cast(len)); + } + std::vector data(len + 1); + data[0] = len; + buffer.readData(&data[0] + 1, len); + string_list_.push_back(data); + + rdata_len -= (len + 1); + } while (rdata_len > 0); + } + + /// \brief Constructor from string. + /// + /// \throw CharStringTooLong the parameter string length exceeds maximum. + /// \throw InvalidRdataText the method cannot process the parameter data + explicit TXTLikeImpl(const std::string& txtstr) { + std::istringstream ss(txtstr); + MasterLexer lexer; + lexer.pushSource(ss); + + try { + buildFromTextHelper(lexer); + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "Failed to construct " << + RRType(typeCode) << " RDATA from '" << txtstr << + "': extra new line"); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct " << + RRType(typeCode) << " RDATA from '" << txtstr << "': " + << ex.what()); + } + } + + /// \brief Constructor using the master lexer. + /// + /// \throw CharStringTooLong the parameter string length exceeds maximum. + /// \throw InvalidRdataText the method cannot process the parameter data + /// + /// \param lexer A \c MasterLexer object parsing a master file for this + /// RDATA. + TXTLikeImpl(MasterLexer& lexer) { + buildFromTextHelper(lexer); + } + +private: + void buildFromTextHelper(MasterLexer& lexer) { + while (true) { + const MasterToken& token = lexer.getNextToken( + MasterToken::QSTRING, true); + if (token.getType() != MasterToken::STRING && + token.getType() != MasterToken::QSTRING) { + break; + } + string_list_.push_back(std::vector()); + stringToCharString(token.getStringRegion(), string_list_.back()); + } + + // Let upper layer handle eol/eof. + lexer.ungetToken(); + + if (string_list_.empty()) { + isc_throw(InvalidRdataText, "Failed to construct " << + RRType(typeCode) << " RDATA: empty input"); + } + } + +public: + /// \brief The copy constructor. + /// + /// Trivial for now, we could've used the default one. + TXTLikeImpl(const TXTLikeImpl& other) : + string_list_(other.string_list_) + {} + + /// \brief Render the TXT-like data in the wire format to an OutputBuffer + /// object. + /// + /// \param buffer An output buffer to store the wire data. + void + toWire(util::OutputBuffer& buffer) const { + for (std::vector >::const_iterator it = + string_list_.begin(); + it != string_list_.end(); + ++it) + { + buffer.writeData(&(*it)[0], (*it).size()); + } + } + + /// \brief Render the TXT-like data in the wire format to an + /// AbstractMessageRenderer object. + /// + /// \param buffer An output AbstractMessageRenderer to send the wire data + /// to. + void + toWire(AbstractMessageRenderer& renderer) const { + for (std::vector >::const_iterator it = + string_list_.begin(); + it != string_list_.end(); + ++it) + { + renderer.writeData(&(*it)[0], (*it).size()); + } + } + + /// \brief Convert the TXT-like data to a string. + /// + /// \return A \c string object that represents the TXT-like data. + std::string + toText() const { + std::string s; + + for (std::vector >::const_iterator it = + string_list_.begin(); it != string_list_.end(); ++it) + { + if (!s.empty()) { + s.push_back(' '); + } + s.push_back('"'); + s.append(charStringToString(*it)); + s.push_back('"'); + } + + return (s); + } + + /// \brief Compare two instances of TXT-like RDATA. + /// + /// It is up to the caller to make sure that \c other is an object of the + /// same \c TXTLikeImpl class. + /// + /// \param other the right-hand operand to compare against. + /// \return < 0 if \c this would be sorted before \c other. + /// \return 0 if \c this is identical to \c other in terms of sorting + /// order. + /// \return > 0 if \c this would be sorted after \c other. + int + compare(const TXTLikeImpl& other) const { + // This implementation is not efficient. Revisit this (TBD). + OutputBuffer this_buffer(0); + toWire(this_buffer); + uint8_t const* const this_data = (uint8_t const*)this_buffer.getData(); + const size_t this_len = this_buffer.getLength(); + + OutputBuffer other_buffer(0); + other.toWire(other_buffer); + uint8_t const* const other_data + = (uint8_t const*)other_buffer.getData(); + const size_t other_len = other_buffer.getLength(); + + const size_t cmplen = min(this_len, other_len); + const int cmp = memcmp(this_data, other_data, cmplen); + + if (cmp != 0) { + return (cmp); + } else { + return ((this_len == other_len) ? 0 : + (this_len < other_len) ? -1 : 1); + } + } + +private: + /// Note: this is a prototype version; we may reconsider + /// this representation later. + std::vector > string_list_; +}; + +} // namespace detail +} // namespace generic +} // namespace rdata +} // namespace dns +} // namespace isc + +#endif // TXT_LIKE_H + +// Local Variables: +// mode: c++ +// End: -- cgit v1.2.3