// Copyright (C) 2012-2013 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 #include #include #include #include #include #include #include #include #include using namespace std; using boost::lexical_cast; using namespace isc::util; using namespace isc::util::encode; // BEGIN_ISC_NAMESPACE // BEGIN_RDATA_NAMESPACE struct SSHFPImpl { // straightforward representation of SSHFP RDATA fields SSHFPImpl(uint8_t algorithm, uint8_t fingerprint_type, vector& fingerprint) : algorithm_(algorithm), fingerprint_type_(fingerprint_type), fingerprint_(fingerprint) {} uint8_t algorithm_; uint8_t fingerprint_type_; const vector fingerprint_; }; // helper function for string and lexer constructors SSHFPImpl* SSHFP::constructFromLexer(MasterLexer& lexer) { const uint32_t algorithm = lexer.getNextToken(MasterToken::NUMBER).getNumber(); if (algorithm > 255) { isc_throw(InvalidRdataText, "SSHFP algorithm number out of range"); } const uint32_t fingerprint_type = lexer.getNextToken(MasterToken::NUMBER).getNumber(); if (fingerprint_type > 255) { isc_throw(InvalidRdataText, "SSHFP fingerprint type out of range"); } const MasterToken& token = lexer.getNextToken(MasterToken::STRING, true); vector fingerprint; if ((token.getType() != MasterToken::END_OF_FILE) && (token.getType() != MasterToken::END_OF_LINE)) { decodeHex(token.getString(), fingerprint); } return (new SSHFPImpl(algorithm, fingerprint_type, fingerprint)); } /// \brief Constructor from string. /// /// The given string must represent a valid SSHFP RDATA. There can be /// extra space characters at the beginning or end of the text (which /// are simply ignored), but other extra text, including a new line, /// will make the construction fail with an exception. /// /// The Algorithm and Fingerprint Type fields must be within their valid /// ranges, but are not contrained to the values defined in RFC4255. /// /// The Fingerprint field may be absent, but if present it must contain a /// valid hex encoding of the fingerprint. /// /// \throw InvalidRdataText if any fields are missing, out of their valid /// ranges, or incorrect. /// /// \param sshfp_str A string containing the RDATA to be created SSHFP::SSHFP(const string& sshfp_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 SSHFPImpl that constructFromLexer() returns. std::auto_ptr impl_ptr(NULL); try { std::istringstream ss(sshfp_str); MasterLexer lexer; lexer.pushSource(ss); impl_ptr.reset(constructFromLexer(lexer)); if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { isc_throw(InvalidRdataText, "extra input text for SSHFP: " << sshfp_str); } } catch (const MasterLexer::LexerError& ex) { isc_throw(InvalidRdataText, "Failed to construct SSHFP from '" << sshfp_str << "': " << ex.what()); } catch (const isc::BadValue& e) { isc_throw(InvalidRdataText, "Bad SSHFP fingerprint: " << e.what()); } impl_ = impl_ptr.release(); } /// \brief Constructor with a context of MasterLexer. /// /// The \c lexer should point to the beginning of valid textual representation /// of an SSHFP RDATA. /// /// \throw MasterLexer::LexerError General parsing error such as missing field. /// \throw InvalidRdataText Fields are out of their valid range, or are /// incorrect. /// \throw BadValue Fingerprint is not a valid hex string. /// /// \param lexer A \c MasterLexer object parsing a master file for the /// RDATA to be created SSHFP::SSHFP(MasterLexer& lexer, const Name*, MasterLoader::Options, MasterLoaderCallbacks&) : impl_(NULL) { impl_ = constructFromLexer(lexer); } /// \brief Constructor from InputBuffer. /// /// The passed buffer must contain a valid SSHFP RDATA. /// /// The Algorithm and Fingerprint Type fields are not checked for unknown /// values. It is okay for the fingerprint data to be missing (see the /// description of the constructor from string). SSHFP::SSHFP(InputBuffer& buffer, size_t rdata_len) { if (rdata_len < 2) { isc_throw(InvalidRdataLength, "SSHFP record too short"); } const uint8_t algorithm = buffer.readUint8(); const uint8_t fingerprint_type = buffer.readUint8(); vector fingerprint; rdata_len -= 2; if (rdata_len > 0) { fingerprint.resize(rdata_len); buffer.readData(&fingerprint[0], rdata_len); } impl_ = new SSHFPImpl(algorithm, fingerprint_type, fingerprint); } SSHFP::SSHFP(uint8_t algorithm, uint8_t fingerprint_type, const string& fingerprint_txt) : impl_(NULL) { vector fingerprint; try { decodeHex(fingerprint_txt, fingerprint); } catch (const isc::BadValue& e) { isc_throw(InvalidRdataText, "Bad SSHFP fingerprint: " << e.what()); } impl_ = new SSHFPImpl(algorithm, fingerprint_type, fingerprint); } SSHFP::SSHFP(const SSHFP& other) : Rdata(), impl_(new SSHFPImpl(*other.impl_)) {} SSHFP::~SSHFP() { delete impl_; } void SSHFP::toWire(OutputBuffer& buffer) const { buffer.writeUint8(impl_->algorithm_); buffer.writeUint8(impl_->fingerprint_type_); if (!impl_->fingerprint_.empty()) { buffer.writeData(&impl_->fingerprint_[0], impl_->fingerprint_.size()); } } void SSHFP::toWire(AbstractMessageRenderer& renderer) const { renderer.writeUint8(impl_->algorithm_); renderer.writeUint8(impl_->fingerprint_type_); if (!impl_->fingerprint_.empty()) { renderer.writeData(&impl_->fingerprint_[0], impl_->fingerprint_.size()); } } string SSHFP::toText() const { return (lexical_cast(static_cast(impl_->algorithm_)) + " " + lexical_cast(static_cast(impl_->fingerprint_type_)) + (impl_->fingerprint_.empty() ? "" : " " + encodeHex(impl_->fingerprint_))); } int SSHFP::compare(const Rdata& other) const { const SSHFP& other_sshfp = dynamic_cast(other); if (impl_->algorithm_ < other_sshfp.impl_->algorithm_) { return (-1); } else if (impl_->algorithm_ > other_sshfp.impl_->algorithm_) { return (1); } if (impl_->fingerprint_type_ < other_sshfp.impl_->fingerprint_type_) { return (-1); } else if (impl_->fingerprint_type_ > other_sshfp.impl_->fingerprint_type_) { return (1); } const size_t this_len = impl_->fingerprint_.size(); const size_t other_len = other_sshfp.impl_->fingerprint_.size(); const size_t cmplen = min(this_len, other_len); if (cmplen > 0) { const int cmp = memcmp(&impl_->fingerprint_[0], &other_sshfp.impl_->fingerprint_[0], cmplen); if (cmp != 0) { return (cmp); } } if (this_len == other_len) { return (0); } else if (this_len < other_len) { return (-1); } else { return (1); } } uint8_t SSHFP::getAlgorithmNumber() const { return (impl_->algorithm_); } uint8_t SSHFP::getFingerprintType() const { return (impl_->fingerprint_type_); } size_t SSHFP::getFingerprintLen() const { return (impl_->fingerprint_.size()); } // END_RDATA_NAMESPACE // END_ISC_NAMESPACE