diff options
Diffstat (limited to 'src/lib/dns/rdataclass.cc')
-rw-r--r-- | src/lib/dns/rdataclass.cc | 7078 |
1 files changed, 7078 insertions, 0 deletions
diff --git a/src/lib/dns/rdataclass.cc b/src/lib/dns/rdataclass.cc new file mode 100644 index 0000000..dcd5e13 --- /dev/null +++ b/src/lib/dns/rdataclass.cc @@ -0,0 +1,7078 @@ +/////////////// +/////////////// +/////////////// THIS FILE IS AUTOMATICALLY GENERATED BY gen-rdatacode.py. +/////////////// DO NOT EDIT! +/////////////// +/////////////// + +// Copyright (C) 2010-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/. + +#include <config.h> + +#include <string> +#include <sstream> +#include <vector> + +#include <boost/lexical_cast.hpp> + +#include <util/buffer.h> +#include <util/encode/base64.h> + +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rcode.h> +#include <dns/tsigkey.h> +#include <dns/tsigerror.h> +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using boost::lexical_cast; +using namespace isc::util; +using namespace isc::util::encode; +using namespace isc::dns; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +namespace isc { +namespace dns { +namespace rdata { +namespace any { + +// straightforward representation of TSIG RDATA fields +struct TSIGImpl { + TSIGImpl(const Name& algorithm, uint64_t time_signed, uint16_t fudge, + vector<uint8_t>& mac, uint16_t original_id, uint16_t error, + vector<uint8_t>& other_data) : + algorithm_(algorithm), time_signed_(time_signed), fudge_(fudge), + mac_(mac), original_id_(original_id), error_(error), + other_data_(other_data) + {} + TSIGImpl(const Name& algorithm, uint64_t time_signed, uint16_t fudge, + size_t macsize, const void* mac, uint16_t original_id, + uint16_t error, size_t other_len, const void* other_data) : + algorithm_(algorithm), time_signed_(time_signed), fudge_(fudge), + mac_(static_cast<const uint8_t*>(mac), + static_cast<const uint8_t*>(mac) + macsize), + original_id_(original_id), error_(error), + other_data_(static_cast<const uint8_t*>(other_data), + static_cast<const uint8_t*>(other_data) + other_len) + {} + template <typename Output> + void toWireCommon(Output& output) const; + + const Name algorithm_; + const uint64_t time_signed_; + const uint16_t fudge_; + const vector<uint8_t> mac_; + const uint16_t original_id_; + const uint16_t error_; + const vector<uint8_t> other_data_; +}; + +// helper function for string and lexer constructors +TSIGImpl* +TSIG::constructFromLexer(MasterLexer& lexer, const Name* origin) { + const Name& algorithm = + createNameFromLexer(lexer, origin ? origin : &Name::ROOT_NAME()); + const Name& canonical_algorithm_name = + (algorithm == TSIGKey::HMACMD5_SHORT_NAME()) ? + TSIGKey::HMACMD5_NAME() : algorithm; + + const string& time_txt = + lexer.getNextToken(MasterToken::STRING).getString(); + uint64_t time_signed; + try { + time_signed = boost::lexical_cast<uint64_t>(time_txt); + } catch (const boost::bad_lexical_cast&) { + isc_throw(InvalidRdataText, "Invalid TSIG Time"); + } + if ((time_signed >> 48) != 0) { + isc_throw(InvalidRdataText, "TSIG Time out of range"); + } + + const uint32_t fudge = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (fudge > 0xffff) { + isc_throw(InvalidRdataText, "TSIG Fudge out of range"); + } + const uint32_t macsize = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (macsize > 0xffff) { + isc_throw(InvalidRdataText, "TSIG MAC Size out of range"); + } + + const string& mac_txt = (macsize > 0) ? + lexer.getNextToken(MasterToken::STRING).getString() : ""; + vector<uint8_t> mac; + decodeBase64(mac_txt, mac); + if (mac.size() != macsize) { + isc_throw(InvalidRdataText, "TSIG MAC Size and data are inconsistent"); + } + + const uint32_t orig_id = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (orig_id > 0xffff) { + isc_throw(InvalidRdataText, "TSIG Original ID out of range"); + } + + const string& error_txt = + lexer.getNextToken(MasterToken::STRING).getString(); + uint32_t error = 0; + // XXX: In the initial implementation we hardcode the mnemonics. + // We'll soon generalize this. + if (error_txt == "NOERROR") { + error = Rcode::NOERROR_CODE; + } else if (error_txt == "BADSIG") { + error = TSIGError::BAD_SIG_CODE; + } else if (error_txt == "BADKEY") { + error = TSIGError::BAD_KEY_CODE; + } else if (error_txt == "BADTIME") { + error = TSIGError::BAD_TIME_CODE; + } else if (error_txt == "BADMODE") { + error = TSIGError::BAD_MODE_CODE; + } else if (error_txt == "BADNAME") { + error = TSIGError::BAD_NAME_CODE; + } else if (error_txt == "BADALG") { + error = TSIGError::BAD_ALG_CODE; + } else if (error_txt == "BADTRUNC") { + error = TSIGError::BAD_TRUNC_CODE; + } else { + /// we cast to uint32_t and range-check, because casting directly to + /// uint16_t will convert negative numbers to large positive numbers + try { + error = boost::lexical_cast<uint32_t>(error_txt); + } catch (const boost::bad_lexical_cast&) { + isc_throw(InvalidRdataText, "Invalid TSIG Error"); + } + if (error > 0xffff) { + isc_throw(InvalidRdataText, "TSIG Error out of range"); + } + } + + const uint32_t otherlen = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (otherlen > 0xffff) { + isc_throw(InvalidRdataText, "TSIG Other Len out of range"); + } + const string otherdata_txt = (otherlen > 0) ? + lexer.getNextToken(MasterToken::STRING).getString() : ""; + vector<uint8_t> other_data; + decodeBase64(otherdata_txt, other_data); + if (other_data.size() != otherlen) { + isc_throw(InvalidRdataText, + "TSIG Other Data length does not match Other Len"); + } + // RFC2845 says Other Data is "empty unless Error == BADTIME". + // However, we don't enforce that. + + return (new TSIGImpl(canonical_algorithm_name, time_signed, fudge, mac, + orig_id, error, other_data)); +} + +/// \brief Constructor from string. +/// +/// The given string must represent a valid TSIG 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. +/// +/// \c tsig_str must be formatted as follows: +/// \code <Algorithm Name> <Time Signed> <Fudge> <MAC Size> [<MAC>] +/// <Original ID> <Error> <Other Len> [<Other Data>] +/// \endcode +/// +/// Note that, since the Algorithm Name field is defined to be "in domain name +/// syntax", but it is not actually a domain name, it does not have to be +/// fully qualified. +/// +/// The Error field is an unsigned 16-bit decimal integer or a valid mnemonic +/// as specified in RFC2845. Currently, "NOERROR", "BADSIG", "BADKEY", and +/// "BADTIME" are supported (case sensitive). In future versions other +/// representations that are compatible with the DNS RCODE may be supported. +/// +/// The MAC and Other Data fields are base-64 encoded strings that do not +/// contain space characters. +/// If the MAC Size field is 0, the MAC field must not appear in \c tsig_str. +/// If the Other Len field is 0, the Other Data field must not appear in +/// \c tsig_str. +/// The decoded data of the MAC field is MAC Size bytes of binary stream. +/// The decoded data of the Other Data field is Other Len bytes of binary +/// stream. +/// +/// An example of valid string is: +/// \code "hmac-sha256. 853804800 300 3 AAAA 2845 0 0" \endcode +/// In this example Other Data is missing because Other Len is 0. +/// +/// Note that RFC2845 does not define the standard presentation format +/// of %TSIG RR, so the above syntax is implementation specific. +/// This is, however, compatible with the format acceptable to BIND 9's +/// RDATA parser. +/// +/// \throw Others Exception from the Name constructors. +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// \throw BadValue if MAC or Other Data is not validly encoded in base-64. +/// +/// \param tsig_str A string containing the RDATA to be created +TSIG::TSIG(const std::string& tsig_str) : 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 TSIGImpl that constructFromLexer() returns. + std::unique_ptr<TSIGImpl> impl_ptr; + + try { + std::istringstream ss(tsig_str); + MasterLexer lexer; + lexer.pushSource(ss); + + impl_ptr.reset(constructFromLexer(lexer, NULL)); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, + "Extra input text for TSIG: " << tsig_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, + "Failed to construct TSIG from '" << tsig_str << "': " + << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of an TSIG RDATA. +/// +/// See \c TSIG::TSIG(const std::string&) for description of the +/// expected RDATA fields. +/// +/// \throw MasterLexer::LexerError General parsing error such as +/// missing field. +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +TSIG::TSIG(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(constructFromLexer(lexer, origin)) +{ +} + +/// \brief Constructor from wire-format data. +/// +/// When a read operation on \c buffer fails (e.g., due to a corrupted +/// message) a corresponding exception from the \c InputBuffer class will +/// be thrown. +/// If the wire-format data does not begin with a valid domain name, +/// a corresponding exception from the \c Name class will be thrown. +/// In addition, this constructor internally involves resource allocation, +/// and if it fails a corresponding standard exception will be thrown. +/// +/// According to RFC3597, the Algorithm field must be a non compressed form +/// of domain name. But this implementation accepts a %TSIG RR even if that +/// field is compressed. +/// +/// \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. +/// But this constructor does not use this parameter; if necessary, the caller +/// must check consistency between the length parameter and the actual +/// RDATA length. +TSIG::TSIG(InputBuffer& buffer, size_t) : + impl_(NULL) +{ + Name algorithm(buffer); + + uint8_t time_signed_buf[6]; + buffer.readData(time_signed_buf, sizeof(time_signed_buf)); + const uint64_t time_signed = + (static_cast<uint64_t>(time_signed_buf[0]) << 40 | + static_cast<uint64_t>(time_signed_buf[1]) << 32 | + static_cast<uint64_t>(time_signed_buf[2]) << 24 | + static_cast<uint64_t>(time_signed_buf[3]) << 16 | + static_cast<uint64_t>(time_signed_buf[4]) << 8 | + static_cast<uint64_t>(time_signed_buf[5])); + + const uint16_t fudge = buffer.readUint16(); + + const uint16_t mac_size = buffer.readUint16(); + vector<uint8_t> mac(mac_size); + if (mac_size > 0) { + buffer.readData(&mac[0], mac_size); + } + + const uint16_t original_id = buffer.readUint16(); + const uint16_t error = buffer.readUint16(); + + const uint16_t other_len = buffer.readUint16(); + vector<uint8_t> other_data(other_len); + if (other_len > 0) { + buffer.readData(&other_data[0], other_len); + } + + const Name& canonical_algorithm_name = + (algorithm == TSIGKey::HMACMD5_SHORT_NAME()) ? + TSIGKey::HMACMD5_NAME() : algorithm; + impl_ = new TSIGImpl(canonical_algorithm_name, time_signed, fudge, mac, + original_id, error, other_data); +} + +TSIG::TSIG(const Name& algorithm, uint64_t time_signed, uint16_t fudge, + uint16_t mac_size, const void* mac, uint16_t original_id, + uint16_t error, uint16_t other_len, const void* other_data) : + impl_(NULL) +{ + // Time Signed is a 48-bit value. + if ((time_signed >> 48) != 0) { + isc_throw(OutOfRange, "TSIG Time Signed is too large: " << + time_signed); + } + if ((mac_size == 0 && mac != NULL) || (mac_size > 0 && mac == NULL)) { + isc_throw(InvalidParameter, "TSIG MAC size and data inconsistent"); + } + if ((other_len == 0 && other_data != NULL) || + (other_len > 0 && other_data == NULL)) { + isc_throw(InvalidParameter, + "TSIG Other data length and data inconsistent"); + } + const Name& canonical_algorithm_name = + (algorithm == TSIGKey::HMACMD5_SHORT_NAME()) ? + TSIGKey::HMACMD5_NAME() : algorithm; + impl_ = new TSIGImpl(canonical_algorithm_name, time_signed, fudge, mac_size, + mac, original_id, error, other_len, other_data); +} + +/// \brief The copy constructor. +/// +/// It internally allocates a resource, and if it fails a corresponding +/// standard exception will be thrown. +/// This constructor never throws an exception otherwise. +TSIG::TSIG(const TSIG& source) : Rdata(), impl_(new TSIGImpl(*source.impl_)) +{} + +TSIG& +TSIG::operator=(const TSIG& source) { + if (this == &source) { + return (*this); + } + + TSIGImpl* newimpl = new TSIGImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +TSIG::~TSIG() { + delete impl_; +} + +/// \brief Convert the \c TSIG to a string. +/// +/// The output of this method is formatted as described in the "from string" +/// constructor (\c TSIG(const std::string&))). +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// +/// \return A \c string object that represents the \c TSIG object. +std::string +TSIG::toText() const { + string result; + + result += impl_->algorithm_.toText() + " " + + lexical_cast<string>(impl_->time_signed_) + " " + + lexical_cast<string>(impl_->fudge_) + " " + + lexical_cast<string>(impl_->mac_.size()) + " "; + if (!impl_->mac_.empty()) { + result += encodeBase64(impl_->mac_) + " "; + } + result += lexical_cast<string>(impl_->original_id_) + " "; + result += TSIGError(impl_->error_).toText() + " "; + result += lexical_cast<string>(impl_->other_data_.size()); + if (!impl_->other_data_.empty()) { + result += " " + encodeBase64(impl_->other_data_); + } + + return (result); +} + +// Common sequence of toWire() operations used for the two versions of +// toWire(). +template <typename Output> +void +TSIGImpl::toWireCommon(Output& output) const { + output.writeUint16(time_signed_ >> 32); + output.writeUint32(time_signed_ & 0xffffffff); + output.writeUint16(fudge_); + const uint16_t mac_size = mac_.size(); + output.writeUint16(mac_size); + if (mac_size > 0) { + output.writeData(&mac_[0], mac_size); + } + output.writeUint16(original_id_); + output.writeUint16(error_); + const uint16_t other_len = other_data_.size(); + output.writeUint16(other_len); + if (other_len > 0) { + output.writeData(&other_data_[0], other_len); + } +} + +/// \brief Render the \c TSIG in the wire format without name compression. +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// This method never throws an exception otherwise. +/// +/// \param buffer An output buffer to store the wire data. +void +TSIG::toWire(OutputBuffer& buffer) const { + impl_->algorithm_.toWire(buffer); + impl_->toWireCommon<OutputBuffer>(buffer); +} + +/// \brief Render the \c TSIG in the wire format with taking into account +/// compression. +/// +/// As specified in RFC3597, the Algorithm field (a domain name) will not +/// be compressed. However, the domain name could be a target of compression +/// of other compressible names (though pretty unlikely), the offset +/// information of the algorithm name may be recorded in \c renderer. +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// This method never throws an exception otherwise. +/// +/// \param renderer DNS message rendering context that encapsulates the +/// output buffer and name compression information. +void +TSIG::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeName(impl_->algorithm_, false); + impl_->toWireCommon<AbstractMessageRenderer>(renderer); +} + +// A helper function commonly used for TSIG::compare(). +int +vectorComp(const vector<uint8_t>& v1, const vector<uint8_t>& v2) { + const size_t this_size = v1.size(); + const size_t other_size = v2.size(); + if (this_size != other_size) { + return (this_size < other_size ? -1 : 1); + } + if (this_size > 0) { + return (memcmp(&v1[0], &v2[0], this_size)); + } + return (0); +} + +/// \brief Compare two instances of \c TSIG RDATA. +/// +/// This method compares \c this and the \c other \c TSIG objects +/// in terms of the DNSSEC sorting order as defined in RFC4034, and returns +/// the result as an integer. +/// +/// This method is expected to be used in a polymorphic way, and the +/// parameter to compare against is therefore of the abstract \c Rdata class. +/// However, comparing two \c Rdata objects of different RR types +/// is meaningless, and \c other must point to a \c TSIG object; +/// otherwise, the standard \c bad_cast exception will be thrown. +/// This method never throws an exception otherwise. +/// +/// \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 +TSIG::compare(const Rdata& other) const { + const TSIG& other_tsig = dynamic_cast<const TSIG&>(other); + + const int ncmp = compareNames(impl_->algorithm_, + other_tsig.impl_->algorithm_); + if (ncmp != 0) { + return (ncmp); + } + + if (impl_->time_signed_ != other_tsig.impl_->time_signed_) { + return (impl_->time_signed_ < other_tsig.impl_->time_signed_ ? -1 : 1); + } + if (impl_->fudge_ != other_tsig.impl_->fudge_) { + return (impl_->fudge_ < other_tsig.impl_->fudge_ ? -1 : 1); + } + const int vcmp = vectorComp(impl_->mac_, other_tsig.impl_->mac_); + if (vcmp != 0) { + return (vcmp); + } + if (impl_->original_id_ != other_tsig.impl_->original_id_) { + return (impl_->original_id_ < other_tsig.impl_->original_id_ ? -1 : 1); + } + if (impl_->error_ != other_tsig.impl_->error_) { + return (impl_->error_ < other_tsig.impl_->error_ ? -1 : 1); + } + return (vectorComp(impl_->other_data_, other_tsig.impl_->other_data_)); +} + +const Name& +TSIG::getAlgorithm() const { + return (impl_->algorithm_); +} + +uint64_t +TSIG::getTimeSigned() const { + return (impl_->time_signed_); +} + +uint16_t +TSIG::getFudge() const { + return (impl_->fudge_); +} + +uint16_t +TSIG::getMACSize() const { + return (impl_->mac_.size()); +} + +const void* +TSIG::getMAC() const { + if (!impl_->mac_.empty()) { + return (&impl_->mac_[0]); + } else { + return (NULL); + } +} + +uint16_t +TSIG::getOriginalID() const { + return (impl_->original_id_); +} + +uint16_t +TSIG::getError() const { + return (impl_->error_); +} + +uint16_t +TSIG::getOtherLen() const { + return (impl_->other_data_.size()); +} + +const void* +TSIG::getOtherData() const { + if (!impl_->other_data_.empty()) { + return (&impl_->other_data_[0]); + } else { + return (NULL); + } +} + +} // end of namespace "any" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2010-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/. + +#include <config.h> + +#include <string> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +using namespace std; +using namespace isc::util; + +namespace isc { +namespace dns { +namespace rdata { +namespace ch { + +A::A(const std::string&) { + // TBD +} + +A::A(MasterLexer&, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) +{ + // TBD +} + +A::A(InputBuffer&, size_t) { + // TBD +} + +A::A(const A&) : Rdata() { + // TBD +} + +void +A::toWire(OutputBuffer&) const { + // TBD +} + +void +A::toWire(AbstractMessageRenderer&) const { + // TBD +} + +string +A::toText() const { + // TBD + isc_throw(InvalidRdataText, "Not implemented yet"); +} + +int +A::compare(const Rdata&) const { + return (0); // dummy. TBD +} + +} // end of namespace "ch" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2011-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/. + +#include <config.h> + +#include <string> +#include <sstream> + +#include <util/buffer.h> +#include <util/strutil.h> + +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <boost/lexical_cast.hpp> + +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using boost::lexical_cast; +using namespace isc::util; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +/// \brief Constructor from string. +/// +/// \c afsdb_str must be formatted as follows: +/// \code <subtype> <server name> +/// \endcode +/// where server name field must represent a valid domain name. +/// +/// An example of valid string is: +/// \code "1 server.example.com." \endcode +/// +/// <b>Exceptions</b> +/// +/// \exception InvalidRdataText The number of RDATA fields (must be 2) is +/// incorrect. +/// \exception std::bad_alloc Memory allocation fails. +/// \exception Other The constructor of the \c Name class will throw if the +/// names in the string is invalid. +AFSDB::AFSDB(const std::string& afsdb_str) : + subtype_(0), server_(Name::ROOT_NAME()) +{ + try { + std::istringstream ss(afsdb_str); + MasterLexer lexer; + lexer.pushSource(ss); + + createFromLexer(lexer, NULL); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for AFSDB: " + << afsdb_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct AFSDB from '" << + afsdb_str << "': " << ex.what()); + } +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of an AFSDB RDATA. The SERVER field can be non-absolute if \c origin +/// is non-NULL, in which case \c origin is used to make it absolute. +/// It must not be represented as a quoted string. +/// +/// The SUBTYPE field must be a valid decimal representation of an +/// unsigned 16-bit integer. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and RRTTL constructors if +/// construction of textual fields as these objects fail. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +/// \param origin If non NULL, specifies the origin of SERVER when it +/// is non-absolute. +AFSDB::AFSDB(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + subtype_(0), server_(".") +{ + createFromLexer(lexer, origin); +} + +void +AFSDB::createFromLexer(MasterLexer& lexer, const Name* origin) +{ + const uint32_t num = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (num > 65535) { + isc_throw(InvalidRdataText, "Invalid AFSDB subtype: " << num); + } + subtype_ = static_cast<uint16_t>(num); + + server_ = createNameFromLexer(lexer, origin); +} + +/// \brief Constructor from wire-format data. +/// +/// This constructor doesn't check the validity of the second parameter (rdata +/// length) for parsing. +/// If necessary, the caller will check consistency. +/// +/// \exception std::bad_alloc Memory allocation fails. +/// \exception Other The constructor of the \c Name class will throw if the +/// names in the wire is invalid. +AFSDB::AFSDB(InputBuffer& buffer, size_t) : + subtype_(buffer.readUint16()), server_(buffer) +{} + +/// \brief Copy constructor. +/// +/// \exception std::bad_alloc Memory allocation fails in copying internal +/// member variables (this should be very rare). +AFSDB::AFSDB(const AFSDB& other) : + Rdata(), subtype_(other.subtype_), server_(other.server_) +{} + +AFSDB& +AFSDB::operator=(const AFSDB& source) { + subtype_ = source.subtype_; + server_ = source.server_; + + return (*this); +} + +/// \brief Convert the \c AFSDB to a string. +/// +/// The output of this method is formatted as described in the "from string" +/// constructor (\c AFSDB(const std::string&))). +/// +/// \exception std::bad_alloc Internal resource allocation fails. +/// +/// \return A \c string object that represents the \c AFSDB object. +string +AFSDB::toText() const { + return (lexical_cast<string>(subtype_) + " " + server_.toText()); +} + +/// \brief Render the \c AFSDB in the wire format without name compression. +/// +/// \exception std::bad_alloc Internal resource allocation fails. +/// +/// \param buffer An output buffer to store the wire data. +void +AFSDB::toWire(OutputBuffer& buffer) const { + buffer.writeUint16(subtype_); + server_.toWire(buffer); +} + +/// \brief Render the \c AFSDB in the wire format with taking into account +/// compression. +/// +/// As specified in RFC3597, TYPE AFSDB is not "well-known", the server +/// field (domain name) will not be compressed. +/// +/// \exception std::bad_alloc Internal resource allocation fails. +/// +/// \param renderer DNS message rendering context that encapsulates the +/// output buffer and name compression information. +void +AFSDB::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint16(subtype_); + renderer.writeName(server_, false); +} + +/// \brief Compare two instances of \c AFSDB RDATA. +/// +/// See documentation in \c Rdata. +int +AFSDB::compare(const Rdata& other) const { + const AFSDB& other_afsdb = dynamic_cast<const AFSDB&>(other); + if (subtype_ < other_afsdb.subtype_) { + return (-1); + } else if (subtype_ > other_afsdb.subtype_) { + return (1); + } + + return (compareNames(server_, other_afsdb.server_)); +} + +const Name& +AFSDB::getServer() const { + return (server_); +} + +uint16_t +AFSDB::getSubtype() const { + return (subtype_); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2014-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/. + +#include <config.h> + +#include <boost/lexical_cast.hpp> +#include <boost/algorithm/string.hpp> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/char_string.h> + +using namespace std; +using boost::lexical_cast; +using namespace isc::util; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +struct CAAImpl { + // straightforward representation of CAA RDATA fields + CAAImpl(uint8_t flags, const std::string& tag, + const detail::CharStringData& value) : + flags_(flags), + tag_(tag), + value_(value) + { + if ((sizeof(flags) + 1 + tag_.size() + value_.size()) > 65535) { + isc_throw(InvalidRdataLength, + "CAA Value field is too large: " << value_.size()); + } + } + + uint8_t flags_; + const std::string tag_; + const detail::CharStringData value_; +}; + +// helper function for string and lexer constructors +CAAImpl* +CAA::constructFromLexer(MasterLexer& lexer) { + const uint32_t flags = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (flags > 255) { + isc_throw(InvalidRdataText, + "CAA flags field out of range"); + } + + // Tag field must not be empty. + const std::string tag = + lexer.getNextToken(MasterToken::STRING).getString(); + if (tag.empty()) { + isc_throw(InvalidRdataText, "CAA tag field is empty"); + } else if (tag.size() > 255) { + isc_throw(InvalidRdataText, + "CAA tag field is too large: " << tag.size()); + } + + // Value field may be empty. + detail::CharStringData value; + MasterToken token = lexer.getNextToken(MasterToken::QSTRING, true); + if ((token.getType() != MasterToken::END_OF_FILE) && + (token.getType() != MasterToken::END_OF_LINE)) + { + detail::stringToCharStringData(token.getStringRegion(), value); + } + + return (new CAAImpl(flags, tag, value)); +} + +/// \brief Constructor from string. +/// +/// The given string must represent a valid CAA 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 Flags, Tag and Value fields must be within their valid ranges, +/// but are not constrained to the values defined in RFC6844. The Tag +/// field must not be empty. +/// +/// \throw InvalidRdataText if any fields are missing, out of their +/// valid ranges, incorrect, or empty. +/// +/// \param caa_str A string containing the RDATA to be created +CAA::CAA(const string& caa_str) : + 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 CAAImpl that constructFromLexer() returns. + std::unique_ptr<CAAImpl> impl_ptr; + + try { + std::istringstream ss(caa_str); + 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 CAA: " + << caa_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct CAA from '" << + caa_str << "': " << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of an CAA RDATA. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing +/// field. +/// \throw InvalidRdataText Fields are out of their valid ranges, +/// incorrect, or empty. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +CAA::CAA(MasterLexer& lexer, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(constructFromLexer(lexer)) +{ +} + +/// \brief Constructor from InputBuffer. +/// +/// The passed buffer must contain a valid CAA RDATA. +/// +/// The Flags, Tag and Value fields must be within their valid ranges, +/// but are not constrained to the values defined in RFC6844. The Tag +/// field must not be empty. +CAA::CAA(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len < 2) { + isc_throw(InvalidRdataLength, "CAA record too short"); + } + + const uint8_t flags = buffer.readUint8(); + const uint8_t tag_length = buffer.readUint8(); + rdata_len -= 2; + if (tag_length == 0) { + isc_throw(InvalidRdataText, "CAA tag field is empty"); + } + + if (rdata_len < tag_length) { + isc_throw(InvalidRdataLength, + "RDATA is too short for CAA tag field"); + } + + std::vector<uint8_t> tag_vec(tag_length); + buffer.readData(&tag_vec[0], tag_length); + std::string tag(tag_vec.begin(), tag_vec.end()); + rdata_len -= tag_length; + + detail::CharStringData value; + value.resize(rdata_len); + if (rdata_len > 0) { + buffer.readData(&value[0], rdata_len); + } + + impl_ = new CAAImpl(flags, tag, value); +} + +CAA::CAA(uint8_t flags, const std::string& tag, const std::string& value) : + impl_(NULL) +{ + if (tag.empty()) { + isc_throw(isc::InvalidParameter, + "CAA tag field is empty"); + } else if (tag.size() > 255) { + isc_throw(isc::InvalidParameter, + "CAA tag field is too large: " << tag.size()); + } + + MasterToken::StringRegion region; + region.beg = &value[0]; // note std ensures this works even if str is empty + region.len = value.size(); + + detail::CharStringData value_vec; + detail::stringToCharStringData(region, value_vec); + + impl_ = new CAAImpl(flags, tag, value_vec); +} + +CAA::CAA(const CAA& other) : + Rdata(), impl_(new CAAImpl(*other.impl_)) +{} + +CAA& +CAA::operator=(const CAA& source) { + if (this == &source) { + return (*this); + } + + CAAImpl* newimpl = new CAAImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +CAA::~CAA() { + delete impl_; +} + +void +CAA::toWire(OutputBuffer& buffer) const { + buffer.writeUint8(impl_->flags_); + + // The constructors must ensure that the tag field is not empty. + assert(!impl_->tag_.empty()); + buffer.writeUint8(impl_->tag_.size()); + buffer.writeData(&impl_->tag_[0], impl_->tag_.size()); + + if (!impl_->value_.empty()) { + buffer.writeData(&impl_->value_[0], + impl_->value_.size()); + } +} + +void +CAA::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint8(impl_->flags_); + + // The constructors must ensure that the tag field is not empty. + assert(!impl_->tag_.empty()); + renderer.writeUint8(impl_->tag_.size()); + renderer.writeData(&impl_->tag_[0], impl_->tag_.size()); + + if (!impl_->value_.empty()) { + renderer.writeData(&impl_->value_[0], + impl_->value_.size()); + } +} + +std::string +CAA::toText() const { + std::string result; + + result = lexical_cast<std::string>(static_cast<int>(impl_->flags_)); + result += " " + impl_->tag_; + result += " \"" + detail::charStringDataToString(impl_->value_) + "\""; + + return (result); +} + +int +CAA::compare(const Rdata& other) const { + const CAA& other_caa = dynamic_cast<const CAA&>(other); + + if (impl_->flags_ < other_caa.impl_->flags_) { + return (-1); + } else if (impl_->flags_ > other_caa.impl_->flags_) { + return (1); + } + + // Do a case-insensitive compare of the tag strings. + const int result = boost::ilexicographical_compare + <std::string, std::string>(impl_->tag_, other_caa.impl_->tag_); + if (result != 0) { + return (result); + } + + return (detail::compareCharStringDatas(impl_->value_, + other_caa.impl_->value_)); +} + +uint8_t +CAA::getFlags() const { + return (impl_->flags_); +} + +const std::string& +CAA::getTag() const { + return (impl_->tag_); +} + +const std::vector<uint8_t>& +CAA::getValue() const { + return (impl_->value_); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2010-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/. + +#include <config.h> + +#include <string> + +#include <util/buffer.h> +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using namespace isc::util; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +/// \brief Constructor from string. +/// +/// The given string must represent a valid CNAME 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 CNAME must be absolute since there's no parameter that specifies +/// the origin name; if it is not absolute, \c MissingNameOrigin +/// exception will be thrown. These must not be represented as a quoted +/// string. +/// +/// \throw Others Exception from the Name and RRTTL constructors. +/// \throw InvalidRdataText Other general syntax errors. +CNAME::CNAME(const std::string& namestr) : + // Fill in dummy name and replace it soon below. + cname_(Name::ROOT_NAME()) +{ + try { + std::istringstream ss(namestr); + MasterLexer lexer; + lexer.pushSource(ss); + + cname_ = createNameFromLexer(lexer, NULL); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for CNAME: " + << namestr); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct CNAME from '" << + namestr << "': " << ex.what()); + } +} + +CNAME::CNAME(InputBuffer& buffer, size_t) : + Rdata(), cname_(buffer) +{ + // we don't need rdata_len for parsing. if necessary, the caller will + // check consistency. +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of a CNAME RDATA. The CNAME field can be +/// non-absolute if \c origin is non-NULL, in which case \c origin is +/// used to make it absolute. It must not be represented as a quoted +/// string. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and RRTTL constructors if +/// construction of textual fields as these objects fail. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +/// \param origin If non NULL, specifies the origin of CNAME when it +/// is non-absolute. +CNAME::CNAME(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + cname_(createNameFromLexer(lexer, origin)) +{} + +CNAME::CNAME(const CNAME& other) : + Rdata(), cname_(other.cname_) +{} + +CNAME::CNAME(const Name& cname) : + cname_(cname) +{} + +void +CNAME::toWire(OutputBuffer& buffer) const { + cname_.toWire(buffer); +} + +void +CNAME::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeName(cname_); +} + +string +CNAME::toText() const { + return (cname_.toText()); +} + +int +CNAME::compare(const Rdata& other) const { + const CNAME& other_cname = dynamic_cast<const CNAME&>(other); + + return (compareNames(cname_, other_cname.cname_)); +} + +const Name& +CNAME::getCname() const { + return (cname_); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2011-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/. + +#include <config.h> + +#include <string> + +#include <util/buffer.h> +#include <util/encode/hex.h> + +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/ds_like.h> + +using namespace std; +using namespace isc::util; +using namespace isc::util::encode; +using namespace isc::dns::rdata::generic::detail; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +/// \brief Constructor from string. +/// +/// A copy of the implementation object is allocated and constructed. +DLV::DLV(const std::string& ds_str) : + impl_(new DLVImpl(ds_str)) +{} + +/// \brief Constructor from wire-format data. +/// +/// A copy of the implementation object is allocated and constructed. +DLV::DLV(InputBuffer& buffer, size_t rdata_len) : + impl_(new DLVImpl(buffer, rdata_len)) +{} + +DLV::DLV(MasterLexer& lexer, const Name* origin, MasterLoader::Options options, + MasterLoaderCallbacks& callbacks) : + impl_(new DLVImpl(lexer, origin, options, callbacks)) +{} + +/// \brief Copy constructor +/// +/// A copy of the implementation object is allocated and constructed. +DLV::DLV(const DLV& source) : + Rdata(), impl_(new DLVImpl(*source.impl_)) +{} + +/// \brief Assignment operator +/// +/// PIMPL-induced logic +DLV& +DLV::operator=(const DLV& source) { + if (this == &source) { + return (*this); + } + + DLVImpl* newimpl = new DLVImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +/// \brief Destructor +/// +/// Deallocates an internal resource. +DLV::~DLV() { + delete impl_; +} + +/// \brief Convert the \c DLV to a string. +/// +/// A pass-thru to the corresponding implementation method. +string +DLV::toText() const { + return (impl_->toText()); +} + +/// \brief Render the \c DLV in the wire format to a OutputBuffer object +/// +/// A pass-thru to the corresponding implementation method. +void +DLV::toWire(OutputBuffer& buffer) const { + impl_->toWire(buffer); +} + +/// \brief Render the \c DLV in the wire format to a AbstractMessageRenderer +/// object +/// +/// A pass-thru to the corresponding implementation method. +void +DLV::toWire(AbstractMessageRenderer& renderer) const { + impl_->toWire(renderer); +} + +/// \brief Compare two instances of \c DLV RDATA. +/// +/// The type check is performed here. Otherwise, a pass-thru to the +/// corresponding implementation method. +int +DLV::compare(const Rdata& other) const { + const DLV& other_ds = dynamic_cast<const DLV&>(other); + + return (impl_->compare(*other_ds.impl_)); +} + +/// \brief Tag accessor +uint16_t +DLV::getTag() const { + return (impl_->getTag()); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2010-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/. + +#include <config.h> + +#include <string> + +#include <util/buffer.h> +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using namespace isc::util; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +/// \brief Constructor from string. +/// +/// The given string must represent a valid DNAME 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 TARGET must be absolute since there's no parameter that specifies +/// the origin name; if it is not absolute, \c MissingNameOrigin +/// exception will be thrown. These must not be represented as a quoted +/// string. +/// +/// \throw Others Exception from the Name and RRTTL constructors. +/// \throw InvalidRdataText Other general syntax errors. +DNAME::DNAME(const std::string& namestr) : + // Fill in dummy name and replace it soon below. + dname_(Name::ROOT_NAME()) +{ + try { + std::istringstream ss(namestr); + MasterLexer lexer; + lexer.pushSource(ss); + + dname_ = createNameFromLexer(lexer, NULL); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for DNAME: " + << namestr); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct DNAME from '" << + namestr << "': " << ex.what()); + } +} + +DNAME::DNAME(InputBuffer& buffer, size_t) : + dname_(buffer) +{ + // we don't need rdata_len for parsing. if necessary, the caller will + // check consistency. +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of a DNAME RDATA. The TARGET field can be +/// non-absolute if \c origin is non-NULL, in which case \c origin is +/// used to make it absolute. It must not be represented as a quoted +/// string. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and RRTTL constructors if +/// construction of textual fields as these objects fail. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +/// \param origin If non NULL, specifies the origin of TARGET when it +/// is non-absolute. +DNAME::DNAME(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + dname_(createNameFromLexer(lexer, origin)) +{} + +DNAME::DNAME(const DNAME& other) : + Rdata(), dname_(other.dname_) +{} + +DNAME::DNAME(const Name& dname) : + dname_(dname) +{} + +void +DNAME::toWire(OutputBuffer& buffer) const { + dname_.toWire(buffer); +} + +void +DNAME::toWire(AbstractMessageRenderer& renderer) const { + // Type DNAME is not "well-known", and name compression must be disabled + // per RFC3597. + renderer.writeName(dname_, false); +} + +string +DNAME::toText() const { + return (dname_.toText()); +} + +int +DNAME::compare(const Rdata& other) const { + const DNAME& other_dname = dynamic_cast<const DNAME&>(other); + + return (compareNames(dname_, other_dname.dname_)); +} + +const Name& +DNAME::getDname() const { + return (dname_); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2010-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/. + +#include <config.h> + +#include <iostream> +#include <string> +#include <sstream> +#include <vector> + +#include <boost/lexical_cast.hpp> +#include <boost/foreach.hpp> + +#include <util/encode/base64.h> +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <memory> + +#include <stdio.h> +#include <time.h> + +using namespace std; +using namespace isc::util; +using namespace isc::util::encode; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +struct DNSKEYImpl { + // straightforward representation of DNSKEY RDATA fields + DNSKEYImpl(uint16_t flags, uint8_t protocol, uint8_t algorithm, + const vector<uint8_t>& keydata) : + flags_(flags), protocol_(protocol), algorithm_(algorithm), + keydata_(keydata) + {} + + uint16_t flags_; + uint8_t protocol_; + uint8_t algorithm_; + const vector<uint8_t> keydata_; +}; + +/// \brief Constructor from string. +/// +/// The given string must represent a valid DNSKEY 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 Protocol and Algorithm fields must be within their valid +/// ranges. The Public Key field must be present and must contain a +/// Base64 encoding of the public key. Whitespace is allowed within the +/// Base64 text. +/// +/// It is okay for the key data to be missing. Note: BIND 9 also accepts +/// DNSKEY missing key data. While the RFC is silent in this case, and it +/// may be debatable what an implementation should do, but since this field +/// is algorithm dependent and this implementations doesn't reject unknown +/// algorithms, it's lenient here. +/// +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// +/// \param dnskey_str A string containing the RDATA to be created +DNSKEY::DNSKEY(const std::string& dnskey_str) : + 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 DNSKEYImpl that constructFromLexer() returns. + std::unique_ptr<DNSKEYImpl> impl_ptr; + + try { + std::istringstream ss(dnskey_str); + 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 DNSKEY: " << dnskey_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, + "Failed to construct DNSKEY from '" << dnskey_str << "': " + << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +/// \brief Constructor from InputBuffer. +/// +/// The passed buffer must contain a valid DNSKEY RDATA. +/// +/// The Protocol and Algorithm fields are not checked for unknown +/// values. It is okay for the key data to be missing (see the description +/// of the constructor from string). +DNSKEY::DNSKEY(InputBuffer& buffer, size_t rdata_len) : + impl_(NULL) +{ + if (rdata_len < 4) { + isc_throw(InvalidRdataLength, "DNSKEY too short: " << rdata_len); + } + + const uint16_t flags = buffer.readUint16(); + const uint16_t protocol = buffer.readUint8(); + const uint16_t algorithm = buffer.readUint8(); + + rdata_len -= 4; + + vector<uint8_t> keydata; + // If key data is missing, it's OK. See the API documentation of the + // constructor. + if (rdata_len > 0) { + keydata.resize(rdata_len); + buffer.readData(&keydata[0], rdata_len); + } + + impl_ = new DNSKEYImpl(flags, protocol, algorithm, keydata); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of an DNSKEY RDATA. +/// +/// See \c DNSKEY::DNSKEY(const std::string&) for description of the +/// expected RDATA fields. +/// +/// \throw MasterLexer::LexerError General parsing error such as +/// missing field. +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +DNSKEY::DNSKEY(MasterLexer& lexer, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(NULL) +{ + impl_ = constructFromLexer(lexer); +} + +DNSKEYImpl* +DNSKEY::constructFromLexer(MasterLexer& lexer) { + const uint32_t flags = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (flags > 0xffff) { + isc_throw(InvalidRdataText, + "DNSKEY flags out of range: " << flags); + } + + const uint32_t protocol = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (protocol > 0xff) { + isc_throw(InvalidRdataText, + "DNSKEY protocol out of range: " << protocol); + } + + const uint32_t algorithm = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (algorithm > 0xff) { + isc_throw(InvalidRdataText, + "DNSKEY algorithm out of range: " << algorithm); + } + + std::string keydata_str; + std::string keydata_substr; + 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. + + token.getString(keydata_substr); + keydata_str.append(keydata_substr); + } + + lexer.ungetToken(); + + vector<uint8_t> keydata; + // If key data is missing, it's OK. See the API documentation of the + // constructor. + if (keydata_str.size() > 0) { + decodeBase64(keydata_str, keydata); + } + + return (new DNSKEYImpl(flags, protocol, algorithm, keydata)); +} + +DNSKEY::DNSKEY(const DNSKEY& source) : + Rdata(), impl_(new DNSKEYImpl(*source.impl_)) +{} + +DNSKEY& +DNSKEY::operator=(const DNSKEY& source) { + if (this == &source) { + return (*this); + } + + DNSKEYImpl* newimpl = new DNSKEYImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +DNSKEY::~DNSKEY() { + delete impl_; +} + +string +DNSKEY::toText() const { + return (boost::lexical_cast<string>(static_cast<int>(impl_->flags_)) + + " " + boost::lexical_cast<string>(static_cast<int>(impl_->protocol_)) + + " " + boost::lexical_cast<string>(static_cast<int>(impl_->algorithm_)) + + " " + encodeBase64(impl_->keydata_)); +} + +void +DNSKEY::toWire(OutputBuffer& buffer) const { + buffer.writeUint16(impl_->flags_); + buffer.writeUint8(impl_->protocol_); + buffer.writeUint8(impl_->algorithm_); + buffer.writeData(&impl_->keydata_[0], impl_->keydata_.size()); +} + +void +DNSKEY::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint16(impl_->flags_); + renderer.writeUint8(impl_->protocol_); + renderer.writeUint8(impl_->algorithm_); + renderer.writeData(&impl_->keydata_[0], impl_->keydata_.size()); +} + +int +DNSKEY::compare(const Rdata& other) const { + const DNSKEY& other_dnskey = dynamic_cast<const DNSKEY&>(other); + + if (impl_->flags_ != other_dnskey.impl_->flags_) { + return (impl_->flags_ < other_dnskey.impl_->flags_ ? -1 : 1); + } + if (impl_->protocol_ != other_dnskey.impl_->protocol_) { + return (impl_->protocol_ < other_dnskey.impl_->protocol_ ? -1 : 1); + } + if (impl_->algorithm_ != other_dnskey.impl_->algorithm_) { + return (impl_->algorithm_ < other_dnskey.impl_->algorithm_ ? -1 : 1); + } + + const size_t this_len = impl_->keydata_.size(); + const size_t other_len = other_dnskey.impl_->keydata_.size(); + const size_t cmplen = min(this_len, other_len); + if (cmplen == 0) { + return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1); + } + const int cmp = memcmp(&impl_->keydata_[0], + &other_dnskey.impl_->keydata_[0], cmplen); + if (cmp != 0) { + return (cmp); + } else { + return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1); + } +} + +uint16_t +DNSKEY::getTag() const { + if (impl_->algorithm_ == 1) { + // See RFC 4034 appendix B.1 for why the key data must contain + // at least 4 bytes with RSA/MD5: 3 trailing bytes to extract + // the tag from, and 1 byte of exponent length subfield before + // modulus. + const int len = impl_->keydata_.size(); + if (len < 4) { + isc_throw(isc::OutOfRange, + "DNSKEY keydata too short for tag extraction"); + } + + return ((impl_->keydata_[len - 3] << 8) + impl_->keydata_[len - 2]); + } + + uint32_t ac = impl_->flags_; + ac += (impl_->protocol_ << 8); + ac += impl_->algorithm_; + + const size_t size = impl_->keydata_.size(); + for (size_t i = 0; i < size; i ++) { + ac += (i & 1) ? impl_->keydata_[i] : (impl_->keydata_[i] << 8); + } + ac += (ac >> 16) & 0xffff; + return (ac & 0xffff); +} + +uint16_t +DNSKEY::getFlags() const { + return (impl_->flags_); +} + +uint8_t +DNSKEY::getAlgorithm() const { + return (impl_->algorithm_); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2011-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/. + +#include <config.h> + +#include <string> + +#include <util/buffer.h> +#include <util/encode/hex.h> + +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/ds_like.h> + +using namespace std; +using namespace isc::util; +using namespace isc::util::encode; +using namespace isc::dns::rdata::generic::detail; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +DS::DS(const std::string& ds_str) : + impl_(new DSImpl(ds_str)) +{} + +DS::DS(InputBuffer& buffer, size_t rdata_len) : + impl_(new DSImpl(buffer, rdata_len)) +{} + +DS::DS(MasterLexer& lexer, const Name* origin, MasterLoader::Options options, + MasterLoaderCallbacks& callbacks) : + impl_(new DSImpl(lexer, origin, options, callbacks)) +{} + +DS::DS(const DS& source) : + Rdata(), impl_(new DSImpl(*source.impl_)) +{} + +DS& +DS::operator=(const DS& source) { + if (this == &source) { + return (*this); + } + + DSImpl* newimpl = new DSImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +DS::~DS() { + delete impl_; +} + +string +DS::toText() const { + return (impl_->toText()); +} + +void +DS::toWire(OutputBuffer& buffer) const { + impl_->toWire(buffer); +} + +void +DS::toWire(AbstractMessageRenderer& renderer) const { + impl_->toWire(renderer); +} + +int +DS::compare(const Rdata& other) const { + const DS& other_ds = dynamic_cast<const DS&>(other); + + return (impl_->compare(*other_ds.impl_)); +} + +uint16_t +DS::getTag() const { + return (impl_->getTag()); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2011-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/. + +#include <config.h> + +#include <exceptions/exceptions.h> +#include <dns/exceptions.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata/generic/detail/char_string.h> +#include <util/buffer.h> + +using namespace std; +using namespace isc::util; +using namespace isc::dns; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +class HINFOImpl { +public: + HINFOImpl(const std::string& hinfo_str) { + std::istringstream ss(hinfo_str); + MasterLexer lexer; + lexer.pushSource(ss); + + try { + parseHINFOData(lexer); + // Should be at end of data now + if (lexer.getNextToken(MasterToken::QSTRING, true).getType() != + MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, + "Invalid HINFO text format: too many fields."); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct HINFO RDATA from " + << hinfo_str << "': " << ex.what()); + } + } + + HINFOImpl(InputBuffer& buffer, size_t rdata_len) { + rdata_len -= detail::bufferToCharString(buffer, rdata_len, cpu); + rdata_len -= detail::bufferToCharString(buffer, rdata_len, os); + if (rdata_len != 0) { + isc_throw(isc::dns::DNSMessageFORMERR, "Error in parsing " << + "HINFO RDATA: bytes left at end: " << + static_cast<int>(rdata_len)); + } + } + + HINFOImpl(MasterLexer& lexer) + { + parseHINFOData(lexer); + } + +private: + void + parseHINFOData(MasterLexer& lexer) { + MasterToken token = lexer.getNextToken(MasterToken::QSTRING); + stringToCharString(token.getStringRegion(), cpu); + token = lexer.getNextToken(MasterToken::QSTRING); + stringToCharString(token.getStringRegion(), os); + } + +public: + detail::CharString cpu; + detail::CharString os; +}; + +HINFO::HINFO(const std::string& hinfo_str) : impl_(new HINFOImpl(hinfo_str)) +{} + + +HINFO::HINFO(InputBuffer& buffer, size_t rdata_len) : + impl_(new HINFOImpl(buffer, rdata_len)) +{} + +HINFO::HINFO(const HINFO& source): + Rdata(), impl_(new HINFOImpl(*source.impl_)) +{ +} + +HINFO::HINFO(MasterLexer& lexer, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(new HINFOImpl(lexer)) +{} + +HINFO& +HINFO::operator=(const HINFO& source) +{ + impl_.reset(new HINFOImpl(*source.impl_)); + return (*this); +} + +HINFO::~HINFO() { +} + +std::string +HINFO::toText() const { + string result; + result += "\""; + result += detail::charStringToString(impl_->cpu); + result += "\" \""; + result += detail::charStringToString(impl_->os); + result += "\""; + return (result); +} + +void +HINFO::toWire(OutputBuffer& buffer) const { + toWireHelper(buffer); +} + +void +HINFO::toWire(AbstractMessageRenderer& renderer) const { + toWireHelper(renderer); +} + +int +HINFO::compare(const Rdata& other) const { + const HINFO& other_hinfo = dynamic_cast<const HINFO&>(other); + + const int cmp = compareCharStrings(impl_->cpu, other_hinfo.impl_->cpu); + if (cmp != 0) { + return (cmp); + } + return (compareCharStrings(impl_->os, other_hinfo.impl_->os)); +} + +const std::string +HINFO::getCPU() const { + return (detail::charStringToString(impl_->cpu)); +} + +const std::string +HINFO::getOS() const { + return (detail::charStringToString(impl_->os)); +} + +template <typename T> +void +HINFO::toWireHelper(T& outputer) const { + outputer.writeData(&impl_->cpu[0], impl_->cpu.size()); + outputer.writeData(&impl_->os[0], impl_->os.size()); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2011-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/. + +#include <config.h> + +#include <string> +#include <sstream> + +#include <util/buffer.h> + +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +/// \brief Constructor from string. +/// +/// \c minfo_str must be formatted as follows: +/// \code <rmailbox name> <emailbox name> +/// \endcode +/// where both fields must represent a valid domain name. +/// +/// An example of valid string is: +/// \code "rmail.example.com. email.example.com." \endcode +/// +/// \throw InvalidRdataText The number of RDATA fields (must be 2) is +/// incorrect. +/// \throw std::bad_alloc Memory allocation for names fails. +/// \throw Other The constructor of the \c Name class will throw if the +/// names in the string is invalid. +MINFO::MINFO(const std::string& minfo_str) : + // We cannot construct both names in the initialization list due to the + // necessary text processing, so we have to initialize them with a dummy + // name and replace them later. + rmailbox_(Name::ROOT_NAME()), emailbox_(Name::ROOT_NAME()) +{ + try { + std::istringstream ss(minfo_str); + MasterLexer lexer; + lexer.pushSource(ss); + + rmailbox_ = createNameFromLexer(lexer, NULL); + emailbox_ = createNameFromLexer(lexer, NULL); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for MINFO: " + << minfo_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct MINFO from '" << + minfo_str << "': " << ex.what()); + } +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of an MINFO RDATA. The RMAILBOX and EMAILBOX fields can be non-absolute +/// if \c origin is non-NULL, in which case \c origin is used to make them +/// absolute. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and constructors if construction of +/// textual fields as these objects fail. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +/// \param origin If non NULL, specifies the origin of SERVER when it +/// is non-absolute. +MINFO::MINFO(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + rmailbox_(createNameFromLexer(lexer, origin)), + emailbox_(createNameFromLexer(lexer, origin)) +{ +} + +/// \brief Constructor from wire-format data. +/// +/// This constructor doesn't check the validity of the second parameter (rdata +/// length) for parsing. +/// If necessary, the caller will check consistency. +/// +/// \throw std::bad_alloc Memory allocation for names fails. +/// \throw Other The constructor of the \c Name class will throw if the +/// names in the wire is invalid. +MINFO::MINFO(InputBuffer& buffer, size_t) : + rmailbox_(buffer), emailbox_(buffer) +{} + +/// \brief Copy constructor. +/// +/// \throw std::bad_alloc Memory allocation fails in copying internal +/// member variables (this should be very rare). +MINFO::MINFO(const MINFO& other) : + Rdata(), rmailbox_(other.rmailbox_), emailbox_(other.emailbox_) +{} + +/// \brief Convert the \c MINFO to a string. +/// +/// The output of this method is formatted as described in the "from string" +/// constructor (\c MINFO(const std::string&))). +/// +/// \throw std::bad_alloc Internal resource allocation fails. +/// +/// \return A \c string object that represents the \c MINFO object. +std::string +MINFO::toText() const { + return (rmailbox_.toText() + " " + emailbox_.toText()); +} + +/// \brief Render the \c MINFO in the wire format without name compression. +/// +/// \throw std::bad_alloc Internal resource allocation fails. +/// +/// \param buffer An output buffer to store the wire data. +void +MINFO::toWire(OutputBuffer& buffer) const { + rmailbox_.toWire(buffer); + emailbox_.toWire(buffer); +} + +MINFO& +MINFO::operator=(const MINFO& source) { + rmailbox_ = source.rmailbox_; + emailbox_ = source.emailbox_; + + return (*this); +} + +/// \brief Render the \c MINFO in the wire format with taking into account +/// compression. +/// +/// As specified in RFC3597, TYPE MINFO is "well-known", the rmailbox and +/// emailbox fields (domain names) will be compressed. +/// +/// \throw std::bad_alloc Internal resource allocation fails. +/// +/// \param renderer DNS message rendering context that encapsulates the +/// output buffer and name compression information. +void +MINFO::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeName(rmailbox_); + renderer.writeName(emailbox_); +} + +/// \brief Compare two instances of \c MINFO RDATA. +/// +/// See documentation in \c Rdata. +int +MINFO::compare(const Rdata& other) const { + const MINFO& other_minfo = dynamic_cast<const MINFO&>(other); + + const int cmp = compareNames(rmailbox_, other_minfo.rmailbox_); + if (cmp != 0) { + return (cmp); + } + return (compareNames(emailbox_, other_minfo.emailbox_)); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2010-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/. + +#include <config.h> + +#include <string> + +#include <boost/lexical_cast.hpp> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using boost::lexical_cast; +using namespace isc::util; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +MX::MX(InputBuffer& buffer, size_t) : + preference_(buffer.readUint16()), mxname_(buffer) +{ + // we don't need rdata_len for parsing. if necessary, the caller will + // check consistency. +} + +/// \brief Constructor from string. +/// +/// The given string must represent a valid MX 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 EXCHANGE name must be absolute since there's no parameter that +/// specifies the origin name; if it is not absolute, \c MissingNameOrigin +/// exception will be thrown. It must not be represented as a quoted +/// string. +/// +/// See the construction that takes \c MasterLexer for other fields. +/// +/// \throw Others Exception from the Name and RRTTL constructors. +/// \throw InvalidRdataText Other general syntax errors. +MX::MX(const std::string& mx_str) : + // Fill in dummy name and replace them soon below. + preference_(0), mxname_(Name::ROOT_NAME()) +{ + try { + std::istringstream ss(mx_str); + MasterLexer lexer; + lexer.pushSource(ss); + + constructFromLexer(lexer, NULL); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for MX: " + << mx_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct MX from '" << + mx_str << "': " << ex.what()); + } +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of an MX RDATA. The EXCHANGE field can be non-absolute if \c origin +/// is non-NULL, in which case \c origin is used to make it absolute. +/// It must not be represented as a quoted string. +/// +/// The PREFERENCE field must be a valid decimal representation of an +/// unsigned 16-bit integer. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and RRTTL constructors if +/// construction of textual fields as these objects fail. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +/// \param origin If non NULL, specifies the origin of EXCHANGE when it +/// is non-absolute. +MX::MX(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + preference_(0), mxname_(Name::ROOT_NAME()) +{ + constructFromLexer(lexer, origin); +} + +void +MX::constructFromLexer(MasterLexer& lexer, const Name* origin) { + const uint32_t num = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (num > 65535) { + isc_throw(InvalidRdataText, "Invalid MX preference: " << num); + } + preference_ = static_cast<uint16_t>(num); + + mxname_ = createNameFromLexer(lexer, origin); +} + +MX::MX(uint16_t preference, const Name& mxname) : + preference_(preference), mxname_(mxname) +{} + +MX::MX(const MX& other) : + Rdata(), preference_(other.preference_), mxname_(other.mxname_) +{} + +void +MX::toWire(OutputBuffer& buffer) const { + buffer.writeUint16(preference_); + mxname_.toWire(buffer); +} + +void +MX::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint16(preference_); + renderer.writeName(mxname_); +} + +string +MX::toText() const { + return (lexical_cast<string>(preference_) + " " + mxname_.toText()); +} + +int +MX::compare(const Rdata& other) const { + const MX& other_mx = dynamic_cast<const MX&>(other); + + if (preference_ < other_mx.preference_) { + return (-1); + } else if (preference_ > other_mx.preference_) { + return (1); + } + + return (compareNames(mxname_, other_mx.mxname_)); +} + +const Name& +MX::getMXName() const { + return (mxname_); +} + +uint16_t +MX::getMXPref() const { + return (preference_); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2011-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/. + +#include <config.h> + +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata/generic/detail/char_string.h> +#include <exceptions/exceptions.h> + +#include <string> +#include <boost/lexical_cast.hpp> + +using namespace std; +using boost::lexical_cast; +using namespace isc::util; +using namespace isc::dns; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +class NAPTRImpl { +public: + NAPTRImpl() : order(0), preference(0), replacement(".") {} + + NAPTRImpl(InputBuffer& buffer, size_t rdata_len) : replacement(".") { + if (rdata_len < 4 || buffer.getLength() - buffer.getPosition() < 4) { + isc_throw(isc::dns::DNSMessageFORMERR, "Error in parsing " + "NAPTR RDATA wire format: insufficient length "); + } + order = buffer.readUint16(); + preference = buffer.readUint16(); + rdata_len -= 4; + + rdata_len -= detail::bufferToCharString(buffer, rdata_len, flags); + rdata_len -= detail::bufferToCharString(buffer, rdata_len, services); + rdata_len -= detail::bufferToCharString(buffer, rdata_len, regexp); + replacement = Name(buffer); + if (rdata_len < 1) { + isc_throw(isc::dns::DNSMessageFORMERR, "Error in parsing " + "NAPTR RDATA wire format: missing replacement name"); + } + rdata_len -= replacement.getLength(); + + if (rdata_len != 0) { + isc_throw(isc::dns::DNSMessageFORMERR, "Error in parsing " << + "NAPTR RDATA: bytes left at end: " << + static_cast<int>(rdata_len)); + } + } + + NAPTRImpl(const std::string& naptr_str) : replacement(".") { + std::istringstream ss(naptr_str); + MasterLexer lexer; + lexer.pushSource(ss); + + try { + parseNAPTRData(lexer); + // Should be at end of data now + if (lexer.getNextToken(MasterToken::QSTRING, true).getType() != + MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, + "Invalid NAPTR text format: too many fields."); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct NAPTR RDATA from " + << naptr_str << "': " << ex.what()); + } + } + + NAPTRImpl(MasterLexer& lexer) : replacement(".") + { + parseNAPTRData(lexer); + } + +private: + void + parseNAPTRData(MasterLexer& lexer) { + MasterToken token = lexer.getNextToken(MasterToken::NUMBER); + if (token.getNumber() > 65535) { + isc_throw(InvalidRdataText, + "Invalid NAPTR text format: order out of range: " + << token.getNumber()); + } + order = token.getNumber(); + token = lexer.getNextToken(MasterToken::NUMBER); + if (token.getNumber() > 65535) { + isc_throw(InvalidRdataText, + "Invalid NAPTR text format: preference out of range: " + << token.getNumber()); + } + preference = token.getNumber(); + + token = lexer.getNextToken(MasterToken::QSTRING); + stringToCharString(token.getStringRegion(), flags); + token = lexer.getNextToken(MasterToken::QSTRING); + stringToCharString(token.getStringRegion(), services); + token = lexer.getNextToken(MasterToken::QSTRING); + stringToCharString(token.getStringRegion(), regexp); + + token = lexer.getNextToken(MasterToken::STRING); + replacement = Name(token.getString()); + } + + +public: + uint16_t order; + uint16_t preference; + detail::CharString flags; + detail::CharString services; + detail::CharString regexp; + Name replacement; +}; + +NAPTR::NAPTR(InputBuffer& buffer, size_t rdata_len) : + impl_(new NAPTRImpl(buffer, rdata_len)) +{} + +NAPTR::NAPTR(const std::string& naptr_str) : impl_(new NAPTRImpl(naptr_str)) +{} + +NAPTR::NAPTR(MasterLexer& lexer, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(new NAPTRImpl(lexer)) +{} + +NAPTR::NAPTR(const NAPTR& naptr) : Rdata(), + impl_(new NAPTRImpl(*naptr.impl_)) +{} + +NAPTR& +NAPTR::operator=(const NAPTR& source) +{ + impl_.reset(new NAPTRImpl(*source.impl_)); + return (*this); +} + +NAPTR::~NAPTR() { +} + +void +NAPTR::toWire(OutputBuffer& buffer) const { + toWireHelper(buffer); + impl_->replacement.toWire(buffer); +} + +void +NAPTR::toWire(AbstractMessageRenderer& renderer) const { + toWireHelper(renderer); + // Type NAPTR is not "well-known", and name compression must be disabled + // per RFC3597. + renderer.writeName(impl_->replacement, false); +} + +string +NAPTR::toText() const { + string result; + result += lexical_cast<string>(impl_->order); + result += " "; + result += lexical_cast<string>(impl_->preference); + result += " \""; + result += detail::charStringToString(impl_->flags); + result += "\" \""; + result += detail::charStringToString(impl_->services); + result += "\" \""; + result += detail::charStringToString(impl_->regexp); + result += "\" "; + result += impl_->replacement.toText(); + return (result); +} + +int +NAPTR::compare(const Rdata& other) const { + const NAPTR other_naptr = dynamic_cast<const NAPTR&>(other); + + if (impl_->order < other_naptr.impl_->order) { + return (-1); + } else if (impl_->order > other_naptr.impl_->order) { + return (1); + } + + if (impl_->preference < other_naptr.impl_->preference) { + return (-1); + } else if (impl_->preference > other_naptr.impl_->preference) { + return (1); + } + + const int fcmp = detail::compareCharStrings(impl_->flags, + other_naptr.impl_->flags); + if (fcmp != 0) { + return (fcmp); + } + + const int scmp = detail::compareCharStrings(impl_->services, + other_naptr.impl_->services); + if (scmp != 0) { + return (scmp); + } + + const int rcmp = detail::compareCharStrings(impl_->regexp, + other_naptr.impl_->regexp); + if (rcmp != 0) { + return (rcmp); + } + + return (compareNames(impl_->replacement, other_naptr.impl_->replacement)); +} + +uint16_t +NAPTR::getOrder() const { + return (impl_->order); +} + +uint16_t +NAPTR::getPreference() const { + return (impl_->preference); +} + +const std::string +NAPTR::getFlags() const { + return (detail::charStringToString(impl_->flags)); +} + +const std::string +NAPTR::getServices() const { + return (detail::charStringToString(impl_->services)); +} + +const std::string +NAPTR::getRegexp() const { + return (detail::charStringToString(impl_->regexp)); +} + +const Name& +NAPTR::getReplacement() const { + return (impl_->replacement); +} + +template <typename T> +void +NAPTR::toWireHelper(T& outputer) const { + outputer.writeUint16(impl_->order); + outputer.writeUint16(impl_->preference); + + outputer.writeData(&impl_->flags[0], impl_->flags.size()); + outputer.writeData(&impl_->services[0], impl_->services.size()); + outputer.writeData(&impl_->regexp[0], impl_->regexp.size()); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2010-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/. + +#include <config.h> + +#include <string> + +#include <util/buffer.h> +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using namespace isc::util; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +/// \brief Constructor from string. +/// +/// The given string must represent a valid NS 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 NSDNAME must be absolute since there's no parameter that +/// specifies the origin name; if it is not absolute, \c +/// MissingNameOrigin exception will be thrown. These must not be +/// represented as a quoted string. +/// +/// \throw Others Exception from the Name and RRTTL constructors. +/// \throw InvalidRdataText Other general syntax errors. +NS::NS(const std::string& namestr) : + // Fill in dummy name and replace them soon below. + nsname_(Name::ROOT_NAME()) +{ + try { + std::istringstream ss(namestr); + MasterLexer lexer; + lexer.pushSource(ss); + + nsname_ = createNameFromLexer(lexer, NULL); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for NS: " + << namestr); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct NS from '" << + namestr << "': " << ex.what()); + } +} + +NS::NS(InputBuffer& buffer, size_t) : + nsname_(buffer) +{ + // we don't need rdata_len for parsing. if necessary, the caller will + // check consistency. +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of an NS RDATA. The NSDNAME field can be +/// non-absolute if \c origin is non-NULL, in which case \c origin is +/// used to make it absolute. It must not be represented as a quoted +/// string. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and RRTTL constructors if +/// construction of textual fields as these objects fail. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +/// \param origin If non NULL, specifies the origin of NSDNAME when it +/// is non-absolute. +NS::NS(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + nsname_(createNameFromLexer(lexer, origin)) +{} + +NS::NS(const NS& other) : + Rdata(), nsname_(other.nsname_) +{} + +void +NS::toWire(OutputBuffer& buffer) const { + nsname_.toWire(buffer); +} + +void +NS::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeName(nsname_); +} + +string +NS::toText() const { + return (nsname_.toText()); +} + +int +NS::compare(const Rdata& other) const { + const NS& other_ns = dynamic_cast<const NS&>(other); + + return (compareNames(nsname_, other_ns.nsname_)); +} + +const Name& +NS::getNSName() const { + return (nsname_); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2010-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/. + +#include <config.h> + +#include <iostream> +#include <iomanip> +#include <string> +#include <sstream> +#include <vector> +#include <cassert> + +#include <boost/lexical_cast.hpp> + +#include <util/encode/base32hex.h> +#include <util/encode/hex.h> +#include <util/buffer.h> + +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rrtype.h> +#include <dns/rrttl.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata/generic/detail/nsec_bitmap.h> +#include <dns/rdata/generic/detail/nsec3param_common.h> + +#include <memory> + +#include <stdio.h> +#include <time.h> + +using namespace std; +using namespace isc::dns::rdata::generic::detail::nsec; +using namespace isc::dns::rdata::generic::detail::nsec3; +using namespace isc::util::encode; +using namespace isc::util; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +struct NSEC3Impl { + // straightforward representation of NSEC3 RDATA fields + NSEC3Impl(uint8_t hashalg, uint8_t flags, uint16_t iterations, + vector<uint8_t>salt, vector<uint8_t>next, + vector<uint8_t> typebits) : + hashalg_(hashalg), flags_(flags), iterations_(iterations), + salt_(salt), next_(next), typebits_(typebits) + {} + + const uint8_t hashalg_; + const uint8_t flags_; + const uint16_t iterations_; + const vector<uint8_t> salt_; + const vector<uint8_t> next_; + const vector<uint8_t> typebits_; +}; + +/// \brief Constructor from string. +/// +/// The given string must represent a valid NSEC3 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 Hash Algorithm, Flags and Iterations fields must be within their +/// valid ranges. The Salt field may contain "-" to indicate that the +/// salt is of length 0. The Salt field must not contain any whitespace. +/// The type mnemonics must be valid, and separated by whitespace. If +/// any invalid mnemonics are found, InvalidRdataText exception is +/// thrown. +/// +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// +/// \param nsec3_str A string containing the RDATA to be created +NSEC3::NSEC3(const std::string& nsec3_str) : + 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 NSEC3Impl that constructFromLexer() returns. + std::unique_ptr<NSEC3Impl> impl_ptr; + + try { + std::istringstream ss(nsec3_str); + 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 NSEC3: " << nsec3_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, + "Failed to construct NSEC3 from '" << nsec3_str << "': " + << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of an NSEC3 RDATA. +/// +/// See \c NSEC3::NSEC3(const std::string&) for description of the +/// expected RDATA fields. +/// +/// \throw MasterLexer::LexerError General parsing error such as +/// missing field. +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +NSEC3::NSEC3(MasterLexer& lexer, const Name*, MasterLoader::Options, + MasterLoaderCallbacks&) : + impl_(NULL) +{ + impl_ = constructFromLexer(lexer); +} + +NSEC3Impl* +NSEC3::constructFromLexer(MasterLexer& lexer) { + vector<uint8_t> salt; + const ParseNSEC3ParamResult params = + parseNSEC3ParamFromLexer("NSEC3", lexer, salt); + + const string& nexthash = + lexer.getNextToken(MasterToken::STRING).getString(); + if (*nexthash.rbegin() == '=') { + isc_throw(InvalidRdataText, "NSEC3 hash has padding: " << nexthash); + } + + vector<uint8_t> next; + decodeBase32Hex(nexthash, next); + if (next.size() > 255) { + isc_throw(InvalidRdataText, "NSEC3 hash is too long: " + << next.size() << " bytes"); + } + + vector<uint8_t> typebits; + // For NSEC3 empty bitmap is possible and allowed. + buildBitmapsFromLexer("NSEC3", lexer, typebits, true); + return (new NSEC3Impl(params.algorithm, params.flags, params.iterations, + salt, next, typebits)); +} + +NSEC3::NSEC3(InputBuffer& buffer, size_t rdata_len) : + impl_(NULL) +{ + vector<uint8_t> salt; + const ParseNSEC3ParamResult params = + parseNSEC3ParamWire("NSEC3", buffer, rdata_len, salt); + + if (rdata_len < 1) { + isc_throw(DNSMessageFORMERR, "NSEC3 too short to contain hash length, " + "length: " << rdata_len + salt.size() + 5); + } + const uint8_t nextlen = buffer.readUint8(); + --rdata_len; + if (nextlen == 0 || rdata_len < nextlen) { + isc_throw(DNSMessageFORMERR, "NSEC3 invalid hash length: " << + static_cast<unsigned int>(nextlen)); + } + + vector<uint8_t> next(nextlen); + buffer.readData(&next[0], nextlen); + rdata_len -= nextlen; + + vector<uint8_t> typebits(rdata_len); + if (rdata_len > 0) { + // Read and parse the bitmaps only when they exist; empty bitmap + // is possible for NSEC3. + buffer.readData(&typebits[0], rdata_len); + checkRRTypeBitmaps("NSEC3", typebits); + } + + impl_ = new NSEC3Impl(params.algorithm, params.flags, params.iterations, + salt, next, typebits); +} + +NSEC3::NSEC3(const NSEC3& source) : + Rdata(), impl_(new NSEC3Impl(*source.impl_)) +{} + +NSEC3& +NSEC3::operator=(const NSEC3& source) { + if (this == &source) { + return (*this); + } + + NSEC3Impl* newimpl = new NSEC3Impl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +NSEC3::~NSEC3() { + delete impl_; +} + +string +NSEC3::toText() const { + ostringstream s; + bitmapsToText(impl_->typebits_, s); + + using boost::lexical_cast; + return (lexical_cast<string>(static_cast<int>(impl_->hashalg_)) + + " " + lexical_cast<string>(static_cast<int>(impl_->flags_)) + + " " + lexical_cast<string>(static_cast<int>(impl_->iterations_)) + + " " + (impl_->salt_.empty() ? "-" : encodeHex(impl_->salt_)) + + " " + encodeBase32Hex(impl_->next_) + s.str()); +} + +template <typename OUTPUT_TYPE> +void +toWireHelper(const NSEC3Impl& impl, OUTPUT_TYPE& output) { + output.writeUint8(impl.hashalg_); + output.writeUint8(impl.flags_); + output.writeUint16(impl.iterations_); + output.writeUint8(impl.salt_.size()); + if (!impl.salt_.empty()) { + output.writeData(&impl.salt_[0], impl.salt_.size()); + } + assert(!impl.next_.empty()); + output.writeUint8(impl.next_.size()); + output.writeData(&impl.next_[0], impl.next_.size()); + if (!impl.typebits_.empty()) { + output.writeData(&impl.typebits_[0], impl.typebits_.size()); + } +} + +void +NSEC3::toWire(OutputBuffer& buffer) const { + toWireHelper(*impl_, buffer); +} + +void +NSEC3::toWire(AbstractMessageRenderer& renderer) const { + toWireHelper(*impl_, renderer); +} + +namespace { +// This is a helper subroutine for compare(). It compares two binary +// data stored in vector<uint8_t> objects based on the "Canonical RR Ordering" +// as defined in Section 6.3 of RFC4034, that is, the data are treated +// "as a left-justified unsigned octet sequence in which the absence of an +// octet sorts before a zero octet." +// +// If check_length_first is true, it treats the compared data as if they +// began with a single-octet "length" field whose value is the size of the +// corresponding vector. In this case, if the sizes of the two vectors are +// different the shorter one is always considered the "smaller"; the contents +// of the vector don't matter. +// +// This function returns: +// -1 if v1 is considered smaller than v2 +// 1 if v1 is considered larger than v2 +// 0 otherwise +int +compareVectors(const vector<uint8_t>& v1, const vector<uint8_t>& v2, + bool check_length_first = true) +{ + const size_t len1 = v1.size(); + const size_t len2 = v2.size(); + if (check_length_first && len1 != len2) { + return (len1 - len2); + } + const size_t cmplen = min(len1, len2); + const int cmp = cmplen == 0 ? 0 : memcmp(&v1.at(0), &v2.at(0), cmplen); + if (cmp != 0) { + return (cmp); + } else { + return (len1 - len2); + } +} +} + +int +NSEC3::compare(const Rdata& other) const { + const NSEC3& other_nsec3 = dynamic_cast<const NSEC3&>(other); + + if (impl_->hashalg_ != other_nsec3.impl_->hashalg_) { + return (impl_->hashalg_ < other_nsec3.impl_->hashalg_ ? -1 : 1); + } + if (impl_->flags_ != other_nsec3.impl_->flags_) { + return (impl_->flags_ < other_nsec3.impl_->flags_ ? -1 : 1); + } + if (impl_->iterations_ != other_nsec3.impl_->iterations_) { + return (impl_->iterations_ < other_nsec3.impl_->iterations_ ? -1 : 1); + } + + int cmp = compareVectors(impl_->salt_, other_nsec3.impl_->salt_); + if (cmp != 0) { + return (cmp); + } + cmp = compareVectors(impl_->next_, other_nsec3.impl_->next_); + if (cmp != 0) { + return (cmp); + } + // Note that bitmap doesn't have a dedicated length field, so we shouldn't + // terminate the comparison just because the lengths are different. + return (compareVectors(impl_->typebits_, other_nsec3.impl_->typebits_, + false)); +} + +uint8_t +NSEC3::getHashalg() const { + return (impl_->hashalg_); +} + +uint8_t +NSEC3::getFlags() const { + return (impl_->flags_); +} + +uint16_t +NSEC3::getIterations() const { + return (impl_->iterations_); +} + +const vector<uint8_t>& +NSEC3::getSalt() const { + return (impl_->salt_); +} + +const vector<uint8_t>& +NSEC3::getNext() const { + return (impl_->next_); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2010-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/. + +#include <config.h> + +#include <util/buffer.h> +#include <util/encode/hex.h> + +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata/generic/detail/nsec3param_common.h> + +#include <boost/lexical_cast.hpp> + +#include <memory> +#include <string> +#include <sstream> +#include <vector> + +using namespace std; +using namespace isc::util; +using namespace isc::util::encode; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +struct NSEC3PARAMImpl { + // straightforward representation of NSEC3PARAM RDATA fields + NSEC3PARAMImpl(uint8_t hashalg, uint8_t flags, uint16_t iterations, + const vector<uint8_t>& salt) : + hashalg_(hashalg), flags_(flags), iterations_(iterations), salt_(salt) + {} + + const uint8_t hashalg_; + const uint8_t flags_; + const uint16_t iterations_; + const vector<uint8_t> salt_; +}; + +/// \brief Constructor from string. +/// +/// The given string must represent a valid NSEC3PARAM 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 Hash Algorithm, Flags and Iterations fields must be within their +/// valid ranges. The Salt field may contain "-" to indicate that the +/// salt is of length 0. The Salt field must not contain any whitespace. +/// +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// +/// \param nsec3param_str A string containing the RDATA to be created +NSEC3PARAM::NSEC3PARAM(const std::string& nsec3param_str) : + 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 NSEC3PARAMImpl that constructFromLexer() returns. + std::unique_ptr<NSEC3PARAMImpl> impl_ptr; + + try { + std::istringstream ss(nsec3param_str); + 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 NSEC3PARAM: " << nsec3param_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, + "Failed to construct NSEC3PARAM from '" << nsec3param_str + << "': " << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of an NSEC3PARAM RDATA. +/// +/// See \c NSEC3PARAM::NSEC3PARAM(const std::string&) for description of +/// the expected RDATA fields. +/// +/// \throw MasterLexer::LexerError General parsing error such as +/// missing field. +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +NSEC3PARAM::NSEC3PARAM(MasterLexer& lexer, const Name*, MasterLoader::Options, + MasterLoaderCallbacks&) : + impl_(NULL) +{ + impl_ = constructFromLexer(lexer); +} + +NSEC3PARAMImpl* +NSEC3PARAM::constructFromLexer(MasterLexer& lexer) { + vector<uint8_t> salt; + const ParseNSEC3ParamResult params = + parseNSEC3ParamFromLexer("NSEC3PARAM", lexer, salt); + + return (new NSEC3PARAMImpl(params.algorithm, params.flags, + params.iterations, salt)); +} + +NSEC3PARAM::NSEC3PARAM(InputBuffer& buffer, size_t rdata_len) : + impl_(NULL) +{ + vector<uint8_t> salt; + const ParseNSEC3ParamResult params = + parseNSEC3ParamWire("NSEC3PARAM", buffer, rdata_len, salt); + + impl_ = new NSEC3PARAMImpl(params.algorithm, params.flags, + params.iterations, salt); +} + +NSEC3PARAM::NSEC3PARAM(const NSEC3PARAM& source) : + Rdata(), impl_(new NSEC3PARAMImpl(*source.impl_)) +{} + +NSEC3PARAM& +NSEC3PARAM::operator=(const NSEC3PARAM& source) { + if (this == &source) { + return (*this); + } + + NSEC3PARAMImpl* newimpl = new NSEC3PARAMImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +NSEC3PARAM::~NSEC3PARAM() { + delete impl_; +} + +string +NSEC3PARAM::toText() const { + using boost::lexical_cast; + return (lexical_cast<string>(static_cast<int>(impl_->hashalg_)) + + " " + lexical_cast<string>(static_cast<int>(impl_->flags_)) + + " " + lexical_cast<string>(static_cast<int>(impl_->iterations_)) + + " " + (impl_->salt_.empty() ? "-" : encodeHex(impl_->salt_))); +} + +template <typename OUTPUT_TYPE> +void +toWireHelper(const NSEC3PARAMImpl& impl, OUTPUT_TYPE& output) { + output.writeUint8(impl.hashalg_); + output.writeUint8(impl.flags_); + output.writeUint16(impl.iterations_); + output.writeUint8(impl.salt_.size()); + if (!impl.salt_.empty()) { + output.writeData(&impl.salt_[0], impl.salt_.size()); + } +} + +void +NSEC3PARAM::toWire(OutputBuffer& buffer) const { + toWireHelper(*impl_, buffer); +} + +void +NSEC3PARAM::toWire(AbstractMessageRenderer& renderer) const { + toWireHelper(*impl_, renderer); +} + +int +NSEC3PARAM::compare(const Rdata& other) const { + const NSEC3PARAM& other_param = dynamic_cast<const NSEC3PARAM&>(other); + + if (impl_->hashalg_ != other_param.impl_->hashalg_) { + return (impl_->hashalg_ < other_param.impl_->hashalg_ ? -1 : 1); + } + if (impl_->flags_ != other_param.impl_->flags_) { + return (impl_->flags_ < other_param.impl_->flags_ ? -1 : 1); + } + if (impl_->iterations_ != other_param.impl_->iterations_) { + return (impl_->iterations_ < other_param.impl_->iterations_ ? -1 : 1); + } + + const size_t this_len = impl_->salt_.size(); + const size_t other_len = other_param.impl_->salt_.size(); + if (this_len != other_len) { + return (this_len - other_len); + } + const size_t cmplen = min(this_len, other_len); + const int cmp = (cmplen == 0) ? 0 : + memcmp(&impl_->salt_.at(0), &other_param.impl_->salt_.at(0), cmplen); + if (cmp != 0) { + return (cmp); + } else { + return (this_len - other_len); + } +} + +uint8_t +NSEC3PARAM::getHashalg() const { + return (impl_->hashalg_); +} + +uint8_t +NSEC3PARAM::getFlags() const { + return (impl_->flags_); +} + +uint16_t +NSEC3PARAM::getIterations() const { + return (impl_->iterations_); +} + +const vector<uint8_t>& +NSEC3PARAM::getSalt() const { + return (impl_->salt_); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2010-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/. + +#include <config.h> + +#include <iostream> +#include <string> +#include <sstream> +#include <vector> + +#include <util/encode/base64.h> +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rrtype.h> +#include <dns/rrttl.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata/generic/detail/nsec_bitmap.h> +#include <dns/rdata/generic/detail/lexer_util.h> + +#include <stdio.h> +#include <time.h> + +using namespace std; +using namespace isc::util; +using namespace isc::util::encode; +using namespace isc::dns::rdata::generic::detail::nsec; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +struct NSECImpl { + // straightforward representation of NSEC RDATA fields + NSECImpl(const Name& next, vector<uint8_t> typebits) : + nextname_(next), typebits_(typebits) + {} + + Name nextname_; + vector<uint8_t> typebits_; +}; + +/// \brief Constructor from string. +/// +/// The given string must represent a valid NSEC 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 Next Domain Name field must be absolute since there's no +/// parameter that specifies the origin name; if it is not absolute, +/// \c MissingNameOrigin exception will be thrown. This must not be +/// represented as a quoted string. +/// +/// The type mnemonics must be valid, and separated by whitespace. If +/// any invalid mnemonics are found, InvalidRdataText exception is +/// thrown. +/// +/// \throw MissingNameOrigin Thrown when the Next Domain Name is not absolute. +/// \throw InvalidRdataText if any fields are out of their valid range. +/// +/// \param nsec_str A string containing the RDATA to be created +NSEC::NSEC(const std::string& nsec_str) : + impl_(NULL) +{ + try { + std::istringstream ss(nsec_str); + MasterLexer lexer; + lexer.pushSource(ss); + + const Name origin_name(createNameFromLexer(lexer, NULL)); + + vector<uint8_t> typebits; + buildBitmapsFromLexer("NSEC", lexer, typebits); + + impl_ = new NSECImpl(origin_name, typebits); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, + "Extra input text for NSEC: " << nsec_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, + "Failed to construct NSEC from '" << nsec_str << "': " + << ex.what()); + } +} + +NSEC::NSEC(InputBuffer& buffer, size_t rdata_len) { + const size_t pos = buffer.getPosition(); + const Name nextname(buffer); + + // rdata_len must be sufficiently large to hold non empty bitmap. + if (rdata_len <= buffer.getPosition() - pos) { + isc_throw(DNSMessageFORMERR, + "NSEC RDATA from wire too short: " << rdata_len << "bytes"); + } + rdata_len -= (buffer.getPosition() - pos); + + vector<uint8_t> typebits(rdata_len); + buffer.readData(&typebits[0], rdata_len); + checkRRTypeBitmaps("NSEC", typebits); + + impl_ = new NSECImpl(nextname, typebits); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of an NSEC RDATA. +/// +/// The Next Domain Name field can be non-absolute if \c origin is +/// non-NULL, in which case \c origin is used to make it absolute. It +/// must not be represented as a quoted string. +/// +/// The type mnemonics must be valid, and separated by whitespace. If +/// any invalid mnemonics are found, InvalidRdataText exception is +/// thrown. +/// +/// \throw MasterLexer::LexerError General parsing error such as +/// missing field. +/// \throw MissingNameOrigin Thrown when the Next Domain Name is not +/// absolute and \c origin is NULL. +/// \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 +/// \param origin The origin to use with a relative Next Domain Name +/// field +NSEC::NSEC(MasterLexer& lexer, const Name* origin, MasterLoader::Options, + MasterLoaderCallbacks&) +{ + const Name next_name(createNameFromLexer(lexer, origin)); + + vector<uint8_t> typebits; + buildBitmapsFromLexer("NSEC", lexer, typebits); + + impl_ = new NSECImpl(next_name, typebits); +} + +NSEC::NSEC(const NSEC& source) : + Rdata(), impl_(new NSECImpl(*source.impl_)) +{} + +NSEC& +NSEC::operator=(const NSEC& source) { + if (this == &source) { + return (*this); + } + + NSECImpl* newimpl = new NSECImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +NSEC::~NSEC() { + delete impl_; +} + +string +NSEC::toText() const { + ostringstream s; + s << impl_->nextname_; + bitmapsToText(impl_->typebits_, s); + return (s.str()); +} + +void +NSEC::toWire(OutputBuffer& buffer) const { + impl_->nextname_.toWire(buffer); + buffer.writeData(&impl_->typebits_[0], impl_->typebits_.size()); +} + +void +NSEC::toWire(AbstractMessageRenderer& renderer) const { + // Type NSEC is not "well-known", and name compression must be disabled + // per RFC3597. + renderer.writeName(impl_->nextname_, false); + renderer.writeData(&impl_->typebits_[0], impl_->typebits_.size()); +} + +const Name& +NSEC::getNextName() const { + return (impl_->nextname_); +} + +int +NSEC::compare(const Rdata& other) const { + const NSEC& other_nsec = dynamic_cast<const NSEC&>(other); + + int cmp = compareNames(impl_->nextname_, other_nsec.impl_->nextname_); + if (cmp != 0) { + return (cmp); + } + + const size_t this_len = impl_->typebits_.size(); + const size_t other_len = other_nsec.impl_->typebits_.size(); + const size_t cmplen = min(this_len, other_len); + cmp = memcmp(&impl_->typebits_[0], &other_nsec.impl_->typebits_[0], + cmplen); + if (cmp != 0) { + return (cmp); + } else { + return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1); + } +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2010-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/. + +#include <config.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <boost/foreach.hpp> + +#include <string> +#include <string.h> + +using namespace std; +using namespace isc::util; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +/// \brief Constructor. +OPT::PseudoRR::PseudoRR(uint16_t code, + boost::shared_ptr<std::vector<uint8_t> >& data) : + code_(code), + data_(data) +{ +} + +uint16_t +OPT::PseudoRR::getCode() const { + return (code_); +} + +const uint8_t* +OPT::PseudoRR::getData() const { + return (&(*data_)[0]); +} + +uint16_t +OPT::PseudoRR::getLength() const { + return (data_->size()); +} + +struct OPTImpl { + OPTImpl() : + rdlength_(0) + {} + + uint16_t rdlength_; + std::vector<OPT::PseudoRR> pseudo_rrs_; +}; + +/// \brief Default constructor. +OPT::OPT() : + impl_(new OPTImpl) +{ +} + +/// \brief Constructor from string. +/// +/// This constructor cannot be used, and always throws an exception. +/// +/// \throw InvalidRdataText OPT RR cannot be constructed from text. +OPT::OPT(const std::string&) : + impl_(NULL) +{ + isc_throw(InvalidRdataText, "OPT RR cannot be constructed from text"); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// This constructor cannot be used, and always throws an exception. +/// +/// \throw InvalidRdataText OPT RR cannot be constructed from text. +OPT::OPT(MasterLexer&, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(NULL) +{ + isc_throw(InvalidRdataText, "OPT RR cannot be constructed from text"); +} + +OPT::OPT(InputBuffer& buffer, size_t rdata_len) : + impl_(NULL) +{ + std::unique_ptr<OPTImpl> impl_ptr(new OPTImpl); + + while (true) { + if (rdata_len == 0) { + break; + } + + if (rdata_len < 4) { + isc_throw(InvalidRdataLength, + "Pseudo OPT RR record too short: " + << rdata_len << " bytes"); + } + + const uint16_t option_code = buffer.readUint16(); + const uint16_t option_length = buffer.readUint16(); + rdata_len -= 4; + + if (static_cast<uint16_t>(impl_ptr->rdlength_ + option_length) < + impl_ptr->rdlength_) + { + isc_throw(InvalidRdataText, + "Option length " << option_length + << " would overflow OPT RR RDLEN (currently " + << impl_ptr->rdlength_ << ")."); + } + + if (rdata_len < option_length) { + isc_throw(InvalidRdataLength, "Corrupt pseudo OPT RR record"); + } + + boost::shared_ptr<std::vector<uint8_t> > + option_data(new std::vector<uint8_t>(option_length)); + buffer.readData(&(*option_data)[0], option_length); + impl_ptr->pseudo_rrs_.push_back(PseudoRR(option_code, option_data)); + impl_ptr->rdlength_ += option_length; + rdata_len -= option_length; + } + + impl_ = impl_ptr.release(); +} + +OPT::OPT(const OPT& other) : + Rdata(), impl_(new OPTImpl(*other.impl_)) +{ +} + +OPT& +OPT::operator=(const OPT& source) { + if (this == &source) { + return (*this); + } + + OPTImpl* newimpl = new OPTImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +OPT::~OPT() { + delete impl_; +} + +std::string +OPT::toText() const { + isc_throw(isc::InvalidOperation, + "OPT RRs do not have a presentation format"); +} + +void +OPT::toWire(OutputBuffer& buffer) const { + BOOST_FOREACH(const PseudoRR& pseudo_rr, impl_->pseudo_rrs_) { + buffer.writeUint16(pseudo_rr.getCode()); + const uint16_t length = pseudo_rr.getLength(); + buffer.writeUint16(length); + if (length > 0) { + buffer.writeData(pseudo_rr.getData(), length); + } + } +} + +void +OPT::toWire(AbstractMessageRenderer& renderer) const { + BOOST_FOREACH(const PseudoRR& pseudo_rr, impl_->pseudo_rrs_) { + renderer.writeUint16(pseudo_rr.getCode()); + const uint16_t length = pseudo_rr.getLength(); + renderer.writeUint16(length); + if (length > 0) { + renderer.writeData(pseudo_rr.getData(), length); + } + } +} + +int +OPT::compare(const Rdata&) const { + isc_throw(isc::InvalidOperation, + "It is meaningless to compare a set of OPT pseudo RRs; " + "they have unspecified order"); + return (0); +} + +void +OPT::appendPseudoRR(uint16_t code, const uint8_t* data, uint16_t length) { + // See if it overflows 16-bit length field. We only worry about the + // pseudo-RR length here, not the whole message length (which should + // be checked and enforced elsewhere). + if (static_cast<uint16_t>(impl_->rdlength_ + length) < + impl_->rdlength_) + { + isc_throw(isc::InvalidParameter, + "Option length " << length + << " would overflow OPT RR RDLEN (currently " + << impl_->rdlength_ << ")."); + } + + boost::shared_ptr<std::vector<uint8_t> > + option_data(new std::vector<uint8_t>(length)); + if (length != 0) { + std::memcpy(&(*option_data)[0], data, length); + } + impl_->pseudo_rrs_.push_back(PseudoRR(code, option_data)); + impl_->rdlength_ += length; +} + +const std::vector<OPT::PseudoRR>& +OPT::getPseudoRRs() const { + return (impl_->pseudo_rrs_); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2010-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/. + +#include <config.h> + +#include <string> + +#include <util/buffer.h> +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using namespace isc::util; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +/// \brief Constructor from string. +/// +/// The given string must represent a valid PTR 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 PTRDNAME must be absolute since there's no parameter that +/// specifies the origin name; if it is not absolute, \c +/// MissingNameOrigin exception will be thrown. These must not be +/// represented as a quoted string. +/// +/// \throw Others Exception from the Name and RRTTL constructors. +/// \throw InvalidRdataText Other general syntax errors. +PTR::PTR(const std::string& type_str) : + // Fill in dummy name and replace them soon below. + ptr_name_(Name::ROOT_NAME()) +{ + try { + std::istringstream ss(type_str); + MasterLexer lexer; + lexer.pushSource(ss); + + ptr_name_ = createNameFromLexer(lexer, NULL); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for PTR: " + << type_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct PTR from '" << + type_str << "': " << ex.what()); + } +} + +PTR::PTR(InputBuffer& buffer, size_t) : + ptr_name_(buffer) +{ + // we don't need rdata_len for parsing. if necessary, the caller will + // check consistency. +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of a PTR RDATA. The PTRDNAME field can be +/// non-absolute if \c origin is non-NULL, in which case \c origin is +/// used to make it absolute. It must not be represented as a quoted +/// string. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and RRTTL constructors if +/// construction of textual fields as these objects fail. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +/// \param origin If non NULL, specifies the origin of PTRDNAME when it +/// is non-absolute. +PTR::PTR(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + ptr_name_(createNameFromLexer(lexer, origin)) +{} + +PTR::PTR(const PTR& source) : + Rdata(), ptr_name_(source.ptr_name_) +{} + +std::string +PTR::toText() const { + return (ptr_name_.toText()); +} + +void +PTR::toWire(OutputBuffer& buffer) const { + ptr_name_.toWire(buffer); +} + +void +PTR::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeName(ptr_name_); +} + +int +PTR::compare(const Rdata& other) const { + // The compare method normally begins with this dynamic cast. + const PTR& other_ptr = dynamic_cast<const PTR&>(other); + + return (compareNames(ptr_name_, other_ptr.ptr_name_)); + +} + +const Name& +PTR::getPTRName() const { + return (ptr_name_); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2011-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/. + +#include <config.h> + +#include <string> +#include <sstream> + +#include <util/buffer.h> + +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +/// \brief Constructor from string. +/// +/// \c rp_str must be formatted as follows: +/// \code <mailbox name> <text name> +/// \endcode +/// where both fields must represent a valid domain name. +/// +/// \throw InvalidRdataText The number of RDATA fields (must be 2) is +/// incorrect. +/// \throw std::bad_alloc Memory allocation for names fails. +/// \throw Other The constructor of the \c Name class will throw if the +/// given name is invalid. +RP::RP(const std::string& rp_str) : + // We cannot construct both names in the initialization list due to the + // necessary text processing, so we have to initialize them with a dummy + // name and replace them later. + mailbox_(Name::ROOT_NAME()), text_(Name::ROOT_NAME()) +{ + try { + std::istringstream ss(rp_str); + MasterLexer lexer; + lexer.pushSource(ss); + + mailbox_ = createNameFromLexer(lexer, NULL); + text_ = createNameFromLexer(lexer, NULL); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for RP: " + << rp_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct RP from '" << + rp_str << "': " << ex.what()); + } +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of an RP RDATA. The MAILBOX and TEXT fields can be non-absolute if \c +/// origin is non-NULL, in which case \c origin is used to make them absolute. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and constructors if construction of +/// textual fields as these objects fail. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +/// \param origin If non NULL, specifies the origin of SERVER when it +/// is non-absolute. +RP::RP(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + mailbox_(createNameFromLexer(lexer, origin)), + text_(createNameFromLexer(lexer, origin)) +{ +} + +/// \brief Constructor from wire-format data. +/// +/// This constructor doesn't check the validity of the second parameter (rdata +/// length) for parsing. +/// If necessary, the caller will check consistency. +/// +/// \throw std::bad_alloc Memory allocation for names fails. +/// \throw Other The constructor of the \c Name class will throw if the +/// names in the wire is invalid. +RP::RP(InputBuffer& buffer, size_t) : mailbox_(buffer), text_(buffer) { +} + +/// \brief Copy constructor. +/// +/// \throw std::bad_alloc Memory allocation fails in copying internal +/// member variables (this should be very rare). +RP::RP(const RP& other) : + Rdata(), mailbox_(other.mailbox_), text_(other.text_) +{} + +/// \brief Convert the \c RP to a string. +/// +/// The output of this method is formatted as described in the "from string" +/// constructor (\c RP(const std::string&))). +/// +/// \throw std::bad_alloc Internal resource allocation fails. +/// +/// \return A \c string object that represents the \c RP object. +std::string +RP::toText() const { + return (mailbox_.toText() + " " + text_.toText()); +} + +/// \brief Render the \c RP in the wire format without name compression. +/// +/// \throw std::bad_alloc Internal resource allocation fails. +/// +/// \param buffer An output buffer to store the wire data. +void +RP::toWire(OutputBuffer& buffer) const { + mailbox_.toWire(buffer); + text_.toWire(buffer); +} + +/// \brief Render the \c RP in the wire format with taking into account +/// compression. +/// +// Type RP is not "well-known", and name compression must be disabled +// per RFC3597. +/// +/// \throw std::bad_alloc Internal resource allocation fails. +/// +/// \param renderer DNS message rendering context that encapsulates the +/// output buffer and name compression information. +void +RP::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeName(mailbox_, false); + renderer.writeName(text_, false); +} + +/// \brief Compare two instances of \c RP RDATA. +/// +/// See documentation in \c Rdata. +int +RP::compare(const Rdata& other) const { + const RP& other_rp = dynamic_cast<const RP&>(other); + + const int cmp = compareNames(mailbox_, other_rp.mailbox_); + if (cmp != 0) { + return (cmp); + } + return (compareNames(text_, other_rp.text_)); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2010-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/. + +#include <config.h> + +#include <string> +#include <iomanip> +#include <iostream> +#include <sstream> +#include <vector> + +#include <boost/lexical_cast.hpp> + +#include <util/encode/base64.h> +#include <util/buffer.h> +#include <util/time_utilities.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rrtype.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata/generic/detail/lexer_util.h> + +#include <stdio.h> +#include <time.h> + +using namespace std; +using namespace isc::util; +using namespace isc::util::encode; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +namespace { +// This is the minimum necessary length of all wire-format RRSIG RDATA: +// - two 8-bit fields (algorithm and labels) +// - two 16-bit fields (covered and tag) +// - three 32-bit fields (original TTL, expire and inception) +const size_t RRSIG_MINIMUM_LEN = 2 * sizeof(uint8_t) + 2 * sizeof(uint16_t) + + 3 * sizeof(uint32_t); +} + +struct RRSIGImpl { + // straightforward representation of RRSIG RDATA fields + RRSIGImpl(const RRType& covered, uint8_t algorithm, uint8_t labels, + uint32_t originalttl, uint32_t timeexpire, + uint32_t timeinception, uint16_t tag, const Name& signer, + const vector<uint8_t>& signature) : + covered_(covered), algorithm_(algorithm), labels_(labels), + originalttl_(originalttl), timeexpire_(timeexpire), + timeinception_(timeinception), tag_(tag), signer_(signer), + signature_(signature) + {} + + const RRType covered_; + uint8_t algorithm_; + uint8_t labels_; + uint32_t originalttl_; + uint32_t timeexpire_; + uint32_t timeinception_; + uint16_t tag_; + const Name signer_; + const vector<uint8_t> signature_; +}; + +// helper function for string and lexer constructors +RRSIGImpl* +RRSIG::constructFromLexer(MasterLexer& lexer, const Name* origin) { + const RRType covered(lexer.getNextToken(MasterToken::STRING).getString()); + const uint32_t algorithm = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (algorithm > 0xff) { + isc_throw(InvalidRdataText, "RRSIG algorithm out of range"); + } + const uint32_t labels = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (labels > 0xff) { + isc_throw(InvalidRdataText, "RRSIG labels out of range"); + } + const uint32_t originalttl = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + const uint32_t timeexpire = + timeFromText32(lexer.getNextToken(MasterToken::STRING).getString()); + const uint32_t timeinception = + timeFromText32(lexer.getNextToken(MasterToken::STRING).getString()); + const uint32_t tag = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (tag > 0xffff) { + isc_throw(InvalidRdataText, "RRSIG key tag out of range"); + } + const Name& signer = createNameFromLexer(lexer, origin); + + string signature_txt; + string signature_part; + // Whitespace is allowed within base64 text, 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)) { + break; + } + token.getString(signature_part); + signature_txt.append(signature_part); + } + lexer.ungetToken(); + + vector<uint8_t> signature; + // missing signature is okay + if (signature_txt.size() > 0) { + decodeBase64(signature_txt, signature); + } + + return (new RRSIGImpl(covered, algorithm, labels, + originalttl, timeexpire, timeinception, + static_cast<uint16_t>(tag), signer, signature)); +} + +/// \brief Constructor from string. +/// +/// The given string must represent a valid RRSIG 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 Signer's Name must be absolute since there's no parameter that +/// specifies the origin name; if this is not absolute, \c MissingNameOrigin +/// exception will be thrown. This must not be represented as a quoted +/// string. +/// +/// See the construction that takes \c MasterLexer for other fields. +/// +/// \throw Others Exception from the Name constructor. +/// \throw InvalidRdataText Other general syntax errors. +RRSIG::RRSIG(const std::string& rrsig_str) : + 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 RRSIGImpl that constructFromLexer() returns. + std::unique_ptr<RRSIGImpl> impl_ptr; + + try { + std::istringstream iss(rrsig_str); + MasterLexer lexer; + lexer.pushSource(iss); + + impl_ptr.reset(constructFromLexer(lexer, NULL)); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for RRSIG: " + << rrsig_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct RRSIG from '" << + rrsig_str << "': " << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of an RRSIG RDATA. The Signer's Name fields can be non absolute if \c +/// origin is non NULL, in which case \c origin is used to make it absolute. +/// This must not be represented as a quoted string. +/// +/// The Original TTL field is a valid decimal representation of an unsigned +/// 32-bit integer. Note that alternate textual representations of \c RRTTL, +/// such as "1H" for 3600 seconds, are not allowed here. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name constructor if +/// construction of textual fields as these objects fail. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +/// \param origin If non NULL, specifies the origin of Signer's Name when +/// it is non absolute. +RRSIG::RRSIG(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(constructFromLexer(lexer, origin)) +{ +} + +RRSIG::RRSIG(InputBuffer& buffer, size_t rdata_len) { + size_t pos = buffer.getPosition(); + + if (rdata_len < RRSIG_MINIMUM_LEN) { + isc_throw(InvalidRdataLength, "RRSIG too short"); + } + + RRType covered(buffer); + uint8_t algorithm = buffer.readUint8(); + uint8_t labels = buffer.readUint8(); + uint32_t originalttl = buffer.readUint32(); + uint32_t timeexpire = buffer.readUint32(); + uint32_t timeinception = buffer.readUint32(); + uint16_t tag = buffer.readUint16(); + Name signer(buffer); + + // rdata_len must be sufficiently large to hold non empty signature data. + if (rdata_len <= buffer.getPosition() - pos) { + isc_throw(InvalidRdataLength, "RRSIG too short"); + } + rdata_len -= (buffer.getPosition() - pos); + + vector<uint8_t> signature(rdata_len); + buffer.readData(&signature[0], rdata_len); + + impl_ = new RRSIGImpl(covered, algorithm, labels, + originalttl, timeexpire, timeinception, tag, + signer, signature); +} + +RRSIG::RRSIG(const RRSIG& source) : + Rdata(), impl_(new RRSIGImpl(*source.impl_)) +{} + +RRSIG& +RRSIG::operator=(const RRSIG& source) { + if (this == &source) { + return (*this); + } + + RRSIGImpl* newimpl = new RRSIGImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +RRSIG::~RRSIG() { + delete impl_; +} + +string +RRSIG::toText() const { + return (impl_->covered_.toText() + + " " + boost::lexical_cast<string>(static_cast<int>(impl_->algorithm_)) + + " " + boost::lexical_cast<string>(static_cast<int>(impl_->labels_)) + + " " + boost::lexical_cast<string>(impl_->originalttl_) + + " " + timeToText32(impl_->timeexpire_) + + " " + timeToText32(impl_->timeinception_) + + " " + boost::lexical_cast<string>(impl_->tag_) + + " " + impl_->signer_.toText() + + " " + encodeBase64(impl_->signature_)); +} + +void +RRSIG::toWire(OutputBuffer& buffer) const { + impl_->covered_.toWire(buffer); + buffer.writeUint8(impl_->algorithm_); + buffer.writeUint8(impl_->labels_); + buffer.writeUint32(impl_->originalttl_); + buffer.writeUint32(impl_->timeexpire_); + buffer.writeUint32(impl_->timeinception_); + buffer.writeUint16(impl_->tag_); + impl_->signer_.toWire(buffer); + buffer.writeData(&impl_->signature_[0], impl_->signature_.size()); +} + +void +RRSIG::toWire(AbstractMessageRenderer& renderer) const { + impl_->covered_.toWire(renderer); + renderer.writeUint8(impl_->algorithm_); + renderer.writeUint8(impl_->labels_); + renderer.writeUint32(impl_->originalttl_); + renderer.writeUint32(impl_->timeexpire_); + renderer.writeUint32(impl_->timeinception_); + renderer.writeUint16(impl_->tag_); + renderer.writeName(impl_->signer_, false); + renderer.writeData(&impl_->signature_[0], impl_->signature_.size()); +} + +int +RRSIG::compare(const Rdata& other) const { + const RRSIG& other_rrsig = dynamic_cast<const RRSIG&>(other); + + if (impl_->covered_.getCode() != other_rrsig.impl_->covered_.getCode()) { + return (impl_->covered_.getCode() < + other_rrsig.impl_->covered_.getCode() ? -1 : 1); + } + if (impl_->algorithm_ != other_rrsig.impl_->algorithm_) { + return (impl_->algorithm_ < other_rrsig.impl_->algorithm_ ? -1 : 1); + } + if (impl_->labels_ != other_rrsig.impl_->labels_) { + return (impl_->labels_ < other_rrsig.impl_->labels_ ? -1 : 1); + } + if (impl_->originalttl_ != other_rrsig.impl_->originalttl_) { + return (impl_->originalttl_ < other_rrsig.impl_->originalttl_ ? + -1 : 1); + } + if (impl_->timeexpire_ != other_rrsig.impl_->timeexpire_) { + return (impl_->timeexpire_ < other_rrsig.impl_->timeexpire_ ? + -1 : 1); + } + if (impl_->timeinception_ != other_rrsig.impl_->timeinception_) { + return (impl_->timeinception_ < other_rrsig.impl_->timeinception_ ? + -1 : 1); + } + if (impl_->tag_ != other_rrsig.impl_->tag_) { + return (impl_->tag_ < other_rrsig.impl_->tag_ ? -1 : 1); + } + + int cmp = compareNames(impl_->signer_, other_rrsig.impl_->signer_); + if (cmp != 0) { + return (cmp); + } + + size_t this_len = impl_->signature_.size(); + size_t other_len = other_rrsig.impl_->signature_.size(); + size_t cmplen = min(this_len, other_len); + cmp = memcmp(&impl_->signature_[0], &other_rrsig.impl_->signature_[0], + cmplen); + if (cmp != 0) { + return (cmp); + } else { + return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1); + } +} + +const RRType& +RRSIG::typeCovered() const { + return (impl_->covered_); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2010-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/. + +#include <config.h> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/name.h> +#include <dns/master_lexer.h> +#include <dns/master_loader.h> +#include <dns/master_loader_callbacks.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/lexer_util.h> + +#include <boost/static_assert.hpp> +#include <boost/lexical_cast.hpp> + +#include <string> +#include <sstream> + +using namespace std; +using boost::lexical_cast; +using namespace isc::util; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +SOA::SOA(InputBuffer& buffer, size_t) : + mname_(buffer), rname_(buffer) +{ + // we don't need rdata_len for parsing. if necessary, the caller will + // check consistency. + buffer.readData(numdata_, sizeof(numdata_)); +} + +namespace { +void +fillParameters(MasterLexer& lexer, uint8_t numdata[20]) { + // Copy serial, refresh, retry, expire, minimum. We accept the extended + // TTL-compatible style for the latter four. + OutputBuffer buffer(20); + buffer.writeUint32(lexer.getNextToken(MasterToken::NUMBER).getNumber()); + for (int i = 0; i < 4; ++i) { + buffer.writeUint32(RRTTL(lexer.getNextToken(MasterToken::STRING). + getString()).getValue()); + } + memcpy(numdata, buffer.getData(), buffer.getLength()); +} +} + +/// \brief Constructor from string. +/// +/// The given string must represent a valid SOA 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 MNAME and RNAME must be absolute since there's no parameter that +/// specifies the origin name; if these are not absolute, \c MissingNameOrigin +/// exception will be thrown. These must not be represented as a quoted +/// string. +/// +/// See the construction that takes \c MasterLexer for other fields. +/// +/// \throw Others Exception from the Name and RRTTL constructors. +/// \throw InvalidRdataText Other general syntax errors. +SOA::SOA(const std::string& soastr) : + // Fill in dummy name and replace them soon below. + mname_(Name::ROOT_NAME()), rname_(Name::ROOT_NAME()) +{ + try { + std::istringstream ss(soastr); + MasterLexer lexer; + lexer.pushSource(ss); + + mname_ = createNameFromLexer(lexer, NULL); + rname_ = createNameFromLexer(lexer, NULL); + fillParameters(lexer, numdata_); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for SOA: " + << soastr); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct SOA from '" << + soastr << "': " << ex.what()); + } +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of an SOA RDATA. The MNAME and RNAME fields can be non absolute if +/// \c origin is non NULL, in which case \c origin is used to make them +/// absolute. These must not be represented as a quoted string. +/// +/// The REFRESH, RETRY, EXPIRE, and MINIMUM fields can be either a valid +/// decimal representation of an unsigned 32-bit integer or other +/// valid textual representation of \c RRTTL such as "1H" (which means 3600). +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and RRTTL constructors if +/// construction of textual fields as these objects fail. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +/// \param origin If non NULL, specifies the origin of MNAME and RNAME when +/// they are non absolute. +SOA::SOA(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + mname_(createNameFromLexer(lexer, origin)), + rname_(createNameFromLexer(lexer, origin)) +{ + fillParameters(lexer, numdata_); +} + +SOA::SOA(const Name& mname, const Name& rname, uint32_t serial, + uint32_t refresh, uint32_t retry, uint32_t expire, uint32_t minimum) : + mname_(mname), rname_(rname) +{ + OutputBuffer b(20); + b.writeUint32(serial); + b.writeUint32(refresh); + b.writeUint32(retry); + b.writeUint32(expire); + b.writeUint32(minimum); + assert(b.getLength() == sizeof(numdata_)); + memcpy(numdata_, b.getData(), sizeof(numdata_)); +} + +SOA::SOA(const SOA& other) : + Rdata(), mname_(other.mname_), rname_(other.rname_) +{ + memcpy(numdata_, other.numdata_, sizeof(numdata_)); +} + +void +SOA::toWire(OutputBuffer& buffer) const { + mname_.toWire(buffer); + rname_.toWire(buffer); + buffer.writeData(numdata_, sizeof(numdata_)); +} + +void +SOA::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeName(mname_); + renderer.writeName(rname_); + renderer.writeData(numdata_, sizeof(numdata_)); +} + +Serial +SOA::getSerial() const { + InputBuffer b(numdata_, sizeof(numdata_)); + return (Serial(b.readUint32())); +} + +uint32_t +SOA::getMinimum() const { + // Make sure the buffer access is safe. + BOOST_STATIC_ASSERT(sizeof(numdata_) == + sizeof(uint32_t) * 4 + sizeof(uint32_t)); + + InputBuffer b(&numdata_[sizeof(uint32_t) * 4], sizeof(uint32_t)); + return (b.readUint32()); +} + +string +SOA::toText() const { + InputBuffer b(numdata_, sizeof(numdata_)); + uint32_t serial = b.readUint32(); + uint32_t refresh = b.readUint32(); + uint32_t retry = b.readUint32(); + uint32_t expire = b.readUint32(); + uint32_t minimum = b.readUint32(); + + return (mname_.toText() + " " + rname_.toText() + " " + + lexical_cast<string>(serial) + " " + + lexical_cast<string>(refresh) + " " + + lexical_cast<string>(retry) + " " + + lexical_cast<string>(expire) + " " + + lexical_cast<string>(minimum)); +} + +int +SOA::compare(const Rdata& other) const { + const SOA& other_soa = dynamic_cast<const SOA&>(other); + + int order = compareNames(mname_, other_soa.mname_); + if (order != 0) { + return (order); + } + + order = compareNames(rname_, other_soa.rname_); + if (order != 0) { + return (order); + } + + return (memcmp(numdata_, other_soa.numdata_, sizeof(numdata_))); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2010-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/. + +#include <config.h> + +#include <stdint.h> +#include <string.h> + +#include <string> +#include <vector> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class. The semantics of the class is provided by +/// a copy of instantiated TXTLikeImpl class common to both TXT and SPF. +#include <dns/rdata/generic/detail/txt_like.h> + +using namespace std; +using namespace isc::util; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +/// \brief The assignment operator +/// +/// It internally allocates a resource, and if it fails a corresponding +/// standard exception will be thrown. +/// This method never throws an exception otherwise. +SPF& +SPF::operator=(const SPF& source) { + if (this == &source) { + return (*this); + } + + SPFImpl* newimpl = new SPFImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +/// \brief The destructor +SPF::~SPF() { + delete impl_; +} + +/// \brief Constructor from wire-format data. +/// +/// It internally allocates a resource, and if it fails a corresponding +/// standard exception will be thrown. +SPF::SPF(InputBuffer& buffer, size_t rdata_len) : + impl_(new SPFImpl(buffer, rdata_len)) +{} + +/// \brief Constructor using the master lexer. +/// +/// This implementation only uses the \c lexer parameters; others are +/// ignored. +/// +/// \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. +SPF::SPF(MasterLexer& lexer, const Name*, MasterLoader::Options, + MasterLoaderCallbacks&) : + impl_(new SPFImpl(lexer)) +{} + +/// \brief Constructor from string. +/// +/// It internally allocates a resource, and if it fails a corresponding +/// standard exception will be thrown. +SPF::SPF(const std::string& txtstr) : + impl_(new SPFImpl(txtstr)) +{} + +/// \brief Copy constructor +/// +/// It internally allocates a resource, and if it fails a corresponding +/// standard exception will be thrown. +SPF::SPF(const SPF& other) : + Rdata(), impl_(new SPFImpl(*other.impl_)) +{} + +/// \brief Render the \c SPF in the wire format to a OutputBuffer object +/// +/// \return is the return of the corresponding implementation method. +void +SPF::toWire(OutputBuffer& buffer) const { + impl_->toWire(buffer); +} + +/// \brief Render the \c SPF in the wire format to an AbstractMessageRenderer +/// object +/// +/// \return is the return of the corresponding implementation method. +void +SPF::toWire(AbstractMessageRenderer& renderer) const { + impl_->toWire(renderer); +} + +/// \brief Convert the \c SPF to a string. +/// +/// \return is the return of the corresponding implementation method. +string +SPF::toText() const { + return (impl_->toText()); +} + +/// \brief Compare two instances of \c SPF RDATA. +/// +/// This method compares \c this and the \c other \c SPF objects. +/// +/// This method is expected to be used in a polymorphic way, and the +/// parameter to compare against is therefore of the abstract \c Rdata class. +/// However, comparing two \c Rdata objects of different RR types +/// is meaningless, and \c other must point to a \c SPF object; +/// otherwise, the standard \c bad_cast exception will be thrown. +/// +/// \param other the right-hand operand to compare against. +/// \return is the return of the corresponding implementation method. +int +SPF::compare(const Rdata& other) const { + const SPF& other_txt = dynamic_cast<const SPF&>(other); + + return (impl_->compare(*other_txt.impl_)); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2012-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/. + +#include <config.h> + +#include <boost/lexical_cast.hpp> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <util/encode/hex.h> +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +using namespace std; +using boost::lexical_cast; +using namespace isc::util; +using namespace isc::util::encode; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +struct SSHFPImpl { + // straightforward representation of SSHFP RDATA fields + SSHFPImpl(uint8_t algorithm, uint8_t fingerprint_type, + const vector<uint8_t>& fingerprint) : + algorithm_(algorithm), + fingerprint_type_(fingerprint_type), + fingerprint_(fingerprint) + {} + + uint8_t algorithm_; + uint8_t fingerprint_type_; + const vector<uint8_t> fingerprint_; +}; + +// helper function for string and lexer constructors +SSHFPImpl* +SSHFP::constructFromLexer(MasterLexer& lexer) { + const uint32_t algorithm = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (algorithm > 255) { + isc_throw(InvalidRdataText, "SSHFP algorithm number out of range"); + } + + const uint32_t fingerprint_type = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (fingerprint_type > 255) { + isc_throw(InvalidRdataText, "SSHFP fingerprint type out of range"); + } + + std::string fingerprint_str; + std::string fingerprint_substr; + 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.getString(fingerprint_substr); + fingerprint_str.append(fingerprint_substr); + } + lexer.ungetToken(); + + vector<uint8_t> fingerprint; + // If fingerprint is missing, it's OK. See the API documentation of the + // constructor. + if (fingerprint_str.size() > 0) { + try { + decodeHex(fingerprint_str, fingerprint); + } catch (const isc::BadValue& e) { + isc_throw(InvalidRdataText, "Bad SSHFP fingerprint: " << e.what()); + } + } + + return (new SSHFPImpl(algorithm, fingerprint_type, fingerprint)); +} + +/// \brief Constructor from string. +/// +/// The given string must represent a valid SSHFP 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 Algorithm and Fingerprint Type fields must be within their valid +/// ranges, but are not constrained to the values defined in RFC4255. +/// +/// The Fingerprint field may be absent, but if present it must contain a +/// valid hex encoding of the fingerprint. For compatibility with BIND 9, +/// whitespace is allowed in the hex text (RFC4255 is silent on the matter). +/// +/// \throw InvalidRdataText if any fields are missing, are out of their +/// valid ranges or are incorrect, or if the fingerprint is not a valid +/// hex string. +/// +/// \param sshfp_str A string containing the RDATA to be created +SSHFP::SSHFP(const string& sshfp_str) : + 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 SSHFPImpl that constructFromLexer() returns. + std::unique_ptr<SSHFPImpl> impl_ptr; + + try { + std::istringstream ss(sshfp_str); + 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 SSHFP: " + << sshfp_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct SSHFP from '" << + sshfp_str << "': " << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of an SSHFP RDATA. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw InvalidRdataText Fields are out of their valid range or are +/// incorrect, or if the fingerprint is not a valid hex string. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +SSHFP::SSHFP(MasterLexer& lexer, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(constructFromLexer(lexer)) +{ +} + +/// \brief Constructor from InputBuffer. +/// +/// The passed buffer must contain a valid SSHFP RDATA. +/// +/// The Algorithm and Fingerprint Type fields are not checked for unknown +/// values. It is okay for the fingerprint data to be missing (see the +/// description of the constructor from string). +SSHFP::SSHFP(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len < 2) { + isc_throw(InvalidRdataLength, "SSHFP record too short"); + } + + const uint8_t algorithm = buffer.readUint8(); + const uint8_t fingerprint_type = buffer.readUint8(); + + vector<uint8_t> fingerprint; + rdata_len -= 2; + if (rdata_len > 0) { + fingerprint.resize(rdata_len); + buffer.readData(&fingerprint[0], rdata_len); + } + + impl_ = new SSHFPImpl(algorithm, fingerprint_type, fingerprint); +} + +SSHFP::SSHFP(uint8_t algorithm, uint8_t fingerprint_type, + const string& fingerprint_txt) : + impl_(NULL) +{ + vector<uint8_t> fingerprint; + try { + decodeHex(fingerprint_txt, fingerprint); + } catch (const isc::BadValue& e) { + isc_throw(InvalidRdataText, "Bad SSHFP fingerprint: " << e.what()); + } + + impl_ = new SSHFPImpl(algorithm, fingerprint_type, fingerprint); +} + +SSHFP::SSHFP(const SSHFP& other) : + Rdata(), impl_(new SSHFPImpl(*other.impl_)) +{} + +SSHFP& +SSHFP::operator=(const SSHFP& source) { + if (this == &source) { + return (*this); + } + + SSHFPImpl* newimpl = new SSHFPImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +SSHFP::~SSHFP() { + delete impl_; +} + +void +SSHFP::toWire(OutputBuffer& buffer) const { + buffer.writeUint8(impl_->algorithm_); + buffer.writeUint8(impl_->fingerprint_type_); + + if (!impl_->fingerprint_.empty()) { + buffer.writeData(&impl_->fingerprint_[0], + impl_->fingerprint_.size()); + } +} + +void +SSHFP::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint8(impl_->algorithm_); + renderer.writeUint8(impl_->fingerprint_type_); + + if (!impl_->fingerprint_.empty()) { + renderer.writeData(&impl_->fingerprint_[0], + impl_->fingerprint_.size()); + } +} + +string +SSHFP::toText() const { + return (lexical_cast<string>(static_cast<int>(impl_->algorithm_)) + " " + + lexical_cast<string>(static_cast<int>(impl_->fingerprint_type_)) + + (impl_->fingerprint_.empty() ? "" : + " " + encodeHex(impl_->fingerprint_))); +} + +int +SSHFP::compare(const Rdata& other) const { + const SSHFP& other_sshfp = dynamic_cast<const SSHFP&>(other); + + if (impl_->algorithm_ < other_sshfp.impl_->algorithm_) { + return (-1); + } else if (impl_->algorithm_ > other_sshfp.impl_->algorithm_) { + return (1); + } + + if (impl_->fingerprint_type_ < other_sshfp.impl_->fingerprint_type_) { + return (-1); + } else if (impl_->fingerprint_type_ > + other_sshfp.impl_->fingerprint_type_) { + return (1); + } + + const size_t this_len = impl_->fingerprint_.size(); + const size_t other_len = other_sshfp.impl_->fingerprint_.size(); + const size_t cmplen = min(this_len, other_len); + + if (cmplen > 0) { + const int cmp = memcmp(&impl_->fingerprint_[0], + &other_sshfp.impl_->fingerprint_[0], + cmplen); + if (cmp != 0) { + return (cmp); + } + } + + if (this_len == other_len) { + return (0); + } else if (this_len < other_len) { + return (-1); + } else { + return (1); + } +} + +uint8_t +SSHFP::getAlgorithmNumber() const { + return (impl_->algorithm_); +} + +uint8_t +SSHFP::getFingerprintType() const { + return (impl_->fingerprint_type_); +} + +const std::vector<uint8_t>& +SSHFP::getFingerprint() const { + return (impl_->fingerprint_); +} + +size_t +SSHFP::getFingerprintLength() const { + return (impl_->fingerprint_.size()); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 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/. + +#include <config.h> + +#include <string> +#include <sstream> +#include <vector> + +#include <boost/lexical_cast.hpp> + +#include <util/buffer.h> +#include <util/encode/base64.h> +#include <util/time_utilities.h> + +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rcode.h> +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using boost::lexical_cast; +using namespace isc::util; +using namespace isc::util::encode; +using namespace isc::dns; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +const uint16_t TKEY::GSS_API_MODE = 3; + +// straightforward representation of TKEY RDATA fields +struct TKEYImpl { + /// \brief Constructor from RDATA field parameters. + /// + /// \param algorithm The DNS name of the algorithm e.g. gss-tsig. + /// \param inception The inception time (in seconds since 1970). + /// \param expire The expire time (in seconds since 1970). + /// \param mode The mode e.g. Diffie-Hellman (2) or GSS-API (3). + /// \param error The error code (extended error space shared with TSIG). + /// \param key The key (can be empty). + /// \param other_data The other data (can be and usually is empty). + TKEYImpl(const Name& algorithm, uint32_t inception, uint32_t expire, + uint16_t mode, uint16_t error, vector<uint8_t>& key, + vector<uint8_t>& other_data) : + algorithm_(algorithm), inception_(inception), expire_(expire), + mode_(mode), error_(error), key_(key), other_data_(other_data) + {} + + /// \brief Constructor from RDATA field parameters. + /// + /// \param algorithm The DNS name of the algorithm e.g. gss-tsig. + /// \param inception The inception time (in seconds since 1970). + /// \param expire The expire time (in seconds since 1970). + /// \param mode The mode e.g. Diffie-Hellman (2) or GSS-API (3). + /// \param error The error code (extended error space shared with TSIG). + /// \param key_len The key length (0 means no key). + /// \param key The key (can be 0). + /// \param other_len The other data length (0 means no other data). + /// \param other_data The other data (can be and usually is 0). + TKEYImpl(const Name& algorithm, uint32_t inception, uint32_t expire, + uint16_t mode, uint16_t error, size_t key_len, + const void* key, size_t other_len, const void* other_data) : + algorithm_(algorithm), inception_(inception), expire_(expire), + mode_(mode), error_(error), + key_(key_len > 0 ? + vector<uint8_t>(static_cast<const uint8_t*>(key), + static_cast<const uint8_t*>(key) + key_len) : + vector<uint8_t>(key_len)), + other_data_(other_len > 0 ? + vector<uint8_t>(static_cast<const uint8_t*>(other_data), + static_cast<const uint8_t*>(other_data) + + other_len) : + vector<uint8_t>(other_len)) + {} + + /// \brief Common part of toWire methods. + /// \tparam Output \c OutputBuffer or \c AbstractMessageRenderer. + template <typename Output> + void toWireCommon(Output& output) const; + + /// \brief The DNS name of the algorithm e.g. gss-tsig. + const Name algorithm_; + + /// \brief The inception time (in seconds since 1970). + const uint32_t inception_; + + /// \brief The expire time (in seconds since 1970). + const uint32_t expire_; + + /// \brief The mode e.g. Diffie-Hellman (2) or GSS-API (3). + const uint16_t mode_; + + /// \brief The error code (extended error space shared with TSIG). + const uint16_t error_; + + /// \brief The key (can be empty). + const vector<uint8_t> key_; + + /// \brief The other data (can be and usually is empty). + const vector<uint8_t> other_data_; +}; + +// helper function for string and lexer constructors +TKEYImpl* +TKEY::constructFromLexer(MasterLexer& lexer, const Name* origin) { + const Name& algorithm = + createNameFromLexer(lexer, origin ? origin : &Name::ROOT_NAME()); + + const uint32_t inception = + timeFromText32(lexer.getNextToken(MasterToken::STRING).getString()); + + const uint32_t expire = + timeFromText32(lexer.getNextToken(MasterToken::STRING).getString()); + + /// The mode is either a mnemonic (only one is defined: GSS-API) or + /// a number. + const string& mode_txt = + lexer.getNextToken(MasterToken::STRING).getString(); + uint32_t mode = 0; + if (mode_txt == "GSS-API") { + mode = GSS_API_MODE; + } else { + /// we cast to uint32_t and range-check, because casting directly to + /// uint16_t will convert negative numbers to large positive numbers + try { + mode = boost::lexical_cast<uint32_t>(mode_txt); + } catch (const boost::bad_lexical_cast&) { + isc_throw(InvalidRdataText, "Invalid TKEY Mode"); + } + if (mode > 0xffff) { + isc_throw(InvalidRdataText, "TKEY Mode out of range"); + } + } + + const string& error_txt = + lexer.getNextToken(MasterToken::STRING).getString(); + uint32_t error = 0; + // XXX: In the initial implementation we hardcode the mnemonics. + // We'll soon generalize this. + if (error_txt == "NOERROR") { + error = Rcode::NOERROR_CODE; + } else if (error_txt == "BADSIG") { + error = TSIGError::BAD_SIG_CODE; + } else if (error_txt == "BADKEY") { + error = TSIGError::BAD_KEY_CODE; + } else if (error_txt == "BADTIME") { + error = TSIGError::BAD_TIME_CODE; + } else if (error_txt == "BADMODE") { + error = TSIGError::BAD_MODE_CODE; + } else if (error_txt == "BADNAME") { + error = TSIGError::BAD_NAME_CODE; + } else if (error_txt == "BADALG") { + error = TSIGError::BAD_ALG_CODE; + } else if (error_txt == "BADTRUNC") { + error = TSIGError::BAD_TRUNC_CODE; + } else { + /// we cast to uint32_t and range-check, because casting directly to + /// uint16_t will convert negative numbers to large positive numbers + try { + error = boost::lexical_cast<uint32_t>(error_txt); + } catch (const boost::bad_lexical_cast&) { + isc_throw(InvalidRdataText, "Invalid TKEY Error"); + } + if (error > 0xffff) { + isc_throw(InvalidRdataText, "TKEY Error out of range"); + } + } + + const uint32_t keylen = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (keylen > 0xffff) { + isc_throw(InvalidRdataText, "TKEY Key Len out of range"); + } + const string keydata_txt = (keylen > 0) ? + lexer.getNextToken(MasterToken::STRING).getString() : ""; + vector<uint8_t> key_data; + decodeBase64(keydata_txt, key_data); + if (key_data.size() != keylen) { + isc_throw(InvalidRdataText, + "TKEY Key Data length does not match Other Len"); + } + + const uint32_t otherlen = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (otherlen > 0xffff) { + isc_throw(InvalidRdataText, "TKEY Other Len out of range"); + } + const string otherdata_txt = (otherlen > 0) ? + lexer.getNextToken(MasterToken::STRING).getString() : ""; + vector<uint8_t> other_data; + decodeBase64(otherdata_txt, other_data); + if (other_data.size() != otherlen) { + isc_throw(InvalidRdataText, + "TKEY Other Data length does not match Other Len"); + } + // RFC2845 says Other Data is "empty unless Error == BADTIME". + // However, we don't enforce that. + + return (new TKEYImpl(algorithm, inception, expire, mode, error, + key_data, other_data)); +} + +/// \brief Constructor from string. +/// +/// The given string must represent a valid TKEY 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. +/// +/// \c tkey_str must be formatted as follows: +/// \code <Algorithm Name> <Inception> <Expire> <Mode> <Error> +/// <Key Len> [<Key Data>] <Other Len> [<Other Data>] +/// \endcode +/// +/// Note that, since the Algorithm Name field is defined to be "in domain name +/// syntax", but it is not actually a domain name, it does not have to be +/// fully qualified. +/// +/// The Mode field is an unsigned 16-bit decimal integer as specified +/// in RFC2930 or a common mnemonic. Currently only "GSS-API" (case sensitive) +/// is supported ("Diffie-Hellman" is not). +/// +/// The Error field is an unsigned 16-bit decimal integer or a valid mnemonic +/// as specified in RFC2845. Currently, "NOERROR", "BADSIG", "BADKEY", +/// "BADTIME", "BADMODE", "BADNAME", and "BADALG" are supported +/// (case sensitive). In future versions other representations that +/// are compatible with the DNS RCODE may be supported. +/// +/// The Key Data and Other Data fields are base-64 encoded strings that do not +/// contain space characters. +/// If the Key Len field is 0, the Key Data field must not appear in +/// \c tkey_str. +/// If the Other Len field is 0, the Other Data field must not appear in +/// \c tkey_str. +/// The decoded data of the Key Data field is Key Len bytes of binary stream. +/// The decoded data of the Other Data field is Other Len bytes of binary +/// stream. +/// +/// An example of valid string is: +/// \code "gss-tsig. 20210501120000 20210501130000 0 3 aabbcc 0" \endcode +/// In this example Other Data is missing because Other Len is 0. +/// +/// Note that RFC2930 does not define the standard presentation format +/// of %TKEY RR, so the above syntax is implementation specific. +/// This is, however, compatible with the format acceptable to BIND 9's +/// RDATA parser. +/// +/// \throw Others Exception from the Name constructors. +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// \throw BadValue if Key Data or Other Data is not validly encoded +/// in base-64. +/// +/// \param tkey_str A string containing the RDATA to be created +TKEY::TKEY(const std::string& tkey_str) : impl_(0) { + // 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 TKEYImpl that constructFromLexer() returns. + std::unique_ptr<TKEYImpl> impl_ptr; + + try { + std::istringstream ss(tkey_str); + MasterLexer lexer; + lexer.pushSource(ss); + + impl_ptr.reset(constructFromLexer(lexer, 0)); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, + "Extra input text for TKEY: " << tkey_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, + "Failed to construct TKEY from '" << tkey_str << "': " + << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of an TKEY RDATA. +/// +/// See \c TKEY::TKEY(const std::string&) for description of the +/// expected RDATA fields. +/// +/// \throw MasterLexer::LexerError General parsing error such as +/// missing field. +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +TKEY::TKEY(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(constructFromLexer(lexer, origin)) +{ +} + +/// \brief Constructor from wire-format data. +/// +/// When a read operation on \c buffer fails (e.g., due to a corrupted +/// message) a corresponding exception from the \c InputBuffer class will +/// be thrown. +/// If the wire-format data does not begin with a valid domain name, +/// a corresponding exception from the \c Name class will be thrown. +/// In addition, this constructor internally involves resource allocation, +/// and if it fails a corresponding standard exception will be thrown. +/// +/// According to RFC3597, the Algorithm field must be a non compressed form +/// of domain name. But this implementation accepts a %TKEY RR even if that +/// field is compressed. +/// +/// \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. +/// But this constructor does not use this parameter; if necessary, the caller +/// must check consistency between the length parameter and the actual +/// RDATA length. +TKEY::TKEY(InputBuffer& buffer, size_t) : + impl_(0) +{ + Name algorithm(buffer); + + const uint32_t inception = buffer.readUint32(); + + const uint32_t expire = buffer.readUint32(); + + const uint16_t mode = buffer.readUint16(); + + const uint16_t error = buffer.readUint16(); + + const uint16_t key_len = buffer.readUint16(); + vector<uint8_t> key(key_len); + if (key_len > 0) { + buffer.readData(&key[0], key_len); + } + + const uint16_t other_len = buffer.readUint16(); + vector<uint8_t> other_data(other_len); + if (other_len > 0) { + buffer.readData(&other_data[0], other_len); + } + + impl_ = new TKEYImpl(algorithm, inception, expire, mode, error, + key, other_data); +} + +TKEY::TKEY(const Name& algorithm, uint32_t inception, uint32_t expire, + uint16_t mode, uint16_t error, uint16_t key_len, + const void* key, uint16_t other_len, const void* other_data) : + impl_(0) +{ + if ((key_len == 0 && key != 0) || (key_len > 0 && key == 0)) { + isc_throw(InvalidParameter, "TKEY Key length and data inconsistent"); + } + if ((other_len == 0 && other_data != 0) || + (other_len > 0 && other_data == 0)) { + isc_throw(InvalidParameter, + "TKEY Other data length and data inconsistent"); + } + impl_ = new TKEYImpl(algorithm, inception, expire, mode, error, + key_len, key, other_len, other_data); +} + +/// \brief The copy constructor. +/// +/// It internally allocates a resource, and if it fails a corresponding +/// standard exception will be thrown. +/// This constructor never throws an exception otherwise. +TKEY::TKEY(const TKEY& source) : Rdata(), impl_(new TKEYImpl(*source.impl_)) +{} + +TKEY& +TKEY::operator=(const TKEY& source) { + if (this == &source) { + return (*this); + } + + TKEYImpl* newimpl = new TKEYImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +TKEY::~TKEY() { + delete impl_; +} + +/// \brief Convert the \c TKEY to a string. +/// +/// The output of this method is formatted as described in the "from string" +/// constructor (\c TKEY(const std::string&))). +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// +/// \return A \c string object that represents the \c TKEY object. +std::string +TKEY::toText() const { + string result; + + result += impl_->algorithm_.toText() + " " + + timeToText32(impl_->inception_) + " " + + timeToText32(impl_->expire_) + " "; + if (impl_->mode_ == GSS_API_MODE) { + result += "GSS-API "; + } else { + result += lexical_cast<string>(impl_->mode_) + " "; + } + result += TSIGError(impl_->error_).toText() + " " + + lexical_cast<string>(impl_->key_.size()) + " "; + if (!impl_->key_.empty()) { + result += encodeBase64(impl_->key_) + " "; + } + result += lexical_cast<string>(impl_->other_data_.size()); + if (!impl_->other_data_.empty()) { + result += " " + encodeBase64(impl_->other_data_); + } + + return (result); +} + +// Common sequence of toWire() operations used for the two versions of +// toWire(). +template <typename Output> +void +TKEYImpl::toWireCommon(Output& output) const { + output.writeUint32(inception_); + output.writeUint32(expire_); + output.writeUint16(mode_); + output.writeUint16(error_); + const uint16_t key_len = key_.size(); + output.writeUint16(key_len); + if (key_len > 0) { + output.writeData(&key_[0], key_len); + } + const uint16_t other_len = other_data_.size(); + output.writeUint16(other_len); + if (other_len > 0) { + output.writeData(&other_data_[0], other_len); + } +} + +/// \brief Render the \c TKEY in the wire format without name compression. +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// This method never throws an exception otherwise. +/// +/// \param buffer An output buffer to store the wire data. +void +TKEY::toWire(OutputBuffer& buffer) const { + impl_->algorithm_.toWire(buffer); + impl_->toWireCommon<OutputBuffer>(buffer); +} + +/// \brief Render the \c TKEY in the wire format with taking into account +/// compression. +/// +/// As specified in RFC3597, the Algorithm field (a domain name) will not +/// be compressed. However, the domain name could be a target of compression +/// of other compressible names (though pretty unlikely), the offset +/// information of the algorithm name may be recorded in \c renderer. +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// This method never throws an exception otherwise. +/// +/// \param renderer DNS message rendering context that encapsulates the +/// output buffer and name compression information. +void +TKEY::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeName(impl_->algorithm_, false); + impl_->toWireCommon<AbstractMessageRenderer>(renderer); +} + +// A helper function commonly used for TKEY::compare(). +int +vectorComp(const vector<uint8_t>& v1, const vector<uint8_t>& v2) { + const size_t this_size = v1.size(); + const size_t other_size = v2.size(); + if (this_size != other_size) { + return (this_size < other_size ? -1 : 1); + } + if (this_size > 0) { + return (memcmp(&v1[0], &v2[0], this_size)); + } + return (0); +} + +/// \brief Compare two instances of \c TKEY RDATA. +/// +/// This method compares \c this and the \c other \c TKEY objects +/// in terms of the DNSSEC sorting order as defined in RFC4034, and returns +/// the result as an integer. +/// +/// This method is expected to be used in a polymorphic way, and the +/// parameter to compare against is therefore of the abstract \c Rdata class. +/// However, comparing two \c Rdata objects of different RR types +/// is meaningless, and \c other must point to a \c TKEY object; +/// otherwise, the standard \c bad_cast exception will be thrown. +/// This method never throws an exception otherwise. +/// +/// \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 +TKEY::compare(const Rdata& other) const { + const TKEY& other_tkey = dynamic_cast<const TKEY&>(other); + + const int ncmp = compareNames(impl_->algorithm_, + other_tkey.impl_->algorithm_); + if (ncmp != 0) { + return (ncmp); + } + + if (impl_->inception_ != other_tkey.impl_->inception_) { + return (impl_->inception_ < other_tkey.impl_->inception_ ? -1 : 1); + } + if (impl_->expire_ != other_tkey.impl_->expire_) { + return (impl_->expire_ < other_tkey.impl_->expire_ ? -1 : 1); + } + if (impl_->mode_ != other_tkey.impl_->mode_) { + return (impl_->mode_ < other_tkey.impl_->mode_ ? -1 : 1); + } + if (impl_->error_ != other_tkey.impl_->error_) { + return (impl_->error_ < other_tkey.impl_->error_ ? -1 : 1); + } + + const int vcmp = vectorComp(impl_->key_, other_tkey.impl_->key_); + if (vcmp != 0) { + return (vcmp); + } + return (vectorComp(impl_->other_data_, other_tkey.impl_->other_data_)); +} + +const Name& +TKEY::getAlgorithm() const { + return (impl_->algorithm_); +} + +uint32_t +TKEY::getInception() const { + return (impl_->inception_); +} + +string +TKEY::getInceptionDate() const { + return (timeToText32(impl_->inception_)); +} + +uint32_t +TKEY::getExpire() const { + return (impl_->expire_); +} + +string +TKEY::getExpireDate() const { + return (timeToText32(impl_->expire_)); +} + +uint16_t +TKEY::getMode() const { + return (impl_->mode_); +} + +uint16_t +TKEY::getError() const { + return (impl_->error_); +} + +uint16_t +TKEY::getKeyLen() const { + return (impl_->key_.size()); +} + +const void* +TKEY::getKey() const { + if (!impl_->key_.empty()) { + return (&impl_->key_[0]); + } else { + return (0); + } +} + +uint16_t +TKEY::getOtherLen() const { + return (impl_->other_data_.size()); +} + +const void* +TKEY::getOtherData() const { + if (!impl_->other_data_.empty()) { + return (&impl_->other_data_[0]); + } else { + return (0); + } +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2014-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/. + +#include <config.h> + +#include <boost/lexical_cast.hpp> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <util/encode/hex.h> +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata_pimpl_holder.h> + +using namespace std; +using boost::lexical_cast; +using namespace isc::util; +using namespace isc::util::encode; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +struct TLSAImpl { + // straightforward representation of TLSA RDATA fields + TLSAImpl(uint8_t certificate_usage, uint8_t selector, + uint8_t matching_type, const vector<uint8_t>& data) : + certificate_usage_(certificate_usage), + selector_(selector), + matching_type_(matching_type), + data_(data) + {} + + uint8_t certificate_usage_; + uint8_t selector_; + uint8_t matching_type_; + const vector<uint8_t> data_; +}; + +// helper function for string and lexer constructors +TLSAImpl* +TLSA::constructFromLexer(MasterLexer& lexer) { + const uint32_t certificate_usage = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (certificate_usage > 255) { + isc_throw(InvalidRdataText, + "TLSA certificate usage field out of range"); + } + + const uint32_t selector = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (selector > 255) { + isc_throw(InvalidRdataText, + "TLSA selector field out of range"); + } + + const uint32_t matching_type = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (matching_type > 255) { + isc_throw(InvalidRdataText, + "TLSA matching type field out of range"); + } + + std::string certificate_assoc_data; + std::string data_substr; + 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.getString(data_substr); + certificate_assoc_data.append(data_substr); + } + lexer.ungetToken(); + + if (certificate_assoc_data.empty()) { + isc_throw(InvalidRdataText, "Empty TLSA certificate association data"); + } + + vector<uint8_t> data; + try { + decodeHex(certificate_assoc_data, data); + } catch (const isc::BadValue& e) { + isc_throw(InvalidRdataText, + "Bad TLSA certificate association data: " << e.what()); + } + + return (new TLSAImpl(certificate_usage, selector, matching_type, data)); +} + +/// \brief Constructor from string. +/// +/// The given string must represent a valid TLSA 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 Certificate Usage, Selector and Matching Type fields must be +/// within their valid ranges, but are not constrained to the values +/// defined in RFC6698. +/// +/// The Certificate Association Data Field field may be absent, but if +/// present it must contain a valid hex encoding of the data. Whitespace +/// is allowed in the hex text. +/// +/// \throw InvalidRdataText if any fields are missing, out of their +/// valid ranges, or are incorrect, or Certificate Association Data is +/// not a valid hex string. +/// +/// \param tlsa_str A string containing the RDATA to be created +TLSA::TLSA(const string& tlsa_str) : + impl_(NULL) +{ + // We use a smart pointer here because if there is an exception in + // this constructor, the destructor is not called and there could be + // a leak of the TLSAImpl that constructFromLexer() returns. + RdataPimplHolder<TLSAImpl> impl_ptr; + + try { + std::istringstream ss(tlsa_str); + 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 TLSA: " + << tlsa_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct TLSA from '" << + tlsa_str << "': " << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of an TLSA RDATA. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw InvalidRdataText Fields are out of their valid range, or are +/// incorrect, or Certificate Association Data is not a valid hex string. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +TLSA::TLSA(MasterLexer& lexer, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(constructFromLexer(lexer)) +{ +} + +/// \brief Constructor from InputBuffer. +/// +/// The passed buffer must contain a valid TLSA RDATA. +/// +/// The Certificate Usage, Selector and Matching Type fields must be +/// within their valid ranges, but are not constrained to the values +/// defined in RFC6698. It is okay for the certificate association data +/// to be missing (see the description of the constructor from string). +TLSA::TLSA(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len < 3) { + isc_throw(InvalidRdataLength, "TLSA record too short"); + } + + const uint8_t certificate_usage = buffer.readUint8(); + const uint8_t selector = buffer.readUint8(); + const uint8_t matching_type = buffer.readUint8(); + + vector<uint8_t> data; + rdata_len -= 3; + + if (rdata_len == 0) { + isc_throw(InvalidRdataLength, + "Empty TLSA certificate association data"); + } + + data.resize(rdata_len); + buffer.readData(&data[0], rdata_len); + + impl_ = new TLSAImpl(certificate_usage, selector, matching_type, data); +} + +TLSA::TLSA(uint8_t certificate_usage, uint8_t selector, + uint8_t matching_type, const std::string& certificate_assoc_data) : + impl_(NULL) +{ + if (certificate_assoc_data.empty()) { + isc_throw(InvalidRdataText, "Empty TLSA certificate association data"); + } + + vector<uint8_t> data; + try { + decodeHex(certificate_assoc_data, data); + } catch (const isc::BadValue& e) { + isc_throw(InvalidRdataText, + "Bad TLSA certificate association data: " << e.what()); + } + + impl_ = new TLSAImpl(certificate_usage, selector, matching_type, data); +} + +TLSA::TLSA(const TLSA& other) : + Rdata(), impl_(new TLSAImpl(*other.impl_)) +{} + +TLSA& +TLSA::operator=(const TLSA& source) { + if (this == &source) { + return (*this); + } + + TLSAImpl* newimpl = new TLSAImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +TLSA::~TLSA() { + delete impl_; +} + +void +TLSA::toWire(OutputBuffer& buffer) const { + buffer.writeUint8(impl_->certificate_usage_); + buffer.writeUint8(impl_->selector_); + buffer.writeUint8(impl_->matching_type_); + + // The constructors must ensure that the certificate association + // data field is not empty. + assert(!impl_->data_.empty()); + buffer.writeData(&impl_->data_[0], impl_->data_.size()); +} + +void +TLSA::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint8(impl_->certificate_usage_); + renderer.writeUint8(impl_->selector_); + renderer.writeUint8(impl_->matching_type_); + + // The constructors must ensure that the certificate association + // data field is not empty. + assert(!impl_->data_.empty()); + renderer.writeData(&impl_->data_[0], impl_->data_.size()); +} + +string +TLSA::toText() const { + // The constructors must ensure that the certificate association + // data field is not empty. + assert(!impl_->data_.empty()); + + return (lexical_cast<string>(static_cast<int>(impl_->certificate_usage_)) + " " + + lexical_cast<string>(static_cast<int>(impl_->selector_)) + " " + + lexical_cast<string>(static_cast<int>(impl_->matching_type_)) + " " + + encodeHex(impl_->data_)); +} + +int +TLSA::compare(const Rdata& other) const { + const TLSA& other_tlsa = dynamic_cast<const TLSA&>(other); + + if (impl_->certificate_usage_ < other_tlsa.impl_->certificate_usage_) { + return (-1); + } else if (impl_->certificate_usage_ > + other_tlsa.impl_->certificate_usage_) { + return (1); + } + + if (impl_->selector_ < other_tlsa.impl_->selector_) { + return (-1); + } else if (impl_->selector_ > other_tlsa.impl_->selector_) { + return (1); + } + + if (impl_->matching_type_ < other_tlsa.impl_->matching_type_) { + return (-1); + } else if (impl_->matching_type_ > + other_tlsa.impl_->matching_type_) { + return (1); + } + + const size_t this_len = impl_->data_.size(); + const size_t other_len = other_tlsa.impl_->data_.size(); + const size_t cmplen = min(this_len, other_len); + + if (cmplen > 0) { + const int cmp = memcmp(&impl_->data_[0], + &other_tlsa.impl_->data_[0], + cmplen); + if (cmp != 0) { + return (cmp); + } + } + + if (this_len == other_len) { + return (0); + } else if (this_len < other_len) { + return (-1); + } else { + return (1); + } +} + +uint8_t +TLSA::getCertificateUsage() const { + return (impl_->certificate_usage_); +} + +uint8_t +TLSA::getSelector() const { + return (impl_->selector_); +} + +uint8_t +TLSA::getMatchingType() const { + return (impl_->matching_type_); +} + +const std::vector<uint8_t>& +TLSA::getData() const { + return (impl_->data_); +} + +size_t +TLSA::getDataLength() const { + return (impl_->data_.size()); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2010-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/. + +#include <config.h> + +#include <stdint.h> +#include <string.h> + +#include <string> +#include <vector> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata/generic/detail/txt_like.h> + +using namespace std; +using namespace isc::util; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +TXT& +TXT::operator=(const TXT& source) { + if (this == &source) { + return (*this); + } + + TXTImpl* newimpl = new TXTImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +TXT::~TXT() { + delete impl_; +} + +TXT::TXT(InputBuffer& buffer, size_t rdata_len) : + impl_(new TXTImpl(buffer, rdata_len)) +{} + +/// \brief Constructor using the master lexer. +/// +/// This implementation only uses the \c lexer parameters; others are +/// ignored. +/// +/// \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. +TXT::TXT(MasterLexer& lexer, const Name*, MasterLoader::Options, + MasterLoaderCallbacks&) : + impl_(new TXTImpl(lexer)) +{} + +TXT::TXT(const std::string& txtstr) : + impl_(new TXTImpl(txtstr)) +{} + +TXT::TXT(const TXT& other) : + Rdata(), impl_(new TXTImpl(*other.impl_)) +{} + +void +TXT::toWire(OutputBuffer& buffer) const { + impl_->toWire(buffer); +} + +void +TXT::toWire(AbstractMessageRenderer& renderer) const { + impl_->toWire(renderer); +} + +string +TXT::toText() const { + return (impl_->toText()); +} + +int +TXT::compare(const Rdata& other) const { + const TXT& other_txt = dynamic_cast<const TXT&>(other); + + return (impl_->compare(*other_txt.impl_)); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2010-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/. + +#include <config.h> + +#include <string> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +using namespace std; +using namespace isc::util; + +namespace isc { +namespace dns { +namespace rdata { +namespace hs { + +A::A(const std::string&) { + // TBD +} + +A::A(MasterLexer&, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) +{ + // TBD +} + +A::A(InputBuffer&, size_t) { + // TBD +} + +A::A(const A&) : Rdata() { + // TBD +} + +void +A::toWire(OutputBuffer&) const { + // TBD +} + +void +A::toWire(AbstractMessageRenderer&) const { + // TBD +} + +string +A::toText() const { + // TBD + isc_throw(InvalidRdataText, "Not implemented yet"); +} + +int +A::compare(const Rdata&) const { + return (0); // dummy. TBD +} + +} // end of namespace "hs" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2010-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/. + +#include <config.h> + +#include <stdint.h> +#include <string.h> + +#include <cerrno> +#include <cstring> +#include <string> + +#include <arpa/inet.h> // XXX: for inet_pton/ntop(), not exist in C++ standards +#include <sys/socket.h> // for AF_INET/AF_INET6 + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> + +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/master_lexer.h> +#include <dns/master_loader_callbacks.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +using namespace std; +using namespace isc::util; + +namespace isc { +namespace dns { +namespace rdata { +namespace in { + +namespace { +void +convertToIPv4Addr(const char* src, size_t src_len, uint32_t* dst) { + // This check specifically rejects invalid input that begins with valid + // address text followed by a nul character (and possibly followed by + // further garbage). It cannot be detected by inet_pton(). + // + // Note that this is private subroutine of the in::A constructors, which + // pass std::string.size() or StringRegion::len as src_len, so it should + // be equal to strlen() unless there's an intermediate nul character. + if (src_len != strlen(src)) { + isc_throw(InvalidRdataText, + "Bad IN/A RDATA text: unexpected nul in string: '" + << src << "'"); + } + const int result = inet_pton(AF_INET, src, dst); + if (result == 0) { + isc_throw(InvalidRdataText, "Bad IN/A RDATA text: '" << src << "'"); + } else if (result < 0) { + isc_throw(isc::Unexpected, + "Unexpected failure in parsing IN/A RDATA text: '" + << src << "': " << std::strerror(errno)); + } +} +} + +/// \brief Constructor from string. +/// +/// The given string must be a valid textual representation of an IPv4 +/// address as specified in RFC1035, that is, four decimal numbers separated +/// by dots without any embedded spaces. Note that it excludes abbreviated +/// forms such as "10.1" to mean "10.0.0.1". +/// +/// Internally, this implementation uses the standard inet_pton() library +/// function for the AF_INET family to parse and convert the textual +/// representation. While standard compliant implementations of this function +/// should accept exactly what this constructor expects, specific +/// implementation may behave differently, in which case this constructor +/// will simply accept the result of inet_pton(). In any case, the user of +/// the class shouldn't assume such specific implementation behavior of +/// inet_pton(). +/// +/// No extra character should be contained in \c addrstr other than the +/// textual address. These include spaces and the nul character. +/// +/// \throw InvalidRdata The text extracted by the lexer isn't recognized as +/// a valid IPv4 address. +/// \throw Unexpected Unexpected system error in conversion (this should be +/// very rare). +/// +/// \param addrstr Textual representation of IPv4 address to be used as the +/// RDATA. +A::A(const std::string& addrstr) { + convertToIPv4Addr(addrstr.c_str(), addrstr.size(), &addr_); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of a class IN A RDATA. +/// +/// The acceptable form of the textual address is generally the same as the +/// string version of the constructor, but this version accepts beginning +/// spaces and trailing spaces or other characters. Trailing non space +/// characters would be considered an invalid form in an RR representation, +/// but handling such errors is not the responsibility of this constructor. +/// It also accepts other unusual syntax that would be considered valid +/// in the context of DNS master file; for example, it accepts an IPv4 +/// address surrounded by parentheses, such as "(192.0.2.1)", although it's +/// very unlikely to be used for this type of RDATA. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw InvalidRdata The text extracted by the lexer isn't recognized as +/// a valid IPv4 address. +/// \throw Unexpected Unexpected system error in conversion (this should be +/// very rare). +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +A::A(MasterLexer& lexer, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) +{ + const MasterToken& token = lexer.getNextToken(MasterToken::STRING); + convertToIPv4Addr(token.getStringRegion().beg, token.getStringRegion().len, + &addr_); +} + +A::A(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len != sizeof(addr_)) { + isc_throw(DNSMessageFORMERR, + "IN/A RDATA construction from wire failed: Invalid length: " + << rdata_len); + } + if (buffer.getLength() - buffer.getPosition() < sizeof(addr_)) { + isc_throw(DNSMessageFORMERR, + "IN/A RDATA construction from wire failed: " + "insufficient buffer length: " + << buffer.getLength() - buffer.getPosition()); + } + buffer.readData(&addr_, sizeof(addr_)); +} + +/// \brief Copy constructor. +A::A(const A& other) : Rdata(), addr_(other.addr_) +{} + +void +A::toWire(OutputBuffer& buffer) const { + buffer.writeData(&addr_, sizeof(addr_)); +} + +void +A::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeData(&addr_, sizeof(addr_)); +} + +/// \brief Return a textual form of the underlying IPv4 address of the RDATA. +string +A::toText() const { + char addr_string[sizeof("255.255.255.255")]; + + if (inet_ntop(AF_INET, &addr_, addr_string, sizeof(addr_string)) == NULL) { + isc_throw(Unexpected, + "Failed to convert IN/A RDATA to textual IPv4 address"); + } + + return (addr_string); +} + +/// \brief Compare two in::A RDATAs. +/// +/// In effect, it compares the two RDATA as an unsigned 32-bit integer. +int +A::compare(const Rdata& other) const { + const A& other_a = dynamic_cast<const A&>(other); + return (memcmp(&addr_, &other_a.addr_, sizeof(addr_))); +} +} // end of namespace "in" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2010-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/. + +#include <config.h> + +#include <exceptions/exceptions.h> +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/master_lexer.h> +#include <dns/master_loader.h> + +#include <stdint.h> +#include <string.h> + +#include <cerrno> +#include <cstring> +#include <string> + +#include <arpa/inet.h> // XXX: for inet_pton/ntop(), not exist in C++ standards +#include <sys/socket.h> // for AF_INET/AF_INET6 + +using namespace std; +using namespace isc::util; + +namespace isc { +namespace dns { +namespace rdata { +namespace in { + +namespace { +void +convertToIPv6Addr(const char* src, size_t src_len, void* dst) { + // See a_1.cc for this check. + if (src_len != strlen(src)) { + isc_throw(InvalidRdataText, + "Bad IN/AAAA RDATA text: unexpected nul in string: '" + << src << "'"); + } + const int result = inet_pton(AF_INET6, src, dst); + if (result == 0) { + isc_throw(InvalidRdataText, "Bad IN/AAAA RDATA text: '" << src << "'"); + } else if (result < 0) { + isc_throw(isc::Unexpected, + "Unexpected failure in parsing IN/AAAA RDATA text: '" + << src << "': " << std::strerror(errno)); + } +} +} + +/// \brief Constructor from string. +/// +/// The given string must be a valid textual representation of an IPv6 +/// address as specified in RFC1886. +/// +/// No extra character should be contained in \c addrstr other than the +/// textual address. These include spaces and the nul character. +/// +/// \throw InvalidRdata The text extracted by the lexer isn't recognized as +/// a valid IPv6 address. +/// \throw Unexpected Unexpected system error in conversion (this should be +/// very rare). +/// +/// \param addrstr Textual representation of IPv6 address to be used as the +/// RDATA. +AAAA::AAAA(const std::string& addrstr) { + convertToIPv6Addr(addrstr.c_str(), addrstr.size(), addr_); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of a class IN AAAA RDATA. +/// +/// The acceptable form of the textual address is generally the same as the +/// string version of the constructor, but this version is slightly more +/// flexible. See the similar constructor of \c in::A class; the same +/// notes apply here. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw InvalidRdata The text extracted by the lexer isn't recognized as +/// a valid IPv6 address. +/// \throw Unexpected Unexpected system error in conversion (this should be +/// very rare). +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +AAAA::AAAA(MasterLexer& lexer, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) +{ + const MasterToken& token = lexer.getNextToken(MasterToken::STRING); + convertToIPv6Addr(token.getStringRegion().beg, token.getStringRegion().len, + addr_); +} + +/// \brief Copy constructor. +AAAA::AAAA(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len != sizeof(addr_)) { + isc_throw(DNSMessageFORMERR, + "IN/AAAA RDATA construction from wire failed: " + "Invalid length: " << rdata_len); + } + if (buffer.getLength() - buffer.getPosition() < sizeof(addr_)) { + isc_throw(DNSMessageFORMERR, + "IN/AAAA RDATA construction from wire failed: " + "insufficient buffer length: " + << buffer.getLength() - buffer.getPosition()); + } + buffer.readData(&addr_, sizeof(addr_)); +} + +AAAA::AAAA(const AAAA& other) : Rdata() { + memcpy(addr_, other.addr_, sizeof(addr_)); +} + +/// \brief Return a textual form of the underlying IPv6 address of the RDATA. +void +AAAA::toWire(OutputBuffer& buffer) const { + buffer.writeData(&addr_, sizeof(addr_)); +} + +void +AAAA::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeData(&addr_, sizeof(addr_)); +} + +string +AAAA::toText() const { + char addr_string[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; + + if (inet_ntop(AF_INET6, &addr_, addr_string, sizeof(addr_string)) + == NULL) { + isc_throw(Unexpected, + "Failed to convert IN/AAAA RDATA to textual IPv6 address"); + } + + return (string(addr_string)); +} + +/// \brief Compare two in::AAAA RDATAs. +/// +/// In effect, it compares the two RDATA as an unsigned 128-bit integer. +int +AAAA::compare(const Rdata& other) const { + const AAAA& other_a = dynamic_cast<const AAAA&>(other); + return (memcmp(&addr_, &other_a.addr_, sizeof(addr_))); +} + +} // end of namespace "in" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2011-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/. + +#include <config.h> + +#include <stdint.h> +#include <string.h> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <util/encode/base64.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +using namespace std; +using namespace isc::util; +using namespace isc::util::encode; + +namespace isc { +namespace dns { +namespace rdata { +namespace in { + +void +DHCID::constructFromLexer(MasterLexer& lexer) { + string digest_txt = lexer.getNextToken(MasterToken::STRING).getString(); + + // Whitespace is allowed within base64 text, so read to the end of input. + string digest_part; + 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.getString(digest_part); + digest_txt.append(digest_part); + } + lexer.ungetToken(); + + decodeBase64(digest_txt, digest_); +} + +/// \brief Constructor from string. +/// +/// \param dhcid_str A base-64 representation of the DHCID binary data. +/// +/// \throw InvalidRdataText if the string could not be parsed correctly. +DHCID::DHCID(const std::string& dhcid_str) { + try { + std::istringstream iss(dhcid_str); + MasterLexer lexer; + lexer.pushSource(iss); + + constructFromLexer(lexer); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for DHCID: " + << dhcid_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct DHCID from '" << + dhcid_str << "': " << ex.what()); + } +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of a DHCID RDATA. +/// +/// \throw BadValue if the text is not valid base-64. +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +DHCID::DHCID(MasterLexer& lexer, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) { + constructFromLexer(lexer); +} + +/// \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 +DHCID::DHCID(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len == 0) { + isc_throw(InvalidRdataLength, "Missing DHCID rdata"); + } + + digest_.resize(rdata_len); + buffer.readData(&digest_[0], rdata_len); +} + +/// \brief The copy constructor. +/// +/// This trivial copy constructor never throws an exception. +DHCID::DHCID(const DHCID& other) : Rdata(), digest_(other.digest_) +{} + +/// \brief Render the \c DHCID in the wire format. +/// +/// \param buffer An output buffer to store the wire data. +void +DHCID::toWire(OutputBuffer& buffer) const { + buffer.writeData(&digest_[0], digest_.size()); +} + +/// \brief Render the \c DHCID in the wire format into a +/// \c MessageRenderer object. +/// +/// \param renderer DNS message rendering context that encapsulates the +/// output buffer in which the \c DHCID is to be stored. +void +DHCID::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeData(&digest_[0], digest_.size()); +} + +/// \brief Convert the \c DHCID to a string. +/// +/// This method returns a \c std::string object representing the \c DHCID. +/// +/// \return A string representation of \c DHCID. +string +DHCID::toText() const { + return (encodeBase64(digest_)); +} + +/// \brief Compare two instances of \c DHCID RDATA. +/// +/// See documentation in \c Rdata. +int +DHCID::compare(const Rdata& other) const { + const DHCID& other_dhcid = dynamic_cast<const DHCID&>(other); + + size_t this_len = digest_.size(); + size_t other_len = other_dhcid.digest_.size(); + size_t cmplen = min(this_len, other_len); + int cmp = memcmp(&digest_[0], &other_dhcid.digest_[0], cmplen); + if (cmp != 0) { + return (cmp); + } else { + return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1); + } +} + +/// \brief Accessor method to get the DHCID digest +/// +/// \return A reference to the binary DHCID data +const std::vector<uint8_t>& +DHCID::getDigest() const { + return (digest_); +} + +} // end of namespace "in" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2011-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/. + +#include <config.h> + +#include <iostream> +#include <sstream> + +#include <boost/lexical_cast.hpp> + +#include <util/buffer.h> +#include <util/strutil.h> + +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using namespace isc::util; +using namespace isc::util::str; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +namespace isc { +namespace dns { +namespace rdata { +namespace in { + +struct SRVImpl { + // straightforward representation of SRV RDATA fields + SRVImpl(uint16_t priority, uint16_t weight, uint16_t port, + const Name& target) : + priority_(priority), weight_(weight), port_(port), + target_(target) + {} + + uint16_t priority_; + uint16_t weight_; + uint16_t port_; + Name target_; +}; + +/// \brief Constructor from string. +/// +/// The given string must represent a valid SRV 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 TARGET name must be absolute since there's no parameter that +/// specifies the origin name; if it is not absolute, \c MissingNameOrigin +/// exception will be thrown. It must not be represented as a quoted +/// string. +/// +/// See the construction that takes \c MasterLexer for other fields. +/// +/// \throw Others Exception from the Name and RRTTL constructors. +/// \throw InvalidRdataText Other general syntax errors. +SRV::SRV(const std::string& srv_str) : + impl_(NULL) +{ + try { + std::istringstream ss(srv_str); + MasterLexer lexer; + lexer.pushSource(ss); + + uint32_t num = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (num > 65535) { + isc_throw(InvalidRdataText, "Invalid SRV priority in: " << srv_str); + } + const uint16_t priority = static_cast<uint16_t>(num); + + num = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (num > 65535) { + isc_throw(InvalidRdataText, "Invalid SRV weight in: " << srv_str); + } + const uint16_t weight = static_cast<uint16_t>(num); + + num = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (num > 65535) { + isc_throw(InvalidRdataText, "Invalid SRV port in: " << srv_str); + } + const uint16_t port = static_cast<uint16_t>(num); + + const Name targetname = createNameFromLexer(lexer, NULL); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for SRV: " + << srv_str); + } + + impl_ = new SRVImpl(priority, weight, port, targetname); + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct SRV from '" << + srv_str << "': " << ex.what()); + } +} + +/// \brief Constructor from wire-format data. +/// +/// When a read operation on \c buffer fails (e.g., due to a corrupted +/// message) a corresponding exception from the \c InputBuffer class will +/// be thrown. +/// If the wire-format data does not end with a valid domain name, +/// a corresponding exception from the \c Name class will be thrown. +/// In addition, this constructor internally involves resource allocation, +/// and if it fails a corresponding standard exception will be thrown. +/// +/// According to RFC2782, the Target field must be a non compressed form +/// of domain name. But this implementation accepts a %SRV RR even if that +/// field is compressed as suggested in RFC3597. +/// +/// \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. +SRV::SRV(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len < 6) { + isc_throw(InvalidRdataLength, "SRV too short"); + } + + const uint16_t priority = buffer.readUint16(); + const uint16_t weight = buffer.readUint16(); + const uint16_t port = buffer.readUint16(); + const Name targetname(buffer); + + impl_ = new SRVImpl(priority, weight, port, targetname); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of an SRV RDATA. The TARGET field can be non-absolute if \c origin +/// is non-NULL, in which case \c origin is used to make it absolute. +/// It must not be represented as a quoted string. +/// +/// The PRIORITY, WEIGHT and PORT fields must each be a valid decimal +/// representation of an unsigned 16-bit integers respectively. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and RRTTL constructors if +/// construction of textual fields as these objects fail. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +/// \param origin If non NULL, specifies the origin of TARGET when it +/// is non-absolute. +SRV::SRV(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) +{ + uint32_t num = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (num > 65535) { + isc_throw(InvalidRdataText, "Invalid SRV priority: " << num); + } + const uint16_t priority = static_cast<uint16_t>(num); + + num = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (num > 65535) { + isc_throw(InvalidRdataText, "Invalid SRV weight: " << num); + } + const uint16_t weight = static_cast<uint16_t>(num); + + num = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (num > 65535) { + isc_throw(InvalidRdataText, "Invalid SRV port: " << num); + } + const uint16_t port = static_cast<uint16_t>(num); + + const Name targetname = createNameFromLexer(lexer, origin); + + impl_ = new SRVImpl(priority, weight, port, targetname); +} + +/// \brief The copy constructor. +/// +/// It internally allocates a resource, and if it fails a corresponding +/// standard exception will be thrown. +/// This constructor never throws an exception otherwise. +SRV::SRV(const SRV& source) : + Rdata(), impl_(new SRVImpl(*source.impl_)) +{} + +SRV& +SRV::operator=(const SRV& source) { + if (this == &source) { + return (*this); + } + + SRVImpl* newimpl = new SRVImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +SRV::~SRV() { + delete impl_; +} + +/// \brief Convert the \c SRV to a string. +/// +/// The output of this method is formatted as described in the "from string" +/// constructor (\c SRV(const std::string&))). +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// +/// \return A \c string object that represents the \c SRV object. +string +SRV::toText() const { + using boost::lexical_cast; + return (lexical_cast<string>(impl_->priority_) + + " " + lexical_cast<string>(impl_->weight_) + + " " + lexical_cast<string>(impl_->port_) + + " " + impl_->target_.toText()); +} + +/// \brief Render the \c SRV in the wire format without name compression. +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// This method never throws an exception otherwise. +/// +/// \param buffer An output buffer to store the wire data. +void +SRV::toWire(OutputBuffer& buffer) const { + buffer.writeUint16(impl_->priority_); + buffer.writeUint16(impl_->weight_); + buffer.writeUint16(impl_->port_); + impl_->target_.toWire(buffer); +} + +/// \brief Render the \c SRV in the wire format with taking into account +/// compression. +/// +/// As specified in RFC2782, the Target field (a domain name) will not be +/// compressed. However, the domain name could be a target of compression +/// of other compressible names (though pretty unlikely), the offset +/// information of the algorithm name may be recorded in \c renderer. +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// This method never throws an exception otherwise. +/// +/// \param renderer DNS message rendering context that encapsulates the +/// output buffer and name compression information. +void +SRV::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint16(impl_->priority_); + renderer.writeUint16(impl_->weight_); + renderer.writeUint16(impl_->port_); + renderer.writeName(impl_->target_, false); +} + +/// \brief Compare two instances of \c SRV RDATA. +/// +/// See documentation in \c Rdata. +int +SRV::compare(const Rdata& other) const { + const SRV& other_srv = dynamic_cast<const SRV&>(other); + + if (impl_->priority_ != other_srv.impl_->priority_) { + return (impl_->priority_ < other_srv.impl_->priority_ ? -1 : 1); + } + if (impl_->weight_ != other_srv.impl_->weight_) { + return (impl_->weight_ < other_srv.impl_->weight_ ? -1 : 1); + } + if (impl_->port_ != other_srv.impl_->port_) { + return (impl_->port_ < other_srv.impl_->port_ ? -1 : 1); + } + + return (compareNames(impl_->target_, other_srv.impl_->target_)); +} + +uint16_t +SRV::getPriority() const { + return (impl_->priority_); +} + +uint16_t +SRV::getWeight() const { + return (impl_->weight_); +} + +uint16_t +SRV::getPort() const { + return (impl_->port_); +} + +const Name& +SRV::getTarget() const { + return (impl_->target_); +} + +} // end of namespace "in" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" |