diff --git a/include/endianness.h b/include/endianness.h index 31b9a53..aa8d2ae 100644 --- a/include/endianness.h +++ b/include/endianness.h @@ -110,6 +110,11 @@ namespace Endian { template struct conversion_dispatcher; + template<> + struct conversion_dispatcher + : public conversion_dispatch_helper + { }; + template<> struct conversion_dispatcher : public conversion_dispatch_helper diff --git a/include/icmpv6.h b/include/icmpv6.h index d4345fe..8543816 100644 --- a/include/icmpv6.h +++ b/include/icmpv6.h @@ -145,30 +145,53 @@ public: /** * \brief The type used to store the new home agent information * option data. - * - * The first member contains the home agent preference field, while - * the second one contains the home agent lifetime. */ - typedef std::pair new_ha_info_type; + typedef std::vector new_ha_info_type; /** * The type used to store the source/target address list options. */ - typedef std::vector addr_list_type; + struct addr_list_type { + typedef std::vector addresses_type; + + uint8_t reserved[6]; + addresses_type addresses; + + addr_list_type(const addresses_type &addresses = addresses_type()) + : addresses(addresses) + { + std::fill(reserved, reserved + sizeof(reserved), 0); + } + + static addr_list_type from_option(const option &opt); + }; /** * The type used to store the nonce option data. */ typedef std::vector nonce_type; + /** + * The type used to store the MTU option. + */ + typedef std::pair mtu_type; + /** * \brief The type used to store the neighbour advertisement * acknowledgement option data. - * - * The first member contains the option code field, while - * the second one contains the status. */ - typedef std::pair naack_type; + struct naack_type { + uint8_t code, status; + uint8_t reserved[4]; + + naack_type(uint8_t code = 0, uint8_t status = 0) + : code(code), status(status) + { + std::fill(reserved, reserved + 4, 0); + } + + static naack_type from_option(const option &opt); + }; /** * \brief The type used to store the link layer address option data. @@ -207,6 +230,8 @@ public: { } + + static lladdr_type from_option(const option &opt); }; /** @@ -226,6 +251,8 @@ public: : prefix_len(prefix_len), A(A), L(L), valid_lifetime(valid_lifetime), preferred_lifetime(preferred_lifetime), prefix(prefix) { } + + static prefix_info_type from_option(const option &opt); }; /** @@ -832,7 +859,7 @@ public: * * \param value The MTU option data. */ - void mtu(uint32_t value); + void mtu(const mtu_type& value); /** * \brief Setter for the shortcut limit option. @@ -1009,7 +1036,7 @@ public: * This method will throw an option_not_found exception if the * option is not found. */ - uint32_t mtu() const; + mtu_type mtu() const; /** * \brief Getter for the shortcut limit option. diff --git a/include/ip.h b/include/ip.h index 2f5cea5..5d55935 100644 --- a/include/ip.h +++ b/include/ip.h @@ -184,6 +184,8 @@ namespace Tins { : security(sec), compartments(comp), handling_restrictions(hand_res), transmission_control(tcc) {} + + static security_type from_option(const option &opt); }; /** @@ -198,6 +200,8 @@ namespace Tins { generic_route_option_type(uint8_t ptr = 0, routes_type rts = routes_type()) : pointer(ptr), routes(rts) {} + + static generic_route_option_type from_option(const option &opt); }; /** diff --git a/include/pdu_option.h b/include/pdu_option.h index b891a14..2f663c9 100644 --- a/include/pdu_option.h +++ b/include/pdu_option.h @@ -37,6 +37,8 @@ #include "exceptions.h" #include "endianness.h" #include "internals.h" +#include "ip_address.h" +#include "ipv6_address.h" namespace Tins { /** @@ -107,35 +109,73 @@ namespace Internals { 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)); + const T *end = (const T*)(opt.data_ptr() + opt.data_size()); 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++)); + *it++ = Endian::be_to_host(*ptr++); else - *it++ = Endian::le_to_host(*(ptr++)); + *it++ = Endian::le_to_host(*ptr++); } return output; } }; - template - struct converter, typename enable_if::value>::type> { + template<> + struct converter > { template - static std::pair convert(const PDUOption& opt) { - if(opt.data_size() != sizeof(T) * 2) + static std::vector convert(const PDUOption& opt) { + if(opt.data_size() % 4 != 0) throw malformed_option(); - const T *ptr = (const T*)opt.data_ptr(); - std::pair output; + const uint32_t *ptr = (const uint32_t*)opt.data_ptr(); + const uint32_t *end = (const uint32_t*)(opt.data_ptr() + opt.data_size()); + + std::vector output(std::distance(ptr, end)); + std::vector::iterator it = output.begin(); + while(ptr < end) { + if(PDUType::endianness == PDUType::BE) + *it++ = IPv4Address(*ptr++); + else + *it++ = Endian::change_endian(*ptr++); + } + return output; + } + }; + + template<> + struct converter > { + template + static std::vector convert(const PDUOption& opt) { + if(opt.data_size() % IPv6Address::address_size != 0) + throw malformed_option(); + const uint8_t *ptr = opt.data_ptr(), *end = opt.data_ptr() + opt.data_size(); + std::vector output; + while(ptr < end) { + output.push_back(IPv6Address(ptr)); + ptr += IPv6Address::address_size; + } + return output; + } + }; + + template + struct converter, typename enable_if::value>::type> { + template + static std::pair convert(const PDUOption& opt) { + if(opt.data_size() != sizeof(T) + sizeof(U)) + throw malformed_option(); + std::pair output; + output.first = *(const T*)opt.data_ptr(); + output.second = *(const U*)(opt.data_ptr() + sizeof(T)); if(PDUType::endianness == PDUType::BE) { - output.first = Endian::be_to_host(*ptr++); - output.second = Endian::be_to_host(*ptr); + output.first = Endian::be_to_host(output.first); + output.second = Endian::be_to_host(output.second); } else { - output.first = Endian::le_to_host(*ptr++); - output.second = Endian::le_to_host(*ptr); + output.first = Endian::le_to_host(output.first); + output.second = Endian::le_to_host(output.second); } return output; } diff --git a/include/tcp.h b/include/tcp.h index d822cd8..05e56b4 100644 --- a/include/tcp.h +++ b/include/tcp.h @@ -495,9 +495,6 @@ namespace Tins { 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();*/ } void internal_add_option(const option &option); diff --git a/src/icmpv6.cpp b/src/icmpv6.cpp index 4086c74..3d1afa4 100644 --- a/src/icmpv6.cpp +++ b/src/icmpv6.cpp @@ -305,9 +305,10 @@ void ICMPv6::redirect_header(PDU::serialization_type data) { add_option(option(REDIRECT_HEADER, data.begin(), data.end())); } -void ICMPv6::mtu(uint32_t value) { +void ICMPv6::mtu(const mtu_type& value) { uint8_t buffer[sizeof(uint16_t) + sizeof(uint32_t)] = {0}; - *((uint32_t*)(buffer + sizeof(uint16_t))) = Endian::host_to_be(value); + *(uint32_t*)buffer = Endian::host_to_be(value.first); + *(uint32_t*)(buffer + sizeof(uint16_t)) = Endian::host_to_be(value.second); add_option(option(MTU, sizeof(buffer), buffer)); } @@ -324,9 +325,12 @@ void ICMPv6::new_advert_interval(uint32_t value) { } void ICMPv6::new_home_agent_info(const new_ha_info_type &value) { + if(value.size() != 3) + throw malformed_option(); uint8_t buffer[sizeof(uint16_t) + sizeof(uint32_t)] = {0}; - *((uint16_t*)(buffer + sizeof(uint16_t))) = Endian::host_to_be(value.first); - *((uint16_t*)(buffer + sizeof(uint16_t) * 2)) = Endian::host_to_be(value.second); + *((uint16_t*)(buffer + sizeof(uint16_t))) = Endian::host_to_be(value[0]); + *((uint16_t*)(buffer + sizeof(uint16_t))) = Endian::host_to_be(value[1]); + *((uint16_t*)(buffer + sizeof(uint16_t) * 2)) = Endian::host_to_be(value[2]); add_option(option(HOME_AGENT_INFO, sizeof(buffer), buffer)); } @@ -339,10 +343,12 @@ void ICMPv6::target_addr_list(const addr_list_type &value) { } void ICMPv6::add_addr_list(uint8_t type, const addr_list_type &value) { + typedef addr_list_type::addresses_type::const_iterator iterator; + std::vector buffer; - buffer.reserve(value.size() + 6); - buffer.insert(buffer.end(), 6, 0); - for(addr_list_type::const_iterator it(value.begin()); it != value.end(); ++it) + buffer.reserve(value.addresses.size() + 6); + buffer.insert(buffer.end(), value.reserved, value.reserved + 6); + for(iterator it = value.addresses.begin(); it != value.addresses.end(); ++it) buffer.insert(buffer.end(), it->begin(), it->end()); add_option(option(type, buffer.begin(), buffer.end())); } @@ -394,8 +400,9 @@ void ICMPv6::link_layer_addr(lladdr_type value) { void ICMPv6::naack(const naack_type &value) { uint8_t buffer[6]; - buffer[0] = value.first; - buffer[1] = value.second; + buffer[0] = value.code; + buffer[1] = value.status; + std::copy(value.reserved, value.reserved + 4, buffer + 2); add_option(option(NAACK, buffer, buffer + sizeof(buffer))); } @@ -551,18 +558,9 @@ ICMPv6::hwaddress_type ICMPv6::target_link_layer_addr() const { ICMPv6::prefix_info_type ICMPv6::prefix_info() const { const option *opt = search_option(PREFIX_INFO); - if(!opt || opt->data_size() != 2 + sizeof(uint32_t) * 3 + ipaddress_type::address_size) + if(!opt) throw option_not_found(); - const uint8_t *ptr = opt->data_ptr(); - prefix_info_type output; - output.prefix_len = *ptr++; - output.L = (*ptr >> 7) & 0x1; - output.A = (*ptr++ >> 6) & 0x1; - output.valid_lifetime = Endian::be_to_host(*(uint32_t*)ptr); - ptr += sizeof(uint32_t); - output.preferred_lifetime = Endian::be_to_host(*(uint32_t*)ptr); - output.prefix = ptr + sizeof(uint32_t) * 2; - return output; + return opt->to(); } PDU::serialization_type ICMPv6::redirect_header() const { @@ -573,11 +571,11 @@ PDU::serialization_type ICMPv6::redirect_header() const { return serialization_type(ptr, ptr + opt->data_size() - 6); } -uint32_t ICMPv6::mtu() const { +ICMPv6::mtu_type ICMPv6::mtu() const { const option *opt = search_option(MTU); - if(!opt || opt->data_size() != sizeof(uint16_t) + sizeof(uint32_t)) + if(!opt) throw option_not_found(); - return Endian::be_to_host(*(const uint32_t*)(opt->data_ptr() + sizeof(uint16_t))); + return opt->to(); } uint8_t ICMPv6::shortcut_limit() const { @@ -596,12 +594,9 @@ uint32_t ICMPv6::new_advert_interval() const { ICMPv6::new_ha_info_type ICMPv6::new_home_agent_info() const { const option *opt = search_option(HOME_AGENT_INFO); - if(!opt || opt->data_size() != sizeof(uint16_t) + sizeof(uint32_t)) + if(!opt) throw option_not_found(); - return std::make_pair( - Endian::be_to_host(*(const uint16_t*)(opt->data_ptr() + sizeof(uint16_t))), - Endian::be_to_host(*(const uint16_t*)(opt->data_ptr() + sizeof(uint16_t) * 2)) - ); + return opt->to(); } ICMPv6::addr_list_type ICMPv6::source_addr_list() const { @@ -614,17 +609,9 @@ ICMPv6::addr_list_type ICMPv6::target_addr_list() const { ICMPv6::addr_list_type ICMPv6::search_addr_list(OptionTypes type) const { const option *opt = search_option(type); - if(!opt || opt->data_size() < 6 + ipaddress_type::address_size) + if(!opt) throw option_not_found(); - addr_list_type output; - const uint8_t *ptr = opt->data_ptr() + 6, *end = opt->data_ptr() + opt->data_size(); - while(ptr < end) { - if(ptr + ipaddress_type::address_size > end) - throw option_not_found(); - output.push_back(ipaddress_type(ptr)); - ptr += ipaddress_type::address_size; - } - return output; + return opt->to(); } ICMPv6::rsa_sign_type ICMPv6::rsa_signature() const { @@ -650,10 +637,10 @@ uint64_t ICMPv6::timestamp() const { } ICMPv6::nonce_type ICMPv6::nonce() const { - const option *opt = safe_search_option( - NONCE, 0 - ); - return nonce_type(opt->data_ptr(), opt->data_ptr() + opt->data_size()); + const option *opt = search_option(NONCE); + if(!opt) + throw option_not_found(); + return opt->to(); } ICMPv6::ip_prefix_type ICMPv6::ip_prefix() const { @@ -671,22 +658,17 @@ ICMPv6::ip_prefix_type ICMPv6::ip_prefix() const { } ICMPv6::lladdr_type ICMPv6::link_layer_addr() const { - // at least the option_code and 1 byte from the link layer address - const option *opt = safe_search_option( - LINK_ADDRESS, 2 - ); - const uint8_t *ptr = opt->data_ptr(); - lladdr_type output(*ptr++); - output.address.assign(ptr, opt->data_ptr() + opt->data_size()); - return output; + const option *opt = search_option(LINK_ADDRESS); + if(!opt) + throw option_not_found(); + return opt->to(); } ICMPv6::naack_type ICMPv6::naack() const { - const option *opt = safe_search_option( - NAACK, 6 - ); - const uint8_t *ptr = opt->data_ptr(); - return naack_type(ptr[0], ptr[1]); + const option *opt = search_option(NAACK); + if(!opt) + throw option_not_found(); + return opt->to(); } ICMPv6::map_type ICMPv6::map() const { @@ -815,5 +797,55 @@ ICMPv6::dns_search_list_type ICMPv6::dns_search_list() const { } return output; } + +// Options stuff + +ICMPv6::addr_list_type ICMPv6::addr_list_type::from_option(const option &opt) +{ + if(opt.data_size() < 6 + ipaddress_type::address_size || (opt.data_size() - 6) % ipaddress_type::address_size != 0) + throw malformed_option(); + addr_list_type output; + const uint8_t *ptr = opt.data_ptr(), *end = opt.data_ptr() + opt.data_size(); + std::copy(ptr, ptr + 6, output.reserved); + ptr += 6; + while(ptr < end) { + output.addresses.push_back(ICMPv6::ipaddress_type(ptr)); + ptr += ICMPv6::ipaddress_type::address_size; + } + return output; +} + +ICMPv6::naack_type ICMPv6::naack_type::from_option(const option &opt) +{ + if(opt.data_size() != 6) + throw malformed_option(); + return naack_type(*opt.data_ptr(), opt.data_ptr()[1]); +} + +ICMPv6::lladdr_type ICMPv6::lladdr_type::from_option(const option &opt) +{ + if(opt.data_size() < 2) + throw malformed_option(); + const uint8_t *ptr = opt.data_ptr(); + lladdr_type output(*ptr++); + output.address.assign(ptr, opt.data_ptr() + opt.data_size()); + return output; +} + +ICMPv6::prefix_info_type ICMPv6::prefix_info_type::from_option(const option &opt) +{ + if(opt.data_size() != 2 + sizeof(uint32_t) * 3 + ICMPv6::ipaddress_type::address_size) + throw malformed_option(); + const uint8_t *ptr = opt.data_ptr(); + prefix_info_type output; + output.prefix_len = *ptr++; + output.L = (*ptr >> 7) & 0x1; + output.A = (*ptr++ >> 6) & 0x1; + output.valid_lifetime = Endian::be_to_host(*(uint32_t*)ptr); + ptr += sizeof(uint32_t); + output.preferred_lifetime = Endian::be_to_host(*(uint32_t*)ptr); + output.prefix = ptr + sizeof(uint32_t) * 2; + return output; +} } diff --git a/src/ip.cpp b/src/ip.cpp index 7776395..d015e1b 100644 --- a/src/ip.cpp +++ b/src/ip.cpp @@ -271,40 +271,24 @@ void IP::add_route_option(option_identifier id, const generic_route_option_type } IP::generic_route_option_type IP::search_route_option(option_identifier id) const { - const option *option = search_option(id); - if(!option || option->data_size() < 1 + sizeof(uint32_t) || - ((option->data_size() - 1) % sizeof(uint32_t)) != 0) + const option *opt = search_option(id); + if(!opt) throw option_not_found(); - generic_route_option_type output; - output.pointer = *option->data_ptr(); - const uint32_t *route = (const uint32_t*)(option->data_ptr() + 1), - *end = route + (option->data_size() - 1) / sizeof(uint32_t); - while(route < end) - output.routes.push_back(address_type(*route++)); - return output; + return opt->to(); } IP::security_type IP::security() const { - const option *option = search_option(130); - if(!option || option->data_size() < 9) + const option *opt = search_option(130); + if(!opt) throw option_not_found(); - security_type output; - const uint16_t *ptr = reinterpret_cast(option->data_ptr()); - output.security = Endian::be_to_host(*ptr++); - output.compartments = Endian::be_to_host(*ptr++); - output.handling_restrictions = Endian::be_to_host(*ptr++); - uint32_t tcc = option->data_ptr()[6]; - tcc = (tcc << 8) | option->data_ptr()[7]; - tcc = (tcc << 8) | option->data_ptr()[8]; - output.transmission_control = tcc; - return output; + return opt->to(); } uint16_t IP::stream_identifier() const { - const option *option = search_option(136); - if(!option || option->data_size() != sizeof(uint16_t)) + const option *opt = search_option(136); + if(!opt) throw option_not_found(); - return Endian::be_to_host(*(const uint16_t*)option->data_ptr()); + return opt->to(); } void IP::add_option(const option &opt) { @@ -454,4 +438,36 @@ bool IP::matches_response(const uint8_t *ptr, uint32_t total_sz) const { } return false; } + +// Option static constructors from options + +IP::security_type IP::security_type::from_option(const option &opt) +{ + if(opt.data_size() != 9) + throw malformed_option(); + security_type output; + const uint16_t *ptr = reinterpret_cast(opt.data_ptr()); + output.security = Endian::be_to_host(*ptr++); + output.compartments = Endian::be_to_host(*ptr++); + output.handling_restrictions = Endian::be_to_host(*ptr++); + uint32_t tcc = opt.data_ptr()[6]; + tcc = (tcc << 8) | opt.data_ptr()[7]; + tcc = (tcc << 8) | opt.data_ptr()[8]; + output.transmission_control = tcc; + return output; +} + +IP::generic_route_option_type IP::generic_route_option_type::from_option( + const option &opt) +{ + if(opt.data_size() < 1 + sizeof(uint32_t) || ((opt.data_size() - 1) % sizeof(uint32_t)) != 0) + throw malformed_option(); + generic_route_option_type output; + output.pointer = *opt.data_ptr(); + const uint32_t *route = (const uint32_t*)(opt.data_ptr() + 1), + *end = route + (opt.data_size() - 1) / sizeof(uint32_t); + while(route < end) + output.routes.push_back(address_type(*route++)); + return output; +} } diff --git a/tests/src/icmpv6.cpp b/tests/src/icmpv6.cpp index a96493d..37b3824 100644 --- a/tests/src/icmpv6.cpp +++ b/tests/src/icmpv6.cpp @@ -215,8 +215,9 @@ TEST_F(ICMPv6Test, RedirectHeader) { TEST_F(ICMPv6Test, MTU) { ICMPv6 icmp; - icmp.mtu(0x9a8df7); - EXPECT_EQ(icmp.mtu(), 0x9a8df7U); + ICMPv6::mtu_type data(0, 0x9a8df7); + icmp.mtu(data); + EXPECT_EQ(data, icmp.mtu()); } TEST_F(ICMPv6Test, ShortcutLimit) { @@ -233,7 +234,10 @@ TEST_F(ICMPv6Test, NewAdvertisementInterval) { TEST_F(ICMPv6Test, NewHomeAgentInformation) { ICMPv6 icmp; - ICMPv6::new_ha_info_type data(0x92fa, 0xaab3); + ICMPv6::new_ha_info_type data; + data.push_back(0); + data.push_back(0x92fa); + data.push_back(0xaab3); icmp.new_home_agent_info(data); EXPECT_EQ(icmp.new_home_agent_info(), data); } @@ -241,19 +245,19 @@ TEST_F(ICMPv6Test, NewHomeAgentInformation) { TEST_F(ICMPv6Test, SourceAddressList) { ICMPv6 icmp; ICMPv6::addr_list_type data; - data.push_back("827d:adae::1"); - data.push_back("2929:1234:fefe::2"); + data.addresses.push_back("827d:adae::1"); + data.addresses.push_back("2929:1234:fefe::2"); icmp.source_addr_list(data); - EXPECT_EQ(icmp.source_addr_list(), data); + EXPECT_EQ(icmp.source_addr_list().addresses, data.addresses); } TEST_F(ICMPv6Test, TargetAddressList) { ICMPv6 icmp; ICMPv6::addr_list_type data; - data.push_back("827d:adae::1"); - data.push_back("2929:1234:fefe::2"); + data.addresses.push_back("827d:adae::1"); + data.addresses.push_back("2929:1234:fefe::2"); icmp.target_addr_list(data); - EXPECT_EQ(icmp.target_addr_list(), data); + EXPECT_EQ(icmp.target_addr_list().addresses, data.addresses); } TEST_F(ICMPv6Test, RSASignature) { @@ -316,9 +320,11 @@ TEST_F(ICMPv6Test, LinkLayerAddress) { TEST_F(ICMPv6Test, NAACK) { ICMPv6 icmp; - ICMPv6::naack_type data(0x92, 0xb3); + ICMPv6::naack_type data(0x92, 0xb3), result; icmp.naack(data); - EXPECT_EQ(icmp.naack(), data); + result = icmp.naack(); + EXPECT_EQ(result.code, data.code); + EXPECT_EQ(result.status, data.status); } TEST_F(ICMPv6Test, MAP) {