diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 4e3a15f..8baf71e 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -15,6 +15,7 @@ IF(libtins_FOUND) dns_queries dns_spoof dns_stats + http_dump icmp_responses interfaces_info tcp_connection_close @@ -40,6 +41,7 @@ IF(libtins_FOUND) ADD_EXECUTABLE(arpmonitor EXCLUDE_FROM_ALL arpmonitor.cpp) ADD_EXECUTABLE(dns_queries EXCLUDE_FROM_ALL dns_queries.cpp) ADD_EXECUTABLE(dns_spoof EXCLUDE_FROM_ALL dns_spoof.cpp) + ADD_EXECUTABLE(http_dump EXCLUDE_FROM_ALL http_dump.cpp) ADD_EXECUTABLE(icmp_responses EXCLUDE_FROM_ALL icmp_responses.cpp) ADD_EXECUTABLE(interfaces_info EXCLUDE_FROM_ALL interfaces_info.cpp) ADD_EXECUTABLE(tcp_connection_close EXCLUDE_FROM_ALL tcp_connection_close.cpp) diff --git a/examples/http_dump.cpp b/examples/http_dump.cpp new file mode 100644 index 0000000..dcd2c8f --- /dev/null +++ b/examples/http_dump.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2016, 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 "tins/tcp_ip.h" +#include "tins/sniffer.h" +#include "tins/ip_address.h" +#include "tins/ipv6_address.h" + +using std::cout; +using std::cerr; +using std::endl; +using std::bind; +using std::string; +using std::ostringstream; +using std::exception; + +using Tins::Sniffer; +using Tins::SnifferConfiguration; +using Tins::TCPIP::StreamFollower; +using Tins::TCPIP::Stream; + +// Convert the client endpoint to a readable string +string client_endpoint(const Stream& stream) { + ostringstream output; + // Use the IPv4 or IPv6 address depending on which protocol the + // connection uses + if (stream.is_v6()) { + output << stream.client_addr_v6(); + } + else { + output << stream.client_addr_v4(); + } + output << ":" << stream.client_port(); + return output.str(); +} + +// Convert the server endpoint to a readable string +string server_endpoint(const Stream& stream) { + ostringstream output; + if (stream.is_v6()) { + output << stream.server_addr_v6(); + } + else { + output << stream.server_addr_v4(); + } + output << ":" << stream.server_port(); + return output.str(); +} + +// Concat both endpoints to get a readable stream identifier +string stream_identifier(const Stream& stream) { + ostringstream output; + output << client_endpoint(stream) << " - " << server_endpoint(stream); + return output.str(); +} + +// Whenever there's new client data on the stream, this callback is executed. +void on_client_data(Stream& stream) { + // Construct a string out of the contents of the client's payload + string data(stream.client_payload().begin(), stream.client_payload().end()); + // Now print it, prepending some information about the stream + cout << client_endpoint(stream) << " >> " + << server_endpoint(stream) << ": " << endl << data << endl; + // Now erase the stored data, as we've already processed it. This is important, + // since if we don't do this, the connection will keep buffering data until + // the stream is closed + stream.client_payload().clear(); +} + +// Whenever there's new server data on the stream, this callback is executed. +// This does the same thing as on_client_data +void on_server_data(Stream& stream) { + string data(stream.server_payload().begin(), stream.server_payload().end()); + cout << server_endpoint(stream) << " >> " + << client_endpoint(stream) << ": " << endl << data << endl; + stream.server_payload().clear(); +} + +// When a connection is closed, this callback is executed. +void on_connection_closed(Stream& stream) { + cout << "[+] Connection closed: " << stream_identifier(stream) << endl; +} + +// When a new connection is captured, this callback will be executed. +void on_new_connection(Stream& stream) { + // Print some information about the new connection + cout << "[+] New connection " << stream_identifier(stream) << endl; + // Now configure the callbacks on it. + // First, we want on_client_data to be called every time there's new client data + stream.client_data_callback(&on_client_data); + // Same thing for server data, but calling on_server_data + stream.server_data_callback(&on_server_data); + // When the connection is closed, call on_connection_closed + stream.stream_closed_callback(&on_connection_closed); +} + +int main(int argc, char* argv[]) { + if (argc != 2) { + cout << "Usage: " << argv[0] << " " << endl; + return 1; + } + using std::placeholders::_1; + + try { + // Construct the sniffer configuration object + SnifferConfiguration config; + // Only capture TCP traffic sent from/to port 80 + config.set_filter("tcp port 80"); + // Construct the sniffer we'll use + Sniffer sniffer(argv[1], config); + + cout << "Starting capture on interface " << argv[1] << endl; + + // Now construct the stream follower + StreamFollower follower; + // We just need to specify the callback to be executed when a new + // stream is captured. In this stream, you should define which callbacks + // will be executed whenever new data is sent on that stream + // (see on_new_connection) + follower.new_stream_callback(&on_new_connection); + // Now start capturing. Every time there's a new packet, call + // follower.process_packet + sniffer.sniff_loop(bind(&StreamFollower::process_packet, &follower, _1)); + } + catch (exception& ex) { + cerr << "Error: " << ex.what() << endl; + return 1; + } +} diff --git a/include/tins/tcp_ip.h b/include/tins/tcp_ip.h index 37216a4..2d3b110 100644 --- a/include/tins/tcp_ip.h +++ b/include/tins/tcp_ip.h @@ -51,7 +51,7 @@ class IPv6Address; namespace TCPIP { -class TINS_API TCPFlow { +class TINS_API Flow { public: enum State { UNKNOWN, @@ -63,13 +63,13 @@ public: typedef std::vector payload_type; typedef std::map buffered_payload_type; - typedef std::function event_callback; + typedef std::function event_callback; - TCPFlow(const IPv4Address& dest_address, uint16_t dest_port, - uint32_t sequence_number); + Flow(const IPv4Address& dest_address, uint16_t dest_port, + uint32_t sequence_number); - TCPFlow(const IPv6Address& dest_address, uint16_t dest_port, - uint32_t sequence_number); + Flow(const IPv6Address& dest_address, uint16_t dest_port, + uint32_t sequence_number); void data_callback(const event_callback& callback); void buffering_callback(const event_callback& callback); @@ -85,10 +85,12 @@ public: uint16_t dport() const; const payload_type& payload() const; payload_type& payload(); - - void state(State new_state); State state() const; uint32_t sequence_number() const; + const buffered_payload_type& buffered_payload() const; + buffered_payload_type& buffered_payload(); + + void state(State new_state); private: void store_payload(uint32_t seq, const payload_type& payload); buffered_payload_type::iterator erase_iterator(buffered_payload_type::iterator iter); @@ -105,7 +107,7 @@ private: State state_; }; -class TINS_API TCPStream { +class TINS_API Stream { public: enum State { SYN_SENT, @@ -118,18 +120,34 @@ public: CLOSED }; - typedef std::function stream_callback; + typedef std::function stream_callback; + typedef Flow::payload_type payload_type; - TCPStream(const PDU& initial_packet); - TCPStream(const TCPFlow& client_flow, const TCPFlow& server_flow); + Stream(const PDU& initial_packet); + Stream(const Flow& client_flow, const Flow& server_flow); void process_packet(PDU& packet); - TCPFlow& client_flow(); - const TCPFlow& client_flow() const; - TCPFlow& server_flow(); - const TCPFlow& server_flow() const; + Flow& client_flow(); + const Flow& client_flow() const; + Flow& server_flow(); + const Flow& server_flow() const; + bool is_finished() const; + bool is_v6() const; + + IPv4Address client_addr_v4() const; + IPv6Address client_addr_v6() const; + IPv4Address server_addr_v4() const; + IPv6Address server_addr_v6() const; + uint16_t client_port() const; + uint16_t server_port() const; + const payload_type& client_payload() const; + payload_type& client_payload(); + const payload_type& server_payload() const; + payload_type& server_payload(); + + void stream_closed_callback(const stream_callback& callback); void client_data_callback(const stream_callback& callback); void server_data_callback(const stream_callback& callback); void client_buffering_callback(const stream_callback& callback); @@ -137,17 +155,17 @@ public: void setup_flows_callbacks(); private: - static TCPFlow extract_client_flow(const PDU& packet); - static TCPFlow extract_server_flow(const PDU& packet); + static Flow extract_client_flow(const PDU& packet); + static Flow extract_server_flow(const PDU& packet); + void on_client_flow_data(const Flow& flow); + void on_server_flow_data(const Flow& flow); + void on_client_buffering(const Flow& flow); + void on_server_buffering(const Flow& flow); - void on_client_flow_data(const TCPFlow& flow); - void on_server_flow_data(const TCPFlow& flow); - void on_client_buffering(const TCPFlow& flow); - void on_server_buffering(const TCPFlow& flow); - - TCPFlow client_flow_; - TCPFlow server_flow_; + Flow client_flow_; + Flow server_flow_; + stream_callback on_stream_closed_; stream_callback on_client_data_callback_; stream_callback on_server_data_callback_; stream_callback on_client_buffering_callback_; @@ -155,22 +173,19 @@ private: State state_; }; -class TINS_API TCPStreamFollower { +class TINS_API StreamFollower { public: - typedef TCPStream::stream_callback stream_callback; + typedef Stream::stream_callback stream_callback; - TCPStreamFollower(); + StreamFollower(); - void process_packet(PDU& packet); + bool process_packet(PDU& packet); + void new_stream_callback(const stream_callback& callback); - void client_data_callback(const stream_callback& callback); - void server_data_callback(const stream_callback& callback); - void client_buffering_callback(const stream_callback& callback); - void server_buffering_callback(const stream_callback& callback); - - TCPStream& find_stream(IPv4Address client_addr, uint16_t client_port, + Stream& find_stream(IPv4Address client_addr, uint16_t client_port, IPv4Address server_addr, uint16_t server_port); private: + static const size_t DEFAULT_MAX_BUFFERED_CHUNKS; typedef std::array address_type; struct stream_id { @@ -187,18 +202,15 @@ private: static size_t hash(const stream_id& id); }; - typedef std::map streams_type; + typedef std::map streams_type; stream_id make_stream_id(const PDU& packet); - TCPStream make_stream(const PDU& packet); static address_type serialize(IPv4Address address); static address_type serialize(const IPv6Address& address); streams_type streams_; - stream_callback on_client_data_callback_; - stream_callback on_server_data_callback_; - stream_callback on_client_buffering_callback_; - stream_callback on_server_buffering_callback_; + stream_callback on_new_connection_; + size_t max_buffered_chunks_; bool attach_to_flows_; }; diff --git a/src/tcp_ip.cpp b/src/tcp_ip.cpp index 9d0573c..8e90f36 100644 --- a/src/tcp_ip.cpp +++ b/src/tcp_ip.cpp @@ -68,33 +68,33 @@ int seq_compare(uint32_t seq1, uint32_t seq2) { } } -// TCPFlow +// Flow -TCPFlow::TCPFlow(const IPv4Address& dest_address, uint16_t dest_port, - uint32_t sequence_number) +Flow::Flow(const IPv4Address& dest_address, uint16_t dest_port, + uint32_t sequence_number) : seq_number_(sequence_number), dest_port_(dest_port), is_v6_(false), state_(UNKNOWN) { OutputMemoryStream output(dest_address_.data(), dest_address_.size()); output.write(dest_address); } -TCPFlow::TCPFlow(const IPv6Address& dest_address, uint16_t dest_port, - uint32_t sequence_number) +Flow::Flow(const IPv6Address& dest_address, uint16_t dest_port, + uint32_t sequence_number) : seq_number_(sequence_number), dest_port_(dest_port), is_v6_(true), state_(UNKNOWN) { OutputMemoryStream output(dest_address_.data(), dest_address_.size()); output.write(dest_address); } -void TCPFlow::data_callback(const event_callback& callback) { +void Flow::data_callback(const event_callback& callback) { on_data_callback_ = callback; } -void TCPFlow::buffering_callback(const event_callback& callback) { +void Flow::buffering_callback(const event_callback& callback) { on_buffering_callback_= callback; } -void TCPFlow::process_packet(PDU& pdu) { +void Flow::process_packet(PDU& pdu) { TCP* tcp = pdu.find_pdu(); RawPDU* raw = pdu.find_pdu(); // If we sent a packet with RST or FIN on, this flow is done @@ -172,7 +172,7 @@ void TCPFlow::process_packet(PDU& pdu) { } } -void TCPFlow::store_payload(uint32_t seq, const payload_type& payload) { +void Flow::store_payload(uint32_t seq, const payload_type& payload) { buffered_payload_type::iterator iter = buffered_payload_.find(seq); // New segment, store it if (iter == buffered_payload_.end()) { @@ -185,7 +185,7 @@ void TCPFlow::store_payload(uint32_t seq, const payload_type& payload) { } } -TCPFlow::buffered_payload_type::iterator TCPFlow::erase_iterator(buffered_payload_type::iterator iter) { +Flow::buffered_payload_type::iterator Flow::erase_iterator(buffered_payload_type::iterator iter) { buffered_payload_type::iterator output = iter; ++output; buffered_payload_.erase(iter); @@ -195,7 +195,7 @@ TCPFlow::buffered_payload_type::iterator TCPFlow::erase_iterator(buffered_payloa return output; } -void TCPFlow::update_state(const TCP& tcp) { +void Flow::update_state(const TCP& tcp) { if ((tcp.flags() & TCP::FIN) != 0) { state_ = FIN_SENT; } @@ -212,15 +212,15 @@ void TCPFlow::update_state(const TCP& tcp) { } } -bool TCPFlow::is_v6() const { +bool Flow::is_v6() const { return is_v6_; } -bool TCPFlow::is_finished() const { +bool Flow::is_finished() const { return state_ == FIN_SENT || state_ == RST_SENT; } -bool TCPFlow::packet_belongs(const PDU& packet) const { +bool Flow::packet_belongs(const PDU& packet) const { if (is_v6()) { const IPv6* ip = packet.find_pdu(); if (!ip || ip->dst_addr() != dst_addr_v6()) { @@ -237,105 +237,177 @@ bool TCPFlow::packet_belongs(const PDU& packet) const { return tcp && tcp->dport() == dport(); } -IPv4Address TCPFlow::dst_addr_v4() const { +IPv4Address Flow::dst_addr_v4() const { InputMemoryStream stream(dest_address_.data(), dest_address_.size()); return stream.read(); } -IPv6Address TCPFlow::dst_addr_v6() const { +IPv6Address Flow::dst_addr_v6() const { InputMemoryStream stream(dest_address_.data(), dest_address_.size()); return stream.read(); } -uint16_t TCPFlow::dport() const { +uint16_t Flow::dport() const { return dest_port_; } -const TCPFlow::payload_type& TCPFlow::payload() const { +const Flow::payload_type& Flow::payload() const { return payload_; } -TCPFlow::payload_type& TCPFlow::payload() { - return payload_; -} - -void TCPFlow::state(State new_state) { - state_ = new_state; -} - -TCPFlow::State TCPFlow::state() const { +Flow::State Flow::state() const { return state_; } -uint32_t TCPFlow::sequence_number() const { +uint32_t Flow::sequence_number() const { return seq_number_; } -// TCPStream +const Flow::buffered_payload_type& Flow::buffered_payload() const { + return buffered_payload_; +} -TCPStream::TCPStream(const PDU& packet) +Flow::buffered_payload_type& Flow::buffered_payload() { + return buffered_payload_; +} + +Flow::payload_type& Flow::payload() { + return payload_; +} + +void Flow::state(State new_state) { + state_ = new_state; +} + +// Stream + +Stream::Stream(const PDU& packet) : client_flow_(extract_client_flow(packet)), server_flow_(extract_server_flow(packet)) { } -TCPStream::TCPStream(const TCPFlow& client_flow, const TCPFlow& server_flow) +Stream::Stream(const Flow& client_flow, const Flow& server_flow) : client_flow_(client_flow), server_flow_(server_flow) { } -void TCPStream::process_packet(PDU& packet) { +void Stream::process_packet(PDU& packet) { if (client_flow_.packet_belongs(packet)) { client_flow_.process_packet(packet); } else if (server_flow_.packet_belongs(packet)) { server_flow_.process_packet(packet); } + if (is_finished() && on_stream_closed_) { + on_stream_closed_(*this); + } } -TCPFlow& TCPStream::client_flow() { +Flow& Stream::client_flow() { return client_flow_; } -const TCPFlow& TCPStream::client_flow() const { +const Flow& Stream::client_flow() const { return client_flow_; } -TCPFlow& TCPStream::server_flow() { +Flow& Stream::server_flow() { return server_flow_; } -const TCPFlow& TCPStream::server_flow() const { +const Flow& Stream::server_flow() const { return server_flow_; } -void TCPStream::client_data_callback(const stream_callback& callback) { +void Stream::stream_closed_callback(const stream_callback& callback) { + on_stream_closed_ = callback; +} + +void Stream::client_data_callback(const stream_callback& callback) { on_client_data_callback_ = callback; } -void TCPStream::server_data_callback(const stream_callback& callback) { +void Stream::server_data_callback(const stream_callback& callback) { on_server_data_callback_ = callback; } -void TCPStream::client_buffering_callback(const stream_callback& callback) { +void Stream::client_buffering_callback(const stream_callback& callback) { on_client_buffering_callback_ = callback; } -void TCPStream::server_buffering_callback(const stream_callback& callback) { +void Stream::server_buffering_callback(const stream_callback& callback) { on_server_buffering_callback_ = callback; } -TCPFlow TCPStream::extract_client_flow(const PDU& packet) { +bool Stream::is_finished() const { + const Flow::State client_state = client_flow_.state(); + const Flow::State server_state = server_flow_.state(); + // If either peer sent a RST then the stream is done + if (client_state == Flow::RST_SENT || server_state == Flow::RST_SENT) { + return true; + } + else { + // Otherwise, only finish if both sent a FIN + return client_state == Flow::FIN_SENT && server_state == Flow::FIN_SENT; + } +} + +bool Stream::is_v6() const { + return server_flow().is_v6(); +} + +IPv4Address Stream::client_addr_v4() const { + return server_flow().dst_addr_v4(); +} + +IPv6Address Stream::client_addr_v6() const { + return server_flow().dst_addr_v6(); +} + +IPv4Address Stream::server_addr_v4() const { + return client_flow().dst_addr_v4(); +} + +IPv6Address Stream::server_addr_v6() const { + return client_flow().dst_addr_v6(); +} + +uint16_t Stream::client_port() const { + return server_flow().dport(); +} + +uint16_t Stream::server_port() const { + return client_flow().dport(); +} + +const Stream::payload_type& Stream::client_payload() const { + return client_flow().payload(); +} + +Stream::payload_type& Stream::client_payload() { + return client_flow().payload(); +} + +const Stream::payload_type& Stream::server_payload() const { + return server_flow().payload(); +} + +Stream::payload_type& Stream::server_payload() { + return server_flow().payload(); +} + +Flow Stream::extract_client_flow(const PDU& packet) { const TCP* tcp = packet.find_pdu(); if (!tcp) { // TODO: define proper exception throw runtime_error("No TCP"); } if (const IP* ip = packet.find_pdu()) { - return TCPFlow(ip->dst_addr(), tcp->dport(), tcp->seq()); + return Flow(ip->dst_addr(), tcp->dport(), tcp->seq()); } else if (const IPv6* ip = packet.find_pdu()) { - return TCPFlow(ip->dst_addr(), tcp->dport(), tcp->seq()); + return Flow(ip->dst_addr(), tcp->dport(), tcp->seq()); } else { // TODO: define proper exception @@ -343,17 +415,17 @@ TCPFlow TCPStream::extract_client_flow(const PDU& packet) { } } -TCPFlow TCPStream::extract_server_flow(const PDU& packet) { +Flow Stream::extract_server_flow(const PDU& packet) { const TCP* tcp = packet.find_pdu(); if (!tcp) { // TODO: define proper exception throw runtime_error("No TCP"); } if (const IP* ip = packet.find_pdu()) { - return TCPFlow(ip->src_addr(), tcp->sport(), tcp->ack_seq()); + return Flow(ip->src_addr(), tcp->sport(), tcp->ack_seq()); } else if (const IPv6* ip = packet.find_pdu()) { - return TCPFlow(ip->src_addr(), tcp->sport(), tcp->ack_seq()); + return Flow(ip->src_addr(), tcp->sport(), tcp->ack_seq()); } else { // TODO: define proper exception @@ -361,46 +433,49 @@ TCPFlow TCPStream::extract_server_flow(const PDU& packet) { } } -void TCPStream::setup_flows_callbacks() { +void Stream::setup_flows_callbacks() { using std::placeholders::_1; - client_flow_.data_callback(bind(&TCPStream::on_client_flow_data, this, _1)); - server_flow_.data_callback(bind(&TCPStream::on_server_flow_data, this, _1)); - client_flow_.buffering_callback(bind(&TCPStream::on_client_buffering, this, _1)); - server_flow_.buffering_callback(bind(&TCPStream::on_server_buffering, this, _1)); + client_flow_.data_callback(bind(&Stream::on_client_flow_data, this, _1)); + server_flow_.data_callback(bind(&Stream::on_server_flow_data, this, _1)); + client_flow_.buffering_callback(bind(&Stream::on_client_buffering, this, _1)); + server_flow_.buffering_callback(bind(&Stream::on_server_buffering, this, _1)); } -void TCPStream::on_client_flow_data(const TCPFlow& flow) { + +void Stream::on_client_flow_data(const Flow& flow) { if (on_client_data_callback_) { on_client_data_callback_(*this); } } -void TCPStream::on_server_flow_data(const TCPFlow& flow) { +void Stream::on_server_flow_data(const Flow& flow) { if (on_server_data_callback_) { on_server_data_callback_(*this); } } -void TCPStream::on_client_buffering(const TCPFlow& flow) { +void Stream::on_client_buffering(const Flow& flow) { if (on_client_buffering_callback_) { on_client_buffering_callback_(*this); } } -void TCPStream::on_server_buffering(const TCPFlow& flow) { +void Stream::on_server_buffering(const Flow& flow) { if (on_server_buffering_callback_) { on_server_buffering_callback_(*this); } } -// TCPStreamFollower +// StreamFollower -TCPStreamFollower::TCPStreamFollower() -: attach_to_flows_(false) { +const size_t StreamFollower::DEFAULT_MAX_BUFFERED_CHUNKS = 512; + +StreamFollower::StreamFollower() +: max_buffered_chunks_(DEFAULT_MAX_BUFFERED_CHUNKS), attach_to_flows_(false) { } -void TCPStreamFollower::process_packet(PDU& packet) { +bool StreamFollower::process_packet(PDU& packet) { stream_id identifier = make_stream_id(packet); streams_type::iterator iter = streams_.find(identifier); bool process = true; @@ -409,17 +484,24 @@ void TCPStreamFollower::process_packet(PDU& packet) { // Start tracking if they're either SYNs or they contain data (attach // to an already running flow). if (tcp.flags() == TCP::SYN || (attach_to_flows_ && tcp.find_pdu() != 0)) { - iter = streams_.insert(make_pair(identifier, make_stream(packet))).first; + iter = streams_.insert(make_pair(identifier, Stream(packet))).first; iter->second.setup_flows_callbacks(); + if (on_new_connection_) { + on_new_connection_(iter->second); + } + else { + // TODO: use proper exception + throw runtime_error("No new connection callback set"); + } if (tcp.flags() == TCP::SYN) { // If it's a SYN, set the proper state - iter->second.client_flow().state(TCPFlow::SYN_SENT); + iter->second.client_flow().state(Flow::SYN_SENT); process = false; } else { // Otherwise, assume the connection is established - iter->second.client_flow().state(TCPFlow::ESTABLISHED); - iter->second.server_flow().state(TCPFlow::ESTABLISHED); + iter->second.client_flow().state(Flow::ESTABLISHED); + iter->second.server_flow().state(Flow::ESTABLISHED); } } else { @@ -429,28 +511,23 @@ void TCPStreamFollower::process_packet(PDU& packet) { // We'll process it if we had already seen this stream or if we just attached to // it and it contains payload if (process) { - iter->second.process_packet(packet); + Stream& stream = iter->second; + stream.process_packet(packet); + size_t total_chunks = stream.client_flow().buffered_payload().size() + + stream.server_flow().buffered_payload().size(); + if (stream.is_finished() || total_chunks > max_buffered_chunks_) { + streams_.erase(iter); + } } + return true; } -void TCPStreamFollower::client_data_callback(const stream_callback& callback) { - on_client_data_callback_ = callback; +void StreamFollower::new_stream_callback(const stream_callback& callback) { + on_new_connection_ = callback; } -void TCPStreamFollower::server_data_callback(const stream_callback& callback) { - on_server_data_callback_ = callback; -} - -void TCPStreamFollower::client_buffering_callback(const stream_callback& callback) { - on_client_buffering_callback_ = callback; -} - -void TCPStreamFollower::server_buffering_callback(const stream_callback& callback) { - on_server_buffering_callback_ = callback; -} - -TCPStream& TCPStreamFollower::find_stream(IPv4Address client_addr, uint16_t client_port, - IPv4Address server_addr, uint16_t server_port) { +Stream& StreamFollower::find_stream(IPv4Address client_addr, uint16_t client_port, + IPv4Address server_addr, uint16_t server_port) { stream_id identifier(serialize(client_addr), client_port, serialize(server_addr), server_port); streams_type::iterator iter = streams_.find(identifier); @@ -463,7 +540,7 @@ TCPStream& TCPStreamFollower::find_stream(IPv4Address client_addr, uint16_t clie } } -TCPStreamFollower::stream_id TCPStreamFollower::make_stream_id(const PDU& packet) { +StreamFollower::stream_id StreamFollower::make_stream_id(const PDU& packet) { const TCP* tcp = packet.find_pdu(); if (!tcp) { // TODO: define proper exception @@ -483,23 +560,14 @@ TCPStreamFollower::stream_id TCPStreamFollower::make_stream_id(const PDU& packet } } -TCPStream TCPStreamFollower::make_stream(const PDU& packet) { - TCPStream stream(packet); - stream.client_data_callback(on_client_data_callback_); - stream.server_data_callback(on_server_data_callback_); - stream.client_buffering_callback(on_client_buffering_callback_); - stream.server_buffering_callback(on_server_buffering_callback_); - return stream; -} - -TCPStreamFollower::address_type TCPStreamFollower::serialize(IPv4Address address) { +StreamFollower::address_type StreamFollower::serialize(IPv4Address address) { address_type addr; OutputMemoryStream output(addr.data(), addr.size()); output.write(address); return addr; } -TCPStreamFollower::address_type TCPStreamFollower::serialize(const IPv6Address& address) { +StreamFollower::address_type StreamFollower::serialize(const IPv6Address& address) { address_type addr; OutputMemoryStream output(addr.data(), addr.size()); output.write(address); @@ -508,10 +576,10 @@ TCPStreamFollower::address_type TCPStreamFollower::serialize(const IPv6Address& // stream_id -TCPStreamFollower::stream_id::stream_id(const address_type& client_addr, - uint16_t client_port, - const address_type& server_addr, - uint16_t server_port) +StreamFollower::stream_id::stream_id(const address_type& client_addr, + uint16_t client_port, + const address_type& server_addr, + uint16_t server_port) : min_address(client_addr), max_address(server_addr), min_address_port(client_port), max_address_port(server_port) { if (min_address > max_address) { @@ -520,7 +588,7 @@ max_address_port(server_port) { } } -bool TCPStreamFollower::stream_id::operator<(const stream_id& rhs) const { +bool StreamFollower::stream_id::operator<(const stream_id& rhs) const { return tie(min_address, min_address_port, max_address, max_address_port) < tie(rhs.min_address, rhs.min_address_port, rhs.max_address, rhs.max_address_port); } diff --git a/tests/src/tcp_ip.cpp b/tests/src/tcp_ip.cpp index 30cd17e..66a110e 100644 --- a/tests/src/tcp_ip.cpp +++ b/tests/src/tcp_ip.cpp @@ -21,7 +21,7 @@ using namespace std; using namespace Tins; using namespace Tins::TCPIP; -class TCPFlowTest : public testing::Test { +class FlowTest : public testing::Test { public: struct order_element { order_element(size_t payload_index, uint32_t payload_size) @@ -40,17 +40,18 @@ public: static const string payload; typedef vector ordering_info_type; - void cumulative_flow_data_handler(TCPFlow& flow); - void cumulative_stream_client_data_handler(TCPStream& stream); - void cumulative_stream_server_data_handler(TCPStream& stream); - void buffered_payload_handle(TCPFlow& session); + void cumulative_flow_data_handler(Flow& flow); + void on_new_stream(Stream& stream); + void cumulative_stream_client_data_handler(Stream& stream); + void cumulative_stream_server_data_handler(Stream& stream); + void buffered_payload_handle(Flow& session); void run_test(uint32_t initial_seq, const ordering_info_type& chunks, const string& payload); void run_test(uint32_t initial_seq, const ordering_info_type& chunks); void run_tests(const ordering_info_type& chunks, const string& payload); void run_tests(const ordering_info_type& chunks); ordering_info_type split_payload(const string& payload, uint32_t chunk_size); - string merge_chunks(const vector& chunks); + string merge_chunks(const vector& chunks); vector chunks_to_packets(uint32_t initial_seq, const ordering_info_type& chunks, const string& payload); @@ -61,77 +62,83 @@ public: uint16_t src_port, IPv4Address dst_addr, uint16_t dst_port); - vector flow_payload_chunks; - vector stream_client_payload_chunks; - vector stream_server_payload_chunks; + vector flow_payload_chunks; + vector stream_client_payload_chunks; + vector stream_server_payload_chunks; }; -const string TCPFlowTest::payload = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " - "Sed at aliquam arcu. Sed at iaculis magna. Nam ut dolor " - "eget velit mattis posuere ut non dui. Aliquam faucibus " - "erat pretium ligula tincidunt eget tristique justo placerat. " - "Phasellus turpis tellus, ornare ultricies egestas vitae, " - "mollis sed neque. Sed et libero in nunc pharetra auctor ut " - "a eros. Mauris quis faucibus nibh. \nLorem ipsum dolor sit " - "amet, consectetur adipiscing elit. Sed at aliquam arcu. " - "Sed at iaculis magna. Nam ut dolor eget velit mattis " - "posuere ut non dui. Aliquam faucibus erat pretium ligula " - "tincidunt eget tristique justo placerat. Phasellus turpis " - "tellus, ornare ultricies egestas vitae, mollis sed neque. " - "Sed et libero in nunc pharetra auctor ut a eros. Mauris " - "quis faucibus nibh. \n\n\nCurabitur sem erat, bibendum " - "quis condimentum ut, imperdiet at est. Duis sagittis rhoncus " - "felis at ultricies. In libero urna, dignissim eu elementum " - "quis, consectetur a neque. Praesent leo sem, cursus sed lobortis " - "sit amet, ornare ac augue. Mauris tristique semper ipsum at " - "consequat. Sed fringilla dolor ut lacus sagittis quis ultricies " - "leo vulputate. Maecenas dignissim imperdiet justo. Cras libero " - "odio, vehicula et adipiscing quis, luctus vel ante. \nAliquam " - "imperdiet est quis nunc malesuada eget convallis tellus " - "ullamcorper. Vivamus ullamcorper eros sit amet odio sollicitudin " - "rutrum. Donec pellentesque faucibus nulla, ut fringilla risus " - "aliquam eget. Sed et ante mi. Morbi a turpis et tellus dapibus " - "iaculis. Etiam faucibus tellus sed metus consequat rutrum. " - "Fusce sit amet nulla massa, tempus vulputate sem. Cras tincidunt " - "quam in libero rutrum interdum. Aliquam quam sapien, facilisis " - "at vestibulum et, venenatis id mauris. Morbi rutrum gravida " - "ultricies. \nAenean et justo ut libero euismod sollicitudin. " - "Nullam enim dui, iaculis vitae bibendum et, commodo in tellus. " - "Nullam eget purus mi, a ullamcorper lorem. Suspendisse potenti. " - "Duis ac justo ut leo euismod gravida sit amet at lectus. Lorem " - "ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sed " - "arcu vitae nisi sollicitudin gravida. Nulla facilisis nibh turpis. " - "Maecenas quis imperdiet arcu. Sed sit amet nulla urna, at " - "vestibulum mauris. Suspendisse quis elit dui. Class aptent taciti " - "sociosqu ad litora torquent per conubia nostra, per inceptos " - "himenaeos. \n"; +const string FlowTest::payload = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " + "Sed at aliquam arcu. Sed at iaculis magna. Nam ut dolor " + "eget velit mattis posuere ut non dui. Aliquam faucibus " + "erat pretium ligula tincidunt eget tristique justo placerat. " + "Phasellus turpis tellus, ornare ultricies egestas vitae, " + "mollis sed neque. Sed et libero in nunc pharetra auctor ut " + "a eros. Mauris quis faucibus nibh. \nLorem ipsum dolor sit " + "amet, consectetur adipiscing elit. Sed at aliquam arcu. " + "Sed at iaculis magna. Nam ut dolor eget velit mattis " + "posuere ut non dui. Aliquam faucibus erat pretium ligula " + "tincidunt eget tristique justo placerat. Phasellus turpis " + "tellus, ornare ultricies egestas vitae, mollis sed neque. " + "Sed et libero in nunc pharetra auctor ut a eros. Mauris " + "quis faucibus nibh. \n\n\nCurabitur sem erat, bibendum " + "quis condimentum ut, imperdiet at est. Duis sagittis rhoncus " + "felis at ultricies. In libero urna, dignissim eu elementum " + "quis, consectetur a neque. Praesent leo sem, cursus sed lobortis " + "sit amet, ornare ac augue. Mauris tristique semper ipsum at " + "consequat. Sed fringilla dolor ut lacus sagittis quis ultricies " + "leo vulputate. Maecenas dignissim imperdiet justo. Cras libero " + "odio, vehicula et adipiscing quis, luctus vel ante. \nAliquam " + "imperdiet est quis nunc malesuada eget convallis tellus " + "ullamcorper. Vivamus ullamcorper eros sit amet odio sollicitudin " + "rutrum. Donec pellentesque faucibus nulla, ut fringilla risus " + "aliquam eget. Sed et ante mi. Morbi a turpis et tellus dapibus " + "iaculis. Etiam faucibus tellus sed metus consequat rutrum. " + "Fusce sit amet nulla massa, tempus vulputate sem. Cras tincidunt " + "quam in libero rutrum interdum. Aliquam quam sapien, facilisis " + "at vestibulum et, venenatis id mauris. Morbi rutrum gravida " + "ultricies. \nAenean et justo ut libero euismod sollicitudin. " + "Nullam enim dui, iaculis vitae bibendum et, commodo in tellus. " + "Nullam eget purus mi, a ullamcorper lorem. Suspendisse potenti. " + "Duis ac justo ut leo euismod gravida sit amet at lectus. Lorem " + "ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sed " + "arcu vitae nisi sollicitudin gravida. Nulla facilisis nibh turpis. " + "Maecenas quis imperdiet arcu. Sed sit amet nulla urna, at " + "vestibulum mauris. Suspendisse quis elit dui. Class aptent taciti " + "sociosqu ad litora torquent per conubia nostra, per inceptos " + "himenaeos. \n"; -void TCPFlowTest::cumulative_flow_data_handler(TCPFlow& flow) { +void FlowTest::cumulative_flow_data_handler(Flow& flow) { flow_payload_chunks.push_back(flow.payload()); flow.payload().clear(); } -void TCPFlowTest::cumulative_stream_client_data_handler(TCPStream& stream) { +void FlowTest::on_new_stream(Stream& stream) { + using std::placeholders::_1; + stream.client_data_callback(bind(&FlowTest::cumulative_stream_client_data_handler, + this, _1)); +} + +void FlowTest::cumulative_stream_client_data_handler(Stream& stream) { stream_client_payload_chunks.push_back(stream.client_flow().payload()); stream.client_flow().payload().clear(); } -void TCPFlowTest::cumulative_stream_server_data_handler(TCPStream& stream) { +void FlowTest::cumulative_stream_server_data_handler(Stream& stream) { stream_server_payload_chunks.push_back(stream.server_flow().payload()); stream.server_flow().payload().clear(); } -void TCPFlowTest::buffered_payload_handle(TCPFlow& session) { +void FlowTest::buffered_payload_handle(Flow& session) { } -void TCPFlowTest::run_test(uint32_t initial_seq, const ordering_info_type& chunks, - const string& payload) { +void FlowTest::run_test(uint32_t initial_seq, const ordering_info_type& chunks, + const string& payload) { using std::placeholders::_1; flow_payload_chunks.clear(); - TCPFlow flow(IPv4Address("1.2.3.4"), 22, initial_seq); - flow.data_callback(bind(&TCPFlowTest::cumulative_flow_data_handler, this, _1)); + Flow flow(IPv4Address("1.2.3.4"), 22, initial_seq); + flow.data_callback(bind(&FlowTest::cumulative_flow_data_handler, this, _1)); vector packets = chunks_to_packets(initial_seq, chunks, payload); for (size_t i = 0; i < packets.size(); ++i) { flow.process_packet(packets[i]); @@ -140,11 +147,11 @@ void TCPFlowTest::run_test(uint32_t initial_seq, const ordering_info_type& chunk EXPECT_EQ(payload, string(flow_payload.begin(), flow_payload.end())); } -void TCPFlowTest::run_test(uint32_t initial_seq, const ordering_info_type& chunks) { +void FlowTest::run_test(uint32_t initial_seq, const ordering_info_type& chunks) { run_test(initial_seq, chunks, payload); } -void TCPFlowTest::run_tests(const ordering_info_type& chunks, const string& payload) { +void FlowTest::run_tests(const ordering_info_type& chunks, const string& payload) { run_test(0, chunks, payload); run_test(20, chunks, payload); run_test(numeric_limits::max() / 2, chunks, payload); @@ -155,12 +162,12 @@ void TCPFlowTest::run_tests(const ordering_info_type& chunks, const string& payl run_test(numeric_limits::max() - 31, chunks, payload); } -void TCPFlowTest::run_tests(const ordering_info_type& chunks) { +void FlowTest::run_tests(const ordering_info_type& chunks) { run_tests(chunks, payload); } -TCPFlowTest::ordering_info_type TCPFlowTest::split_payload(const string& payload, - uint32_t chunk_size) { +FlowTest::ordering_info_type FlowTest::split_payload(const string& payload, + uint32_t chunk_size) { ordering_info_type output; uint32_t chunk_count = payload.size() / chunk_size; for (uint32_t i = 0; i < chunk_count; ++i) { @@ -173,18 +180,18 @@ TCPFlowTest::ordering_info_type TCPFlowTest::split_payload(const string& payload return output; } -string TCPFlowTest::merge_chunks(const vector& chunks) { +string FlowTest::merge_chunks(const vector& chunks) { string output; for (size_t i = 0; i < chunks.size(); ++i) { - TCPFlow::payload_type this_chunk = chunks[i]; + Flow::payload_type this_chunk = chunks[i]; output += string(this_chunk.begin(), this_chunk.end()); } return output; } -vector TCPFlowTest::chunks_to_packets(uint32_t initial_seq, - const ordering_info_type& chunks, - const string& payload) { +vector FlowTest::chunks_to_packets(uint32_t initial_seq, + const ordering_info_type& chunks, + const string& payload) { vector output; for (size_t i = 0; i < chunks.size(); ++i) { const order_element& element = chunks[i]; @@ -198,9 +205,9 @@ vector TCPFlowTest::chunks_to_packets(uint32_t initial_seq, return output; } -vector TCPFlowTest::three_way_handshake(uint32_t client_seq, uint32_t server_seq, - IPv4Address client_addr, uint16_t client_port, - IPv4Address server_addr, uint16_t server_port) { +vector FlowTest::three_way_handshake(uint32_t client_seq, uint32_t server_seq, + IPv4Address client_addr, uint16_t client_port, + IPv4Address server_addr, uint16_t server_port) { vector output; output.push_back(EthernetII() / IP(server_addr, client_addr) / TCP(server_port, client_port)); output.push_back(EthernetII() / IP(client_addr, server_addr) / TCP(client_port, server_port)); @@ -216,9 +223,9 @@ vector TCPFlowTest::three_way_handshake(uint32_t client_seq, uint32_ return output; } -void TCPFlowTest::set_endpoints(vector& packets, IPv4Address src_addr, - uint16_t src_port, IPv4Address dst_addr, - uint16_t dst_port) { +void FlowTest::set_endpoints(vector& packets, IPv4Address src_addr, + uint16_t src_port, IPv4Address dst_addr, + uint16_t dst_port) { for (size_t i = 0; i < packets.size(); ++i) { packets[i].rfind_pdu().src_addr(src_addr); packets[i].rfind_pdu().dst_addr(dst_addr); @@ -227,12 +234,12 @@ void TCPFlowTest::set_endpoints(vector& packets, IPv4Address src_add } } -TEST_F(TCPFlowTest, ReassembleStreamPlain) { +TEST_F(FlowTest, ReassembleStreamPlain) { ordering_info_type chunks = split_payload(payload, 5); run_tests(chunks); } -TEST_F(TCPFlowTest, ReassembleStreamReordering) { +TEST_F(FlowTest, ReassembleStreamReordering) { ordering_info_type chunks = split_payload(payload, 5); // e.g. input [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] // after this it's [2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8] @@ -244,13 +251,13 @@ TEST_F(TCPFlowTest, ReassembleStreamReordering) { run_tests(chunks); } -TEST_F(TCPFlowTest, ReassembleStreamReversed) { +TEST_F(FlowTest, ReassembleStreamReversed) { ordering_info_type chunks = split_payload(payload, 5); reverse(chunks.begin(), chunks.end()); run_tests(chunks); } -TEST_F(TCPFlowTest, Overlapping) { +TEST_F(FlowTest, Overlapping) { string payload = "Hello world. This is a payload"; ordering_info_type chunks; // "Hello " @@ -270,35 +277,100 @@ TEST_F(TCPFlowTest, Overlapping) { run_tests(chunks, payload); } -TEST_F(TCPFlowTest, TCPStreamFollower_ThreeWayHandshake) { +TEST_F(FlowTest, StreamFollower_ThreeWayHandshake) { using std::placeholders::_1; vector packets = three_way_handshake(29, 60, "1.2.3.4", 22, "4.3.2.1", 25); - TCPStreamFollower follower; - follower.client_data_callback(bind(&TCPFlowTest::cumulative_stream_client_data_handler, - this, _1)); + StreamFollower follower; + follower.new_stream_callback(bind(&FlowTest::on_new_stream, this, _1)); for (size_t i = 0; i < packets.size(); ++i) { follower.process_packet(packets[i]); } - TCPStream& stream = follower.find_stream("1.2.3.4", 22, "4.3.2.1", 25); - EXPECT_EQ(TCPFlow::ESTABLISHED, stream.client_flow().state()); - EXPECT_EQ(TCPFlow::SYN_SENT, stream.server_flow().state()); + Stream& stream = follower.find_stream("1.2.3.4", 22, "4.3.2.1", 25); + EXPECT_EQ(Flow::ESTABLISHED, stream.client_flow().state()); + EXPECT_EQ(Flow::SYN_SENT, stream.server_flow().state()); EXPECT_EQ(30, stream.client_flow().sequence_number()); EXPECT_EQ(60, stream.server_flow().sequence_number()); EXPECT_EQ(IPv4Address("4.3.2.1"), stream.client_flow().dst_addr_v4()); EXPECT_EQ(25, stream.client_flow().dport()); EXPECT_EQ(IPv4Address("1.2.3.4"), stream.server_flow().dst_addr_v4()); EXPECT_EQ(22, stream.server_flow().dport()); + EXPECT_EQ(IPv4Address("1.2.3.4"), stream.client_addr_v4()); + EXPECT_EQ(IPv4Address("4.3.2.1"), stream.server_addr_v4()); + EXPECT_EQ(22, stream.client_port()); + EXPECT_EQ(25, stream.server_port()); IP server_packet = IP("1.2.3.4", "4.3.2.1") / TCP(22, 25); server_packet.rfind_pdu().flags(TCP::ACK); follower.process_packet(server_packet); - EXPECT_EQ(TCPFlow::ESTABLISHED, stream.server_flow().state()); + EXPECT_EQ(Flow::ESTABLISHED, stream.server_flow().state()); EXPECT_EQ(61, stream.server_flow().sequence_number()); } -TEST_F(TCPFlowTest, TCPStreamFollower_FollowStream) { +TEST_F(FlowTest, StreamFollower_RSTClosesStream) { + using std::placeholders::_1; + + vector packets = three_way_handshake(29, 60, "1.2.3.4", 22, "4.3.2.1", 25); + StreamFollower follower; + follower.new_stream_callback(bind(&FlowTest::on_new_stream, this, _1)); + for (size_t i = 0; i < packets.size(); ++i) { + follower.process_packet(packets[i]); + } + Stream stream = follower.find_stream("1.2.3.4", 22, "4.3.2.1", 25); + + IP server_packet = IP("1.2.3.4", "4.3.2.1") / TCP(22, 25); + server_packet.rfind_pdu().flags(TCP::RST); + stream.process_packet(server_packet); + + EXPECT_EQ(Flow::RST_SENT, stream.server_flow().state()); + EXPECT_TRUE(stream.is_finished()); +} + +TEST_F(FlowTest, StreamFollower_FINClosesStream) { + using std::placeholders::_1; + + vector packets = three_way_handshake(29, 60, "1.2.3.4", 22, "4.3.2.1", 25); + StreamFollower follower; + follower.new_stream_callback(bind(&FlowTest::on_new_stream, this, _1)); + for (size_t i = 0; i < packets.size(); ++i) { + follower.process_packet(packets[i]); + } + Stream stream = follower.find_stream("1.2.3.4", 22, "4.3.2.1", 25); + + IP server_packet = IP("1.2.3.4", "4.3.2.1") / TCP(22, 25); + server_packet.rfind_pdu().flags(TCP::FIN | TCP::ACK); + stream.process_packet(server_packet); + + EXPECT_EQ(Flow::FIN_SENT, stream.server_flow().state()); + EXPECT_FALSE(stream.is_finished()); + + IP client_packet = IP("4.3.2.1", "1.2.3.4") / TCP(25, 22); + client_packet.rfind_pdu().flags(TCP::FIN | TCP::ACK); + stream.process_packet(client_packet); + + EXPECT_EQ(Flow::FIN_SENT, stream.client_flow().state()); + EXPECT_TRUE(stream.is_finished()); +} + +TEST_F(FlowTest, StreamFollower_StreamIsRemovedWhenFinished) { + using std::placeholders::_1; + + vector packets = three_way_handshake(29, 60, "1.2.3.4", 22, "4.3.2.1", 25); + StreamFollower follower; + follower.new_stream_callback(bind(&FlowTest::on_new_stream, this, _1)); + for (size_t i = 0; i < packets.size(); ++i) { + follower.process_packet(packets[i]); + } + IP server_packet = IP("1.2.3.4", "4.3.2.1") / TCP(22, 25); + server_packet.rfind_pdu().flags(TCP::RST); + follower.process_packet(server_packet); + + // We shouldn't be able to find it + EXPECT_THROW(follower.find_stream("1.2.3.4", 22, "4.3.2.1", 25), runtime_error); +} + +TEST_F(FlowTest, StreamFollower_FollowStream) { using std::placeholders::_1; vector packets = three_way_handshake(29, 60, "1.2.3.4", 22, "4.3.2.1", 25); @@ -306,9 +378,8 @@ TEST_F(TCPFlowTest, TCPStreamFollower_FollowStream) { vector chunk_packets = chunks_to_packets(30 /*initial_seq*/, chunks, payload); set_endpoints(chunk_packets, "1.2.3.4", 22, "4.3.2.1", 25); packets.insert(packets.end(), chunk_packets.begin(), chunk_packets.end()); - TCPStreamFollower follower; - follower.client_data_callback(bind(&TCPFlowTest::cumulative_stream_client_data_handler, - this, _1)); + StreamFollower follower; + follower.new_stream_callback(bind(&FlowTest::on_new_stream, this, _1)); for (size_t i = 0; i < packets.size(); ++i) { follower.process_packet(packets[i]); }