diff --git a/include/tins/dhcpv6.h b/include/tins/dhcpv6.h index 0226ace..d30dbdc 100644 --- a/include/tins/dhcpv6.h +++ b/include/tins/dhcpv6.h @@ -38,7 +38,11 @@ #include "ipv6_address.h" #include "pdu_option.h" -namespace Tins { +namespace Tins { +namespace Memory { +class OutputMemoryStream; +} // Memory + /** * \class DHCPv6 * \brief Represents a DHCPv6 PDU. @@ -869,7 +873,7 @@ public: } private: void write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *); - uint8_t* write_option(const option &option, uint8_t* buffer) const; + void write_option(const option &option, Memory::OutputMemoryStream& stream) const; options_type::const_iterator search_option_iterator(OptionTypes type) const; options_type::iterator search_option_iterator(OptionTypes type); diff --git a/include/tins/eapol.h b/include/tins/eapol.h index 38b427b..a80a941 100644 --- a/include/tins/eapol.h +++ b/include/tins/eapol.h @@ -38,7 +38,10 @@ namespace Tins { - +namespace Memory { +class OutputMemoryStream; +} // Memory + /** \cond * Forward declaration. Avoid header inclusion. */ @@ -163,7 +166,7 @@ namespace Tins { * \param buffer The pointer in which to save the serialization. * \param total_sz The total size of the buffer. */ - virtual void write_body(uint8_t *buffer, uint32_t total_sz) = 0; + virtual void write_body(Memory::OutputMemoryStream& stream) = 0; private: /** * \brief Serialices this EAPOL PDU. @@ -353,7 +356,7 @@ namespace Tins { uint8_t key_sign[16]; } TINS_END_PACK; - void write_body(uint8_t *buffer, uint32_t total_sz); + void write_body(Memory::OutputMemoryStream& stream); key_type _key; @@ -731,7 +734,7 @@ namespace Tins { #endif } TINS_END_PACK; - void write_body(uint8_t *buffer, uint32_t total_sz); + void write_body(Memory::OutputMemoryStream& stream); rsnhdr _header; diff --git a/include/tins/exceptions.h b/include/tins/exceptions.h index d23b00a..413423a 100644 --- a/include/tins/exceptions.h +++ b/include/tins/exceptions.h @@ -49,7 +49,7 @@ public: /** * \brief Exception thrown when an option is not found. */ -class option_not_found : exception_base { +class option_not_found : public exception_base { public: // try to avoid allocations by doing this. const char* what() const throw() { @@ -67,6 +67,16 @@ public: } }; +/** + * \brief Exception thrown when serializing a packet fails. + */ +class serialization_error : public exception_base { +public: + const char* what() const throw() { + return "Serialization error"; + } +}; + /** * \brief Exception thrown when a PDU is not found when using PDU::rfind_pdu. */ diff --git a/include/tins/icmpv6.h b/include/tins/icmpv6.h index c7d24d8..b2727bb 100644 --- a/include/tins/icmpv6.h +++ b/include/tins/icmpv6.h @@ -46,6 +46,7 @@ namespace Tins { namespace Memory { class InputMemoryStream; +class OutputMemoryStream; } // memory /** @@ -1379,7 +1380,7 @@ private: void internal_add_option(const option &option); void write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent); bool has_options() const; - uint8_t *write_option(const option &opt, uint8_t *buffer); + void write_option(const option &opt, Memory::OutputMemoryStream& stream); void parse_options(Memory::InputMemoryStream& stream); void add_addr_list(uint8_t type, const addr_list_type &value); addr_list_type search_addr_list(OptionTypes type) const; diff --git a/include/tins/ip.h b/include/tins/ip.h index c33029e..b249553 100644 --- a/include/tins/ip.h +++ b/include/tins/ip.h @@ -40,6 +40,9 @@ #include "cxxstd.h" namespace Tins { +namespace Memory { +class OutputMemoryStream; +} // Memory /** * \class IP @@ -725,7 +728,7 @@ namespace Tins { void internal_add_option(const option &option); void init_ip_fields(); void write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent); - uint8_t* write_option(const option &opt, uint8_t* buffer); + void write_option(const option &opt, Memory::OutputMemoryStream& stream); void add_route_option(option_identifier id, const generic_route_option_type &data); generic_route_option_type search_route_option(option_identifier id) const; void checksum(uint16_t new_check); diff --git a/include/tins/ipv6.h b/include/tins/ipv6.h index ec0b4fc..ca85aa1 100644 --- a/include/tins/ipv6.h +++ b/include/tins/ipv6.h @@ -40,6 +40,10 @@ #include "ipv6_address.h" namespace Tins { +namespace Memory { +class OutputMemoryStream; +} // Memory + class PacketSender; /** @@ -299,7 +303,7 @@ public: private: void write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent); void set_last_next_header(uint8_t value); - static uint8_t *write_header(const ext_header &header, uint8_t *buffer); + static void write_header(const ext_header &header, Memory::OutputMemoryStream& stream); static bool is_extension_header(uint8_t header_id); TINS_BEGIN_PACK diff --git a/include/tins/llc.h b/include/tins/llc.h index 66228ac..04def9e 100644 --- a/include/tins/llc.h +++ b/include/tins/llc.h @@ -383,6 +383,7 @@ namespace Tins { #endif typedef std::vector field_type; + typedef std::list field_list; void write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent); @@ -395,7 +396,7 @@ namespace Tins { } control_field; Format _type; uint8_t information_field_length; - std::list information_fields; + field_list information_fields; }; } diff --git a/include/tins/memory_helpers.h b/include/tins/memory_helpers.h index 8a9a5f1..a9428db 100644 --- a/include/tins/memory_helpers.h +++ b/include/tins/memory_helpers.h @@ -3,6 +3,7 @@ #include #include +#include #include "exceptions.h" #include "ip_address.h" #include "ipv6_address.h" @@ -20,6 +21,10 @@ void read_value(const uint8_t* buffer, T& value) { std::memcpy(&value, buffer, sizeof(value)); } +inline void write_data(uint8_t* buffer, const uint8_t* ptr, uint32_t size) { + std::memcpy(buffer, ptr, size); +} + template void write_value(uint8_t* buffer, const T& value) { std::memcpy(buffer, &value, sizeof(value)); @@ -105,6 +110,73 @@ private: uint32_t size_; }; +class OutputMemoryStream { +public: + OutputMemoryStream(uint8_t* buffer, uint32_t total_sz) + : buffer_(buffer), size_(total_sz) { + } + + void skip(uint32_t size) { + buffer_ += size; + size_ -= size; + } + + template + void write(const T& value) { + if (TINS_UNLIKELY(size_ < sizeof(value))) { + throw serialization_error(); + } + write_value(buffer_, value); + skip(sizeof(value)); + } + + template + void write(ForwardIterator start, ForwardIterator end) { + const uint32_t length = std::distance(start, end); + if (TINS_UNLIKELY(size_ < length)) { + throw serialization_error(); + } + std::copy(start, end, buffer_); + skip(length); + } + + void write(const uint8_t* ptr, uint32_t length) { + write(ptr, ptr + length); + } + + void write(const IPv4Address& address) { + write(static_cast(address)); + } + + void write(const IPv6Address& address) { + write(address.begin(), address.end()); + } + + template + void write(const HWAddress& address) { + write(address.begin(), address.end()); + } + + void fill(uint32_t size, uint8_t value) { + if (TINS_UNLIKELY(size_ < size)) { + throw serialization_error(); + } + std::fill(buffer_, buffer_ + size, value); + skip(size); + } + + uint8_t* pointer() { + return buffer_; + } + + uint32_t size() const { + return size_; + } +private: + uint8_t* buffer_; + uint32_t size_; +}; + } // Memory } // Tins diff --git a/include/tins/tcp.h b/include/tins/tcp.h index 20a20db..a2b4a4d 100644 --- a/include/tins/tcp.h +++ b/include/tins/tcp.h @@ -44,6 +44,10 @@ #include "cxxstd.h" namespace Tins { +namespace Memory { +class OutputMemoryStream; +} // Memory + /** * \class TCP * \brief Represents a TCP PDU. @@ -583,7 +587,7 @@ namespace Tins { options_type::const_iterator search_option_iterator(OptionTypes type) const; options_type::iterator search_option_iterator(OptionTypes type); - uint8_t *write_option(const option &opt, uint8_t *buffer); + void write_option(const option &opt, Memory::OutputMemoryStream& stream); tcphdr _tcp; uint16_t _options_size, _total_options_size; diff --git a/src/arp.cpp b/src/arp.cpp index fe9f54f..c6da88c 100644 --- a/src/arp.cpp +++ b/src/arp.cpp @@ -28,7 +28,6 @@ */ #include -#include #include #include "arp.h" #include "ip.h" @@ -42,6 +41,7 @@ using std::runtime_error; using Tins::Memory::InputMemoryStream; +using Tins::Memory::OutputMemoryStream; namespace Tins { @@ -109,10 +109,8 @@ uint32_t ARP::header_size() const { } void ARP::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *) { - #ifdef TINS_DEBUG - assert(total_sz >= sizeof(arphdr)); - #endif - memcpy(buffer, &_arp, sizeof(arphdr)); + OutputMemoryStream stream(buffer, total_sz); + stream.write(_arp); } bool ARP::matches_response(const uint8_t *ptr, uint32_t total_sz) const { diff --git a/src/bootp.cpp b/src/bootp.cpp index 10d31c9..d2a0d24 100644 --- a/src/bootp.cpp +++ b/src/bootp.cpp @@ -29,12 +29,12 @@ #include #include -#include #include "bootp.h" #include "exceptions.h" #include "memory_helpers.h" using Tins::Memory::InputMemoryStream; +using Tins::Memory::OutputMemoryStream; namespace Tins{ @@ -116,11 +116,9 @@ void BootP::vend(const vend_type &new_vend) { } void BootP::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent) { - #ifdef TINS_DEBUG - assert(total_sz >= sizeof(bootphdr) + _vend.size()); - #endif - std::memcpy(buffer, &_bootp, sizeof(bootphdr)); - std::copy(_vend.begin(), _vend.end(), buffer + sizeof(bootphdr)); + OutputMemoryStream stream(buffer, total_sz); + stream.write(_bootp); + stream.write(_vend.begin(), _vend.end()); } bool BootP::matches_response(const uint8_t *ptr, uint32_t total_sz) const { diff --git a/src/dhcp.cpp b/src/dhcp.cpp index 934cc86..9377d31 100644 --- a/src/dhcp.cpp +++ b/src/dhcp.cpp @@ -28,7 +28,6 @@ */ #include -#include #include #include "endianness.h" #include "dhcp.h" @@ -43,6 +42,7 @@ using std::runtime_error; using std::find_if; using Tins::Memory::InputMemoryStream; +using Tins::Memory::OutputMemoryStream; namespace Tins { @@ -57,8 +57,8 @@ DHCP::DHCP() DHCP::DHCP(const uint8_t *buffer, uint32_t total_sz) : BootP(buffer, total_sz, 0), _size(sizeof(uint32_t)) { - const uint32_t bootp_size = BootP::header_size() - vend().size(); - InputMemoryStream stream(buffer + bootp_size, total_sz - bootp_size); + InputMemoryStream stream(buffer, total_sz); + stream.skip(BootP::header_size() - vend().size()); const uint32_t magic_number = stream.read(); if (magic_number != Endian::host_to_be(0x63825363)) throw malformed_packet(); @@ -238,22 +238,19 @@ uint32_t DHCP::header_size() const { } void DHCP::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent) { - #ifdef TINS_DEBUG - assert(total_sz >= header_size()); - #endif - if(_size) { - vend_type &result(BootP::vend()); + if (_size) { + vend_type &result = BootP::vend(); result.resize(_size); - uint8_t *ptr = &result[0] + sizeof(uint32_t); + // Build a stream over the vend vector + OutputMemoryStream stream(&result[0], result.size()); // Magic cookie - *((uint32_t*)&result[0]) = Endian::host_to_be(0x63825363); - for(options_type::const_iterator it = _options.begin(); it != _options.end(); ++it) { - *(ptr++) = it->option(); - *(ptr++) = static_cast(it->length_field()); - std::copy(it->data_ptr(), it->data_ptr() + it->data_size(), ptr); - ptr += it->data_size(); + stream.write(Endian::host_to_be(0x63825363)); + for (options_type::const_iterator it = _options.begin(); it != _options.end(); ++it) { + stream.write(it->option()); + stream.write(it->length_field()); + stream.write(it->data_ptr(), it->data_size()); } } BootP::write_serialization(buffer, total_sz, parent); } -} +} // Tins diff --git a/src/dhcpv6.cpp b/src/dhcpv6.cpp index 9b22276..af069e0 100644 --- a/src/dhcpv6.cpp +++ b/src/dhcpv6.cpp @@ -36,6 +36,7 @@ using std::find_if; using Tins::Memory::InputMemoryStream; +using Tins::Memory::OutputMemoryStream; namespace Tins { @@ -109,16 +110,10 @@ DHCPv6::options_type::iterator DHCPv6::search_option_iterator(OptionTypes type) return find_if(options_.begin(), options_.end(), comparator); } -uint8_t* DHCPv6::write_option(const option &opt, uint8_t* buffer) const { - uint16_t uint16_t_buffer = Endian::host_to_be(opt.option()); - std::memcpy(buffer, &uint16_t_buffer, sizeof(uint16_t)); - uint16_t_buffer = Endian::host_to_be(static_cast(opt.length_field())); - std::memcpy(&buffer[sizeof(uint16_t)], &uint16_t_buffer, sizeof(uint16_t)); - return std::copy( - opt.data_ptr(), - opt.data_ptr() + opt.data_size(), - buffer + sizeof(uint16_t) * 2 - ); +void DHCPv6::write_option(const option &opt, OutputMemoryStream& stream) const { + stream.write(Endian::host_to_be(opt.option())); + stream.write(Endian::host_to_be(opt.length_field())); + stream.write(opt.data_ptr(), opt.data_size()); } void DHCPv6::msg_type(MessageType type) { @@ -163,13 +158,14 @@ bool DHCPv6::matches_response(const uint8_t *ptr, uint32_t total_sz) const { void DHCPv6::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *) { const uint32_t required_size = is_relay_message() ? 2 : 4; - buffer = std::copy(header_data, header_data + required_size, buffer); - if(is_relay_message()) { - buffer = link_addr.copy(buffer); - buffer = peer_addr.copy(buffer); + OutputMemoryStream stream(buffer, total_sz); + stream.write(header_data, required_size); + if (is_relay_message()) { + stream.write(link_addr); + stream.write(peer_addr); } - for(options_type::const_iterator it = options_.begin(); it != options_.end(); ++it) { - buffer = write_option(*it, buffer); + for (options_type::const_iterator it = options_.begin(); it != options_.end(); ++it) { + write_option(*it, stream); } } diff --git a/src/dns.cpp b/src/dns.cpp index 780f0d5..33b1183 100644 --- a/src/dns.cpp +++ b/src/dns.cpp @@ -29,7 +29,6 @@ #include #include -#include #include #include #include @@ -45,6 +44,7 @@ using std::string; using std::list; using Tins::Memory::InputMemoryStream; +using Tins::Memory::OutputMemoryStream; namespace Tins { @@ -361,12 +361,9 @@ const uint8_t* DNS::compose_name(const uint8_t *ptr, char *out_ptr) const { } void DNS::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent) { - #ifdef TINS_DEBUG - assert(total_sz >= sizeof(dns) + records_data.size());//extra_size); - #endif - std::memcpy(buffer, &dns, sizeof(dns)); - buffer += sizeof(dns); - std::copy(records_data.begin(), records_data.end(), buffer); + OutputMemoryStream stream(buffer, total_sz); + stream.write(dns); + stream.write(records_data.begin(), records_data.end()); } // Optimization. Creating an IPv4Address and then using IPv4Address::to_string diff --git a/src/dot1q.cpp b/src/dot1q.cpp index 6fc310e..9eba9ff 100644 --- a/src/dot1q.cpp +++ b/src/dot1q.cpp @@ -29,13 +29,13 @@ #include #include -#include #include "dot1q.h" #include "internals.h" #include "exceptions.h" #include "memory_helpers.h" using Tins::Memory::InputMemoryStream; +using Tins::Memory::OutputMemoryStream; namespace Tins { @@ -88,33 +88,35 @@ uint32_t Dot1Q::header_size() const { } uint32_t Dot1Q::trailer_size() const { - if(_append_padding) { + if (_append_padding) { uint32_t total_size = sizeof(_header); - if(inner_pdu()) + if (inner_pdu()) { total_size += inner_pdu()->size(); + } return (total_size > 50) ? 0 : (50 - total_size); } - else + else { return 0; + } } void Dot1Q::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *) { - uint32_t trailer = trailer_size(); - #ifdef TINS_DEBUG - assert(total_sz >= sizeof(_header) + trailer); - #endif + OutputMemoryStream stream(buffer, total_sz); if (inner_pdu()) { + // Set the appropriate payload type flag Constants::Ethernet::e flag = Internals::pdu_flag_to_ether_type( inner_pdu()->pdu_type() ); payload_type(static_cast(flag)); } - std::memcpy(buffer, &_header, sizeof(_header)); - - buffer += sizeof(_header); - if(inner_pdu()) - buffer += inner_pdu()->size(); - std::fill(buffer, buffer + trailer, 0); + stream.write(_header); + + // Skip inner PDU size + if (inner_pdu()) { + stream.skip(inner_pdu()->size()); + } + // Write trailer + stream.fill(trailer_size(), 0); } #if TINS_IS_LITTLE_ENDIAN diff --git a/src/dot3.cpp b/src/dot3.cpp index a4f8c82..472565d 100644 --- a/src/dot3.cpp +++ b/src/dot3.cpp @@ -50,6 +50,7 @@ #include "memory_helpers.h" using Tins::Memory::InputMemoryStream; +using Tins::Memory::OutputMemoryStream; namespace Tins { @@ -129,12 +130,9 @@ bool Dot3::matches_response(const uint8_t *ptr, uint32_t total_sz) const { } void Dot3::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent) { - #ifdef TINS_DEBUG - assert(total_sz >= header_size()); - #endif - _eth.length = Endian::host_to_be(static_cast(size() - sizeof(_eth))); - - memcpy(buffer, &_eth, sizeof(ethhdr)); + OutputMemoryStream stream(buffer, total_sz); + _eth.length = Endian::host_to_be(size() - sizeof(_eth)); + stream.write(_eth); } #ifndef _WIN32 diff --git a/src/eapol.cpp b/src/eapol.cpp index e4c00c8..a3d89f6 100644 --- a/src/eapol.cpp +++ b/src/eapol.cpp @@ -39,6 +39,7 @@ #include "memory_helpers.h" using Tins::Memory::InputMemoryStream; +using Tins::Memory::OutputMemoryStream; namespace Tins { @@ -96,11 +97,10 @@ void EAPOL::type(uint8_t new_type) { } void EAPOL::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *) { - #ifdef TINS_DEBUG - assert(total_sz >= header_size()); - #endif + OutputMemoryStream stream(buffer, total_sz); + stream.write(_header); std::memcpy(buffer, &_header, sizeof(_header)); - write_body(buffer + sizeof(_header), total_sz - sizeof(_header)); + write_body(stream); } /* RC4EAPOL */ @@ -157,16 +157,12 @@ uint32_t RC4EAPOL::header_size() const { return static_cast(sizeof(eapolhdr) + sizeof(_header) + _key.size()); } -void RC4EAPOL::write_body(uint8_t *buffer, uint32_t total_sz) { - #ifdef TINS_DEBUG - assert(total_sz >= sizeof(_header) + _key.size()); - #endif - if(_key.size()) { +void RC4EAPOL::write_body(OutputMemoryStream& stream) { + if (_key.size()) { _header.key_length = Endian::host_to_be(static_cast(_key.size())); } - std::memcpy(buffer, &_header, sizeof(_header)); - buffer += sizeof(_header); - std::copy(_key.begin(), _key.end(), buffer); + stream.write(_header); + stream.write(_key.begin(), _key.end()); } /* RSNEAPOL */ @@ -273,21 +269,17 @@ uint32_t RSNEAPOL::header_size() const { return static_cast(sizeof(eapolhdr) + sizeof(_header) + _key.size()); } -void RSNEAPOL::write_body(uint8_t *buffer, uint32_t total_sz) { - #ifdef TINS_DEBUG - assert(total_sz >= header_size() - sizeof(eapolhdr)); - #endif - if(_key.size()) { - if(!_header.key_t) { +void RSNEAPOL::write_body(OutputMemoryStream& stream) { + if (_key.size()) { + if (!_header.key_t) { _header.key_length = Endian::host_to_be(32); wpa_length(static_cast(_key.size())); } - else if(_key.size()) { + else if (_key.size()) { wpa_length(static_cast(_key.size())); } } - std::memcpy(buffer, &_header, sizeof(_header)); - buffer += sizeof(_header); - std::copy(_key.begin(), _key.end(), buffer); + stream.write(_header); + stream.write(_key.begin(), _key.end()); } } diff --git a/src/ethernetII.cpp b/src/ethernetII.cpp index 152016c..4b27b15 100644 --- a/src/ethernetII.cpp +++ b/src/ethernetII.cpp @@ -56,6 +56,7 @@ #include "memory_helpers.h" using Tins::Memory::InputMemoryStream; +using Tins::Memory::OutputMemoryStream; namespace Tins { @@ -152,11 +153,7 @@ bool EthernetII::matches_response(const uint8_t *ptr, uint32_t total_sz) const { } void EthernetII::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent) { - #ifdef TINS_DEBUG - assert(total_sz >= header_size() + trailer_size()); - #endif - - /* Inner type defaults to IP */ + OutputMemoryStream stream(buffer, total_sz); if (inner_pdu()) { Constants::Ethernet::e flag = Internals::pdu_flag_to_ether_type( inner_pdu()->pdu_type() @@ -165,13 +162,13 @@ void EthernetII::write_serialization(uint8_t *buffer, uint32_t total_sz, const P payload_type(static_cast(flag)); } } - memcpy(buffer, &_eth, sizeof(ethhdr)); - uint32_t trailer = trailer_size(); + stream.write(_eth); + const uint32_t trailer = trailer_size(); if (trailer) { - uint32_t trailer_offset = header_size(); - if (inner_pdu()) - trailer_offset += inner_pdu()->size(); - memset(buffer + trailer_offset, 0, trailer); + if (inner_pdu()) { + stream.skip(inner_pdu()->size()); + } + stream.fill(trailer, 0); } } diff --git a/src/icmp.cpp b/src/icmp.cpp index 2842395..3f7fba6 100644 --- a/src/icmp.cpp +++ b/src/icmp.cpp @@ -42,6 +42,7 @@ #include "memory_helpers.h" using Tins::Memory::InputMemoryStream; +using Tins::Memory::OutputMemoryStream; namespace Tins { @@ -207,23 +208,7 @@ void ICMP::use_length_field(bool value) { } void ICMP::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *) { - #ifdef TINS_DEBUG - assert(total_sz >= sizeof(icmphdr)); - #endif - - uint32_t uint32_t_buffer; - if(type() == TIMESTAMP_REQUEST || type() == TIMESTAMP_REPLY) { - uint32_t_buffer = original_timestamp(); - memcpy(buffer + sizeof(icmphdr), &uint32_t_buffer, sizeof(uint32_t)); - uint32_t_buffer = receive_timestamp(); - memcpy(buffer + sizeof(icmphdr) + sizeof(uint32_t), &uint32_t_buffer, sizeof(uint32_t)); - uint32_t_buffer = transmit_timestamp(); - memcpy(buffer + sizeof(icmphdr) + 2 * sizeof(uint32_t), &uint32_t_buffer, sizeof(uint32_t)); - } - else if(type() == ADDRESS_MASK_REQUEST || type() == ADDRESS_MASK_REPLY) { - uint32_t_buffer = address_mask(); - memcpy(buffer + sizeof(icmphdr), &uint32_t_buffer, sizeof(uint32_t)); - } + OutputMemoryStream stream(buffer, total_sz); // If extensions are allowed and we have to set the length field if (are_extensions_allowed()) { @@ -236,6 +221,19 @@ void ICMP::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *) } } + // Write the header using checksum 0 + _icmp.check = 0; + stream.write(_icmp); + + if(type() == TIMESTAMP_REQUEST || type() == TIMESTAMP_REPLY) { + stream.write(original_timestamp()); + stream.write(receive_timestamp()); + stream.write(transmit_timestamp()); + } + else if(type() == ADDRESS_MASK_REQUEST || type() == ADDRESS_MASK_REPLY) { + stream.write(address_mask()); + } + if (has_extensions()) { uint8_t* extensions_ptr = buffer + sizeof(icmphdr); if (inner_pdu()) { @@ -258,14 +256,12 @@ void ICMP::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *) extensions_.serialize(extensions_ptr, total_sz - (extensions_ptr - buffer)); } - // checksum calc - _icmp.check = 0; - memcpy(buffer, &_icmp, sizeof(icmphdr)); + // Calculate checksum uint32_t checksum = Utils::do_checksum(buffer, buffer + total_sz); - - while (checksum >> 16) + while (checksum >> 16) { checksum = (checksum & 0xffff) + (checksum >> 16); - + } + // Write back only the 2 checksum bytes _icmp.check = Endian::host_to_be(~checksum); memcpy(buffer + 2, &_icmp.check, sizeof(uint16_t)); } diff --git a/src/icmp_extension.cpp b/src/icmp_extension.cpp index 38a7cdd..64ba093 100644 --- a/src/icmp_extension.cpp +++ b/src/icmp_extension.cpp @@ -8,6 +8,7 @@ using std::runtime_error; using Tins::Memory::InputMemoryStream; +using Tins::Memory::OutputMemoryStream; namespace Tins { @@ -57,16 +58,11 @@ uint32_t ICMPExtension::size() const { } void ICMPExtension::serialize(uint8_t* buffer, uint32_t buffer_size) const { - if (buffer_size < size()) { - throw runtime_error("Serialization buffer is too small"); - } - *(uint16_t*)buffer = Endian::host_to_be(size()); - buffer += sizeof(uint16_t); - *buffer = extension_class_; - buffer += sizeof(uint8_t); - *buffer = extension_type_; - buffer += sizeof(uint8_t); - copy(payload_.begin(), payload_.end(), buffer); + OutputMemoryStream stream(buffer, buffer_size); + stream.write(Endian::host_to_be(size())); + stream.write(extension_class_); + stream.write(extension_type_); + stream.write(payload_.begin(), payload_.end()); } ICMPExtension::serialization_type ICMPExtension::serialize() const { @@ -140,25 +136,18 @@ void ICMPExtensionsStructure::add_extension(const ICMPExtension& extension) { } void ICMPExtensionsStructure::serialize(uint8_t* buffer, uint32_t buffer_size) { - const uint32_t structure_size = size(); - if (buffer_size < structure_size) { - throw malformed_packet(); - } + OutputMemoryStream stream(buffer, buffer_size); uint8_t* original_ptr = buffer; - memcpy(buffer, &version_and_reserved_, sizeof(version_and_reserved_)); - buffer += sizeof(uint16_t); + stream.write(version_and_reserved_); // Make checksum 0, for now, we'll compute it at the end - memset(buffer, 0, sizeof(uint16_t)); - buffer += sizeof(uint16_t); - buffer_size -= BASE_HEADER_SIZE; + stream.write(0); typedef extensions_type::const_iterator iterator; for (iterator iter = extensions_.begin(); iter != extensions_.end(); ++iter) { - iter->serialize(buffer, buffer_size); - buffer += iter->size(); - buffer_size -= iter->size(); + iter->serialize(stream.pointer(), stream.size()); + stream.skip(iter->size()); } - uint16_t checksum = ~Utils::sum_range(original_ptr, original_ptr + structure_size); + uint16_t checksum = ~Utils::sum_range(original_ptr, original_ptr + size()); memcpy(original_ptr + sizeof(uint16_t), &checksum, sizeof(checksum)); checksum_ = checksum; } diff --git a/src/icmpv6.cpp b/src/icmpv6.cpp index 22a50bc..bf8e806 100644 --- a/src/icmpv6.cpp +++ b/src/icmpv6.cpp @@ -40,6 +40,7 @@ #include "memory_helpers.h" using Tins::Memory::InputMemoryStream; +using Tins::Memory::OutputMemoryStream; namespace Tins { @@ -213,12 +214,7 @@ bool ICMPv6::matches_response(const uint8_t *ptr, uint32_t total_sz) const { } void ICMPv6::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent) { - #ifdef TINS_DEBUG - assert(total_sz >= header_size()); - #endif - uint32_t full_sz = total_sz; - uint8_t *buffer_start = buffer; - _header.cksum = 0; + OutputMemoryStream stream(buffer, total_sz); // If extensions are allowed and we have to set the length field if (are_extensions_allowed()) { @@ -230,36 +226,26 @@ void ICMPv6::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU * _header.rfc4884.length = length_value / sizeof(uint64_t); } } + // Initially set checksum to 0, we'll calculate it at the end + _header.cksum = 0; + stream.write(_header); - std::memcpy(buffer, &_header, sizeof(_header)); - buffer += sizeof(_header); - total_sz -= sizeof(_header); - if(has_target_addr()) { - buffer = _target_address.copy(buffer); - total_sz -= sizeof(ipaddress_type::address_size); + if (has_target_addr()) { + stream.write(_target_address); } - if(has_dest_addr()) { - buffer = _dest_address.copy(buffer); - total_sz -= sizeof(ipaddress_type::address_size); + if (has_dest_addr()) { + stream.write(_dest_address); } - if(type() == ROUTER_ADVERT) { - std::memcpy(buffer, &reach_time, sizeof(uint32_t)); - buffer += sizeof(uint32_t); - std::memcpy(buffer, &retrans_timer, sizeof(uint32_t)); - buffer += sizeof(uint32_t); - total_sz -= sizeof(uint32_t) * 2; + if (type() == ROUTER_ADVERT) { + stream.write(reach_time); + stream.write(retrans_timer); } for(options_type::const_iterator it = _options.begin(); it != _options.end(); ++it) { - #ifdef TINS_DEBUG - assert(total_sz >= it->data_size() + sizeof(uint8_t) * 2); - // total_sz is only used if TINS_DEBUG is defined. - total_sz -= it->data_size() + sizeof(uint8_t) * 2; - #endif - buffer = write_option(*it, buffer); + write_option(*it, stream); } if (has_extensions()) { - uint8_t* extensions_ptr = buffer; + uint8_t* extensions_ptr = stream.pointer(); if (inner_pdu()) { // Get the size of the next pdu, padded to the next 32 bit boundary uint32_t inner_pdu_size = get_adjusted_inner_pdu_size(); @@ -278,21 +264,25 @@ void ICMPv6::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU * extensions_ptr += inner_pdu_size; } // Now serialize the exensions where they should be - extensions_.serialize(extensions_ptr, total_sz - (extensions_ptr - buffer)); + extensions_.serialize( + extensions_ptr, + total_sz - (extensions_ptr - stream.pointer()) + ); } const Tins::IPv6 *ipv6 = tins_cast(parent); - if(ipv6) { + if (ipv6) { uint32_t checksum = Utils::pseudoheader_checksum( - ipv6->src_addr(), - ipv6->dst_addr(), - size(), - Constants::IP::PROTO_ICMPV6 - ) + Utils::do_checksum(buffer_start, buffer_start + full_sz); - while (checksum >> 16) + ipv6->src_addr(), + ipv6->dst_addr(), + size(), + Constants::IP::PROTO_ICMPV6 + ) + Utils::do_checksum(buffer, buffer + total_sz); + while (checksum >> 16) { checksum = (checksum & 0xffff) + (checksum >> 16); + } this->checksum(~checksum); - memcpy(buffer_start + 2, &_header.cksum, sizeof(uint16_t)); + memcpy(buffer + 2, &_header.cksum, sizeof(uint16_t)); } } @@ -329,10 +319,10 @@ bool ICMPv6::remove_option(OptionTypes type) { return true; } -uint8_t *ICMPv6::write_option(const option &opt, uint8_t *buffer) { - *buffer++ = opt.option(); - *buffer++ = static_cast((opt.length_field() + sizeof(uint8_t) * 2) / 8); - return std::copy(opt.data_ptr(), opt.data_ptr() + opt.data_size(), buffer); +void ICMPv6::write_option(const option &opt, OutputMemoryStream& stream) { + stream.write(opt.option()); + stream.write((opt.length_field() + sizeof(uint8_t) * 2) / 8); + stream.write(opt.data_ptr(), opt.data_size()); } const ICMPv6::option *ICMPv6::search_option(OptionTypes type) const { diff --git a/src/ip.cpp b/src/ip.cpp index 944b8d9..02b38ff 100644 --- a/src/ip.cpp +++ b/src/ip.cpp @@ -53,6 +53,7 @@ using std::list; using Tins::Memory::InputMemoryStream; +using Tins::Memory::OutputMemoryStream; namespace Tins { @@ -353,17 +354,17 @@ IP::options_type::iterator IP::search_option_iterator(option_identifier id) { return find_if(_ip_options.begin(), _ip_options.end(), comparator); } -uint8_t* IP::write_option(const option &opt, uint8_t* buffer) { - option_identifier opt_type = opt.option(); - memcpy(buffer, &opt_type, 1); - if(*buffer <= 1) - return ++buffer; - buffer++; - *buffer = static_cast(opt.length_field()); - if(opt.data_size() == opt.length_field()) - *buffer += 2; - buffer++; - return std::copy(opt.data_ptr(), opt.data_ptr() + opt.data_size(), buffer); +void IP::write_option(const option &opt, OutputMemoryStream& stream) { + stream.write(opt.option()); + // Check what we wrote. We'll do this for any option != [END, NOOP] + if (*(stream.pointer() - 1) > NOOP) { + uint8_t length = opt.length_field(); + if (opt.data_size() == opt.length_field()) { + length += 2; + } + stream.write(length); + stream.write(opt.data_ptr(), opt.data_size()); + } } /* Virtual method overriding. */ @@ -415,40 +416,40 @@ void IP::prepare_for_serialize(const PDU *parent) { } void IP::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU* parent) { - uint32_t my_sz = header_size(); - #ifdef TINS_DEBUG - assert(total_sz >= my_sz); - #endif + OutputMemoryStream stream(buffer, total_sz); checksum(0); - if(inner_pdu()) { + if (inner_pdu()) { uint32_t new_flag = Internals::pdu_flag_to_ip_type(inner_pdu()->pdu_type()); - if(new_flag == 0xff && Internals::pdu_type_registered(inner_pdu()->pdu_type())) { + if (new_flag == 0xff && Internals::pdu_type_registered(inner_pdu()->pdu_type())) { new_flag = static_cast( Internals::pdu_type_to_id(inner_pdu()->pdu_type()) ); } - if(!is_fragmented() && new_flag != 0xff) + if (!is_fragmented() && new_flag != 0xff) { protocol(new_flag); + } } #if __FreeBSD__ || defined(__FreeBSD_kernel__) || __APPLE__ - if(!parent) + if(!parent) { total_sz = Endian::host_to_be(total_sz); + } #endif tot_len(total_sz); - head_len(static_cast(my_sz / sizeof(uint32_t))); + head_len(static_cast(header_size() / sizeof(uint32_t))); - memcpy(buffer, &_ip, sizeof(_ip)); + stream.write(_ip); - uint8_t* ptr_buffer = buffer + sizeof(_ip); for(options_type::const_iterator it = _ip_options.begin(); it != _ip_options.end(); ++it) { - ptr_buffer = write_option(*it, ptr_buffer); + write_option(*it, stream); } - memset(buffer + sizeof(_ip) + _options_size, 0, _padded_options_size - _options_size); + // Add option padding + stream.fill(_padded_options_size - _options_size, 0); uint32_t check = Utils::do_checksum(buffer, buffer + sizeof(_ip) + _padded_options_size); - while (check >> 16) + while (check >> 16) { check = (check & 0xffff) + (check >> 16); + } checksum(~check); ((iphdr*)buffer)->check = _ip.check; } diff --git a/src/ipv6.cpp b/src/ipv6.cpp index eb9be58..6d97866 100644 --- a/src/ipv6.cpp +++ b/src/ipv6.cpp @@ -48,6 +48,7 @@ #include "memory_helpers.h" using Tins::Memory::InputMemoryStream; +using Tins::Memory::OutputMemoryStream; namespace Tins { @@ -191,12 +192,10 @@ bool IPv6::matches_response(const uint8_t *ptr, uint32_t total_sz) const { } void IPv6::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent) { - #ifdef TINS_DEBUG - assert(total_sz >= header_size()); - #endif - if(inner_pdu()) { + OutputMemoryStream stream(buffer, total_sz); + if (inner_pdu()) { uint8_t new_flag = Internals::pdu_flag_to_ip_type(inner_pdu()->pdu_type()); - if(new_flag == 0xff && Internals::pdu_type_registered(inner_pdu()->pdu_type())) { + if (new_flag == 0xff && Internals::pdu_type_registered(inner_pdu()->pdu_type())) { new_flag = static_cast( Internals::pdu_type_to_id(inner_pdu()->pdu_type()) ); @@ -204,10 +203,9 @@ void IPv6::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *pa set_last_next_header(new_flag); } payload_length(static_cast(total_sz - sizeof(_header))); - std::memcpy(buffer, &_header, sizeof(_header)); - buffer += sizeof(_header); + stream.write(_header); for(headers_type::const_iterator it = ext_headers.begin(); it != ext_headers.end(); ++it) { - buffer = write_header(*it, buffer); + write_header(*it, stream); } } @@ -249,10 +247,11 @@ void IPv6::set_last_next_header(uint8_t value) { ext_headers.back().option(value); } -uint8_t *IPv6::write_header(const ext_header &header, uint8_t *buffer) { - *buffer++ = header.option(); - *buffer++ = static_cast((header.length_field() > 8) ? (header.length_field() - 8) : 0); - return std::copy(header.data_ptr(), header.data_ptr() + header.data_size(), buffer); +void IPv6::write_header(const ext_header &header, OutputMemoryStream& stream) { + const uint8_t length = (header.length_field() > 8) ? (header.length_field() - 8) : 0; + stream.write(header.option()); + stream.write(length); + stream.write(header.data_ptr(), header.data_size()); } -} +} // Tins diff --git a/src/llc.cpp b/src/llc.cpp index dd9a67a..d964dd4 100644 --- a/src/llc.cpp +++ b/src/llc.cpp @@ -39,6 +39,7 @@ #include "memory_helpers.h" using Tins::Memory::InputMemoryStream; +using Tins::Memory::OutputMemoryStream; namespace Tins { @@ -198,33 +199,26 @@ void LLC::clear_information_fields() { } void LLC::write_serialization(uint8_t *buffer, uint32_t total_sz, const Tins::PDU *parent) { - #ifdef TINS_DEBUG - assert(total_sz >= header_size()); - #endif - if(inner_pdu() && inner_pdu()->pdu_type() == PDU::STP) { + OutputMemoryStream stream(buffer, total_sz); + if (inner_pdu() && inner_pdu()->pdu_type() == PDU::STP) { dsap(0x42); ssap(0x42); } - std::memcpy(buffer, &_header, sizeof(_header)); - buffer += sizeof(_header); + stream.write(_header); switch (type()) { case LLC::UNNUMBERED: - std::memcpy(buffer, &(control_field.unnumbered), sizeof(un_control_field)); - buffer += sizeof(un_control_field); + stream.write(control_field.unnumbered); break; case LLC::INFORMATION: - std::memcpy(buffer, &(control_field.info), sizeof(info_control_field)); - buffer += sizeof(info_control_field); + stream.write(control_field.info); break; case LLC::SUPERVISORY: - std::memcpy(buffer, &(control_field.super), sizeof(super_control_field)); - buffer += sizeof(super_control_field); + stream.write(control_field.super); break; } - for (std::list::const_iterator it = information_fields.begin(); it != information_fields.end(); it++) { - std::copy(it->begin(), it->end(), buffer); - buffer += it->size(); + for (field_list::const_iterator it = information_fields.begin(); it != information_fields.end(); it++) { + stream.write(it->begin(), it->end()); } } diff --git a/src/loopback.cpp b/src/loopback.cpp index 6e33c9c..f9870be 100644 --- a/src/loopback.cpp +++ b/src/loopback.cpp @@ -56,6 +56,7 @@ #endif using Tins::Memory::InputMemoryStream; +using Tins::Memory::OutputMemoryStream; namespace Tins { @@ -95,15 +96,15 @@ uint32_t Loopback::header_size() const { } void Loopback::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *) { - #ifdef TINS_DEBUG - assert(total_sz >= sizeof(_family)); - #endif + OutputMemoryStream stream(buffer, total_sz); #ifndef _WIN32 - if(tins_cast(inner_pdu())) + if (tins_cast(inner_pdu())) { _family = PF_INET; - else if(tins_cast(inner_pdu())) + } + else if (tins_cast(inner_pdu())) { _family = PF_LLC; - *reinterpret_cast(buffer) = _family; + } + stream.write(_family); #endif // _WIN32 } diff --git a/src/pppoe.cpp b/src/pppoe.cpp index 82a92f5..4a95343 100644 --- a/src/pppoe.cpp +++ b/src/pppoe.cpp @@ -37,6 +37,7 @@ #include "memory_helpers.h" using Tins::Memory::InputMemoryStream; +using Tins::Memory::OutputMemoryStream; namespace Tins { @@ -109,25 +110,15 @@ uint32_t PPPoE::header_size() const { void PPPoE::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *) { - #ifdef TINS_DEBUG - assert(total_sz == sizeof(_header) + _tags_size); - #endif - std::memcpy(buffer, &_header, sizeof(_header)); - if(_tags_size > 0) - ((pppoe_hdr*)buffer)->payload_length = Endian::host_to_be(_tags_size); - buffer += sizeof(_header); - uint16_t uint16_t_buffer; + OutputMemoryStream stream(buffer, total_sz); + if (_tags_size > 0) { + payload_length(_tags_size); + } + stream.write(_header); for(tags_type::const_iterator it = _tags.begin(); it != _tags.end(); ++it) { - uint16_t_buffer = it->option(); - std::memcpy(buffer, &uint16_t_buffer, sizeof(uint16_t)); - uint16_t_buffer = Endian::host_to_be(static_cast(it->length_field())); - std::memcpy(buffer + sizeof(uint16_t), &uint16_t_buffer, sizeof(uint16_t)); - std::copy( - it->data_ptr(), - it->data_ptr() + it->data_size(), - buffer + sizeof(uint16_t) * 2 - ); - buffer += sizeof(uint16_t) * 2 + it->data_size(); + stream.write(it->option()); + stream.write(Endian::host_to_be(it->length_field())); + stream.write(it->data_ptr(), it->data_size()); } } diff --git a/src/radiotap.cpp b/src/radiotap.cpp index 6031cdd..e1c9e3d 100644 --- a/src/radiotap.cpp +++ b/src/radiotap.cpp @@ -50,8 +50,12 @@ #include "utils.h" #include "packet_sender.h" #include "exceptions.h" +#include "memory_helpers.h" + +using Tins::Memory::OutputMemoryStream; namespace Tins { + void check_size(uint32_t total_sz, size_t field_size) { if(total_sz < field_size) throw malformed_packet(); @@ -471,87 +475,69 @@ bool RadioTap::matches_response(const uint8_t *ptr, uint32_t total_sz) const { } void RadioTap::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent) { - uint32_t sz = header_size(); + OutputMemoryStream stream(buffer, total_sz); uint8_t *buffer_start = buffer; - #ifdef TINS_DEBUG - assert(total_sz >= sz); - #endif - _radio.it_len = Endian::host_to_le(sz); - memcpy(buffer, &_radio, sizeof(_radio)); - buffer += sizeof(_radio); + _radio.it_len = Endian::host_to_le(header_size()); + stream.write(_radio); if(_radio.flags.tsft) { - memcpy(buffer, &_tsft, sizeof(_tsft)); - buffer += sizeof(_tsft); + stream.write(_tsft); } if(_radio.flags.flags) { - memcpy(buffer, &_flags, sizeof(_flags)); - buffer += sizeof(_flags); + stream.write(_flags); } if(_radio.flags.rate) { - memcpy(buffer, &_rate, sizeof(_rate)); - buffer += sizeof(_rate); + stream.write(_rate); } if(_radio.flags.channel) { - if(((buffer - buffer_start) & 1) == 1) - *(buffer++) = 0; - memcpy(buffer, &_channel_freq, sizeof(_channel_freq)); - buffer += sizeof(_channel_freq); - memcpy(buffer, &_channel_type, sizeof(_channel_type)); - buffer += sizeof(_channel_type); + if(((buffer - buffer_start) & 1) == 1) { + stream.write(0); + } + stream.write(_channel_freq); + stream.write(_channel_type); } if(_radio.flags.dbm_signal) { - memcpy(buffer, &_dbm_signal, sizeof(_dbm_signal)); - buffer += sizeof(_dbm_signal); + stream.write(_dbm_signal); } if(_radio.flags.dbm_noise) { - memcpy(buffer, &_dbm_noise, sizeof(_dbm_noise)); - buffer += sizeof(_dbm_noise); + stream.write(_dbm_noise); } if(_radio.flags.lock_quality) { - if(((buffer - buffer_start) & 1) == 1) - *(buffer++) = 0; - memcpy(buffer, &_signal_quality, sizeof(_signal_quality)); - buffer += sizeof(_signal_quality); + if(((buffer - buffer_start) & 1) == 1) { + stream.write(0); + } + stream.write(_signal_quality); } if(_radio.flags.antenna) { - memcpy(buffer, &_antenna, sizeof(_antenna)); - buffer += sizeof(_antenna); + stream.write(_antenna); } if(_radio.flags.db_signal) { - memcpy(buffer, &_db_signal, sizeof(_db_signal)); - buffer += sizeof(_db_signal); + stream.write(_db_signal); } if(_radio.flags.rx_flags) { - if(((buffer - buffer_start) & 1) == 1) - *(buffer++) = 0; - memcpy(buffer, &_rx_flags, sizeof(_rx_flags)); - buffer += sizeof(_rx_flags); + if(((buffer - buffer_start) & 1) == 1) { + stream.write(0); + } + stream.write(_rx_flags); } if(_radio.flags.channel_plus) { - uint32_t offset = ((buffer - buffer_start) % 4); - if(offset) { - offset = 4 - offset; - while(offset--) { - *buffer++ = 0; - } + const uint32_t padding = ((stream.pointer() - buffer_start) % 4); + if (padding != 0) { + stream.fill(4 - padding, 0); } uint32_t dummy = _channel_type; // nasty Big Endian fix dummy = Endian::le_to_host(Endian::host_to_le(dummy)); - memcpy(buffer, &dummy, sizeof(dummy)); - buffer += sizeof(dummy); - memcpy(buffer, &_channel_freq, sizeof(_channel_freq)); - buffer += sizeof(_channel_freq); - memcpy(buffer, &_channel, sizeof(_channel)); - buffer += sizeof(_channel); - memcpy(buffer, &_max_power, sizeof(_max_power)); - buffer += sizeof(_max_power); + stream.write(dummy); + stream.write(_channel_freq); + stream.write(_channel); + stream.write(_max_power); } - if((_flags & 0x10) != 0 && inner_pdu()) { + if ((_flags & 0x10) != 0 && inner_pdu()) { uint32_t crc32 = Endian::host_to_le( - Utils::crc32(buffer, inner_pdu()->size()) + Utils::crc32(stream.pointer(), inner_pdu()->size()) ); - memcpy(buffer + inner_pdu()->size(), &crc32, sizeof(uint32_t)); + stream.skip(inner_pdu()->size()); + stream.write(crc32); } } } diff --git a/src/rawpdu.cpp b/src/rawpdu.cpp index e24bc44..00f7f12 100644 --- a/src/rawpdu.cpp +++ b/src/rawpdu.cpp @@ -32,7 +32,9 @@ #endif #include #include "rawpdu.h" +#include "memory_helpers.h" +using Tins::Memory::OutputMemoryStream; namespace Tins { RawPDU::RawPDU(const uint8_t *pload, uint32_t size) @@ -51,10 +53,8 @@ uint32_t RawPDU::header_size() const { } void RawPDU::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *) { - #ifdef TINS_DEBUG - assert(total_sz >= _payload.size()); - #endif - std::copy(_payload.begin(), _payload.end(), buffer); + OutputMemoryStream stream(buffer, total_sz); + stream.write(_payload.begin(), _payload.end()); } void RawPDU::payload(const payload_type &pload) { diff --git a/src/rsn_information.cpp b/src/rsn_information.cpp index 0b89994..e1a90aa 100644 --- a/src/rsn_information.cpp +++ b/src/rsn_information.cpp @@ -38,6 +38,7 @@ #include "dot11/dot11_base.h" using Tins::Memory::InputMemoryStream; +using Tins::Memory::OutputMemoryStream; namespace Tins { @@ -107,27 +108,18 @@ RSNInformation::serialization_type RSNInformation::serialize() const { const uint16_t akm_cyphers_size = Endian::host_to_le(_akm_cyphers.size()); serialization_type buffer(size); - serialization_type::value_type *ptr = &buffer[0]; - std::memcpy(ptr, &_version, sizeof(_version)); - ptr += sizeof(uint16_t); - std::memcpy(ptr, &_group_suite, sizeof(uint32_t)); - ptr += sizeof(uint32_t); - std::memcpy(ptr, &pairwise_cyphers_size, sizeof(pairwise_cyphers_size)); - ptr += sizeof(uint16_t); - for(cyphers_type::const_iterator it = _pairwise_cyphers.begin(); it != _pairwise_cyphers.end(); ++it) { - const uint32_t value = Endian::host_to_le(*it); - std::memcpy(ptr, &value, sizeof(uint32_t)); - ptr += sizeof(uint32_t); + OutputMemoryStream stream(&buffer[0], buffer.size()); + stream.write(_version); + stream.write(_group_suite); + stream.write(pairwise_cyphers_size); + for (cyphers_type::const_iterator it = _pairwise_cyphers.begin(); it != _pairwise_cyphers.end(); ++it) { + stream.write(Endian::host_to_le(*it)); } - std::memcpy(ptr, &akm_cyphers_size, sizeof(akm_cyphers_size)); - ptr += sizeof(uint16_t); - for(akm_type::const_iterator it = _akm_cyphers.begin(); it != _akm_cyphers.end(); ++it) { - const uint32_t value = Endian::host_to_le(*it); - std::memcpy(ptr, &value, sizeof(uint32_t)); - ptr += sizeof(uint32_t); + stream.write(akm_cyphers_size); + for (akm_type::const_iterator it = _akm_cyphers.begin(); it != _akm_cyphers.end(); ++it) { + stream.write(Endian::host_to_le(*it)); } - std::memcpy(ptr, &_capabilities, sizeof(uint16_t)); - + stream.write(_capabilities); return buffer; } diff --git a/src/sll.cpp b/src/sll.cpp index 8bfbb64..c95a71a 100644 --- a/src/sll.cpp +++ b/src/sll.cpp @@ -35,6 +35,7 @@ #include "memory_helpers.h" using Tins::Memory::InputMemoryStream; +using Tins::Memory::OutputMemoryStream; namespace Tins { @@ -81,12 +82,14 @@ uint32_t SLL::header_size() const { } void SLL::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *) { - if(inner_pdu()) { + OutputMemoryStream stream(buffer, total_sz); + if (inner_pdu()) { Constants::Ethernet::e flag = Internals::pdu_flag_to_ether_type( inner_pdu()->pdu_type() ); protocol(static_cast(flag)); } - std::memcpy(buffer, &_header, sizeof(_header)); -} + stream.write(_header); } + +} // Tins diff --git a/src/snap.cpp b/src/snap.cpp index fce6b6b..f26b132 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -46,6 +46,7 @@ #include "memory_helpers.h" using Tins::Memory::InputMemoryStream; +using Tins::Memory::OutputMemoryStream; namespace Tins { @@ -95,9 +96,7 @@ uint32_t SNAP::header_size() const { } void SNAP::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent) { - #ifdef TINS_DEBUG - assert(total_sz >= sizeof(_snap)); - #endif + OutputMemoryStream stream(buffer, total_sz); if (inner_pdu()) { Constants::Ethernet::e flag = Internals::pdu_flag_to_ether_type( inner_pdu()->pdu_type() @@ -106,7 +105,7 @@ void SNAP::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *pa static_cast(flag) ); } - std::memcpy(buffer, &_snap, sizeof(_snap)); + stream.write(_snap); } } // Tins diff --git a/src/stp.cpp b/src/stp.cpp index 71f85ca..2b62469 100644 --- a/src/stp.cpp +++ b/src/stp.cpp @@ -37,6 +37,7 @@ #include "memory_helpers.h" using Tins::Memory::InputMemoryStream; +using Tins::Memory::OutputMemoryStream; namespace Tins { @@ -109,10 +110,8 @@ void STP::bridge_id(const bpdu_id_type &id) { } void STP::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *) { - #ifdef TINS_DEBUG - assert(total_sz >= sizeof(_header)); - #endif - std::memcpy(buffer, &_header, sizeof(_header)); + OutputMemoryStream stream(buffer, total_sz); + stream.write(_header); } uint32_t STP::header_size() const { diff --git a/src/tcp.cpp b/src/tcp.cpp index d004801..2337c49 100644 --- a/src/tcp.cpp +++ b/src/tcp.cpp @@ -41,7 +41,9 @@ #include "memory_helpers.h" using std::find_if; + using Tins::Memory::InputMemoryStream; +using Tins::Memory::OutputMemoryStream; namespace Tins { @@ -291,46 +293,46 @@ uint32_t TCP::header_size() const { } void TCP::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent) { - assert(total_sz >= header_size()); - uint8_t *tcp_start = buffer; + OutputMemoryStream stream(buffer, total_sz); + // Set checksum to 0, we'll calculate it at the end checksum(0); - buffer += sizeof(tcphdr); _tcp.doff = (sizeof(tcphdr) + _total_options_size) / sizeof(uint32_t); - for(options_type::iterator it = _options.begin(); it != _options.end(); ++it) - buffer = write_option(*it, buffer); - - if(_options_size < _total_options_size) { - uint16_t padding = _options_size; - while(padding < _total_options_size) { - *(buffer++) = 1; - padding++; - } + stream.write(_tcp); + for(options_type::const_iterator it = _options.begin(); it != _options.end(); ++it) { + write_option(*it, stream); } - memcpy(tcp_start, &_tcp, sizeof(tcphdr)); + if (_options_size < _total_options_size) { + const uint16_t padding = _total_options_size - _options_size; + stream.fill(padding, 1); + } const Tins::IP *ip_packet = tins_cast(parent); if(ip_packet) { - uint32_t check = Utils::pseudoheader_checksum(ip_packet->src_addr(), - ip_packet->dst_addr(), - size(), Constants::IP::PROTO_TCP) + - Utils::do_checksum(tcp_start, tcp_start + total_sz); - while (check >> 16) + uint32_t check = Utils::pseudoheader_checksum( + ip_packet->src_addr(), + ip_packet->dst_addr(), + size(), + Constants::IP::PROTO_TCP) + Utils::do_checksum(buffer, buffer + total_sz); + while (check >> 16) { check = (check & 0xffff) + (check >> 16); + } checksum(~check); - ((tcphdr*)tcp_start)->check = _tcp.check; + ((tcphdr*)buffer)->check = _tcp.check; } else { const Tins::IPv6 *ipv6_packet = tins_cast(parent); if(ipv6_packet) { - uint32_t check = Utils::pseudoheader_checksum(ipv6_packet->src_addr(), - ipv6_packet->dst_addr(), - size(), Constants::IP::PROTO_TCP) + - Utils::do_checksum(tcp_start, tcp_start + total_sz); - while (check >> 16) + uint32_t check = Utils::pseudoheader_checksum( + ipv6_packet->src_addr(), + ipv6_packet->dst_addr(), + size(), + Constants::IP::PROTO_TCP) + Utils::do_checksum(buffer, buffer + total_sz); + while (check >> 16) { check = (check & 0xffff) + (check >> 16); + } checksum(~check); - ((tcphdr*)tcp_start)->check = _tcp.check; + ((tcphdr*)buffer)->check = _tcp.check; } } } @@ -353,19 +355,18 @@ TCP::options_type::iterator TCP::search_option_iterator(OptionTypes type) { /* options */ -uint8_t *TCP::write_option(const option &opt, uint8_t *buffer) { - if(opt.option() == 0 || opt.option() == 1) { - *buffer = opt.option(); - return buffer + 1; - } - else { - buffer[0] = opt.option(); - buffer[1] = static_cast(opt.length_field()); - // only add the identifier and size field sizes if the length +void TCP::write_option(const option &opt, OutputMemoryStream& stream) { + stream.write(opt.option()); + // Only do this for non EOL nor NOP options + if(opt.option() > 1) { + uint8_t length = opt.length_field(); + // Only add the identifier and size field sizes if the length // field hasn't been spoofed. - if(opt.length_field() == opt.data_size()) - buffer[1] += (sizeof(uint8_t) << 1); - return std::copy(opt.data_ptr(), opt.data_ptr() + opt.data_size(), buffer + 2); + if(opt.length_field() == opt.data_size()) { + length += (sizeof(uint8_t) << 1); + } + stream.write(length); + stream.write(opt.data_ptr(), opt.data_size()); } } diff --git a/src/udp.cpp b/src/udp.cpp index 3b4b6e1..59fa550 100644 --- a/src/udp.cpp +++ b/src/udp.cpp @@ -42,6 +42,7 @@ #include "memory_helpers.h" using Tins::Memory::InputMemoryStream; +using Tins::Memory::OutputMemoryStream; namespace Tins { @@ -77,9 +78,8 @@ uint32_t UDP::header_size() const { } void UDP::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent) { - #ifdef TINS_DEBUG - assert(total_sz >= sizeof(udphdr)); - #endif + OutputMemoryStream stream(buffer, total_sz); + // Set checksum to 0, we'll calculate it at the end _udp.check = 0; if(inner_pdu()) { length(static_cast(sizeof(udphdr) + inner_pdu()->size())); @@ -87,17 +87,18 @@ void UDP::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *par else { length(static_cast(sizeof(udphdr))); } - std::memcpy(buffer, &_udp, sizeof(udphdr)); + stream.write(_udp); const Tins::IP *ip_packet = tins_cast(parent); if(ip_packet) { uint32_t checksum = Utils::pseudoheader_checksum( - ip_packet->src_addr(), - ip_packet->dst_addr(), - size(), - Constants::IP::PROTO_UDP - ) + Utils::do_checksum(buffer, buffer + total_sz); - while (checksum >> 16) + ip_packet->src_addr(), + ip_packet->dst_addr(), + size(), + Constants::IP::PROTO_UDP + ) + Utils::do_checksum(buffer, buffer + total_sz); + while (checksum >> 16) { checksum = (checksum & 0xffff)+(checksum >> 16); + } _udp.check = Endian::host_to_be(~checksum); ((udphdr*)buffer)->check = _udp.check; } @@ -105,13 +106,14 @@ void UDP::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *par const Tins::IPv6 *ip6_packet = tins_cast(parent); if(ip6_packet) { uint32_t checksum = Utils::pseudoheader_checksum( - ip6_packet->src_addr(), - ip6_packet->dst_addr(), - size(), - Constants::IP::PROTO_UDP - ) + Utils::do_checksum(buffer, buffer + total_sz); - while (checksum >> 16) + ip6_packet->src_addr(), + ip6_packet->dst_addr(), + size(), + Constants::IP::PROTO_UDP + ) + Utils::do_checksum(buffer, buffer + total_sz); + while (checksum >> 16) { checksum = (checksum & 0xffff)+(checksum >> 16); + } _udp.check = Endian::host_to_be(~checksum); ((udphdr*)buffer)->check = _udp.check; }