1
0
mirror of https://github.com/mfontanini/libtins synced 2026-01-27 12:14:26 +01:00

Added PDUOption::to<>. TCP options now use this method when being converted to their appropriate types.

This commit is contained in:
Matias Fontanini
2013-12-16 14:11:53 -03:00
parent 0e54579200
commit 112a357726
13 changed files with 199 additions and 38 deletions

View File

@@ -145,7 +145,7 @@ namespace Tins {
/**
* The DHCP option type.
*/
typedef PDUOption<uint8_t> option;
typedef PDUOption<uint8_t, DHCP> option;
/**
* The type used to store the DHCP options.

View File

@@ -46,7 +46,7 @@ public:
/**
* Represents a DHCPv6 option.
*/
typedef PDUOption<uint16_t> option;
typedef PDUOption<uint16_t, DHCPv6> option;
/**
* The message types.

View File

@@ -57,7 +57,7 @@ public:
/**
* \brief IEEE 802.11 options struct.
*/
typedef PDUOption<uint8_t> option;
typedef PDUOption<uint8_t, Dot11> option;
/**
* The type used to store tagged options.

View File

@@ -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.
*

View File

@@ -135,7 +135,7 @@ public:
/**
* The type used to represent ICMPv6 options.
*/
typedef PDUOption<uint8_t> option;
typedef PDUOption<uint8_t, ICMPv6> option;
/**
* The type used to store options.

View File

@@ -99,14 +99,14 @@ private:
void skip_line(std::istream &input);
bool from_hex(const std::string &str, uint32_t &result);
template<bool, typename>
template<bool, typename T = void>
struct enable_if {
typedef T type;
};
template<typename T>
struct enable_if<true, T> {
typedef T type;
struct enable_if<false, T> {
};
PDU *pdu_from_flag(Constants::Ethernet::e flag, const uint8_t *buffer,
@@ -173,6 +173,31 @@ HWAddress<n> last_address_from_mask(HWAddress<n> addr, const HWAddress<n> &mask)
inline bool is_dot3(const uint8_t *ptr, size_t sz) {
return (sz >= 13 && ptr[12] < 8);
}
template<typename T>
struct is_unsigned_integral {
static const bool value = false;
};
template<>
struct is_unsigned_integral<uint8_t> {
static const bool value = true;
};
template<>
struct is_unsigned_integral<uint16_t> {
static const bool value = true;
};
template<>
struct is_unsigned_integral<uint32_t> {
static const bool value = true;
};
template<>
struct is_unsigned_integral<uint64_t> {
static const bool value = true;
};
} // namespace Internals
} // namespace Tins
/**

View File

@@ -169,7 +169,7 @@ namespace Tins {
/**
* The IP options type.
*/
typedef PDUOption<option_identifier> option;
typedef PDUOption<option_identifier, IP> option;
/**
* The type of the security option.

View File

@@ -60,7 +60,7 @@ public:
/**
* The type used to represent IPv6 extension headers.
*/
typedef PDUOption<uint8_t> ext_header;
typedef PDUOption<uint8_t, IPv6> ext_header;
/**
* The type used to store the extension headers.

View File

@@ -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.

View File

@@ -32,10 +32,120 @@
#include <vector>
#include <iterator>
#include <algorithm>
#include <stdint.h>
#include "exceptions.h"
#include "endianness.h"
#include "internals.h"
namespace Tins {
/**
* \cond
*/
template<typename OptionType, class PDUType>
class PDUOption;
namespace Internals {
template<typename T, typename X, typename PDUType>
T convert_to_integral(const PDUOption<X, PDUType> & 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<typename T, typename = void>
struct converter {
template<typename X, typename PDUType>
static T convert(const PDUOption<X, PDUType>& opt) {
return T::from_option(opt);
}
};
template<>
struct converter<uint8_t> {
template<typename X, typename PDUType>
static uint8_t convert(const PDUOption<X, PDUType>& opt) {
if(opt.data_size() != 1)
throw malformed_option();
return *opt.data_ptr();
}
};
template<>
struct converter<uint16_t> {
template<typename X, typename PDUType>
static uint16_t convert(const PDUOption<X, PDUType>& opt) {
return convert_to_integral<uint16_t>(opt);
}
};
template<>
struct converter<uint32_t> {
template<typename X, typename PDUType>
static uint32_t convert(const PDUOption<X, PDUType>& opt) {
return convert_to_integral<uint32_t>(opt);
}
};
template<>
struct converter<uint64_t> {
template<typename X, typename PDUType>
static uint64_t convert(const PDUOption<X, PDUType>& opt) {
return convert_to_integral<uint64_t>(opt);
}
};
template<typename T>
struct converter<std::vector<T>, typename enable_if<is_unsigned_integral<T>::value>::type> {
template<typename X, typename PDUType>
static std::vector<T> convert(const PDUOption<X, PDUType>& 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<T> output(std::distance(ptr, end));
typename std::vector<T>::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<typename T>
struct converter<std::pair<T, T>, typename enable_if<is_unsigned_integral<T>::value>::type> {
template<typename X, typename PDUType>
static std::pair<T, T> convert(const PDUOption<X, PDUType>& opt) {
if(opt.data_size() != sizeof(T) * 2)
throw malformed_option();
const T *ptr = (const T*)opt.data_ptr();
std::pair<T, T> 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 <b>must</b>
* store data sequentially. std::vector<uint8_t> is the default
* container.
*/
template<typename OptionType, class Container = std::vector<uint8_t> >
template<typename OptionType, class PDUType>
class PDUOption {
public:
typedef Container container_type;
typedef std::vector<uint8_t> 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<TCP::sack_type>().
*/
template<typename T>
T to() const {
return Internals::converter<T>::convert(*this);
}
private:
option_type option_;
uint16_t size_;

View File

@@ -72,7 +72,7 @@ public:
/**
* The type used to store a TLV option.
*/
typedef PDUOption<TagTypes> tag;
typedef PDUOption<TagTypes, PPPoE> tag;
/**
* The type used to store the options.

View File

@@ -102,7 +102,7 @@ namespace Tins {
/**
* The type used to store TCP options.
*/
typedef PDUOption<uint8_t> option;
typedef PDUOption<uint8_t, TCP> option;
/**
* The type used to store the options.
@@ -490,11 +490,14 @@ namespace Tins {
static const uint16_t DEFAULT_WINDOW;
template<class T>
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<T>();
/*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);

View File

@@ -123,7 +123,7 @@ void TCP::mss(uint16_t value) {
}
uint16_t TCP::mss() const {
return Endian::host_to_be(generic_search<uint16_t>(MSS));
return generic_search<uint16_t>(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<sack_type>();
}
void TCP::timestamp(uint32_t value, uint32_t reply) {
@@ -180,12 +174,10 @@ void TCP::timestamp(uint32_t value, uint32_t reply) {
}
std::pair<uint32_t, uint32_t> 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<uint32_t>(buffer >> 32), buffer & 0xffffffff);
return opt->to<std::pair<uint32_t, uint32_t> >();
}
void TCP::altchecksum(AltChecksums value) {