Cheeze/lib/libtins/examples/stream_dump.cpp
2024-02-21 14:52:47 +03:00

193 lines
6.9 KiB
C++

/*
* Copyright (c) 2017, 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/stream_follower.h"
#include "tins/sniffer.h"
#include "tins/packet.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::to_string;
using std::ostringstream;
using std::exception;
using Tins::Sniffer;
using Tins::SnifferConfiguration;
using Tins::PDU;
using Tins::TCPIP::StreamFollower;
using Tins::TCPIP::Stream;
// This example takes an interface and a port as an argument and
// it listens for TCP streams on the given interface and port.
// It will reassemble TCP streams and show the traffic sent by
// both the client and the server.
// 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;
}
// 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;
}
// 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) {
if (stream.is_partial_stream()) {
// We found a partial stream. This means this connection/stream had
// been established before we started capturing traffic.
//
// In this case, we need to allow for the stream to catch up, as we
// may have just captured an out of order packet and if we keep waiting
// for the holes to be filled, we may end up waiting forever.
//
// Calling enable_recovery_mode will skip out of order packets that
// fall withing the range of the given window size.
// See Stream::enable_recover_mode for more information
cout << "[+] New connection " << stream_identifier(stream) << endl;
// Enable recovery mode using a window of 10kb
stream.enable_recovery_mode(10 * 1024);
}
else {
// 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 != 3) {
cout << "Usage: " << argv[0] << " <interface> <port>" << endl;
return 1;
}
try {
// Construct the sniffer configuration object
SnifferConfiguration config;
// Only capture TCP traffic sent from/to the given port
config.set_filter("tcp port " + to_string(stoi(string(argv[2]))));
// 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);
// Allow following partial TCP streams (e.g. streams that were
// open before the sniffer started running)
follower.follow_partial_streams(true);
// Now start capturing. Every time there's a new packet, call
// follower.process_packet
sniffer.sniff_loop([&](PDU& packet) {
follower.process_packet(packet);
return true;
});
}
catch (exception& ex) {
cerr << "Error: " << ex.what() << endl;
return 1;
}
}