// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include #include #include #include #include #include #include #include #include #include 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 unique_ptr here because if there is an exception in this // constructor, the destructor is not called and there could be a // leak of the CAAImpl that constructFromLexer() returns. std::unique_ptr impl_ptr; try { std::istringstream ss(caa_str); MasterLexer lexer; lexer.pushSource(ss); impl_ptr.reset(constructFromLexer(lexer)); if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { isc_throw(InvalidRdataText, "extra input text for CAA: " << caa_str); } } catch (const MasterLexer::LexerError& ex) { isc_throw(InvalidRdataText, "Failed to construct CAA from '" << caa_str << "': " << ex.what()); } impl_ = impl_ptr.release(); } /// \brief Constructor with a context of MasterLexer. /// /// The \c lexer should point to the beginning of valid textual /// representation of an CAA RDATA. /// /// \throw MasterLexer::LexerError General parsing error such as missing /// field. /// \throw InvalidRdataText Fields are out of their valid ranges, /// incorrect, or empty. /// /// \param lexer A \c MasterLexer object parsing a master file for the /// RDATA to be created CAA::CAA(MasterLexer& lexer, const Name*, MasterLoader::Options, MasterLoaderCallbacks&) : impl_(constructFromLexer(lexer)) { } /// \brief Constructor from InputBuffer. /// /// The passed buffer must contain a valid CAA RDATA. /// /// The Flags, Tag and Value fields must be within their valid ranges, /// but are not constrained to the values defined in RFC6844. The Tag /// field must not be empty. CAA::CAA(InputBuffer& buffer, size_t rdata_len) { if (rdata_len < 2) { isc_throw(InvalidRdataLength, "CAA record too short"); } const uint8_t flags = buffer.readUint8(); const uint8_t tag_length = buffer.readUint8(); rdata_len -= 2; if (tag_length == 0) { isc_throw(InvalidRdataText, "CAA tag field is empty"); } if (rdata_len < tag_length) { isc_throw(InvalidRdataLength, "RDATA is too short for CAA tag field"); } std::vector 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(static_cast(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(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 (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& CAA::getValue() const { return (impl_->value_); } // END_RDATA_NAMESPACE // END_ISC_NAMESPACE