1
0
mirror of https://github.com/mfontanini/libtins synced 2026-01-23 02:35:57 +01:00

Document new TCP stream classes

This commit is contained in:
Matias Fontanini
2016-02-07 19:24:46 -08:00
parent 07b5d74179
commit 5b60b79fd8
6 changed files with 444 additions and 55 deletions

View File

@@ -273,6 +273,16 @@ public:
}
};
/**
* \brief Exception thrown when a stream is not found
*/
class stream_not_found : public exception_base {
public:
const char* what() const throw() {
return "Stream not found";
}
};
namespace Crypto {
namespace WPA2 {
/**

View File

@@ -51,8 +51,29 @@ class IPv6Address;
namespace TCPIP {
/**
* \brief Represents an unidirectional TCP flow between 2 endpoints
*
* This class will keep the state for all the traffic sent by
* one of the peers in a TCP connection. This contains the sequence number,
* payload ready to be read and buffered payload, along with some other
* properties of the flow.
*
* A TCP stream (see class Stream) is made out of 2 Flows, so you should
* probably have a look at that class first.
*
* You shouldn't normally need to interact with this class. Stream already
* provides proxys to most of its Flow's attributes.
*/
class TINS_API Flow {
public:
/**
* \brief Enum that indicates the state of this flow.
*
* Note that although similar, this is not mapped to a TCP state-machine
* state. This is mostly used internally to know which packets the flow is
* expecting and to know when it's done sending data.
*/
enum State {
UNKNOWN,
SYN_SENT,
@@ -61,35 +82,154 @@ public:
RST_SENT
};
/**
* The type used to store the payload
*/
typedef std::vector<uint8_t> payload_type;
/**
* The type used to store the buffered payload
*/
typedef std::map<uint32_t, payload_type> buffered_payload_type;
/**
* The type used to store the callbacks that this class triggers
*/
typedef std::function<void(Flow&)> event_callback;
Flow(const IPv4Address& dest_address, uint16_t dest_port,
uint32_t sequence_number);
Flow(const IPv6Address& dest_address, uint16_t dest_port,
/**
* Construct a Flow from an IPv4 address
*
* \param dst_address This flow's destination address
* \param dst_port This flow's destination port
* \param sequence_number The initial sequence number to be used
*/
Flow(const IPv4Address& dst_address, uint16_t dst_port,
uint32_t sequence_number);
/**
* Construct a Flow from an IPv6 address
*
* \param dst_address This flow's destination address
* \param dst_port This flow's destination port
* \param sequence_number The initial sequence number to be used
*/
Flow(const IPv6Address& dst_address, uint16_t dst_port,
uint32_t sequence_number);
/**
* \brief Sets the callback that will be executed when data is readable
*
* Whenever this flow has readable data, this callback will be executed.
* By readable, this means that there's non-out-of-order data captured.
*
* \param callback The callback to be executed
*/
void data_callback(const event_callback& callback);
/**
* \brief Sets the callback that will be executed when data is buffered.
*
* Whenever this flow receives out-of-order data, this callback will be
* executed.
*
* \param callback The callback to be executed
*/
void buffering_callback(const event_callback& callback);
/**
* \brief Processes a packet.
*
* If this packet contains data and starts or overlaps with the current
* sequence number, then the data will be appended to this flow's payload
* and the data_callback will be executed.
*
* If this packet contains out-of-order data, it will be buffered and the
* buffering_callback will be executed.
*
* \param pdu The packet to be processed
* \sa Flow::data_callback
* \sa Flow::buffering_callback
*/
void process_packet(PDU& pdu);
/**
* Indicates whether this flow uses IPv6 addresses
*/
bool is_v6() const;
/**
* \brief Indicates whether this flow is finished
*
* A finished is considered to be finished if either it sent a
* packet with the FIN or RST flags on.
*/
bool is_finished() const;
/**
* \brief Indicates whether a packet belongs to this flow
*
* Since Flow represents a unidirectional stream, this will only check
* the destination endpoint and not the source one.
*
* \param packet The packet to be checked
*/
bool packet_belongs(const PDU& packet) const;
/**
* \brief Getter for the IPv4 destination address
*
* Note that it's only safe to execute this method if is_v6() == false
*/
IPv4Address dst_addr_v4() const;
/**
* \brief Getter for the IPv6 destination address
*
* Note that it's only safe to execute this method if is_v6() == true
*/
IPv6Address dst_addr_v6() const;
/**
* Getter for this flow's destination port
*/
uint16_t dport() const;
/**
* Getter for this flow's payload (const)
*/
const payload_type& payload() const;
/**
* Getter for this flow's destination port
*/
payload_type& payload();
/**
* Getter for this flow's state
*/
State state() const;
/**
* Getter for this flow's sequence number
*/
uint32_t sequence_number() const;
/**
* Getter for this flow's buffered payload (const)
*/
const buffered_payload_type& buffered_payload() const;
/**
* Getter for this flow's buffered payload
*/
buffered_payload_type& buffered_payload();
/**
* Sets the state of this flow
*
* \param new_state The new state of this flow
*/
void state(State new_state);
private:
void store_payload(uint32_t seq, const payload_type& payload);
@@ -103,57 +243,208 @@ private:
uint16_t dest_port_;
event_callback on_data_callback_;
event_callback on_buffering_callback_;
bool is_v6_;
State state_;
bool is_v6_;
};
/**
* \brief Represents a TCP stream
*
* A TCP stream is made out of 2 Flows, one in each direction, plus
* some other attributes and callbacks.
*
* This class works using callbacks. Whenever the stream is created, you should
* set at least the client/server callbacks so you are notified whenever the
* client/server has sent data. Note that setting these is not mandatory, so
* you can subscribe to just the callbacks you need.
*
* \sa Stream::auto_cleanup_payloads
*/
class TINS_API Stream {
public:
enum State {
SYN_SENT,
SYN_RCVD,
ESTABLISHED,
CLOSE_WAIT,
FIN_WAIT_1,
FIN_WAIT_2,
TIME_WAIT,
CLOSED
};
/**
* The type used for callbacks
*/
typedef std::function<void(Stream&)> stream_callback;
/**
* The type used to store payloads
*/
typedef Flow::payload_type payload_type;
/**
* \brief Constructs a TCP stream using the provided packet.
*/
Stream(const PDU& initial_packet);
Stream(const Flow& client_flow, const Flow& server_flow);
/**
* \brief Processes this packet.
*
* This will forward the packet appropriately to the client
* or server flow.
*/
void process_packet(PDU& packet);
/**
* Getter for the client flow
*/
Flow& client_flow();
/**
* Getter for the client flow (const)
*/
const Flow& client_flow() const;
/**
* Getter for the server flow
*/
Flow& server_flow();
/**
* Getter for the server flow (const)
*/
const Flow& server_flow() const;
/**
* \brief Indicates whether this stream is finished.
*
* This stream is finished if either peer sent a packet with
* the RST flag on, or both peers sent a FIN.
*/
bool is_finished() const;
/**
* Indicates whether this packet uses IPv6 addresses
*/
bool is_v6() const;
/**
* \brief Retrieves the client's IPv4 address
*
* Note that it's only valid to call this method if is_v6() == false
*/
IPv4Address client_addr_v4() const;
/**
* \brief Retrieves the client's IPv6 address
*
* Note that it's only valid to call this method if is_v6() == true
*/
IPv6Address client_addr_v6() const;
/**
* \brief Retrieves the server's IPv4 address
*
* Note that it's only valid to call this method if is_v6() == false
*/
IPv4Address server_addr_v4() const;
/**
* \brief Retrieves the server's IPv6 address
*
* Note that it's only valid to call this method if is_v6() == true
*/
IPv6Address server_addr_v6() const;
/**
* Getter for the client's port
*/
uint16_t client_port() const;
/**
* Getter for the server's port
*/
uint16_t server_port() const;
/**
* Getter for the client's payload (const)
*/
const payload_type& client_payload() const;
/**
* Getter for the client's payload
*/
payload_type& client_payload();
/**
* Getter for the server's payload (const)
*/
const payload_type& server_payload() const;
/**
* Getter for the server's payload
*/
payload_type& server_payload();
/**
* \brief Sets the callback to be executed when the stream is closed
*
* \param callback The callback to be set
*/
void stream_closed_callback(const stream_callback& callback);
/**
* \brief Sets the callback to be executed when there's client data
*
* \sa Flow::data_callback
* \param callback The callback to be set
*/
void client_data_callback(const stream_callback& callback);
/**
* \brief Sets the callback to be executed when there's server data
*
* \sa Flow::data_callback
* \param callback The callback to be set
*/
void server_data_callback(const stream_callback& callback);
/**
* \brief Sets the callback to be executed when there's new buffered
* client data
*
* \sa Flow::buffering_callback
* \param callback The callback to be set
*/
void client_buffering_callback(const stream_callback& callback);
/**
* \brief Sets the callback to be executed when there's new buffered
* client data
*
* \sa Flow::buffering_callback
* \param callback The callback to be set
*/
void server_buffering_callback(const stream_callback& callback);
/**
* \brief Sets the internal callbacks.
*
* This shouldn't normally need to be called except if you're constructing
* this object and then moving it around before persisting it somewhere.
*/
void setup_flows_callbacks();
/**
* \brief Indicates whether each flow's payloads should be automatically
* erased.
*
* If this property is true, then whenever there's new data for a stream,
* the appropriate callback will be executed and then the payload will be
* erased.
*
* If this property is false, then the payload <b>will not</b> be erased
* and the user is responsible for clearing the payload vector.
*
* Setting this property to false is useful if it's desired to hold all
* of the data sent on the stream before processing it. Note that this
* can lead to the memory growing a lot.
*
* This property is true by default.
*
* \param value The value to be set for this property
*/
void auto_cleanup_payloads(bool value);
private:
static Flow extract_client_flow(const PDU& packet);
static Flow extract_server_flow(const PDU& packet);
@@ -170,20 +461,91 @@ private:
stream_callback on_server_data_callback_;
stream_callback on_client_buffering_callback_;
stream_callback on_server_buffering_callback_;
State state_;
bool auto_cleanup_;
};
/**
* \brief Represents a class that follows TCP and reassembles streams
*
* This class processes packets and whenever it detects a new connection
* being open, it starts tracking it. This will follow all data sent by
* each peer and make it available to the user in a simple way.
*
* In order to use this class, just create an instance and set the
* new stream callback to some function that you want:
*
* \code
* void on_new_stream(TCPStream& stream) {
* // Do something with it.
* // This is the perfect time to set the stream's client/server
* // write callbacks so you are notified whenever there's new
* // data on the stream
* }
*
* // Create it
* StreamFollower follower;
* // Set the callback
* follower.new_stream_callback(&on_new_stream);
* \endcode
*/
class TINS_API StreamFollower {
public:
/**
* \brief The type used for callbacks
*/
typedef Stream::stream_callback stream_callback;
/**
* Default constructor
*/
StreamFollower();
/**
* \brief Processes a packet
*
* This will detect if this packet belongs to an existing stream
* and process it, or if it belongs to a new one, in which case it
* starts tracking it.
*
* This method always returns true so it can be easily plugged as
* the argument to Sniffer::sniff_loop.
*
* \param packet The packet to be processed
* \return Always true
*/
bool process_packet(PDU& packet);
/**
* \brief Sets the callback to be executed when a new stream is captured.
*
* Whenever a new stream is captured, the provided callback will be
* executed.
*
* \param callback The callback to be set
*/
void new_stream_callback(const stream_callback& callback);
/**
* Finds the stream identified by the provided arguments.
*
* \param client_addr The client's address
* \param client_port The client's port
* \param server_addr The server's address
* \param server_addr The server's port
*/
Stream& find_stream(IPv4Address client_addr, uint16_t client_port,
IPv4Address server_addr, uint16_t server_port);
IPv4Address server_addr, uint16_t server_port);
/**
* Finds the stream identified by the provided arguments.
*
* \param client_addr The client's address
* \param client_port The client's port
* \param server_addr The server's address
* \param server_addr The server's port
*/
Stream& find_stream(IPv6Address client_addr, uint16_t client_port,
IPv6Address server_addr, uint16_t server_port);
private:
static const size_t DEFAULT_MAX_BUFFERED_CHUNKS;
typedef std::array<uint8_t, 16> address_type;
@@ -205,6 +567,7 @@ private:
typedef std::map<stream_id, Stream> streams_type;
stream_id make_stream_id(const PDU& packet);
Stream& find_stream(const stream_id& id);
static address_type serialize(IPv4Address address);
static address_type serialize(const IPv6Address& address);