diff options
Diffstat (limited to 'src/lib/dns/rdata/generic')
-rw-r--r-- | src/lib/dns/rdata/generic/caa_257.cc | 306 | ||||
-rw-r--r-- | src/lib/dns/rdata/generic/caa_257.h | 72 | ||||
-rw-r--r-- | src/lib/dns/rdata/generic/detail/char_string.cc | 84 | ||||
-rw-r--r-- | src/lib/dns/rdata/generic/detail/char_string.h | 37 | ||||
-rw-r--r-- | src/lib/dns/rdata/generic/opt_41.cc | 182 | ||||
-rw-r--r-- | src/lib/dns/rdata/generic/opt_41.h | 57 | ||||
-rw-r--r-- | src/lib/dns/rdata/generic/sshfp_44.cc | 30 | ||||
-rw-r--r-- | src/lib/dns/rdata/generic/sshfp_44.h | 4 | ||||
-rw-r--r-- | src/lib/dns/rdata/generic/tlsa_52.cc | 350 | ||||
-rw-r--r-- | src/lib/dns/rdata/generic/tlsa_52.h | 65 |
10 files changed, 1147 insertions, 40 deletions
diff --git a/src/lib/dns/rdata/generic/caa_257.cc b/src/lib/dns/rdata/generic/caa_257.cc new file mode 100644 index 0000000000..7d46a57662 --- /dev/null +++ b/src/lib/dns/rdata/generic/caa_257.cc @@ -0,0 +1,306 @@ +// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include <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; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +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 auto_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::auto_ptr<CAAImpl> impl_ptr(NULL); + + 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_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/caa_257.h b/src/lib/dns/rdata/generic/caa_257.h new file mode 100644 index 0000000000..47a1369c97 --- /dev/null +++ b/src/lib/dns/rdata/generic/caa_257.h @@ -0,0 +1,72 @@ +// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +// BEGIN_HEADER_GUARD + +#include <stdint.h> + +#include <dns/name.h> +#include <dns/rdata.h> + +#include <string> +#include <vector> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +struct CAAImpl; + +class CAA : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + CAA(uint8_t flags, const std::string& tag, const std::string& value); + CAA& operator=(const CAA& source); + ~CAA(); + + /// + /// Specialized methods + /// + + /// \brief Return the Flags field of the CAA RDATA. + uint8_t getFlags() const; + + /// \brief Return the Tag field of the CAA RDATA. + const std::string& getTag() const; + + /// \brief Return the Value field of the CAA RDATA. + /// + /// Note: The const reference which is returned is valid only during + /// the lifetime of this \c generic::CAA object. It should not be + /// used afterwards. + const std::vector<uint8_t>& getValue() const; + +private: + CAAImpl* constructFromLexer(MasterLexer& lexer); + + CAAImpl* impl_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/detail/char_string.cc b/src/lib/dns/rdata/generic/detail/char_string.cc index 4c8965a10b..328fa7b406 100644 --- a/src/lib/dns/rdata/generic/detail/char_string.cc +++ b/src/lib/dns/rdata/generic/detail/char_string.cc @@ -93,6 +93,39 @@ stringToCharString(const MasterToken::StringRegion& str_region, result[0] = result.size() - 1; } +void +stringToCharStringData(const MasterToken::StringRegion& str_region, + CharStringData& result) +{ + bool escape = false; + const char* s = str_region.beg; + const char* const s_end = str_region.beg + str_region.len; + + for (size_t n = str_region.len; n != 0; --n, ++s) { + int c = (*s & 0xff); + if (escape && std::isdigit(c) != 0) { + c = decimalToNumber(s, s_end); + // decimalToNumber() already throws if (s_end - s) is less + // than 3, so the following assertion is unnecessary. But we + // assert it anyway. 'n' is an unsigned type (size_t) and + // can underflow. + assert(n >= 3); + // 'n' and 's' are also updated by 1 in the for statement's + // expression, so we update them by 2 instead of 3 here. + n -= 2; + s += 2; + } else if (!escape && c == '\\') { + escape = true; + continue; + } + escape = false; + result.push_back(c); + } + if (escape) { // terminated by non-escaped '\' + isc_throw(InvalidRdataText, "character-string ends with '\\'"); + } +} + std::string charStringToString(const CharString& char_string) { std::string s; @@ -116,6 +149,29 @@ charStringToString(const CharString& char_string) { return (s); } +std::string +charStringDataToString(const CharStringData& char_string) { + std::string s; + for (CharString::const_iterator it = char_string.begin(); + it != char_string.end(); ++it) { + const uint8_t ch = *it; + if ((ch < 0x20) || (ch >= 0x7f)) { + // convert to escaped \xxx (decimal) format + s.push_back('\\'); + s.push_back('0' + ((ch / 100) % 10)); + s.push_back('0' + ((ch / 10) % 10)); + s.push_back('0' + (ch % 10)); + continue; + } + if ((ch == '"') || (ch == ';') || (ch == '\\')) { + s.push_back('\\'); + } + s.push_back(ch); + } + + return (s); +} + int compareCharStrings(const detail::CharString& self, const detail::CharString& other) { if (self.size() == 0 && other.size() == 0) { @@ -144,6 +200,34 @@ int compareCharStrings(const detail::CharString& self, } } +int compareCharStringDatas(const detail::CharStringData& self, + const detail::CharStringData& other) { + if (self.size() == 0 && other.size() == 0) { + return (0); + } + if (self.size() == 0) { + return (-1); + } + if (other.size() == 0) { + return (1); + } + const size_t self_len = self.size(); + const size_t other_len = other.size(); + const size_t cmp_len = std::min(self_len, other_len); + const int cmp = std::memcmp(&self[0], &other[0], cmp_len); + if (cmp < 0) { + return (-1); + } else if (cmp > 0) { + return (1); + } else if (self_len < other_len) { + return (-1); + } else if (self_len > other_len) { + return (1); + } else { + return (0); + } +} + size_t bufferToCharString(isc::util::InputBuffer& buffer, size_t rdata_len, CharString& target) { diff --git a/src/lib/dns/rdata/generic/detail/char_string.h b/src/lib/dns/rdata/generic/detail/char_string.h index 8e3e2944d0..01fccadc59 100644 --- a/src/lib/dns/rdata/generic/detail/char_string.h +++ b/src/lib/dns/rdata/generic/detail/char_string.h @@ -34,6 +34,9 @@ namespace detail { /// be the bare char basis. typedef std::vector<uint8_t> CharString; +/// \brief Type for DNS character string without the length prefix. +typedef std::vector<uint8_t> CharStringData; + /// \brief Convert a DNS character-string into corresponding binary data. /// /// This helper function takes a string object that is expected to be a @@ -53,6 +56,20 @@ typedef std::vector<uint8_t> CharString; void stringToCharString(const MasterToken::StringRegion& str_region, CharString& result); +/// \brief Convert a DNS character-string into corresponding binary data. +/// +/// This method functions similar to \c stringToCharString() except it +/// does not include the 1-octet length prefix in the \c result, and the +/// result is not limited to MAX_CHARSTRING_LEN octets. +/// +/// \throw InvalidRdataText Upon syntax errors. +/// +/// \brief str_region A string that represents a character-string. +/// \brief result A placeholder vector where the resulting data are to be +/// stored. Expected to be empty, but it's not checked. +void stringToCharStringData(const MasterToken::StringRegion& str_region, + CharStringData& result); + /// \brief Convert a CharString into a textual DNS character-string. /// /// This method converts a binary 8-bit representation of a DNS @@ -67,6 +84,15 @@ void stringToCharString(const MasterToken::StringRegion& str_region, /// \return A string representation of \c char_string. std::string charStringToString(const CharString& char_string); +/// \brief Convert a CharStringData into a textual DNS character-string. +/// +/// Reverse of \c stringToCharStringData(). See \c stringToCharString() +/// vs. \c stringToCharStringData(). +/// +/// \param char_string The \c CharStringData to convert. +/// \return A string representation of \c char_string. +std::string charStringDataToString(const CharStringData& char_string); + /// \brief Compare two CharString objects /// /// \param self The CharString field to compare @@ -77,6 +103,17 @@ std::string charStringToString(const CharString& char_string); /// 0 if \c self and \c other are equal int compareCharStrings(const CharString& self, const CharString& other); +/// \brief Compare two CharStringData objects +/// +/// \param self The CharStringData field to compare +/// \param other The CharStringData field to compare to +/// +/// \return -1 if \c self would be sorted before \c other +/// 1 if \c self would be sorted after \c other +/// 0 if \c self and \c other are equal +int compareCharStringDatas(const CharStringData& self, + const CharStringData& other); + /// \brief Convert a buffer containing a character-string to CharString /// /// This method reads one character-string from the given buffer (in wire diff --git a/src/lib/dns/rdata/generic/opt_41.cc b/src/lib/dns/rdata/generic/opt_41.cc index 136bdf931a..6b292e9fa1 100644 --- a/src/lib/dns/rdata/generic/opt_41.cc +++ b/src/lib/dns/rdata/generic/opt_41.cc @@ -14,25 +14,68 @@ #include <config.h> -#include <string> - #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; // BEGIN_ISC_NAMESPACE // BEGIN_RDATA_NAMESPACE +/// \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&) { +OPT::OPT(const std::string&) : + impl_(NULL) +{ isc_throw(InvalidRdataText, "OPT RR cannot be constructed from text"); } @@ -42,50 +85,141 @@ OPT::OPT(const std::string&) { /// /// \throw InvalidRdataText OPT RR cannot be constructed from text. OPT::OPT(MasterLexer&, const Name*, - MasterLoader::Options, MasterLoaderCallbacks&) + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(NULL) { isc_throw(InvalidRdataText, "OPT RR cannot be constructed from text"); } -OPT::OPT(InputBuffer& buffer, size_t rdata_len) { - // setPosition() will throw against a short buffer anyway, but it's safer - // to check it explicitly here. - if (buffer.getLength() - buffer.getPosition() < rdata_len) { - isc_throw(InvalidRdataLength, "RDLEN of OPT is too large"); +OPT::OPT(InputBuffer& buffer, size_t rdata_len) : + impl_(NULL) +{ + std::auto_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); } - // This simple implementation ignores any options - buffer.setPosition(buffer.getPosition() + rdata_len); + OPTImpl* newimpl = new OPTImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); } -OPT::OPT(const OPT&) : Rdata() { - // there's nothing to copy in this simple implementation. +OPT::~OPT() { + delete impl_; } std::string OPT::toText() const { - // OPT records do not have a text format. - return (""); + isc_throw(isc::InvalidOperation, + "OPT RRs do not have a presentation format"); } void -OPT::toWire(OutputBuffer&) const { - // nothing to do, as this simple version doesn't support any options. +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&) const { - // nothing to do, as this simple version doesn't support any options. +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& other) const { - //const OPT& other_opt = dynamic_cast<const OPT&>(other); - // right now we don't need other_opt: - static_cast<void>(dynamic_cast<const OPT&>(other)); - +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)); + 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_RDATA_NAMESPACE // END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/opt_41.h b/src/lib/dns/rdata/generic/opt_41.h index 0cb7043cca..cc51977e3a 100644 --- a/src/lib/dns/rdata/generic/opt_41.h +++ b/src/lib/dns/rdata/generic/opt_41.h @@ -18,6 +18,10 @@ #include <dns/rdata.h> +#include <boost/shared_ptr.hpp> + +#include <vector> + // BEGIN_ISC_NAMESPACE // BEGIN_COMMON_DECLARATIONS @@ -25,15 +29,64 @@ // BEGIN_RDATA_NAMESPACE +struct OPTImpl; + class OPT : public Rdata { public: // BEGIN_COMMON_MEMBERS // END_COMMON_MEMBERS // The default constructor makes sense for OPT as it can be empty. - OPT() {} + OPT(); + OPT& operator=(const OPT& source); + ~OPT(); + + /// \brief A class representing a pseudo RR (or option) within an + /// OPT RR (see RFC 6891). + class PseudoRR { + public: + /// \brief Constructor. + /// \param code The OPTION-CODE field of the pseudo RR. + /// \param data The OPTION-DATA field of the pseudo + /// RR. OPTION-LENGTH is set to the length of this vector. + PseudoRR(uint16_t code, + boost::shared_ptr<std::vector<uint8_t> >& data); + + /// \brief Return the option code of this pseudo RR. + uint16_t getCode() const; + + /// \brief Return the option data of this pseudo RR. + const uint8_t* getData() const; + + /// \brief Return the length of the option data of this + /// pseudo RR. + uint16_t getLength() const; + + private: + uint16_t code_; + boost::shared_ptr<std::vector<uint8_t> > data_; + }; + + /// \brief Append a pseudo RR (option) in this OPT RR. + /// + /// \param code The OPTION-CODE field of the pseudo RR. + /// \param data The OPTION-DATA field of the pseudo RR. + /// \param length The size of the \c data argument. OPTION-LENGTH is + /// set to this size. + /// \throw \c isc::InvalidParameter if this pseudo RR would cause + /// the OPT RDATA to overflow its RDLENGTH. + void appendPseudoRR(uint16_t code, const uint8_t* data, uint16_t length); + + /// \brief Return a vector of the pseudo RRs (options) in this + /// OPT RR. + /// + /// Note: The returned reference is only valid during the lifetime + /// of this \c generic::OPT object. It should not be used + /// afterwards. + const std::vector<PseudoRR>& getPseudoRRs() const; + private: - // RR-type specific members are here. + OPTImpl* impl_; }; // END_RDATA_NAMESPACE diff --git a/src/lib/dns/rdata/generic/sshfp_44.cc b/src/lib/dns/rdata/generic/sshfp_44.cc index 2865ed18c8..4c1e83274a 100644 --- a/src/lib/dns/rdata/generic/sshfp_44.cc +++ b/src/lib/dns/rdata/generic/sshfp_44.cc @@ -14,8 +14,6 @@ #include <config.h> -#include <string> - #include <boost/lexical_cast.hpp> #include <exceptions/exceptions.h> @@ -38,7 +36,7 @@ using namespace isc::util::encode; struct SSHFPImpl { // straightforward representation of SSHFP RDATA fields SSHFPImpl(uint8_t algorithm, uint8_t fingerprint_type, - vector<uint8_t>& fingerprint) : + const vector<uint8_t>& fingerprint) : algorithm_(algorithm), fingerprint_type_(fingerprint_type), fingerprint_(fingerprint) @@ -82,7 +80,11 @@ SSHFP::constructFromLexer(MasterLexer& lexer) { // If fingerprint is missing, it's OK. See the API documentation of the // constructor. if (fingerprint_str.size() > 0) { - decodeHex(fingerprint_str, fingerprint); + 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)); @@ -102,8 +104,9 @@ SSHFP::constructFromLexer(MasterLexer& lexer) { /// 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, out of their valid -/// ranges, or incorrect. +/// \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) : @@ -128,9 +131,6 @@ SSHFP::SSHFP(const string& sshfp_str) : } catch (const MasterLexer::LexerError& ex) { isc_throw(InvalidRdataText, "Failed to construct SSHFP from '" << sshfp_str << "': " << ex.what()); - } catch (const isc::BadValue& e) { - isc_throw(InvalidRdataText, - "Bad SSHFP fingerprint: " << e.what()); } impl_ = impl_ptr.release(); @@ -142,9 +142,8 @@ SSHFP::SSHFP(const string& sshfp_str) : /// 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. -/// \throw BadValue Fingerprint is not a valid hex string. +/// \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 @@ -293,8 +292,13 @@ SSHFP::getFingerprintType() const { return (impl_->fingerprint_type_); } +const std::vector<uint8_t>& +SSHFP::getFingerprint() const { + return (impl_->fingerprint_); +} + size_t -SSHFP::getFingerprintLen() const { +SSHFP::getFingerprintLength() const { return (impl_->fingerprint_.size()); } diff --git a/src/lib/dns/rdata/generic/sshfp_44.h b/src/lib/dns/rdata/generic/sshfp_44.h index 28ce0f3d22..85ae8067c5 100644 --- a/src/lib/dns/rdata/generic/sshfp_44.h +++ b/src/lib/dns/rdata/generic/sshfp_44.h @@ -17,6 +17,7 @@ #include <stdint.h> #include <string> +#include <vector> #include <dns/name.h> #include <dns/rdata.h> @@ -45,7 +46,8 @@ public: /// uint8_t getAlgorithmNumber() const; uint8_t getFingerprintType() const; - size_t getFingerprintLen() const; + const std::vector<uint8_t>& getFingerprint() const; + size_t getFingerprintLength() const; private: SSHFPImpl* constructFromLexer(MasterLexer& lexer); diff --git a/src/lib/dns/rdata/generic/tlsa_52.cc b/src/lib/dns/rdata/generic/tlsa_52.cc new file mode 100644 index 0000000000..774bdef6cf --- /dev/null +++ b/src/lib/dns/rdata/generic/tlsa_52.cc @@ -0,0 +1,350 @@ +// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include <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; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +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_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/tlsa_52.h b/src/lib/dns/rdata/generic/tlsa_52.h new file mode 100644 index 0000000000..7d8c69a3d1 --- /dev/null +++ b/src/lib/dns/rdata/generic/tlsa_52.h @@ -0,0 +1,65 @@ +// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +// BEGIN_HEADER_GUARD + +#include <stdint.h> + +#include <dns/name.h> +#include <dns/rdata.h> + +#include <string> +#include <vector> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +struct TLSAImpl; + +class TLSA : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + TLSA(uint8_t certificate_usage, uint8_t selector, + uint8_t matching_type, const std::string& certificate_assoc_data); + TLSA& operator=(const TLSA& source); + ~TLSA(); + + /// + /// Specialized methods + /// + uint8_t getCertificateUsage() const; + uint8_t getSelector() const; + uint8_t getMatchingType() const; + const std::vector<uint8_t>& getData() const; + size_t getDataLength() const; + +private: + TLSAImpl* constructFromLexer(MasterLexer& lexer); + + TLSAImpl* impl_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: |