mirror of
https://github.com/mfontanini/libtins
synced 2026-01-23 02:35:57 +01:00
Refactor TCP stream code and add http_dump example
This commit is contained in:
@@ -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)
|
||||
|
||||
157
examples/http_dump.cpp
Normal file
157
examples/http_dump.cpp
Normal file
@@ -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 <iostream>
|
||||
#include <sstream>
|
||||
#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] << " <interface>" << 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;
|
||||
}
|
||||
}
|
||||
@@ -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<uint8_t> payload_type;
|
||||
typedef std::map<uint32_t, payload_type> buffered_payload_type;
|
||||
typedef std::function<void(TCPFlow&)> event_callback;
|
||||
typedef std::function<void(Flow&)> 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<void(TCPStream&)> stream_callback;
|
||||
typedef std::function<void(Stream&)> 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<uint8_t, 16> address_type;
|
||||
|
||||
struct stream_id {
|
||||
@@ -187,18 +202,15 @@ private:
|
||||
static size_t hash(const stream_id& id);
|
||||
};
|
||||
|
||||
typedef std::map<stream_id, TCPStream> streams_type;
|
||||
typedef std::map<stream_id, Stream> 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_;
|
||||
};
|
||||
|
||||
|
||||
262
src/tcp_ip.cpp
262
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<TCP>();
|
||||
RawPDU* raw = pdu.find_pdu<RawPDU>();
|
||||
// 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<IPv6>();
|
||||
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<IPv4Address>();
|
||||
}
|
||||
|
||||
IPv6Address TCPFlow::dst_addr_v6() const {
|
||||
IPv6Address Flow::dst_addr_v6() const {
|
||||
InputMemoryStream stream(dest_address_.data(), dest_address_.size());
|
||||
return stream.read<IPv6Address>();
|
||||
}
|
||||
|
||||
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<TCP>();
|
||||
if (!tcp) {
|
||||
// TODO: define proper exception
|
||||
throw runtime_error("No TCP");
|
||||
}
|
||||
if (const IP* ip = packet.find_pdu<IP>()) {
|
||||
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<IPv6>()) {
|
||||
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<TCP>();
|
||||
if (!tcp) {
|
||||
// TODO: define proper exception
|
||||
throw runtime_error("No TCP");
|
||||
}
|
||||
if (const IP* ip = packet.find_pdu<IP>()) {
|
||||
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<IPv6>()) {
|
||||
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<RawPDU>() != 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<TCP>();
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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<order_element> 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<TCPFlow::payload_type>& chunks);
|
||||
string merge_chunks(const vector<Flow::payload_type>& chunks);
|
||||
vector<EthernetII> 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<TCPFlow::payload_type> flow_payload_chunks;
|
||||
vector<TCPFlow::payload_type> stream_client_payload_chunks;
|
||||
vector<TCPFlow::payload_type> stream_server_payload_chunks;
|
||||
vector<Flow::payload_type> flow_payload_chunks;
|
||||
vector<Flow::payload_type> stream_client_payload_chunks;
|
||||
vector<Flow::payload_type> 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<EthernetII> 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<uint32_t>::max() / 2, chunks, payload);
|
||||
@@ -155,12 +162,12 @@ void TCPFlowTest::run_tests(const ordering_info_type& chunks, const string& payl
|
||||
run_test(numeric_limits<uint32_t>::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<TCPFlow::payload_type>& chunks) {
|
||||
string FlowTest::merge_chunks(const vector<Flow::payload_type>& 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<EthernetII> TCPFlowTest::chunks_to_packets(uint32_t initial_seq,
|
||||
const ordering_info_type& chunks,
|
||||
const string& payload) {
|
||||
vector<EthernetII> FlowTest::chunks_to_packets(uint32_t initial_seq,
|
||||
const ordering_info_type& chunks,
|
||||
const string& payload) {
|
||||
vector<EthernetII> output;
|
||||
for (size_t i = 0; i < chunks.size(); ++i) {
|
||||
const order_element& element = chunks[i];
|
||||
@@ -198,9 +205,9 @@ vector<EthernetII> TCPFlowTest::chunks_to_packets(uint32_t initial_seq,
|
||||
return output;
|
||||
}
|
||||
|
||||
vector<EthernetII> 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<EthernetII> 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<EthernetII> 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<EthernetII> TCPFlowTest::three_way_handshake(uint32_t client_seq, uint32_
|
||||
return output;
|
||||
}
|
||||
|
||||
void TCPFlowTest::set_endpoints(vector<EthernetII>& packets, IPv4Address src_addr,
|
||||
uint16_t src_port, IPv4Address dst_addr,
|
||||
uint16_t dst_port) {
|
||||
void FlowTest::set_endpoints(vector<EthernetII>& 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<IP>().src_addr(src_addr);
|
||||
packets[i].rfind_pdu<IP>().dst_addr(dst_addr);
|
||||
@@ -227,12 +234,12 @@ void TCPFlowTest::set_endpoints(vector<EthernetII>& 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<EthernetII> 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<TCP>().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<EthernetII> 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<TCP>().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<EthernetII> 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<TCP>().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<TCP>().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<EthernetII> 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<TCP>().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<EthernetII> 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<EthernetII> 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]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user