diff --git a/include/tcp.h b/include/tcp.h index 05061d2..abf6a2f 100644 --- a/include/tcp.h +++ b/include/tcp.h @@ -23,7 +23,7 @@ #define __TCP_H -#include +#include #include #ifndef WIN32 #include @@ -66,9 +66,35 @@ namespace Tins { */ enum Options { - MSS = 2, + EOL = 0, + NOP = 1, + MSS = 2, TSOPT = 8 }; + + /** + * \brief Class that represents a TCP option field. + */ + struct TCPOption { + /** + * \brief Creates an instance of a TCPOption. + * \param okind The option kind. + * \param olength The option's data length. + * \param odata The option's data(if any). + */ + TCPOption(uint8_t okind, uint8_t olength, uint8_t *odata) : + kind(okind), length(olength), data(odata) { } + + /** + * \brief Writes the option into a buffer. + * \param buffer The buffer in which to write the option. + * \return The buffer pointer incremented by the size of this option. + */ + uint8_t *write(uint8_t *buffer); + + uint8_t kind, length; + uint8_t *data; + }; /** * \brief TCP constructor. @@ -151,6 +177,13 @@ namespace Tins { */ inline uint8_t data_offset() const { return this->_tcp.doff; } + /** + * \brief Getter for the option list. + * + * \return The options list. + */ + inline const std::list &options() const { return _options; } + /** * \brief Gets the value of a flag. * @@ -164,56 +197,56 @@ namespace Tins { /** * \brief Setter for the destination port field. * - * \param new_dport uint16_t with the new destination port. + * \param new_dport The new destination port. */ void dport(uint16_t new_dport); /** * \brief Setter for the source port field. * - * \param new_sport uint16_t with the new source port. + * \param new_sport The new source port. */ void sport(uint16_t new_sport); /** * \brief Setter for the sequence number. * - * \param new_seq uint32_t with the new sequence number. + * \param new_seq The new sequence number. */ void seq(uint32_t new_seq); /** * \brief Setter for the acknowledge number. * - * \param new_ack_seq uint32_t with the new acknowledge number. + * \param new_ack_seq The new acknowledge number. */ void ack_seq(uint32_t new_ack_seq); /** * \brief Setter for the window size. * - * \param new_window uint16_t with the new window size. + * \param new_window The new window size. */ void window(uint16_t new_window); /** * \brief Setter for the checksum field. * - * \param new_check uint16_t with the new checksum. + * \param new_check The new checksum. */ void check(uint16_t new_check); /** * \brief Setter for the urgent pointer field. * - * \param new_urg_ptr uint16_t with the new urgent pointer. + * \param new_urg_ptr The new urgent pointer. */ void urg_ptr(uint16_t new_urg_ptr); /** * \brief Setter for the data offset pointer field. * - * \param new_doff uint8_t with the new data offset pointer. + * \param new_doff The new data offset pointer. */ void data_offset(uint8_t new_doff); @@ -234,34 +267,34 @@ namespace Tins { /** * \brief Set the maximum segment size. * - * \param value uint16_t with the new maximum segment size. + * \param value The new maximum segment size. */ void set_mss(uint16_t value); /** * \brief Set the timestamp. * - * \param value uint32_t with the current value of the timestamp clock. - * \param reply uint32_t with the echo reply field. + * \param value The current value of the timestamp clock. + * \param reply The echo reply field. */ void set_timestamp(uint32_t value, uint32_t reply); /** * \brief Set a TCP flag value. * - * \param tcp_flag Flag which indicates the flag to be set. - * \param value uint8_t with the new value for this flag. Must be 0 or 1. + * \param tcp_flag The flag to be set. + * \param value The new value for this flag. Must be 0 or 1. */ void set_flag(Flags tcp_flag, uint8_t value); /** * \brief Adds a TCP option. * - * \param tcp_option Options indicating the option to be set. - * \param length uint8_t with the length of this option(optional). - * \param data uint8_t* containing this option's data(optional). + * \param tcp_option The option type flag to be set. + * \param length The length of this option(optional). + * \param data Pointer to this option's data(optional). */ - void add_option(Options tcp_option, uint8_t length = 0, uint8_t *data = 0); + void add_option(Options tcp_option, uint8_t length = 0, const uint8_t *data = 0); /** * \brief Returns the header size. @@ -316,16 +349,6 @@ namespace Tins { uint16_t urg_ptr; } __attribute__((packed)); - struct TCPOption { - TCPOption(uint8_t okind, uint8_t olength, uint8_t *odata) : - kind(okind), length(olength), data(odata) { } - - uint8_t *write(uint8_t *buffer); - - uint8_t kind, length; - uint8_t *data; - }; - static const uint16_t DEFAULT_WINDOW; /** \brief Serialices this TCP PDU. @@ -336,7 +359,7 @@ namespace Tins { void write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent); tcphdr _tcp; - std::vector _options; + std::list _options; uint32_t _options_size, _total_options_size; }; }; diff --git a/src/dhcp.cpp b/src/dhcp.cpp index 98a7666..c64c75c 100644 --- a/src/dhcp.cpp +++ b/src/dhcp.cpp @@ -40,20 +40,26 @@ Tins::DHCP::DHCP() : _size(sizeof(uint32_t) + 1) { Tins::DHCP::DHCP(const uint8_t *buffer, uint32_t total_sz) : BootP(buffer, total_sz, 0) { buffer += BootP::header_size(); total_sz -= BootP::header_size(); - uint8_t args[2]; + uint8_t args[2] = {0}; while(total_sz) { - for(unsigned i(0); i < 2; ++i) { + for(unsigned i(0); i < 2 && args[0] != END; ++i) { args[i] = *(buffer++); total_sz--; if(!total_sz) throw std::runtime_error("Not enought size for a DHCP header in the buffer."); } - // Not enough size for this option - if(total_sz < args[1]) - throw std::runtime_error("Not enought size for a DHCP header in the buffer."); - add_option((Options)args[0], args[1], buffer); - buffer += args[1]; - total_sz -= args[1]; + // If the END-OF-OPTIONS was not found... + if(args[0] != END) { + // Not enough size for this option + if(total_sz < args[1]) + throw std::runtime_error("Not enought size for a DHCP header in the buffer."); + add_option((Options)args[0], args[1], buffer); + buffer += args[1]; + total_sz -= args[1]; + } + // Otherwise, break the loop. + else + total_sz = 0; } } diff --git a/src/tcp.cpp b/src/tcp.cpp index e08c110..5f662e0 100644 --- a/src/tcp.cpp +++ b/src/tcp.cpp @@ -47,16 +47,47 @@ Tins::TCP::TCP(const uint8_t *buffer, uint32_t total_sz) : PDU(IPPROTO_TCP) { throw std::runtime_error("Not enought size for an TCP header in the buffer."); std::memcpy(&_tcp, buffer, sizeof(tcphdr)); - /* Options... */ - + buffer += sizeof(tcphdr); total_sz -= sizeof(tcphdr); + + uint32_t index = 0, header_end = (data_offset() * sizeof(uint32_t)) - sizeof(tcphdr); + if(total_sz >= header_end) { + uint8_t args[2] = {0}; + while(index < header_end) { + for(unsigned i(0); i < 2 && args[0] != NOP; ++i) { + args[i] = buffer[index++]; + if(index == header_end) + throw std::runtime_error("Not enought size for a TCP header in the buffer."); + } + // We don't want to store NOPs and EOLs + if(args[0] != NOP && args[0] != EOL) { + if(args[1]) { + // Not enough size for this option + if(header_end - index < args[1] - (sizeof(uint8_t) << 1)) { + throw std::runtime_error("Not enought size for a TCP header in the buffer."); + } + args[1] -= (sizeof(uint8_t) << 1); + add_option((Options)args[0], args[1], buffer + index); + } + index += args[1]; + } + else if(args[0] == EOL) + index = header_end; + else // Skip the NOP + args[0] = 0; + } + buffer += index; + total_sz -= index; + _total_options_size = header_end; + _options_size = (_total_options_size / 4) * 4; + } if(total_sz) - inner_pdu(new RawPDU(buffer + sizeof(tcphdr), total_sz)); + inner_pdu(new RawPDU(buffer, total_sz)); } Tins::TCP::~TCP() { - for(unsigned i(0); i < _options.size(); ++i) - delete[] _options[i].data; + for(std::list::iterator it = _options.begin(); it != _options.end(); ++it) + delete[] it->data; } void Tins::TCP::dport(uint16_t new_dport) { @@ -166,7 +197,7 @@ void Tins::TCP::set_flag(Flags tcp_flag, uint8_t value) { }; } -void Tins::TCP::add_option(Options tcp_option, uint8_t length, uint8_t *data) { +void Tins::TCP::add_option(Options tcp_option, uint8_t length, const uint8_t *data) { uint8_t *new_data = new uint8_t[length], padding; memcpy(new_data, data, length); _options.push_back(TCPOption(tcp_option, length, new_data)); @@ -184,8 +215,8 @@ void Tins::TCP::write_serialization(uint8_t *buffer, uint32_t total_sz, const PD uint8_t *tcp_start = buffer; buffer += sizeof(tcphdr); _tcp.doff = (sizeof(tcphdr) + _total_options_size) / sizeof(uint32_t); - for(unsigned i(0); i < _options.size(); ++i) - buffer = _options[i].write(buffer); + for(std::list::iterator it = _options.begin(); it != _options.end(); ++it) + buffer = it->write(buffer); if(_options_size < _total_options_size) { uint8_t padding = _total_options_size;