diff --git a/include/dhcp.h b/include/dhcp.h index a01fe52..9b9097f 100644 --- a/include/dhcp.h +++ b/include/dhcp.h @@ -145,7 +145,7 @@ namespace Tins { /** * The DHCP option type. */ - typedef PDUOption option; + typedef PDUOption option; /** * The type used to store the DHCP options. diff --git a/include/dhcpv6.h b/include/dhcpv6.h index 4d10822..94e9194 100644 --- a/include/dhcpv6.h +++ b/include/dhcpv6.h @@ -46,7 +46,7 @@ public: /** * Represents a DHCPv6 option. */ - typedef PDUOption option; + typedef PDUOption option; /** * The message types. diff --git a/include/dot11/dot11_base.h b/include/dot11/dot11_base.h index 565638f..8d01373 100644 --- a/include/dot11/dot11_base.h +++ b/include/dot11/dot11_base.h @@ -57,7 +57,7 @@ public: /** * \brief IEEE 802.11 options struct. */ - typedef PDUOption option; + typedef PDUOption option; /** * The type used to store tagged options. diff --git a/include/endianness.h b/include/endianness.h index 79e6424..31b9a53 100644 --- a/include/endianness.h +++ b/include/endianness.h @@ -55,6 +55,16 @@ namespace Tins { namespace Endian { + /** + * \brief "Changes" a 8-bit integral value's endianess. This is an + * identity function. + * + * \param data The data to convert. + */ + inline uint8_t do_change_endian(uint8_t data) { + return data; + } + /** * \brief Changes a 16-bit integral value's endianess. * diff --git a/include/icmpv6.h b/include/icmpv6.h index c46ea1d..d4345fe 100644 --- a/include/icmpv6.h +++ b/include/icmpv6.h @@ -135,7 +135,7 @@ public: /** * The type used to represent ICMPv6 options. */ - typedef PDUOption option; + typedef PDUOption option; /** * The type used to store options. diff --git a/include/internals.h b/include/internals.h index 4b6d691..d953413 100644 --- a/include/internals.h +++ b/include/internals.h @@ -99,14 +99,14 @@ private: void skip_line(std::istream &input); bool from_hex(const std::string &str, uint32_t &result); -template +template struct enable_if { - + typedef T type; }; template -struct enable_if { - typedef T type; +struct enable_if { + }; PDU *pdu_from_flag(Constants::Ethernet::e flag, const uint8_t *buffer, @@ -173,6 +173,31 @@ HWAddress last_address_from_mask(HWAddress addr, const HWAddress &mask) inline bool is_dot3(const uint8_t *ptr, size_t sz) { return (sz >= 13 && ptr[12] < 8); } + +template +struct is_unsigned_integral { + static const bool value = false; +}; + +template<> +struct is_unsigned_integral { + static const bool value = true; +}; + +template<> +struct is_unsigned_integral { + static const bool value = true; +}; + +template<> +struct is_unsigned_integral { + static const bool value = true; +}; + +template<> +struct is_unsigned_integral { + static const bool value = true; +}; } // namespace Internals } // namespace Tins /** diff --git a/include/ip.h b/include/ip.h index 9c3a653..2f5cea5 100644 --- a/include/ip.h +++ b/include/ip.h @@ -169,7 +169,7 @@ namespace Tins { /** * The IP options type. */ - typedef PDUOption option; + typedef PDUOption option; /** * The type of the security option. diff --git a/include/ipv6.h b/include/ipv6.h index 06e289d..ce6598f 100644 --- a/include/ipv6.h +++ b/include/ipv6.h @@ -60,7 +60,7 @@ public: /** * The type used to represent IPv6 extension headers. */ - typedef PDUOption ext_header; + typedef PDUOption ext_header; /** * The type used to store the extension headers. diff --git a/include/pdu.h b/include/pdu.h index db56abe..358d0ee 100644 --- a/include/pdu.h +++ b/include/pdu.h @@ -65,6 +65,14 @@ namespace Tins { */ typedef byte_array serialization_type; + /** + * The typep used to identify the endianness of every PDU. + */ + enum endian_type { + BE, + LE + }; + /** * \brief Enum which identifies each type of PDU. * @@ -123,6 +131,12 @@ namespace Tins { IPSEC_ESP, USER_DEFINED_PDU = 1000 }; + + /** + * The endianness used by this PDU. This can be overriden + * by subclasses. + */ + static const endian_type endianness = BE; /** * \brief Default constructor. diff --git a/include/pdu_option.h b/include/pdu_option.h index 9f51b0e..b891a14 100644 --- a/include/pdu_option.h +++ b/include/pdu_option.h @@ -32,10 +32,120 @@ #include #include +#include #include #include "exceptions.h" +#include "endianness.h" +#include "internals.h" namespace Tins { +/** + * \cond + */ +template +class PDUOption; + +namespace Internals { + template + T convert_to_integral(const PDUOption & opt) { + if(opt.data_size() != sizeof(T)) + throw malformed_option(); + T data = *(T*)opt.data_ptr(); + if(PDUType::endianness == PDUType::BE) + data = Endian::be_to_host(data); + else + data = Endian::le_to_host(data); + return data; + } + + template + struct converter { + template + static T convert(const PDUOption& opt) { + return T::from_option(opt); + } + }; + + template<> + struct converter { + template + static uint8_t convert(const PDUOption& opt) { + if(opt.data_size() != 1) + throw malformed_option(); + return *opt.data_ptr(); + } + }; + + template<> + struct converter { + template + static uint16_t convert(const PDUOption& opt) { + return convert_to_integral(opt); + } + }; + + template<> + struct converter { + template + static uint32_t convert(const PDUOption& opt) { + return convert_to_integral(opt); + } + }; + + template<> + struct converter { + template + static uint64_t convert(const PDUOption& opt) { + return convert_to_integral(opt); + } + }; + + template + struct converter, typename enable_if::value>::type> { + template + static std::vector convert(const PDUOption& opt) { + if(opt.data_size() % sizeof(T) != 0) + throw malformed_option(); + const T *ptr = (const T*)opt.data_ptr(); + const T *end = ptr + (opt.data_size() / sizeof(T)); + + std::vector output(std::distance(ptr, end)); + typename std::vector::iterator it = output.begin(); + while(ptr < end) { + if(PDUType::endianness == PDUType::BE) + *it++ = Endian::be_to_host(*(ptr++)); + else + *it++ = Endian::le_to_host(*(ptr++)); + } + return output; + } + }; + + template + struct converter, typename enable_if::value>::type> { + template + static std::pair convert(const PDUOption& opt) { + if(opt.data_size() != sizeof(T) * 2) + throw malformed_option(); + const T *ptr = (const T*)opt.data_ptr(); + std::pair output; + if(PDUType::endianness == PDUType::BE) { + output.first = Endian::be_to_host(*ptr++); + output.second = Endian::be_to_host(*ptr); + } + else { + output.first = Endian::le_to_host(*ptr++); + output.second = Endian::le_to_host(*ptr); + } + return output; + } + }; +} + +/** + * \endcond + */ + /** * \class PDUOption * \brief Represents a PDU option field. @@ -46,16 +156,11 @@ namespace Tins { * * The OptionType template parameter indicates the type that will be * used to store this option's identifier. - * - * The Container template parameter indicates the container which will - * be used to store this option's data. The container must - * store data sequentially. std::vector is the default - * container. */ -template > +template class PDUOption { public: - typedef Container container_type; + typedef std::vector container_type; typedef typename container_type::value_type data_type; typedef OptionType option_type; @@ -158,6 +263,18 @@ public: size_t length_field() const { return size_; } + + /** + * \brief Constructs a T from this PDUOption. + * + * Use this method to convert a PDUOption to the specific type that + * represents it. For example, if you know an option is of type + * PDU::SACK, you could use option.to(). + */ + template + T to() const { + return Internals::converter::convert(*this); + } private: option_type option_; uint16_t size_; diff --git a/include/pppoe.h b/include/pppoe.h index 245ce36..7ab729b 100644 --- a/include/pppoe.h +++ b/include/pppoe.h @@ -72,7 +72,7 @@ public: /** * The type used to store a TLV option. */ - typedef PDUOption tag; + typedef PDUOption tag; /** * The type used to store the options. diff --git a/include/tcp.h b/include/tcp.h index a5ff3b0..d822cd8 100644 --- a/include/tcp.h +++ b/include/tcp.h @@ -102,7 +102,7 @@ namespace Tins { /** * The type used to store TCP options. */ - typedef PDUOption option; + typedef PDUOption option; /** * The type used to store the options. @@ -490,11 +490,14 @@ namespace Tins { static const uint16_t DEFAULT_WINDOW; template - T generic_search(OptionTypes opt) const { - const option *option = search_option(opt); - if(option && option->data_size() == sizeof(T)) + T generic_search(OptionTypes opt_type) const { + const option *opt = search_option(opt_type); + if(!opt) + throw option_not_found(); + return opt->to(); + /*if(option && option->data_size() == sizeof(T)) return *(const T*)(&option->data_ptr()[0]); - throw option_not_found(); + throw option_not_found();*/ } void internal_add_option(const option &option); diff --git a/src/tcp.cpp b/src/tcp.cpp index 6aa18c5..96913c9 100644 --- a/src/tcp.cpp +++ b/src/tcp.cpp @@ -123,7 +123,7 @@ void TCP::mss(uint16_t value) { } uint16_t TCP::mss() const { - return Endian::host_to_be(generic_search(MSS)); + return generic_search(MSS); } void TCP::winscale(uint8_t value) { @@ -161,16 +161,10 @@ void TCP::sack(const sack_type &edges) { } TCP::sack_type TCP::sack() const { - const option *option = search_option(SACK); - if(!option || (option->data_size() % sizeof(uint32_t)) != 0) + const option *opt = search_option(SACK); + if(!opt) throw option_not_found(); - const uint32_t *ptr = (const uint32_t*)option->data_ptr(); - const uint32_t *end = ptr + (option->data_size() / sizeof(uint32_t)); - sack_type edges(end - ptr); - sack_type::iterator it = edges.begin(); - while(ptr < end) - *it++ = Endian::host_to_be(*(ptr++)); - return edges; + return opt->to(); } void TCP::timestamp(uint32_t value, uint32_t reply) { @@ -180,12 +174,10 @@ void TCP::timestamp(uint32_t value, uint32_t reply) { } std::pair TCP::timestamp() const { - const option *option = search_option(TSOPT); - if(!option || option->data_size() != (sizeof(uint32_t) << 1)) + const option *opt = search_option(TSOPT); + if(!opt) throw option_not_found(); - uint64_t buffer = *(const uint64_t*)option->data_ptr(); - buffer = Endian::be_to_host(buffer); - return std::make_pair(static_cast(buffer >> 32), buffer & 0xffffffff); + return opt->to >(); } void TCP::altchecksum(AltChecksums value) {