// Copyright (C) 2012-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 LABELSEQUENCE_H
#define LABELSEQUENCE_H

#include <dns/name.h>
#include <util/buffer.h>

namespace isc {
namespace dns {

/// \brief Light-weight Accessor to Name data.
///
/// The purpose of this class is to easily match Names and parts of Names,
/// without needing to copy the underlying data on each label strip.
///
/// It can only work on existing Name objects, or data as provided by the
/// Name object or another LabelSequence, and the data or Name MUST
/// remain in scope during the entire lifetime of its associated
/// LabelSequence(s).
///
/// Upon creation of a LabelSequence, it records the offsets of the
/// labels in the wireformat data of the Name. When stripLeft() or
/// stripRight() is called on the LabelSequence, no changes in the
/// original data occur, but the internal pointers of the
/// LabelSequence are modified.
///
/// LabelSequences can be compared to other LabelSequences, and their
/// data can be requested (which then points to part of the original
/// data of the original Name object).
class LabelSequence {
    // Name calls the private toText(bool) method of LabelSequence.
    friend std::string Name::toText(bool) const;

public:
    /// \brief Max possible size of serialized image generated by \c serialize
    ///
    /// A fixed length buffer of this size can be always passed to
    /// \c serialize() safely.  (But the application shouldn't use the
    /// specific size value; it must use this constant variable).
    static const size_t MAX_SERIALIZED_LENGTH =
        Name::MAX_WIRE + Name::MAX_LABELS + 1;

    ///
    /// \name Well-known LabelSequence constants
    ///
    //@{
    /// Wildcard label ("*")
    static const LabelSequence& WILDCARD();
    //@}

    /// \brief Constructs a LabelSequence for the given name
    ///
    /// \note The associated Name MUST remain in scope during the lifetime
    /// of this LabelSequence, since getData() refers to data from the
    /// Name object (the only data the LabelSequence stores are pointers
    /// to the labels in the Name object).
    ///
    /// \param name The Name to construct a LabelSequence for
    explicit LabelSequence(const Name& name):
        data_(&name.ndata_[0]),
        offsets_(&name.offsets_[0]),
        first_label_(0),
        last_label_(name.getLabelCount() - 1) {
    }

    /// \brief Constructor from serialized image.
    ///
    /// This constructor restores a \c LabelSequence object from a serialized
    /// binary image previously generated by \c serialize().  Any other input
    /// to this constructor will result in undefined behavior.
    ///
    /// The binary data passed to this constructor MUST remain in scope and
    /// MUST NOT be modified during the lifetime of this LabelSequence.
    ///
    /// As long as the data were previously generated by a call to
    /// \c serialize() on a valid \c LabelSequence object, this constructor
    /// should succeed.  While any other case is undefined, this constructor
    /// may perform some validity checks internally for safety.  Nevertheless,
    /// applications must not rely on such checks.
    ///
    /// \param buf Pointer to the serialized image generated by \c serialize().
    explicit LabelSequence(const void* buf);

    /// \brief Construct 'extendable' LabelSequence
    ///
    /// This form of LabelSequence copies the data from the given
    /// labelsequence into the given external buffer, which is subsequently
    /// extendable by calling extend()
    ///
    /// The data is placed into the given buffer as follows:
    /// - binary sequence of name data, starting at position 0,
    ///   length determined by source LabelSequence
    /// - offsets, starting at position Name::MAX_WIRE, length
    ///   determined by source LabelSequence
    /// The offsets are updated to be correct for the potentially partial
    /// name data (as stripLeft() and stripRight may have been called on
    /// the source LabelSequence).
    ///
    /// \note The given buf MUST remain in scope during the lifetime of
    /// the LabelSequence created here.
    /// \note The buffer should never be modified except through
    /// calls to extend().
    /// \note Also, only associate the buffer with at most one
    /// LabelSequence. Behaviour is undefined if two LabelSequences are
    /// using the same buffer.
    ///
    /// \param src LabelSequence to copy the initial data from
    /// \param buf external buffer to store this labelsequence's data in
    LabelSequence(const LabelSequence& src, uint8_t buf[MAX_SERIALIZED_LENGTH]);

    /// \brief Copy constructor.
    ///
    /// \note The associated data MUST remain in scope during the lifetime
    /// of this LabelSequence, since only the pointers are copied.
    ///
    /// \note No validation is done on the given data upon construction;
    ///       use with care.
    ///
    /// \param ls The LabelSequence to construct a LabelSequence from
    LabelSequence(const LabelSequence& ls):
        data_(ls.data_),
        offsets_(ls.offsets_),
        first_label_(ls.first_label_),
        last_label_(ls.last_label_) {
    }

    /// \brief Assignment operator.
    ///
    /// \note The associated data MUST remain in scope during the lifetime
    /// of this LabelSequence, since only the pointers are copied.
    ///
    /// \note No validation is done on the given data upon construction;
    ///       use with care.
    ///
    /// \param other The LabelSequence to assign a LabelSequence from
    LabelSequence& operator=(const LabelSequence& other) {
        if (this != &other) {
            // Not self-assignment.
            data_ = other.data_;
            offsets_ = other.offsets_;
            first_label_ = other.first_label_;
            last_label_ = other.last_label_;
        }
        return (*this);
    }

    /// \brief Return the wire-format data for this LabelSequence
    ///
    /// The data is returned as a pointer to (the part of) the original
    /// wireformat data, from either the original Name object, or the
    /// raw data given in the constructor, and the given len value is
    /// set to the number of octets that match this labelsequence.
    ///
    /// \note The data pointed to is only valid if the original Name
    /// object or data is still in scope
    ///
    /// \param len Pointer to a size_t where the length of the data
    ///        will be stored (in number of octets)
    /// \return Pointer to the wire-format data of this label sequence
    const uint8_t* getData(size_t* len) const;

    /// \brief Return the length of the wire-format data of this LabelSequence
    ///
    /// This method returns the number of octets for the data that would
    /// be returned by the \c getData() method.
    ///
    /// Note that the return value of this method is always positive.
    /// Note also that if the return value of this method is 1, it means the
    /// sequence consists of the null label, i.e., a single "dot", and vice
    /// versa.
    ///
    /// \note The data pointed to is only valid if the original Name
    /// object or data is still in scope
    ///
    /// \return The length of the data of the label sequence in octets.
    size_t getDataLength() const;

    /// \brief Return the size of serialized image of the \c LabelSequence.
    ///
    /// This method calculates the size of necessary storage to store
    /// serialized image of this \c LabelSequence (which would be dumped by
    /// \c serialize()) and returns it.  The size is in bytes.
    ///
    /// \throw none.
    ///
    /// \return The size of serialized image of the \c LabelSequence.
    size_t getSerializedLength() const;

    /// \brief Serialize the \c LabelSequence object in to a buffer.
    ///
    /// This method dumps a serialized image of this \c LabelSequence
    /// that would be restored by the corresponding constructor into the
    /// given buffer.  The buffer size must be at least equal to
    /// the value returned by getSerializedLength() (it can be larger than
    /// that).
    ///
    /// Be careful about where the buffer is located; due to the nature
    /// of the buffer, it's quite possible that the memory region is being used
    /// to construct another active \c LabelSequence.  In such a case
    /// the serialization would silently break that sequence object, and
    /// it will be very difficult to identify the cause.  This method
    /// has minimal level checks to avoid such disruption: If the serialization
    /// would break "this" \c LabelSequence object, it doesn't write anything
    /// to the given buffer and throw a \c isc::BadValue exception.
    ///
    /// In general, it should be safe to call this method on a
    /// \c LabelSequence object constructed from a \c Name object or
    /// a copy of such \c LabelSequence.  When you construct \c LabelSequence
    /// from pre-serialized data, calling this method on it can be unsafe.
    /// One safe (but a bit less efficient) way in such a case is to make
    /// the source \c LabelSequence temporary and immediately create a
    /// local copy using an explicit buffer, and call this method on the
    /// latter:
    /// \code
    ///    // don't do this, it's not safe (and would result in exception):
    ///    // LabelSequence(buf).serialize(buf, buf_len);
    ///
    ///    // The following are the safe way:
    ///    uint8_t ext_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
    ///    LabelSequence seq(LabelSequence(buf), ext_buf);
    ///    ... (strip the labels, etc)
    ///    seq.serialize(buf, buf_len); // it's safe to override buf here
    /// \endcode
    ///
    /// The serialized image would be as follows:
    /// - olen: number of offsets (1 byte)
    /// - binary sequence of offsets (olen bytes, verbatim copy of offsets_
    ///   of this size)
    /// - binary sequence of name data (length determined by itself, verbatim
    ///   copy of data_ of the corresponding size)
    ///
    /// Applications must use the resulting image as opaque value and must not
    /// use it for other purposes than input to the corresponding constructor
    /// to restore it.  Application behavior that assumes the specific
    /// organization of the image is not guaranteed.
    ///
    /// \throw isc::BadValue buf_len is too short (this method never throws
    /// otherwise) or the serialization would override internal data of
    /// of the source LabelSequence.
    ///
    /// \param buf Pointer to the placeholder to dump the serialized image
    /// \param buf_len The size of available region in \c buf
    void serialize(void* buf, size_t buf_len) const;

    /// \brief Compares two label sequences for equality.
    ///
    /// Performs a (optionally case-sensitive) comparison between this
    /// LabelSequence and another LabelSequence for equality.
    ///
    /// \param other The LabelSequence to compare with
    /// \param case_sensitive If true, comparison is case-sensitive
    /// \return true if The label sequences consist are the same length,
    ///         and contain the same data.
    bool equals(const LabelSequence& other, bool case_sensitive = false) const;

    /// \brief Compares two label sequences for equality (case ignored).
    ///
    /// This is equivalent to <code>this->equals(other)</code>.
    ///
    /// The operator version is convenient some specific cases such as in
    /// unit tests.
    bool operator==(const LabelSequence& other) const {
        return (equals(other));
    }

    /// \brief Compares two label sequences.
    ///
    /// Performs a (optionally case-insensitive) comparison between this
    /// LabelSequence and another LabelSequence.
    ///
    /// \param other The LabelSequence to compare with
    /// \param case_sensitive If true, comparison is case-insensitive
    /// \return a <code>NameComparisonResult</code> object representing the
    /// comparison result.
    NameComparisonResult compare(const LabelSequence& other,
                                 bool case_sensitive = false) const;

    /// \brief Remove labels from the front of this LabelSequence
    ///
    /// \note No actual memory is changed, this operation merely updates the
    /// internal pointers based on the offsets in the Name object.
    ///
    /// \exception OutOfRange if i is greater than or equal to the number
    ///           of labels currently pointed to by this LabelSequence
    ///
    /// \param i The number of labels to remove.
    void stripLeft(size_t i);

    /// \brief Remove labels from the end of this LabelSequence
    ///
    /// \note No actual memory is changed, this operation merely updates the
    /// internal pointers based on the offsets originally provided.
    ///
    /// \exception OutOfRange if i is greater than or equal to the number
    ///           of labels currently pointed to by this LabelSequence
    ///
    /// \param i The number of labels to remove.
    void stripRight(size_t i);

    /// \brief Returns the current number of labels for this LabelSequence
    ///
    /// \return The number of labels
    size_t getLabelCount() const {
        return (last_label_ - first_label_ + 1);
    }

    /// \brief Convert the LabelSequence to a string.
    ///
    /// This method returns a <code>std::string</code> object representing the
    /// LabelSequence as a string.  The returned string ends with a dot
    /// '.' if the label sequence is absolute.
    ///
    /// This function assumes the underlying data is in proper
    /// uncompressed wire format.  If it finds an unexpected label
    /// character including compression pointer, an exception of class
    /// \c BadLabelType will be thrown.  In addition, if resource
    /// allocation for the result string fails, a corresponding standard
    /// exception will be thrown.
    ///
    /// \return a string representation of the <code>LabelSequence</code>.
    std::string toText() const;

    /// \brief Convert the LabelSequence to a string without escape sequences.
    ///
    /// The string returned will contain a single character value for any
    /// escape sequences in the label(s).
    ///
    /// \param omit_final_dot whether to omit the trailing dot in the output.
    /// \return a string representation of the <code>LabelSequence</code>
    /// that does not contain escape sequences.
    std::string toRawText(bool omit_final_dot) const;

    /// \brief Extend this LabelSequence with the given labelsequence
    ///
    /// The given labels are appended to the name data, and internal
    /// offset data is updated accordingly.
    ///
    /// The data from the given LabelSequence is copied into the buffer
    /// associated with this LabelSequence; the appended LabelSequence
    /// (the 'labels' argument) can be released if it is not needed for
    /// other operations anymore.
    ///
    /// If this LabelSequence is absolute, its root label will be stripped
    /// before the given LabelSequence is appended; after extend(),
    /// this LabelSequence will be absolute if, and only if, the appended
    /// LabelSequence was. A side-effect of this property is that adding
    /// the root label to an absolute LabelSequence has no effect (the
    /// root label is stripped, then added again).
    ///
    /// Some minimal checking is done on the data, but internal integrity
    /// is not assumed. Do NOT modify the given buffer except through calls
    /// to this method, and do NOT call this method if the buffer is
    /// associated to another LabelSequence (behaviour of the other
    /// LabelSequence is undefined in that scenario).
    ///
    /// \exception BadValue If the buffer does not appear to be associated
    /// with this LabelSequence, or if the maximum wire length or maximum
    /// number of labels would be exceeded by this operation
    ///
    /// \param labels The labels to append to this LabelSequence
    /// \param buf The buffer associated with this LabelSequence
    void extend(const LabelSequence& labels,
                uint8_t buf[MAX_SERIALIZED_LENGTH]);

private:
    /// \brief Convert the LabelSequence to a string.
    ///
    /// This method is a version of the zero-argument toText() method,
    /// that accepts a <code>omit_final_dot</code> argument. The
    /// returned string ends with a dot '.' if
    /// <code>omit_final_dot</code> is <code>false</code>.
    ///
    /// This method is used as a helper for <code>Name::toText()</code>
    /// only.
    ///
    /// \param omit_final_dot whether to omit the trailing dot in the output.
    /// \return a string representation of the <code>LabelSequence</code>.
    std::string toText(bool omit_final_dot) const;
public:
    /// \brief Calculate a simple hash for the label sequence.
    ///
    /// This method calculates a hash value for the label sequence as binary
    /// data.  If \c case_sensitive is false, it ignores the case stored in
    /// the labels; specifically, it normalizes the labels by converting all
    /// upper case characters to lower case ones and calculates the hash value
    /// for the result.
    ///
    /// This method is intended to provide a lightweight way to store a
    /// relatively small number of label sequences in a hash table.
    /// For this reason it only takes into account data up to 16 octets
    /// (16 was derived from BIND 9's implementation).  Also, the function does
    /// not provide any unpredictability; a specific sequence will always have
    /// the same hash value.  It should therefore not be used in the context
    /// where an untrusted third party can mount a denial of service attack by
    /// forcing the application to create a very large number of label
    /// sequences that have the same hash value and expected to be stored in
    /// a hash table.
    ///
    /// \exception None
    ///
    /// \param case_sensitive
    /// \return A hash value for this label sequence.
    size_t getHash(bool case_sensitive) const;

    /// \brief Checks whether the label sequence is absolute
    ///
    /// \return true if the last label is the root label
    bool isAbsolute() const;

private:
    const uint8_t* data_;       // wire-format name data
    const uint8_t* offsets_;    // an array of offsets in data_ for the labels
    size_t first_label_;        // index of offsets_ for the first label
    size_t last_label_;         // index of offsets_ for the last label.
                                // can be equal to first_label_, but must not
                                // be smaller (the class ensures that)
};


///
/// \brief Insert the label sequence as a string into stream.
///
/// This method convert the \c label_sequence into a string and inserts
/// it into the output stream \c os.
///
/// This function overloads the global operator<< to behave as described in
/// ostream::operator<< but applied to \c LabelSequence objects.
///
/// \param os A \c std::ostream object on which the insertion operation is
/// performed.
/// \param label_sequence The \c LabelSequence 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 LabelSequence& label_sequence);

inline const LabelSequence&
LabelSequence::WILDCARD() {
    static const uint8_t wildcard_buf[4] = { 0x01, 0x00, 0x01, '*' };
    static const LabelSequence wild_ls(wildcard_buf);
    return (wild_ls);
}

} // end namespace dns
} // end namespace isc

#endif
