Program Listing for File packet.hh

Return to documentation for file (pcap/packet.hh)

// SPDX-License-Identifier: LGPL-3.0-or-later
/* pcap/packet.hh - libnokogiri pcap packets */
#if !defined(LIBNOKOGIRI_PCAP_PACKET_HH)
#define LIBNOKOGIRI_PCAP_PACKET_HH

#include <cstdint>
#include <optional>
#include <variant>
#include <vector>
#include <array>
#include <string_view>

#include <libnokogiri/config.hh>
#include <libnokogiri/common.hh>

#include <libnokogiri/internal/defs.hh>

namespace libnokogiri::pcap {
    using libnokogiri::internal::enum_pair_t;
    enum struct packet_type_t : uint8_t {
        Standard = 0x00,
        Modified = 0x01,
    };

    const std::array<const enum_pair_t<packet_type_t>, 2> packet_type_s{{
        { packet_type_t::Standard, "Standard"sv },
        { packet_type_t::Modified, "Modified"sv }
    }};

    struct packet_header_t final {
    private:
        std::uint32_t _timestamp;
        std::uint32_t _usecs;
        std::uint32_t _have;
        std::uint32_t _was;
    public:
        constexpr packet_header_t() noexcept :
            _timestamp{0U}, _usecs{0U}, _have{0U}, _was{0U}
            { /* NOP */ }

        constexpr packet_header_t(std::uint32_t timestamp, std::uint32_t useconds,
                std::uint32_t pkt_len_have, std::uint32_t pkt_len_actual) noexcept :
            _timestamp{timestamp}, _usecs{useconds}, _have{pkt_len_have}, _was{pkt_len_actual}
            { /* NOP */ }

        packet_header_t(const packet_header_t&) = delete;
        packet_header_t& operator=(const packet_header_t&) = delete;

        packet_header_t(packet_header_t&&) = default;
        packet_header_t& operator=(packet_header_t&&) = default;

        [[nodiscard]]
        std::uint32_t timestamp() const noexcept { return _timestamp; }
        void timestamp(const std::uint32_t timestamp) noexcept { _timestamp = timestamp; }

        [[nodiscard]]
        std::uint32_t useconds() const noexcept { return _usecs; }
        void useconds(const std::uint32_t useconds) noexcept { _usecs = useconds; }

        [[nodiscard]]
        std::uint32_t captured_len() const noexcept { return _have; }
        void captured_len(const std::uint32_t captured_len) noexcept { _have = captured_len; }

        [[nodiscard]]
        std::uint32_t actual_len() const noexcept { return _was; }
        void actual_len(const std::uint32_t actual_len) noexcept { _was = actual_len; }

        [[nodiscard]]
        bool full_packet() const noexcept { return _was == _have; }
    };

    struct packet_header_modified_t final {
    private:
        packet_header_t _base_header;
        std::uint32_t _if_index;
        std::uint16_t _proto;
        std::uint8_t _type;
        std::uint8_t _padding;
    public:
        constexpr packet_header_modified_t() noexcept :
            _base_header{}, _if_index{0U}, _proto{0U}, _type{0U}, _padding{0U}
            { /* NOP */ }

        constexpr packet_header_modified_t(packet_header_t&& base_header, std::uint32_t if_index,
                std::uint16_t protocol, std::uint8_t type) noexcept :
            _base_header{std::move(base_header)}, _if_index{if_index}, _proto{protocol}, _type{type},
            _padding{0U} { /* NOP */ }

        packet_header_modified_t(std::nullptr_t) noexcept { /* NOP */ }

        packet_header_modified_t(const packet_header_modified_t&) = delete;
        packet_header_modified_t& operator=(const packet_header_modified_t&) = delete;

        packet_header_modified_t(packet_header_modified_t&&) = default;
        packet_header_modified_t& operator=(packet_header_modified_t&&) = default;

        [[nodiscard]]
        const packet_header_t& base_header() const noexcept { return _base_header; }
        void base_header(packet_header_t&& base_header) noexcept { _base_header = std::move(base_header); }

        [[nodiscard]]
        std::uint32_t interface_index() const noexcept { return _if_index; }
        void interface_index(std::uint32_t interface_index) noexcept { _if_index = interface_index; }

        [[nodiscard]]
        std::uint16_t protocol() const noexcept { return _proto; }
        void protocol(std::uint16_t protocol) noexcept { _proto = protocol; }

        [[nodiscard]]
        std::uint8_t type() const noexcept { return _type; }
        void type(std::uint8_t type) noexcept { _type = type; }
    };


    // /*! \struct libnokogiri::pcap::packet_t
    //  \brief pcap packet template

    //  This template provides the framework for reading and writing packets.

    //  They can have custom headers, but the two standardized packets are already
    //  specified as libnokogiri::pcap::generic_packet_t and libnokogiri::pcap::modified_packet_t .

    //  All this template does is change out the packet header type, the rest of the logic is identical.
    // */
    // template<typename T, typename U>
    // struct packet_t final {
    // public:
    //  using data_t = std::optional<std::unique_ptr<U>>;
    // private:
    //  T _packet_header;
    //  std::uintptr_t _offset;
    //  data_t _data;
    // public:
    //  constexpr packet_t() noexcept :
    //      _packet_header{}, _offset{0U}, _data{std::nullopt}
    //      { /* NOP */ }

    //  constexpr packet_t(T header, std::uintptr_t offset, data_t data) noexcept :
    //      _packet_header{header}, _offset{offset}, _data{data}
    //      { /* NOP */ }

    //  /*! Retrieve the packet header */
    //  [[nodiscard]]
    //  T header() const noexcept { return _packet_header; }
    //  /*! Set the packet header */
    //  void header(T header) noexcept { _packet_header = header; }

    //  /*! Retrieve the offset in the file the packet is located at */
    //  [[nodiscard]]
    //  std::uintptr_t packet_offset() const noexcept { return _offset; }
    //  /*! Set the offset in the file the packet is located at */
    //  void packet_offset(std::uintptr_t packet_offset) noexcept { _offset = packet_offset; }

    //  /*! Retrieve the packet data */
    //  [[nodiscard]]
    //  data_t packet_data() const noexcept { return _data; }
    //  /*! Set the packet data */
    //  void packet_data(data_t packet_data) noexcept { _data = packet_data; }
    // };

    // /*! Type alias for generic conforming pcap packets with the standard header */
    // template<typename T>
    // using generic_packet_t = packet_t<packet_header_t, T>;
    // /*! Type alias for packets from the modified pcap files */
    // template<typename T>
    // using modified_packet_t = packet_t<packet_header_modified_t, T>;


    struct packet_t {
    public:
        using pkt_header_t = std::variant<
            std::monostate,
            packet_header_t,
            packet_header_modified_t
        >;
    private:
        std::vector<std::uint8_t> _raw_data;
        pkt_header_t _packet_header;

        template<typename T>
        [[nodiscard]]
        std::enable_if_t<
            std::is_pod_v<T> &&
            !libnokogiri::internal::has_nullable_ctor_v<T> &&
            !std::is_same_v<T, void*>,
        T*>
        index(const std::size_t offset) {
            if (offset < _raw_data.size()) {
                return new (reinterpret_cast<std::uint8_t *>(_raw_data.data()) + (offset * sizeof(T))) T{};
            }
            return nullptr;
        }

        template<typename T>
        [[nodiscard]]
        std::enable_if_t<
            libnokogiri::internal::has_nullable_ctor_v<T> &&
            !std::is_same_v<T, void*>,
        T*>
        index(const std::size_t offset) {
            if (offset < _raw_data.size()) {
                return new (reinterpret_cast<std::uint8_t *>(_raw_data.data()) + (offset * sizeof(T))) T{nullptr};
            }
            return nullptr;
        }

        template<typename T>
        [[nodiscard]]
        std::enable_if_t<std::is_same_v<T, void*>, void*>
        index(const std::size_t offset) {
            if (offset < _raw_data.size()) {
                return reinterpret_cast<std::uint8_t *>(_raw_data.data()) + offset;
            }
            return nullptr;
        }

    public:

        packet_t(std::size_t length, pkt_header_t header = {}) noexcept :
            _raw_data{std::vector<std::uint8_t>(length)},
            _packet_header{std::move(header)} { /* NOP */ }


        packet_t(const packet_t&) = delete;
        packet_t& operator=(const packet_t&) = delete;

        packet_t(packet_t&&) = default;
        packet_t& operator=(packet_t&&) = default;

        [[nodiscard]]
        std::size_t length() const noexcept { return _raw_data.size(); }

        [[nodiscard]]
        pkt_header_t& header() noexcept { return _packet_header; }

        [[nodiscard]]
        bool is_complete() const noexcept {
            return std::visit([](auto& header) -> bool {
                using T = std::decay_t<decltype(header)>;
                if constexpr (std::is_same_v<T, packet_header_modified_t>) {
                    return header.base_header().full_packet();
                } else if constexpr (std::is_same_v<T, packet_header_t>) {
                    return header.full_packet();
                } else {
                    return false;
                }
            }, _packet_header);
        }

        template<typename T>
        [[nodiscard]]
        T& as() { *index<T>(); }

        template<typename T>
        [[nodiscard]]
        T *operator [](const off_t idx) { return index<T>(idx); }

        [[nodiscard]]
        auto begin() noexcept { return _raw_data.begin(); }
        [[nodiscard]]
        auto end() noexcept { return _raw_data.end(); }

        template<typename T>
        [[nodiscard]]
        const T *operator [](const off_t idx) const { return index<const T>(idx); }

        template<typename T>
        [[nodiscard]]
        T *at(const off_t idx) { return index<T>(idx); }

        template<typename T>
        [[nodiscard]]
        const T *at(const off_t idx) const { return index<const T>(idx); }

        void *address(const off_t offset) noexcept { return index<void *>(offset); }
    };



    struct packet_storage_t final {
    private:
        std::uint32_t _len;
        std::uintptr_t _offset;
        packet_t _packet_cache;
    public:
        packet_storage_t() noexcept :
            _len{0U}, _offset{0U}, _packet_cache{0}
            { /* NOP */ }

        packet_storage_t(std::uint32_t len, std::uintptr_t offset) noexcept :
            _len{len}, _offset{offset}, _packet_cache{0}
            { /* NOP */ }

        packet_storage_t(std::uint32_t len, std::uintptr_t offset, packet_t&& packet) noexcept :
            _len{len}, _offset{offset}, _packet_cache{std::move(packet)}
            { /* NOP */ }

        packet_storage_t(const packet_storage_t&) = delete;
        packet_storage_t& operator=(const packet_storage_t&) = delete;

        packet_storage_t(packet_storage_t&&) = default;
        packet_storage_t& operator=(packet_storage_t&&) = default;

        [[nodiscard]]
        std::uint32_t length() const noexcept { return _len; }

        [[nodiscard]]
        std::uintptr_t offset() const noexcept { return _offset; }

        [[nodiscard]]
        packet_t& get_packet() noexcept { return _packet_cache; }

        void set_packet(packet_t&& pkt) noexcept { _packet_cache = std::move(pkt); }
    };
}

#endif /* LIBNOKOGIRI_PCAP_PACKET_HH */