// Copyright (C) 2011-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/. #ifndef TXT_LIKE_H #define TXT_LIKE_H #include #include #include #include #include #include #include namespace isc { namespace dns { namespace rdata { namespace generic { namespace detail { /// \brief \c rdata::TXTLikeImpl class represents the TXT-like RDATA for TXT /// and SPF types. /// /// This class implements the basic interfaces inherited by the TXT and SPF /// classes from the abstract \c rdata::Rdata class, and provides trivial /// accessors to TXT-like RDATA. templateclass TXTLikeImpl { public: /// \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, normally expected /// to be the value of the RDLENGTH field of the corresponding RR. /// /// Exceptions /// /// \c InvalidRdataLength is thrown if rdata_len exceeds the maximum. /// \c DNSMessageFORMERR is thrown if the RR is malformed. TXTLikeImpl(util::InputBuffer& buffer, size_t rdata_len) { if (rdata_len > MAX_RDLENGTH) { isc_throw(InvalidRdataLength, "RDLENGTH too large: " << rdata_len); } if (rdata_len == 0) { // note that this couldn't happen in the loop. isc_throw(DNSMessageFORMERR, "Error in parsing " << RRType(typeCode) << " RDATA: 0-length character string"); } do { const uint8_t len = buffer.readUint8(); if (rdata_len < len + 1) { isc_throw(DNSMessageFORMERR, "Error in parsing " << RRType(typeCode) << " RDATA: character string length is too large: " << static_cast(len)); } std::vector data(len + 1); data[0] = len; buffer.readData(&data[0] + 1, len); string_list_.push_back(data); rdata_len -= (len + 1); } while (rdata_len > 0); } /// \brief Constructor from string. /// /// \throw CharStringTooLong the parameter string length exceeds maximum. /// \throw InvalidRdataText the method cannot process the parameter data explicit TXTLikeImpl(const std::string& txtstr) { std::istringstream ss(txtstr); MasterLexer lexer; lexer.pushSource(ss); try { buildFromTextHelper(lexer); if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { isc_throw(InvalidRdataText, "Failed to construct " << RRType(typeCode) << " RDATA from '" << txtstr << "': extra new line"); } } catch (const MasterLexer::LexerError& ex) { isc_throw(InvalidRdataText, "Failed to construct " << RRType(typeCode) << " RDATA from '" << txtstr << "': " << ex.what()); } } /// \brief Constructor using the master lexer. /// /// \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. TXTLikeImpl(MasterLexer& lexer) { buildFromTextHelper(lexer); } private: void buildFromTextHelper(MasterLexer& lexer) { while (true) { const MasterToken& token = lexer.getNextToken( MasterToken::QSTRING, true); if (token.getType() != MasterToken::STRING && token.getType() != MasterToken::QSTRING) { break; } string_list_.push_back(std::vector()); stringToCharString(token.getStringRegion(), string_list_.back()); } // Let upper layer handle eol/eof. lexer.ungetToken(); if (string_list_.empty()) { isc_throw(InvalidRdataText, "Failed to construct " << RRType(typeCode) << " RDATA: empty input"); } } public: /// \brief The copy constructor. /// /// Trivial for now, we could've used the default one. TXTLikeImpl(const TXTLikeImpl& other) : string_list_(other.string_list_) {} /// \brief Render the TXT-like data in the wire format to an OutputBuffer /// object. /// /// \param buffer An output buffer to store the wire data. void toWire(util::OutputBuffer& buffer) const { for (std::vector >::const_iterator it = string_list_.begin(); it != string_list_.end(); ++it) { buffer.writeData(&(*it)[0], (*it).size()); } } /// \brief Render the TXT-like data in the wire format to an /// AbstractMessageRenderer object. /// /// \param renderer An output AbstractMessageRenderer to send the wire data /// to. void toWire(AbstractMessageRenderer& renderer) const { for (std::vector >::const_iterator it = string_list_.begin(); it != string_list_.end(); ++it) { renderer.writeData(&(*it)[0], (*it).size()); } } /// \brief Convert the TXT-like data to a string. /// /// \return A \c string object that represents the TXT-like data. std::string toText() const { std::string s; for (std::vector >::const_iterator it = string_list_.begin(); it != string_list_.end(); ++it) { if (!s.empty()) { s.push_back(' '); } s.push_back('"'); s.append(charStringToString(*it)); s.push_back('"'); } return (s); } /// \brief Compare two instances of TXT-like RDATA. /// /// It is up to the caller to make sure that \c other is an object of the /// same \c TXTLikeImpl class. /// /// \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 compare(const TXTLikeImpl& other) const { // This implementation is not efficient. Revisit this (TBD). util::OutputBuffer this_buffer(0); toWire(this_buffer); uint8_t const* const this_data = (uint8_t const*)this_buffer.getData(); const size_t this_len = this_buffer.getLength(); util::OutputBuffer other_buffer(0); other.toWire(other_buffer); uint8_t const* const other_data = (uint8_t const*)other_buffer.getData(); const size_t other_len = other_buffer.getLength(); const size_t cmplen = std::min(this_len, other_len); const int cmp = memcmp(this_data, other_data, cmplen); if (cmp != 0) { return (cmp); } else { return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1); } } private: /// Note: this is a prototype version; we may reconsider /// this representation later. std::vector > string_list_; }; } // namespace detail } // namespace generic } // namespace rdata } // namespace dns } // namespace isc #endif // TXT_LIKE_H