// 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/. #ifndef RDATA_H #define RDATA_H #include #include #include #include #include #include #include #include namespace isc { namespace dns { class AbstractMessageRenderer; class RRType; class RRClass; class Name; namespace rdata { /// /// \brief A standard DNS module exception that is thrown if RDATA parser /// encounters an invalid or inconsistent data length. /// class InvalidRdataLength : public DNSTextError { public: InvalidRdataLength(const char* file, size_t line, const char* what) : DNSTextError(file, line, what) {} }; /// /// \brief A standard DNS module exception that is thrown if RDATA parser /// fails to recognize a given textual representation. /// class InvalidRdataText : public DNSTextError { public: InvalidRdataText(const char* file, size_t line, const char* what) : DNSTextError(file, line, what) {} }; /// /// \brief A standard DNS module exception that is thrown if RDATA parser /// encounters a character-string (as defined in RFC1035) exceeding /// the maximum allowable length (\c MAX_CHARSTRING_LEN). /// class CharStringTooLong : public DNSTextError { public: CharStringTooLong(const char* file, size_t line, const char* what) : DNSTextError(file, line, what) {} }; // Forward declaration to define RdataPtr. class Rdata; /// /// The \c RdataPtr type is a pointer-like type, pointing to an /// object of some concrete derived class of \c Rdata. /// typedef boost::shared_ptr RdataPtr; typedef boost::shared_ptr ConstRdataPtr; /// \brief Possible maximum length of RDATA, which is the maximum unsigned /// 16 bit value. const size_t MAX_RDLENGTH = 65535; /// \brief The maximum allowable length of character-string containing in /// RDATA as defined in RFC1035, not including the 1-byte length field. const unsigned int MAX_CHARSTRING_LEN = 255; /// \brief The \c Rdata class is an abstract base class that provides /// a set of common interfaces to manipulate concrete RDATA objects. /// /// Generally, a separate derived class directly inherited from the base /// \c Rdata class is defined for each well known RDATA. /// Each of such classes will define the common logic based on the /// corresponding protocol standard. /// /// Since some types of RRs are class specific and the corresponding RDATA /// may have different semantics (e.g. type A for class IN and type A for /// class CH have different representations and semantics), we separate /// \c Rdata derived classes for such RR types in different namespaces. /// The namespace of types specific to a class is named the lower-cased class /// name; for example, RDATA of class IN-specific types are defined in the /// \c in namespace, and RDATA of class CH-specific types are defined in /// the \c ch namespace, and so on. /// The derived classes are named using the RR type name (upper cased) such as /// \c A or \c AAAA. /// Thus RDATA of type A RR for class IN and CH are defined as \c in::A and /// \c ch::A, respectively. /// Many other RR types are class independent; the derived \c Rdata classes /// for such RR types are defined in the \c generic namespace. Examples are /// \c generic::NS and \c generic::SOA. /// /// If applications need to refer to these derived classes, it is generally /// recommended to prepend at least some part of the namespace because the /// same class name can be used in different namespaces. /// So, instead of doing /// \code using namespace isc::dns::rdata::in; /// A& rdata_type_a; \endcode /// it is advisable to prepend at least \c in from the namespace: /// \code using namespace isc::dns::rdata; /// in::A& rdata_type_a; \endcode /// /// In many cases, however, an application doesn't have to care about such /// derived classes. /// For instance, to parse an incoming DNS message an application wouldn't /// have to perform type specific operation unless the application is /// specifically concerned about a particular type. /// So, this API generally handles \c Rdata in a polymorphic way through /// a pointer or reference to this base abstract class. class Rdata { /// /// \name Constructors and Destructor /// /// Note: The copy constructor and the assignment operator are intentionally /// defined as private. Concrete classes should generally specialize their /// own versions of these methods. //@{ protected: /// The default constructor. /// /// This is intentionally defined as \c protected as this base class should /// never be instantiated (except as part of a derived class). In many /// cases, the derived class wouldn't define a public default constructor /// either, because an \c Rdata object without concrete data isn't /// meaningful. Rdata() {} private: Rdata(const Rdata& source); void operator=(const Rdata& source); public: /// The destructor. virtual ~Rdata() {}; //@} /// /// \name Converter methods /// //@{ /// \brief Convert an \c Rdata to a string. /// /// This method returns a \c std::string object representing the \c Rdata. /// /// This is a pure virtual method without the definition; the actual /// representation is specific to each derived concrete class and /// should be explicitly defined in the derived class. /// /// \return A string representation of \c Rdata. virtual std::string toText() const = 0; /// \brief Render the \c Rdata in the wire format into a buffer. /// /// This is a pure virtual method without the definition; the actual /// conversion is specific to each derived concrete class and /// should be explicitly defined in the derived class. /// /// \param buff An output buffer to store the wire data. virtual void toWire(isc::util::OutputBuffer& buff) const = 0; /// \brief Render the \c Rdata in the wire format into a /// \c MessageRenderer object. /// /// This is a pure virtual method without the definition; the actual /// conversion is specific to each derived concrete class and /// should be explicitly defined in the derived class. /// /// \param renderer DNS message rendering context that encapsulates the /// output buffer in which the \c Rdata is to be stored. virtual void toWire(AbstractMessageRenderer& renderer) const = 0; //@} /// /// \name Comparison method /// //@{ /// \brief Compare two instances of \c Rdata. /// /// This method compares \c this and the \c other Rdata objects /// in terms of the DNSSEC sorting order as defined in RFC4034, and returns /// the result as an integer. /// /// This is a pure virtual method without the definition; the actual /// comparison logic is specific to each derived concrete class and /// should be explicitly defined in the derived class. /// /// Specific implementations of this method must confirm that \c this /// and the \c other are objects of the same concrete derived class of /// \c Rdata. This is normally done by \c dynamic_cast in the /// implementation. It also means if the assumption isn't met /// an exception of class \c std::bad_cast will be thrown. /// /// Here is an implementation choice: instead of relying on /// \c dynamic_cast, we could first convert the data into wire-format /// and compare the pair as opaque data. This would be more polymorphic, /// but might involve significant overhead, especially for a large size /// of RDATA. /// /// \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. virtual int compare(const Rdata& other) const = 0; //@} /// \brief Get the wire format length of an Rdata. /// /// IMPLEMENTATION NOTE: Currently this base class implementation is /// non-optimal as it renders the wire data to a buffer and returns /// the buffer's length. What would perform better is to add /// implementations of \c getLength() method to every RDATA /// type. This is why this method is virtual. Once all Rdata types /// have \c getLength() implementations, this base class /// implementation must be removed and the method should become a /// pure interface. /// /// \return The length of the wire format representation of the /// RDATA. virtual uint16_t getLength() const; }; namespace generic { /// \brief The \c GenericImpl class is the actual implementation of the /// \c generic::Generic class. /// /// The implementation is hidden from applications. This approach requires /// dynamic memory allocation on construction, copy, or assignment, but /// we believe it should be acceptable as "unknown" RDATA should be pretty /// rare. struct GenericImpl; /// \brief The \c generic::Generic class represents generic "unknown" RDATA. /// /// This class is used as a placeholder for all non well-known type of RDATA. /// By definition, the stored data is regarded as opaque binary without /// assuming any structure. class Generic : public Rdata { public: /// /// \name Constructors, Assignment Operator and Destructor. /// //@{ /// \brief Constructor from a string. /// /// This method constructs a \c generic::Generic object from a textual /// representation as defined in RFC3597. /// /// If \c rdata_string isn't a valid textual representation of this type /// of RDATA, an exception of class \c InvalidRdataText or /// \c InvalidRdataLength will be thrown. /// If resource allocation to store the data fails, a corresponding standard /// exception will be thrown. /// /// \param rdata_string A string of textual representation of generic /// RDATA. explicit Generic(const std::string& rdata_string); /// /// \brief Constructor from wire-format data. /// /// The \c buffer parameter normally stores a complete DNS message /// containing the generic RDATA to be constructed. /// The current read position of the buffer points to the head of the /// data. /// /// This method reads \c rdata_len bytes from the \c buffer, and internally /// stores the data as an opaque byte sequence. /// /// \c rdata_len must not exceed \c MAX_RDLENGTH; otherwise, an exception /// of class \c InvalidRdataLength will be thrown. /// If resource allocation to hold the data fails, a corresponding /// standard exception will be thrown; if the \c buffer doesn't /// contain \c rdata_len bytes of unread data, an exception of /// class \c isc::OutOfRange will be thrown. /// /// \param buff A reference to an \c InputBuffer object storing the /// \c Rdata to parse. /// \param rdata_len The length in buffer of the \c Rdata. In bytes. Generic(isc::util::InputBuffer& buff, size_t rdata_len); /// \brief Constructor from master lexer. /// Generic(MasterLexer& lexer, const Name* name, MasterLoader::Options options, MasterLoaderCallbacks& callbacks); /// /// \brief The destructor. virtual ~Generic(); /// /// \brief The copy constructor. /// /// If resource allocation to copy the data fails, a corresponding standard /// exception will be thrown. /// /// \param source A reference to a \c generic::Generic object to copy from. Generic(const Generic& source); /// /// \brief The assignment operator. /// /// If resource allocation to copy the data fails, a corresponding standard /// exception will be thrown. /// /// \param source A reference to a \c generic::Generic object to copy from. Generic& operator=(const Generic& source); //@} /// /// \name Converter methods /// //@{ /// \brief Convert an \c generic::Generic object to a string. /// /// This method converts a generic "unknown" RDATA object into a textual /// representation of such unknown data as defined in RFC3597. /// /// If resource allocation to copy the data fails, a corresponding standard /// exception will be thrown. /// /// \return A string representation of \c generic::Generic. virtual std::string toText() const; /// /// \brief Render the \c generic::Generic in the wire format into a buffer. /// /// This will require \c rdata_len bytes of remaining capacity in the /// \c buffer. If this is not the case and resource allocation for the /// necessary memory space fails, a corresponding standard exception will /// be thrown. /// /// \param buff An output buffer to store the wire data. virtual void toWire(isc::util::OutputBuffer& buff) const; /// \brief Render the \c generic::Generic in the wire format into a /// \c MessageRenderer object. /// /// This will require \c rdata_len bytes of remaining capacity in the /// \c buffer. If this is not the case and resource allocation for the /// necessary memory space fails, a corresponding standard exception will /// be thrown. /// /// \param renderer DNS message rendering context that encapsulates the /// output buffer in which the \c Generic object is to be stored. virtual void toWire(AbstractMessageRenderer& renderer) const; //@} /// /// \name Comparison method /// //@{ /// \brief Compare two instances of \c generic::Generic objects. /// /// As defined in RFC4034, this method simply compares the wire-format /// representations of the two objects as left-justified unsigned octet /// sequences. /// /// The object referenced by \c other must have been instantiated as /// a c generic::Generic class object; otherwise, an exception of class /// \c std::bad_cast will be thrown. /// Note that the comparison is RR type/class agnostic: this method doesn't /// check whether the two \c Rdata objects to compare are of the comparable /// RR type/class. For example, \c this object may come from an \c RRset /// of \c RRType x, and the \c other may come from a different \c RRset /// of \c RRType y (where x != y). This situation would be considered a /// bug, but this method cannot detect this type of error. /// The caller must ensure this condition. /// /// \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. virtual int compare(const Rdata& other) const; //@} private: std::unique_ptr constructFromLexer(MasterLexer& lexer); std::unique_ptr impl_; }; /// /// \brief Insert the name as a string into stream. /// /// This method convert the \c rdata into a string and inserts it into the /// output stream \c os. /// /// This function overloads the global \c operator<< to behave as described in /// \c ostream::operator<< but applied to \c generic::Generic Rdata objects. /// /// \param os A \c std::ostream object on which the insertion operation is /// performed. /// \param rdata The \c Generic object output by the operation. /// \return A reference to the same \c std::ostream object referenced by /// parameter \c os after the insertion operation. std::ostream& operator<<(std::ostream& os, const Generic& rdata); } // end of namespace "generic" // // Non class-member functions related to Rdata // /// /// \name Parameterized Polymorphic RDATA Factories /// /// This set of global functions provide a unified interface to create an /// \c Rdata object in a parameterized polymorphic way, /// that is, these functions take a pair of \c RRType and \c RRClass /// objects and data specific to that pair, and create an object of /// the corresponding concrete derived class of \c Rdata. /// /// These will be useful when parsing/constructing a DNS message or /// parsing a master file, where information for a specific type of RDATA /// is given but the resulting object, once created, should better be used /// in a polymorphic way. /// /// For example, if a master file parser encounters an NS RR /// \verbatim example.com. 3600 IN NS ns.example.com.\endverbatim /// it stores the text fragments "IN", "NS", and "ns.example.com." in /// \c std::string objects \c class_txt, \c type_txt, and \c nsname_txt, /// respectively, then it would create a new \c RdataPtr object as follows: /// \code RdataPtr rdata = createRdata(RRType(type_txt), RRClass(class_txt), /// nsname_txt); \endcode /// On success, \c rdata will point to an object of the \c generic::NS class /// that internally holds a domain name of "ns.example.com." /// /// Internally, these functions uses the corresponding /// \c RRParamRegistry::createRdata methods of the \c RRParamRegistry. /// See also the description on these methods for related notes. //@{ /// \brief Create RDATA of a given pair of RR type and class from a string. /// /// This method creates from a string an \c Rdata object of the given pair /// of RR type and class. /// /// \param rrtype An \c RRType object specifying the type/class pair. /// \param rrclass An \c RRClass object specifying the type/class pair. /// \param rdata_string A string of textual representation of the \c Rdata. /// \return An \c RdataPtr object pointing to the created \c Rdata /// object. RdataPtr createRdata(const RRType& rrtype, const RRClass& rrclass, const std::string& rdata_string); /// \brief Create RDATA of a given pair of RR type and class from /// wire-format data. /// /// This method creates from wire-format binary data an \c Rdata object /// of the given pair of RR type and class. /// /// \c len must not exceed the protocol defined maximum value, \c MAX_RDLENGTH; /// otherwise, an exception of class \c InvalidRdataLength will be thrown. /// /// In some cases, the length of the RDATA is determined without the /// information of \c len. For example, the RDATA length of an IN/A RR /// must always be 4. If \c len is not equal to the actual length in such /// cases, an exception of class InvalidRdataLength will be thrown. /// /// \param rrtype An \c RRType object specifying the type/class pair. /// \param rrclass An \c RRClass object specifying the type/class pair. /// \param buff A reference to an \c InputBuffer object storing the /// \c Rdata to parse. /// \param len The length in buffer of the \c Rdata. In bytes. /// \return An \c RdataPtr object pointing to the created \c Rdata /// object. RdataPtr createRdata(const RRType& rrtype, const RRClass& rrclass, isc::util::InputBuffer& buff, size_t len); /// \brief Create RDATA of a given pair of RR type and class, copying /// of another RDATA of same kind. /// /// This method creates an \c Rdata object of the given pair of /// RR type and class, copying the content of the given \c Rdata, /// \c source. /// /// \param rrtype An \c RRType object specifying the type/class pair. /// \param rrclass An \c RRClass object specifying the type/class pair. /// \param source A reference to an \c Rdata object whose content /// is to be copied to the created \c Rdata object. /// \return An \c RdataPtr object pointing to the created /// \c Rdata object. RdataPtr createRdata(const RRType& rrtype, const RRClass& rrclass, const Rdata& source); /// \brief Create RDATA of a given pair of RR type and class using the /// master lexer. /// /// This is a more generic form of factory from textual RDATA, and is mainly /// intended to be used internally by the master file parser (\c MasterLoader) /// of this library. /// /// The \c lexer is expected to be at the beginning of textual RDATA of the /// specified type and class. This function (and its underlying Rdata /// implementations) extracts necessary tokens from the lexer and constructs /// the RDATA from them. /// /// Due to the intended usage of this version, this function handles error /// cases quite differently from other versions. It internally catches /// most of syntax and semantics errors of the input (reported as exceptions), /// calls the corresponding callback specified by the \c callbacks parameters, /// and returns a null smart pointer. If the caller rather wants to get /// an exception in these cases, it can pass a callback that internally /// throws on error. Some critical exceptions such as \c std::bad_alloc are /// still propagated to the upper layer as it doesn't make sense to try /// recovery from such a situation within this function. /// /// Whether or not the creation succeeds, this function updates the lexer /// until it reaches either the end of line or file, starting from the end of /// the RDATA text (or the point of failure if the parsing fails in the /// middle of it). The caller can therefore assume it's ready for reading /// the next data (which is normally a subsequent RR in the zone file) on /// return, whether or not this function succeeds. /// /// \param rrtype An \c RRType object specifying the type/class pair. /// \param rrclass An \c RRClass object specifying the type/class pair. /// \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 any domain name fields /// of the RDATA that are non absolute. /// \param options Master loader options controlling how to deal with errors /// or non critical issues in the parsed RDATA. /// \param callbacks Callback to be called when an error or non critical issue /// is found. /// \return An \c RdataPtr object pointing to the created /// \c Rdata object. Will be null if parsing fails. RdataPtr createRdata(const RRType& rrtype, const RRClass& rrclass, MasterLexer& lexer, const Name* origin, MasterLoader::Options options, MasterLoaderCallbacks& callbacks); //@} /// /// \brief Gives relative ordering of two names in terms of DNSSEC RDATA /// ordering. /// /// This method compares two names as defined in Sections 6.2 and 6.3 of /// RFC4034: Comparing two names in their canonical form /// (i.e., converting upper case ASCII characters to lower ones) and /// as a left-justified unsigned octet sequence. Note that the ordering is /// different from that for owner names. For example, "a.example" should be /// sorted before "example" as RDATA, but the ordering is the opposite when /// compared as owner names. /// /// Normally, applications would not need this function directly. /// This is mostly an internal helper function for \c Rdata related classes /// to implement their \c compare() method. /// This function is publicly open, however, for the convenience of /// external developers who want to implement new or experimental RR types. /// /// This function never throws an exception as long as the given names are /// valid \c Name objects. /// /// Additional note about applicability: In fact, BIND9's similar function, /// \c dns_name_rdatacompare(), is only used in rdata implementations and /// for testing purposes. /// /// \param n1,n2 \c Name class objects to compare. /// \return -1 if \c n1 would be sorted before \c n2. /// \return 0 if \c n1 is identical to \c n2 in terms of sorting order. /// \return 1 if \c n1 would be sorted after \c n2. /// int compareNames(const Name& n1, const Name& n2); } // end of namespace "rdata" } } #endif // RDATA_H