/* * 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 #include #include #include using std::vector; using std::memcpy; using std::equal; using Tins::Memory::InputMemoryStream; using Tins::Memory::OutputMemoryStream; namespace Tins { namespace Internals { template void class_option_data2option(InputIterator start, InputIterator end, vector& buffer, size_t start_index = 0) { size_t index = start_index; uint16_t uint16_t_buffer; while (start != end) { buffer.resize(buffer.size() + sizeof(uint16_t) + start->size()); uint16_t_buffer = Endian::host_to_be(static_cast(start->size())); memcpy(&buffer[index], &uint16_t_buffer, sizeof(uint16_t)); index += sizeof(uint16_t); if (!start->empty()) { memcpy(&*buffer.begin() + index, &*start->begin(), start->size()); } index += start->size(); start++; } } template OutputType option2class_option_data(const uint8_t* ptr, uint32_t total_sz) { typedef typename OutputType::value_type value_type; OutputType output; size_t index = 0; while (index + 2 < total_sz) { uint16_t size; memcpy(&size, ptr + index, sizeof(uint16_t)); size = Endian::be_to_host(size); index += sizeof(uint16_t); if (index + size > total_sz) { throw option_not_found(); } output.push_back( value_type(ptr + index, ptr + index + size) ); index += size; } if (index != total_sz) { throw malformed_option(); } return output; } } // Internals PDU::metadata DHCPv6::extract_metadata(const uint8_t* /*buffer*/, uint32_t total_sz) { if (TINS_UNLIKELY(total_sz < 2)) { throw malformed_packet(); } return metadata(total_sz, pdu_flag, PDU::UNKNOWN); } DHCPv6::DHCPv6() : header_data_(), options_size_() { } DHCPv6::DHCPv6(const uint8_t* buffer, uint32_t total_sz) : options_size_() { InputMemoryStream stream(buffer, total_sz); if (!stream) { throw malformed_packet(); } // Relay Agent/Server Messages const MessageType message_type = (MessageType)*stream.pointer(); bool is_relay_msg = (message_type == RELAY_FORWARD || message_type == RELAY_REPLY); uint32_t required_size = is_relay_msg ? 2 : 4; stream.read(&header_data_, required_size); if (is_relay_message()) { stream.read(link_addr_); stream.read(peer_addr_); } while (stream) { uint16_t opt = stream.read_be(); uint16_t data_size = stream.read_be(); if (!stream.can_read(data_size)) { throw malformed_packet(); } add_option(option(opt, stream.pointer(), stream.pointer() + data_size)); stream.skip(data_size); } } void DHCPv6::add_option(const option& opt) { options_.push_back(opt); options_size_ += opt.data_size() + sizeof(uint16_t) * 2; } bool DHCPv6::remove_option(OptionTypes type) { options_type::iterator iter = search_option_iterator(type); if (iter == options_.end()) { return false; } options_size_ -= iter->data_size() + sizeof(uint16_t) * 2; options_.erase(iter); return true; } const DHCPv6::option* DHCPv6::search_option(OptionTypes type) const { // Search for the iterator. If we found something, return it, otherwise return nullptr. options_type::const_iterator iter = search_option_iterator(type); return (iter != options_.end()) ? &*iter : 0; } DHCPv6::options_type::const_iterator DHCPv6::search_option_iterator(OptionTypes type) const { return Internals::find_option_const