Skip to content

Commit

Permalink
Refactor TCP stream code and add http_dump example
Browse files Browse the repository at this point in the history
  • Loading branch information
mfontanini committed Feb 11, 2016
1 parent 76b0c91 commit 07b5d74
Show file tree
Hide file tree
Showing 5 changed files with 535 additions and 225 deletions.
2 changes: 2 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ IF(libtins_FOUND)
dns_queries
dns_spoof
dns_stats
http_dump
icmp_responses
interfaces_info
tcp_connection_close
Expand All @@ -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)
Expand Down
157 changes: 157 additions & 0 deletions examples/http_dump.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
}
94 changes: 53 additions & 41 deletions include/tins/tcp_ip.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class IPv6Address;

namespace TCPIP {

class TINS_API TCPFlow {
class TINS_API Flow {
public:
enum State {
UNKNOWN,
Expand All @@ -63,13 +63,13 @@ class TINS_API TCPFlow {

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);
Expand All @@ -85,10 +85,12 @@ class TINS_API TCPFlow {
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);
Expand All @@ -105,7 +107,7 @@ class TINS_API TCPFlow {
State state_;
};

class TINS_API TCPStream {
class TINS_API Stream {
public:
enum State {
SYN_SENT,
Expand All @@ -118,59 +120,72 @@ class TINS_API TCPStream {
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);
void server_buffering_callback(const stream_callback& callback);

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_;
stream_callback on_server_buffering_callback_;
State state_;
};

class TINS_API TCPStreamFollower {
class TINS_API StreamFollower {
public:
typedef TCPStream::stream_callback stream_callback;

TCPStreamFollower();
typedef Stream::stream_callback stream_callback;

void process_packet(PDU& packet);
StreamFollower();

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);
bool process_packet(PDU& packet);
void new_stream_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 {
Expand All @@ -187,18 +202,15 @@ class TINS_API TCPStreamFollower {
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_;
};

Expand Down
Loading

0 comments on commit 07b5d74

Please sign in to comment.