diff --git a/include/tcp.h b/include/tcp.h index 18ddf9d..78d1e32 100644 --- a/include/tcp.h +++ b/include/tcp.h @@ -23,6 +23,7 @@ #define __TCP_H +#include #include #ifndef WIN32 #include @@ -43,8 +44,14 @@ namespace Tins { ECE, CWR }; + + enum Options { + MSS = 2, + TSOPT = 8 + }; TCP(uint16_t dport = 0, uint16_t sport = 0); + ~TCP(); inline uint16_t dport() const { return _tcp.dport; } inline uint16_t sport() const { return _tcp.sport; } @@ -63,9 +70,10 @@ namespace Tins { void urg_ptr(uint16_t new_urg_ptr); void payload(uint8_t *new_payload, uint32_t new_payload_size); + void set_mss(uint16_t value); + void set_timestamp(uint32_t value, uint32_t reply); void set_flag(Flags tcp_flag, uint8_t value); - - uint16_t do_checksum() const; + void add_option(Options tcp_option, uint8_t length = 0, uint8_t *data = 0); /* Virtual methods */ uint32_t header_size() const; @@ -106,11 +114,24 @@ 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; + uint16_t do_checksum(uint8_t *start, uint8_t *end) const; + tcphdr _tcp; + std::vector _options; uint8_t *_payload; - uint32_t _payload_size; + uint32_t _payload_size, _options_size, _total_options_size; }; }; diff --git a/src/tcp.cpp b/src/tcp.cpp index bdd0656..6360e03 100644 --- a/src/tcp.cpp +++ b/src/tcp.cpp @@ -30,7 +30,8 @@ const uint16_t Tins::TCP::DEFAULT_WINDOW = 32678; -Tins::TCP::TCP(uint16_t dport, uint16_t sport) : PDU(IPPROTO_TCP), _payload(0), _payload_size(0) { +Tins::TCP::TCP(uint16_t dport, uint16_t sport) : PDU(IPPROTO_TCP), _payload(0), _payload_size(0), + _options_size(0), _total_options_size(0) { std::memset(&_tcp, 0, sizeof(tcphdr)); _tcp.dport = Utils::net_to_host_s(dport); _tcp.sport = Utils::net_to_host_s(sport); @@ -38,6 +39,11 @@ Tins::TCP::TCP(uint16_t dport, uint16_t sport) : PDU(IPPROTO_TCP), _payload(0), _tcp.window = Utils::net_to_host_s(DEFAULT_WINDOW); } +Tins::TCP::~TCP() { + for(unsigned i(0); i < _options.size(); ++i) + delete[] _options[i].data; +} + void Tins::TCP::dport(uint16_t new_dport) { _tcp.dport = Utils::net_to_host_s(new_dport); } @@ -72,6 +78,16 @@ void Tins::TCP::payload(uint8_t *new_payload, uint32_t new_payload_size) { _payload_size = new_payload_size; } +void Tins::TCP::set_mss(uint16_t value) { + value = Utils::net_to_host_s(value); + add_option(MSS, 2, (uint8_t*)&value); +} + +void Tins::TCP::set_timestamp(uint32_t value, uint32_t reply) { + uint64_t buffer = ((uint64_t)Utils::net_to_host_l(reply) << 32) | Utils::net_to_host_l(value); + add_option(TSOPT, 8, (uint8_t*)&buffer); +} + void Tins::TCP::set_flag(Flags tcp_flag, uint8_t value) { switch(tcp_flag) { case FIN: @@ -101,21 +117,59 @@ void Tins::TCP::set_flag(Flags tcp_flag, uint8_t value) { }; } -uint16_t Tins::TCP::do_checksum() const { - const uint8_t *ptr = (const uint8_t*)_payload, *end = (const uint8_t*)_payload + _payload_size; - uint16_t checksum(0); - while(ptr < end) - checksum += *(ptr++); +void Tins::TCP::add_option(Options tcp_option, uint8_t length, 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)); + _options_size += length + (sizeof(uint8_t) << 1); + padding = _options_size & 3; + _total_options_size = (padding) ? _options_size - padding + 4 : _options_size; +} + +uint16_t Tins::TCP::do_checksum(uint8_t *start, uint8_t *end) const { + unsigned checksum(0); + while(start < end) + checksum += *(start++); return checksum; } uint32_t Tins::TCP::header_size() const { - return sizeof(tcphdr) + _payload_size; + return sizeof(tcphdr) + _payload_size + _total_options_size; } void Tins::TCP::write_serialization(uint8_t *buffer, uint32_t total_sz) { assert(total_sz >= header_size()); - _tcp.check = Utils::net_to_host_s(do_checksum()); - memcpy(buffer, &_tcp, sizeof(tcphdr)); - memcpy(buffer + sizeof(tcphdr), _payload, _payload_size); + 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); + + if(_options_size < _total_options_size) { + uint8_t padding = _total_options_size; + while(padding < _options_size) { + *(buffer++) = 1; + padding++; + } + } + + memcpy(buffer, _payload, _payload_size); + _tcp.check = Utils::net_to_host_s(do_checksum(tcp_start + sizeof(tcphdr), buffer)); + memcpy(tcp_start, &_tcp, sizeof(tcphdr)); +} + + +/* TCPOptions */ + +uint8_t *Tins::TCP::TCPOption::write(uint8_t *buffer) { + if(kind == 1) { + *buffer = kind; + return buffer + 1; + } + else { + buffer[0] = kind; + buffer[1] = length + (sizeof(uint8_t) << 1); + memcpy(buffer + 2, data, length); + return buffer + length + (sizeof(uint8_t) << 1); + } }