285 lines
9.4 KiB
C++
285 lines
9.4 KiB
C++
/*
|
|
* Copyright (c) 2017, Matias Fontanini
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following disclaimer
|
|
* in the documentation and/or other materials provided with the
|
|
* distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
*/
|
|
|
|
#include <algorithm>
|
|
#include <tins/pdu.h>
|
|
#include <tins/ip_address.h>
|
|
#include <tins/ipv6_address.h>
|
|
#include <tins/hw_address.h>
|
|
#include <tins/endianness.h>
|
|
#include <tins/pdu_option.h>
|
|
#include <tins/memory_helpers.h>
|
|
|
|
using std::vector;
|
|
using std::pair;
|
|
using std::string;
|
|
using std::memcpy;
|
|
using std::distance;
|
|
|
|
using Tins::Memory::InputMemoryStream;
|
|
|
|
namespace Tins {
|
|
namespace Internals {
|
|
namespace Converters {
|
|
|
|
template <typename T>
|
|
T convert_to_integral(const uint8_t* ptr, uint32_t data_size, PDU::endian_type endian) {
|
|
if (data_size != sizeof(T)) {
|
|
throw malformed_option();
|
|
}
|
|
T data = *(T*)ptr;
|
|
if (endian == PDU::BE) {
|
|
data = Endian::be_to_host(data);
|
|
}
|
|
else {
|
|
data = Endian::le_to_host(data);
|
|
}
|
|
return data;
|
|
}
|
|
|
|
template<typename T>
|
|
vector<T> convert_vector(const uint8_t* ptr, uint32_t data_size, PDU::endian_type endian) {
|
|
if (data_size % sizeof(T) != 0) {
|
|
throw malformed_option();
|
|
}
|
|
InputMemoryStream input(ptr, data_size);
|
|
|
|
vector<T> output(data_size / sizeof(T));
|
|
typename vector<T>::iterator it = output.begin();
|
|
while (input) {
|
|
const T value = input.read<T>();
|
|
if (endian == PDU::BE) {
|
|
*it++ = Endian::be_to_host(value);
|
|
}
|
|
else {
|
|
*it++ = Endian::le_to_host(value);
|
|
}
|
|
}
|
|
return output;
|
|
}
|
|
|
|
template<typename T, typename U>
|
|
typename enable_if<is_unsigned_integral<T>::value && is_unsigned_integral<U>::value,
|
|
vector<std::pair<T, U> > >::type
|
|
convert_vector(const uint8_t* ptr, uint32_t data_size, PDU::endian_type endian) {
|
|
if (data_size % (sizeof(T) + sizeof(U)) != 0) {
|
|
throw malformed_option();
|
|
}
|
|
InputMemoryStream input(ptr, data_size);
|
|
|
|
std::vector<std::pair<T, U> > output;
|
|
while (input) {
|
|
pair<T, U> data;
|
|
data.first = input.read<T>();
|
|
data.second = input.read<U>();
|
|
if (endian == PDU::BE) {
|
|
data.first = Endian::be_to_host(data.first);
|
|
data.second = Endian::be_to_host(data.second);
|
|
}
|
|
else {
|
|
data.first = Endian::le_to_host(data.first);
|
|
data.second = Endian::le_to_host(data.second);
|
|
}
|
|
output.push_back(data);
|
|
}
|
|
return output;
|
|
}
|
|
|
|
template<typename T, typename U>
|
|
typename enable_if<is_unsigned_integral<T>::value && is_unsigned_integral<U>::value,
|
|
std::pair<T, U> >::type
|
|
convert_pair(const uint8_t* ptr, uint32_t data_size, PDU::endian_type endian) {
|
|
if (data_size != sizeof(T) + sizeof(U)) {
|
|
throw malformed_option();
|
|
}
|
|
InputMemoryStream input(ptr, data_size);
|
|
pair<T, U> output;
|
|
output.first = input.read<T>();
|
|
output.second = input.read<U>();
|
|
if (endian == PDU::BE) {
|
|
output.first = Endian::be_to_host(output.first);
|
|
output.second = Endian::be_to_host(output.second);
|
|
}
|
|
else {
|
|
output.first = Endian::le_to_host(output.first);
|
|
output.second = Endian::le_to_host(output.second);
|
|
}
|
|
return output;
|
|
}
|
|
|
|
uint8_t convert(const uint8_t* ptr, uint32_t data_size, PDU::endian_type, type_to_type<uint8_t>) {
|
|
if (data_size != 1) {
|
|
throw malformed_option();
|
|
}
|
|
return *ptr;
|
|
}
|
|
|
|
int8_t convert(const uint8_t* ptr, uint32_t data_size, PDU::endian_type, type_to_type<int8_t>) {
|
|
if (data_size != 1) {
|
|
throw malformed_option();
|
|
}
|
|
return *ptr;
|
|
}
|
|
|
|
uint16_t convert(const uint8_t* ptr, uint32_t data_size, PDU::endian_type endian,
|
|
type_to_type<uint16_t>) {
|
|
return convert_to_integral<uint16_t>(ptr, data_size, endian);
|
|
|
|
}
|
|
|
|
uint32_t convert(const uint8_t* ptr, uint32_t data_size, PDU::endian_type endian,
|
|
type_to_type<uint32_t>) {
|
|
return convert_to_integral<uint32_t>(ptr, data_size, endian);
|
|
}
|
|
|
|
uint64_t convert(const uint8_t* ptr, uint32_t data_size,
|
|
PDU::endian_type endian, type_to_type<uint64_t>) {
|
|
return convert_to_integral<uint64_t>(ptr, data_size, endian);
|
|
}
|
|
|
|
HWAddress<6> convert(const uint8_t* ptr, uint32_t data_size, PDU::endian_type,
|
|
type_to_type<HWAddress<6> >) {
|
|
if (data_size != 6) {
|
|
throw malformed_option();
|
|
}
|
|
return HWAddress<6>(ptr);
|
|
}
|
|
|
|
IPv4Address convert(const uint8_t* ptr, uint32_t data_size, PDU::endian_type endian,
|
|
type_to_type<IPv4Address>) {
|
|
if (data_size != sizeof(uint32_t)) {
|
|
throw malformed_option();
|
|
}
|
|
InputMemoryStream input(ptr, data_size);
|
|
const uint32_t ip_int = input.read<uint32_t>();
|
|
if (endian == PDU::BE) {
|
|
return IPv4Address(ip_int);
|
|
}
|
|
else {
|
|
return IPv4Address(Endian::change_endian(ip_int));
|
|
}
|
|
}
|
|
|
|
IPv6Address convert(const uint8_t* ptr, uint32_t data_size, PDU::endian_type,
|
|
type_to_type<IPv6Address>) {
|
|
if (data_size != IPv6Address::address_size) {
|
|
throw malformed_option();
|
|
}
|
|
return IPv6Address(ptr);
|
|
}
|
|
|
|
string convert(const uint8_t* ptr, uint32_t data_size, PDU::endian_type,
|
|
type_to_type<string>) {
|
|
return string(ptr, ptr + data_size);
|
|
}
|
|
|
|
vector<float> convert(const uint8_t* ptr, uint32_t data_size, PDU::endian_type,
|
|
type_to_type<vector<float> >) {
|
|
vector<float> output;
|
|
const uint8_t* end = ptr + data_size;
|
|
while (ptr != end) {
|
|
output.push_back(float(*(ptr++) & 0x7f) / 2);
|
|
}
|
|
return output;
|
|
}
|
|
|
|
vector<uint8_t> convert(const uint8_t* ptr, uint32_t data_size, PDU::endian_type endian,
|
|
type_to_type<vector<uint8_t> >) {
|
|
return convert_vector<uint8_t>(ptr, data_size, endian);
|
|
}
|
|
|
|
vector<uint16_t> convert(const uint8_t* ptr, uint32_t data_size, PDU::endian_type endian,
|
|
type_to_type<vector<uint16_t> >) {
|
|
return convert_vector<uint16_t>(ptr, data_size, endian);
|
|
}
|
|
|
|
vector<uint32_t> convert(const uint8_t* ptr, uint32_t data_size, PDU::endian_type endian,
|
|
type_to_type<vector<uint32_t> >) {
|
|
return convert_vector<uint32_t>(ptr, data_size, endian);
|
|
}
|
|
|
|
vector<IPv4Address> convert(const uint8_t* ptr, uint32_t data_size, PDU::endian_type endian,
|
|
type_to_type<vector<IPv4Address> >) {
|
|
if (data_size % 4 != 0) {
|
|
throw malformed_option();
|
|
}
|
|
InputMemoryStream input(ptr, data_size);
|
|
vector<IPv4Address> output(data_size / sizeof(uint32_t));
|
|
vector<IPv4Address>::iterator it = output.begin();
|
|
while (input) {
|
|
const uint32_t ip_int = input.read<uint32_t>();
|
|
if (endian == PDU::BE) {
|
|
*it++ = IPv4Address(ip_int);
|
|
}
|
|
else {
|
|
*it++ = IPv4Address(Endian::change_endian(ip_int));
|
|
}
|
|
}
|
|
return output;
|
|
}
|
|
|
|
vector<IPv6Address> convert(const uint8_t* ptr, uint32_t data_size, PDU::endian_type,
|
|
type_to_type<vector<IPv6Address> >) {
|
|
if (data_size % IPv6Address::address_size != 0) {
|
|
throw malformed_option();
|
|
}
|
|
const uint8_t* end = ptr + data_size;
|
|
vector<IPv6Address> output;
|
|
while (ptr < end) {
|
|
output.push_back(IPv6Address(ptr));
|
|
ptr += IPv6Address::address_size;
|
|
}
|
|
return output;
|
|
}
|
|
|
|
vector<pair<uint8_t, uint8_t> > convert(const uint8_t* ptr, uint32_t data_size,
|
|
PDU::endian_type endian,
|
|
type_to_type<vector<pair<uint8_t, uint8_t> > >) {
|
|
return convert_vector<uint8_t, uint8_t>(ptr, data_size, endian);
|
|
}
|
|
|
|
pair<uint8_t, uint8_t> convert(const uint8_t* ptr, uint32_t data_size, PDU::endian_type endian,
|
|
type_to_type<pair<uint8_t, uint8_t> >) {
|
|
return convert_pair<uint8_t, uint8_t>(ptr, data_size, endian);
|
|
}
|
|
|
|
pair<uint16_t, uint32_t> convert(const uint8_t* ptr, uint32_t data_size, PDU::endian_type endian,
|
|
type_to_type<pair<uint16_t, uint32_t> >) {
|
|
return convert_pair<uint16_t, uint32_t>(ptr, data_size, endian);
|
|
}
|
|
|
|
pair<uint32_t, uint32_t> convert(const uint8_t* ptr, uint32_t data_size, PDU::endian_type endian,
|
|
type_to_type<pair<uint32_t, uint32_t> >) {
|
|
return convert_pair<uint32_t, uint32_t>(ptr, data_size, endian);
|
|
}
|
|
|
|
} // Converters
|
|
} // Internals
|
|
} // Tins
|