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

Add HTTP requests example

This commit is contained in:
Matias Fontanini
2016-02-15 08:29:14 -08:00
parent eb1c43d293
commit 91a724fe2d
5 changed files with 188 additions and 8 deletions

View File

@@ -1,5 +1,6 @@
FIND_PACKAGE(libtins QUIET)
FIND_PACKAGE(Threads QUIET)
FIND_PACKAGE(Boost COMPONENTS regex)
IF(libtins_FOUND)
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/examples)
@@ -22,6 +23,11 @@ IF(libtins_FOUND)
traceroute
wps_detect
)
IF(Boost_REGEX_FOUND)
SET(LIBTINS_CXX11_EXAMPLES ${LIBTINS_CXX11_EXAMPLES} http_requests)
ELSE()
MESSAGE(WARNING "Disabling HTTP requests example since boost.regex was not found")
ENDIF()
ELSE(HAVE_CXX11)
MESSAGE(WARNING "Disabling some examples since C++11 support is disabled.")
ENDIF(HAVE_CXX11)
@@ -46,6 +52,10 @@ IF(libtins_FOUND)
ADD_EXECUTABLE(interfaces_info EXCLUDE_FROM_ALL interfaces_info.cpp)
ADD_EXECUTABLE(tcp_connection_close EXCLUDE_FROM_ALL tcp_connection_close.cpp)
ADD_EXECUTABLE(wps_detect EXCLUDE_FROM_ALL wps_detect.cpp)
IF (Boost_REGEX_FOUND)
ADD_EXECUTABLE(http_requests EXCLUDE_FROM_ALL http_requests.cpp)
TARGET_LINK_LIBRARIES(http_requests ${Boost_LIBRARIES})
ENDIF()
ENDIF(HAVE_CXX11)
ADD_EXECUTABLE(beacon_display EXCLUDE_FROM_ALL beacon_display.cpp)

145
examples/http_requests.cpp Normal file
View File

@@ -0,0 +1,145 @@
/*
* 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 <string>
#include <iostream>
#include <stdexcept>
#include <boost/regex.hpp>
#include "tins/tcp_ip/stream_follower.h"
#include "tins/sniffer.h"
using std::string;
using std::cout;
using std::cerr;
using std::endl;
using std::exception;
using boost::regex;
using boost::match_results;
using Tins::Packet;
using Tins::Sniffer;
using Tins::SnifferConfiguration;
using Tins::TCPIP::Stream;
using Tins::TCPIP::StreamFollower;
// This example captures and follows TCP streams seen on port 80. It will
// wait until both the client and server send data and then apply a regex
// to both payloads, extrating some information and printing it.
// Don't buffer more than 3kb of data in either request/response
const size_t MAX_PAYLOAD = 3 * 1024;
// The regex to be applied on the request. This will extract the HTTP
// method being used, the request's path and the Host header value.
regex request_regex("([\\w]+) ([^ ]+).+\r\nHost: ([\\d\\w\\.-]+)\r\n");
// The regex to be applied on the response. This finds the response code.
regex response_regex("HTTP/[^ ]+ ([\\d]+)");
void on_server_data(Stream& stream) {
match_results<Stream::payload_type::const_iterator> client_match;
match_results<Stream::payload_type::const_iterator> server_match;
const Stream::payload_type& client_payload = stream.client_payload();
const Stream::payload_type& server_payload = stream.server_payload();
// Run the regexes on client/server payloads
bool valid = regex_search(server_payload.begin(), server_payload.end(),
server_match, response_regex) &&
regex_search(client_payload.begin(), client_payload.end(),
client_match, request_regex);
// If we matched both the client and the server regexes
if (valid) {
// Extract all fields
string method = string(client_match[1].first, client_match[1].second);
string url = string(client_match[2].first, client_match[2].second);
string host = string(client_match[3].first, client_match[3].second);
string response_code = string(server_match[1].first, server_match[1].second);
// Now print them
cout << method << " http://" << host << url << " -> " << response_code << endl;
// Once we've seen the first request on this stream, ignore it
stream.ignore_client_data();
stream.ignore_server_data();
}
// Just in case the server returns invalid data, stop at 3kb
if (stream.server_payload().size() > MAX_PAYLOAD) {
stream.ignore_server_data();
}
}
void on_client_data(Stream& stream) {
// Don't hold more than 3kb of data from the client's flow
if (stream.client_payload().size() > MAX_PAYLOAD) {
stream.ignore_client_data();
}
}
void on_new_connection(Stream& stream) {
stream.client_data_callback(&on_client_data);
stream.server_data_callback(&on_server_data);
// Don't automatically cleanup the stream's data, as we'll manage
// the buffer ourselves and let it grow until we see a full request
// and response
stream.auto_cleanup_payloads(false);
}
int main(int argc, char* argv[]) {
if (argc != 2) {
cout << "Usage: " << argv[0] << " <interface>" << endl;
return 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([&](Packet& packet) {
follower.process_packet(packet);
return true;
});
}
catch (exception& ex) {
cerr << "Error: " << ex.what() << endl;
return 1;
}
}

View File

@@ -129,13 +129,12 @@ int main(int argc, char* argv[]) {
cout << "Usage: " << argv[0] << " <interface> <port>" << 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 " + string(argv[2]));
// 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);

View File

@@ -335,6 +335,22 @@ public:
*/
void auto_cleanup_payloads(bool value);
/**
* \brief Indicates whether the client flow's payloads should be
* automatically erased.
*
* \sa auto_cleanup_payloads
*/
void auto_cleanup_client_data(bool value);
/**
* \brief Indicates whether the server flow's payloads should be
* automatically erased.
*
* \sa auto_cleanup_payloads
*/
void auto_cleanup_server_data(bool value);
/**
* Enables tracking of acknowledged segments
*
@@ -370,7 +386,8 @@ private:
hwaddress_type server_hw_addr_;
timestamp_type create_time_;
timestamp_type last_seen_;
bool auto_cleanup_;
bool auto_cleanup_client_;
bool auto_cleanup_server_;
};
} // TCPIP

View File

@@ -61,7 +61,7 @@ namespace TCPIP {
Stream::Stream(PDU& packet, const timestamp_type& ts)
: client_flow_(extract_client_flow(packet)),
server_flow_(extract_server_flow(packet)), create_time_(ts),
last_seen_(ts), auto_cleanup_(true) {
last_seen_(ts), auto_cleanup_client_(true), auto_cleanup_server_(true) {
// Update client flow state
client_flow().process_packet(packet);
const EthernetII* eth = packet.find_pdu<EthernetII>();
@@ -249,7 +249,16 @@ void Stream::setup_flows_callbacks() {
}
void Stream::auto_cleanup_payloads(bool value) {
auto_cleanup_ = value;
auto_cleanup_client_data(value);
auto_cleanup_server_data(value);
}
void Stream::auto_cleanup_client_data(bool value) {
auto_cleanup_client_ = value;
}
void Stream::auto_cleanup_server_data(bool value) {
auto_cleanup_client_ = value;
}
void Stream::enable_ack_tracking() {
@@ -265,7 +274,7 @@ void Stream::on_client_flow_data(const Flow& /*flow*/) {
if (on_client_data_callback_) {
on_client_data_callback_(*this);
}
if (auto_cleanup_) {
if (auto_cleanup_client_) {
client_payload().clear();
}
}
@@ -274,7 +283,7 @@ void Stream::on_server_flow_data(const Flow& /*flow*/) {
if (on_server_data_callback_) {
on_server_data_callback_(*this);
}
if (auto_cleanup_) {
if (auto_cleanup_server_) {
server_payload().clear();
}
}