diff options
Diffstat (limited to 'src/lib/dns/rdataclass.cc')
-rw-r--r-- | src/lib/dns/rdataclass.cc | 2462 |
1 files changed, 2462 insertions, 0 deletions
diff --git a/src/lib/dns/rdataclass.cc b/src/lib/dns/rdataclass.cc new file mode 100644 index 0000000..b2d05f5 --- /dev/null +++ b/src/lib/dns/rdataclass.cc @@ -0,0 +1,2462 @@ +// Copyright (C) 2010-2024 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 <exceptions/isc_assert.h> +#include <dns/exceptions.h> +#include <dns/master_lexer.h> +#include <dns/master_loader.h> +#include <dns/master_loader_callbacks.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rcode.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrtype.h> +#include <dns/time_utils.h> +#include <dns/tsigkey.h> +#include <dns/tsigerror.h> +#include <dns/txt_like.h> +#include <util/buffer.h> +#include <util/encode/encode.h> +#include <util/buffer.h> + +#include <cerrno> +#include <cstring> +#include <iomanip> +#include <iostream> +#include <string> +#include <sstream> +#include <vector> + +#include <arpa/inet.h> // XXX: for inet_pton/ntop(), not exist in C++ standards +#include <stdio.h> +#include <stdint.h> +#include <sys/socket.h> // for AF_INET/AF_INET6 +#include <time.h> + +#include <boost/lexical_cast.hpp> +#include <boost/shared_ptr.hpp> + +using namespace isc::util; +using namespace isc::util::encode; +using namespace isc::dns; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +using namespace std; +using boost::lexical_cast; + +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 +std::unique_ptr<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 (std::unique_ptr<TSIGImpl>(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) { + try { + std::istringstream ss(tsig_str); + MasterLexer lexer; + lexer.pushSource(ss); + + impl_ = constructFromLexer(lexer, 0); + + 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()); + } +} + +/// \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) { + 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_.reset(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) { + // 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) || (mac_size > 0 && !mac)) { + isc_throw(InvalidParameter, "TSIG MAC size and data inconsistent"); + } + if ((other_len == 0 && other_data) || (other_len > 0 && !other_data)) { + 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_.reset(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); + } + + impl_.reset(new TSIGImpl(*source.impl_)); + + return (*this); +} + +TSIG::~TSIG() { +} + +/// \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 (0); + } +} + +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 (0); + } +} + +} // end of namespace "any" + +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 { + // TBD + return (0); +} + +} // end of namespace "ch" + +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, 0); + + 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_); +} + +/// \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&) { + 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&) { + isc_throw(InvalidRdataText, "OPT RR cannot be constructed from text"); +} + +OPT::OPT(InputBuffer& buffer, size_t rdata_len) { + impl_.reset(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_->rdlength_ + option_length) < + impl_->rdlength_) { + isc_throw(InvalidRdataText, + "Option length " << option_length + << " would overflow OPT RR RDLEN (currently " + << impl_->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_->pseudo_rrs_.push_back(PseudoRR(option_code, option_data)); + impl_->rdlength_ += option_length; + rdata_len -= option_length; + } +} + +OPT::OPT(const OPT& other) : + Rdata(), impl_(new OPTImpl(*other.impl_)) { +} + +OPT& +OPT::operator=(const OPT& source) { + if (this == &source) { + return (*this); + } + + impl_.reset(new OPTImpl(*source.impl_)); + + return (*this); +} + +OPT::~OPT() { +} + +std::string +OPT::toText() const { + isc_throw(isc::InvalidOperation, + "OPT RRs do not have a presentation format"); +} + +void +OPT::toWire(OutputBuffer& buffer) const { + for (auto const& 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 { + for (auto const& 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_); +} + +/// \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, 0); + + 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_); +} + +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 +std::unique_ptr<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 (std::unique_ptr<RRSIGImpl>(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) { + // 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. + boost::shared_ptr<RRSIGImpl> impl_ptr; + + try { + std::istringstream iss(rrsig_str); + MasterLexer lexer; + lexer.pushSource(iss); + + impl_ = constructFromLexer(lexer, 0); + + 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()); + } +} + +/// \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_.reset(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); + } + + impl_.reset(new RRSIGImpl(*source.impl_)); + + return (*this); +} + +RRSIG::~RRSIG() { +} + +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_); +} + +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, 0); + rname_ = createNameFromLexer(lexer, 0); + 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); + isc_throw_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_))); +} + +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 +std::unique_ptr<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 (std::unique_ptr<TKEYImpl>(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) { + + try { + std::istringstream ss(tkey_str); + MasterLexer lexer; + lexer.pushSource(ss); + + impl_ = 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()); + } +} + +/// \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) { + 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_.reset(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) { + 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_.reset(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); + } + + impl_.reset(new TKEYImpl(*source.impl_)); + + return (*this); +} + +TKEY::~TKEY() { +} + +/// \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); + } +} + + +TXT& +TXT::operator=(const TXT& source) { + if (this == &source) { + return (*this); + } + + impl_.reset(new TXTImpl(*source.impl_)); + + return (*this); +} + +TXT::~TXT() { +} + +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" + +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)) == 0) { + 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_))); +} + +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)) == 0) { + 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_))); +} + +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" |