// Copyright (C) 2012 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. #ifndef DATASRC_MEMORY_RDATA_READER_H #define DATASRC_MEMORY_RDATA_READER_H 1 #include "rdata_field.h" #include #include #include namespace isc { // Some forward declarations namespace dns{ class RRClass; class RRType; } namespace datasrc { namespace memory { /// \brief Class to read serialized rdata /// /// This class allows you to read the data encoded by RDataEncoder. /// It is rather low-level -- it provides sequence of data fields /// and names. It does not give you convenient Rdata or RRset class. /// /// It allows two types of operation. First one is by providing callbacks /// to the constructor and then iterating by repeatedly calling next, or /// calling iterate once. The callbacks are then called with each part of /// the data. /// /// \code /// void handleLabel(const dns::LabelSequence& label, unsigned int flags) { /// ... /// } /// void handleData(const uint8_t* data, size_t size) { /// ... /// } /// /// RdataReader reader(RRClass::IN(), RRType::AAAA(), size, data, /// &handleLabel, handleData); /// reader.iterate(); /// \endcode /// /// The other way is to use the return value of next() and loop through /// it manually. It has the inconvenience of having the type condition, but /// the code is in one place. The used way is matter of personal preferrence, /// there's no much difference on the technical side. /// /// \code /// RdataReader reader(RRClass::IN(), RRType::AAAA(), size, data, /// &handleLabel, handleData); /// RdataReader::Result data; /// while (data = reader.next()) { /// switch(data.type()) { /// case RdataReader::NAME: /// ... /// break; /// case RdataReader::DATA: /// ... /// break; /// default: assert(0); // Can not happen /// } /// } /// \endcode /// /// \note It is caller's responsibility to pass valid data here. This means /// the data returned by RdataEncoder and the corresponding class and type. /// If this is not the case, all the kinds of pointer hell might get loose. class RdataReader { public: /// \brief Function called on each name encountered in the data. typedef boost::function NameAction; /// \brief Function called on each data field in the data. typedef boost::function DataAction; /// \brief a NameAction that does nothing. /// /// This is a NameAction function that does nothing. It is used /// as a default in the constructor. static void emptyNameAction(const dns::LabelSequence& label, unsigned attributes); /// \brief a DataAction that does nothing. /// /// This is a DataAction function that does nothing. It is used /// as a default in the constructor. static void emptyDataAction(const uint8_t* data, size_t size); /// \brief Constructor /// /// This constructs the reader on top of some serialized data. /// It does not copy the data, you have to make sure the data /// is valid for the whole life of this object and that they /// don't change. /// /// \param rrclass The class the encoded rdata belongs to. /// \param rrtype The type of the encode rdata. /// \param data The actual data. /// \param rdata_count The number of Rdata encoded in the data. /// \param sig_count The number of RRSig rdata bundled with the data. /// \param name_action The callback to be called on each encountered name. /// \param data_action The callback to be called on each data chunk. RdataReader(const dns::RRClass& rrclass, const dns::RRType& rrtype, const uint8_t* data, size_t rdata_count, size_t sig_count, const NameAction& name_action = &emptyNameAction, const DataAction& data_action = &emptyDataAction); /// \brief The type of data returned from this iteration. enum DataType { NAME, ///< This iteration returns domain label DATA, ///< This iteration returns unstructuder data END ///< No more data to return }; /// \brief Data from one iteration /// /// Each time you call next() or nextSig(), it returns some data. /// This holds the data. /// /// It is valid only for as long as the RdataReader that returned it. /// /// All the methods can be called under any circumstances. However, /// if the required property is not valid for the given type (eg. /// when calling size() on type() == NAME), it returns some "empty" /// value (0, NULL, or the like). class Result { public: /// \brief Default constructor /// /// It creates an empty result (with no data) of type END. Result() : // TODO: Do we maybe want to have a static one to copy // instead of constructing new one from the root Name? label_(dns::Name::ROOT_NAME()), data_(NULL), size_(0), type_(END), compressible_(false), additional_(false) {} /// \brief Constructor from a domain label /// /// Creates the NAME type result. Used internally from RdataReader. /// /// \param label The label to hold /// \param attributes The attributes, as stored by the serialized /// data. Result(const dns::LabelSequence& label, unsigned attributes); /// \brief Constructor from data /// /// Creates the DATA type result. Used internally from RdataReader. /// /// \param data The data pointer to hold. /// \param size The size to hold. Result(const uint8_t* data, size_t size); /// \brief The type of data returned. DataType type() const { return (type_); } /// \brief The raw data. /// /// This is only valid if type() == DATA. const uint8_t* data() const { return (data_); } /// \brief The size of the raw data. /// /// This is the number of bytes the data takes. It is valid only /// if type() == DATA. size_t size() const { return (size_); } /// \brief The domain label. /// /// This holds the domain label. It is only valid if type() == NAME. const dns::LabelSequence& label() const { return (label_); } /// \brief Is the name in label() compressible? /// /// This is valid only if type() == NAME. bool compressible() const { return (compressible_); } /// \brief Does the name expect additional processing? /// /// This is valid only if type() == NAME. bool additional() const { return (additional_); } /// \brief If there are data returned. /// /// This returns if there are any data at all returned. This is /// equivalent to action != END, but it allows for more convenient /// code of a loop through the data. operator bool() const { return (type() != END); } private: dns::LabelSequence label_; const uint8_t* data_; size_t size_; DataType type_; bool compressible_; bool additional_; }; /// \brief Step to next piece of data. /// /// This returns the next available data. Also, the apropriate hook /// (name_action or data_action, depending on the data type) as passed /// to the constructor is called. /// /// If there are no more data, a Result with type END is returned and /// no callback is called. Result next(); /// \brief Call next() until the end. /// /// This is just convenience method to iterate through all the data. /// It calls next until it reaches the end (it does not revind before, /// therefore if you already called next() yourself, it does not start /// at the beginning). /// /// The method only makes sense if you set the callbacks in constructor. void iterate() { while (next()) { } } /// \brief Step to next piece of RRSig data. /// /// This is almost the same as next(), but it iterates through the /// associated RRSig data, not the data for the given RRType. Result nextSig(); /// \brief Iterate through all RRSig data. /// /// This is almost the same as iterate(), but it iterates through the /// RRSig data instead. void iterateSig() { while (nextSig()) { } } /// \brief Rewind the iterator to the beginnig of data. /// /// The following next() and nextSig() will start iterating from the /// beginning again. void rewind(); /// \brief Returns the size of associated data. /// /// This should be the same as the return value of /// RdataEncoder::getStorageLength() for the same set of data. /// The intended use of this method is to tell the caller the size of /// data that were possibly dynamically allocated so that the caller can /// use it for deallocation. /// /// This method only uses the parameters given at the construction of the /// object, and does not rely on or modify other mutable states. /// In practice, when the caller wants to call this method, that would be /// the only purpose of that RdataReader object (although it doesn't have /// to be so). size_t getSize() const; private: const NameAction name_action_; const DataAction data_action_; const RdataEncodeSpec& spec_; // Total number of var-length fields, count of signatures const size_t var_count_total_, sig_count_, spec_count_; // Pointer to the beginning of length fields const uint16_t* const lengths_; // Pointer to the beginning of the data (after the lengths) const uint8_t* const data_; // Pointer to the first data signature // Will be computed during the normal RR iteration const uint8_t* sigs_; // The positions in data. size_t data_pos_, spec_pos_, length_pos_; size_t sig_pos_, sig_data_pos_; Result nextInternal(const NameAction& name_action, const DataAction& data_action); }; } } } #endif