* status_code
* status_msg
* trans_depth
+ * request_body_len
+ * response_body_len
+ * info_code
+ * info_msg
+ * proxied
+ * orig_filenames
+ * resp_filenames
return ((const HttpTransactionEndEvent*)event)->get_trans_depth();
}
+static uint64_t get_request_body_len(const DataEvent* event, const Packet*, const Flow*)
+{
+ return ((const HttpTransactionEndEvent*)event)->get_request_body_len();
+}
+
+static uint64_t get_response_body_len(const DataEvent* event, const Packet*, const Flow*)
+{
+ return ((const HttpTransactionEndEvent*)event)->get_response_body_len();
+}
+
+static uint64_t get_info_code(const DataEvent* event, const Packet*, const Flow*)
+{
+ return ((const HttpTransactionEndEvent*)event)->get_info_code();
+}
+
+static const Field& get_info_msg(const DataEvent* event, const Packet*, const Flow*)
+{
+ return ((const HttpTransactionEndEvent*)event)->get_info_msg();
+}
+
+static const char* get_proxied(const DataEvent* event, const Packet*, const Flow*)
+{
+ return ((const HttpTransactionEndEvent*)event)->get_proxied().c_str();
+}
+
+static const char* get_orig_filenames(const DataEvent* event, const Packet*, const Flow*)
+{
+ return ((const HttpTransactionEndEvent*)event)->get_filename(HttpCommon::SRC_CLIENT).c_str();
+}
+
+static const char* get_resp_filenames(const DataEvent* event, const Packet*, const Flow*)
+{
+ return ((const HttpTransactionEndEvent*)event)->get_filename(HttpCommon::SRC_SERVER).c_str();
+}
+
static struct timeval get_timestamp(const DataEvent*, const Packet* p, const Flow*)
{
return p->pkth->ts;
static const map<string, ExtractorEvent::StrGetFn> str_getters =
{
- {"version", get_version}
+ {"version", get_version},
+ {"proxied", get_proxied},
+ {"orig_filenames", get_orig_filenames},
+ {"resp_filenames", get_resp_filenames}
};
static const map<string, ExtractorEvent::NumGetFn> num_getters =
{"id.resp_p", get_ip_dst_port},
{"uid", get_uid},
{"pkt_num", get_pkt_num},
- {"trans_depth", get_trans_depth}
+ {"trans_depth", get_trans_depth},
+ {"request_body_len", get_request_body_len},
+ {"response_body_len", get_response_body_len},
+ {"info_code", get_info_code}
};
static const map<string, HttpExtractorEventHandler::SubGetFn> sub_getters =
{"referrer", get_referrer},
{"origin", get_origin},
{"status_code", get_stat_code},
- {"status_msg", get_stat_msg}
+ {"status_msg", get_stat_msg},
+ {"info_msg", get_info_msg}
};
template<class T, class U, class V>
"version",
"status_code",
"status_msg",
- "trans_depth"
+ "trans_depth",
+ "request_body_len",
+ "response_body_len",
+ "info_code",
+ "info_msg",
+ "proxied",
+ "orig_filenames",
+ "resp_filenames"
},
};
#include "service_inspectors/http_inspect/http_transaction.h"
using namespace snort;
+using namespace HttpEnums;
HttpTransactionEndEvent::HttpTransactionEndEvent(const HttpTransaction* const trans)
: transaction(trans) { }
return 0;
}
+
+uint64_t HttpTransactionEndEvent::get_request_body_len() const
+{
+ return transaction->get_body_len(HttpCommon::SRC_CLIENT);
+}
+
+uint64_t HttpTransactionEndEvent::get_response_body_len() const
+{
+ return transaction->get_body_len(HttpCommon::SRC_SERVER);
+}
+
+uint8_t HttpTransactionEndEvent::get_info_code() const
+{
+ return transaction->get_info_code();
+}
+
+const Field& HttpTransactionEndEvent::get_info_msg() const
+{
+ return transaction->get_info_msg();
+}
+
+const std::string& HttpTransactionEndEvent::get_filename(HttpCommon::SourceId src_id) const
+{
+ return transaction->get_filename(src_id);
+}
+
+const std::string& HttpTransactionEndEvent::get_proxied() const
+{
+ if (proxies != nullptr)
+ return *proxies;
+
+ const std::pair<HeaderId, const char*> proxy_headers[] =
+ {
+ { HEAD_FORWARDED, "FORWARDED" },
+ { HEAD_X_FORWARDED_FOR, "X-FORWARDED-FOR" },
+ { HEAD_X_FORWARDED_FROM, "X-FORWARDED-FROM" },
+ { HEAD_CLIENT_IP, "CLIENT-IP" },
+ { HEAD_VIA, "VIA" },
+ { HEAD_XROXY_CONNECTION, "XROXY-CONNECTION" },
+ { HEAD_PROXY_CONNECTION, "PROXY-CONNECTION" }
+ };
+
+ proxies = new std::string();
+ for (auto& hdr: proxy_headers)
+ {
+ const Field& val = get_client_header(hdr.first);
+ if (val.length() > 0)
+ {
+ if (!proxies->empty())
+ proxies->append(",");
+ proxies->append(hdr.second);
+ proxies->append(" -> ");
+ proxies->append((const char*)val.start(), val.length());
+ }
+ }
+
+ return *proxies;
+}
{
public:
HttpTransactionEndEvent(const HttpTransaction* const);
+ ~HttpTransactionEndEvent() override
+ { delete proxies; }
const Field& get_host_hdr() const;
const Field& get_uri() const;
const Field& get_origin_hdr() const;
HttpEnums::VersionId get_version() const;
uint64_t get_trans_depth() const;
+ uint64_t get_request_body_len() const;
+ uint64_t get_response_body_len() const;
+ uint8_t get_info_code() const;
+ const Field& get_info_msg() const;
+ const std::string& get_filename(HttpCommon::SourceId) const;
+ const std::string& get_proxied() const;
private:
const Field& get_client_header(uint64_t sub_id) const;
const HttpTransaction* const transaction;
+ mutable std::string* proxies = nullptr;
};
}
#endif
../../service_inspectors/http_inspect/http_flow_data.cc
../../service_inspectors/http_inspect/http_test_manager.cc
../../service_inspectors/http_inspect/http_test_input.cc
+ ../../service_inspectors/http_inspect/http_field.cc
LIBS ${ZLIB_LIBRARIES}
)
add_cpputest( pub_sub_ftp_events_test
#include "service_inspectors/http_inspect/http_flow_data.h"
#include "service_inspectors/http_inspect/http_inspect.h"
#include "service_inspectors/http_inspect/http_module.h"
+#include "service_inspectors/http_inspect/http_msg_header.h"
#include "service_inspectors/http_inspect/http_msg_section.h"
#include "service_inspectors/http_inspect/http_transaction.h"
#include "service_inspectors/http_inspect/test/http_unit_test_helpers.h"
HttpParaList::UriParam::UriParam() { }
HttpParaList::JsNormParam::~JsNormParam() { }
HttpParaList::~HttpParaList() { }
-const Field Field::FIELD_NULL { STAT_NO_SOURCE };
-const Field& HttpMsgSection::get_classic_buffer(unsigned, uint64_t, uint64_t)
-{ return Field::FIELD_NULL; }
HttpInspect::HttpInspect(const HttpParaList* para) :
params(para), xtra_trueip_id(0), xtra_uri_id(0),
xtra_host_id(0), xtra_jsnorm_id(0)
bool HttpStreamSplitter::finish(snort::Flow*) { return false; }
void HttpStreamSplitter::prep_partial_flush(snort::Flow*, uint32_t) { }
+HttpMsgHeader::HttpMsgHeader(const uint8_t* buffer, const uint16_t buf_size,
+ HttpFlowData* session_data_, SourceId source_id_, bool buf_owner, Flow* flow_,
+ const HttpParaList* params_) :
+ HttpMsgHeadShared(buffer, buf_size, session_data_, source_id_, buf_owner, flow_, params_)
+{}
+void HttpMsgHeader::publish(unsigned){}
+void HttpMsgHeader::gen_events() {}
+void HttpMsgHeader::update_flow() {}
+void HttpMsgHeader::prepare_body() {}
+#ifdef REG_TEST
+void HttpMsgHeader::print_section(FILE*) {}
+#endif
+HttpMsgHeadShared::HttpMsgHeadShared(const uint8_t* buffer, const uint16_t buf_size,
+ HttpFlowData* session_data_, HttpCommon::SourceId source_id_, bool buf_owner,
+ snort::Flow* flow_, const HttpParaList* params_): HttpMsgSection(buffer, buf_size,
+ session_data_, source_id_, buf_owner, flow_, params_), own_msg_buffer(buf_owner)
+{}
+HttpMsgHeadShared::~HttpMsgHeadShared() {}
+HttpMsgSection::HttpMsgSection(const uint8_t* buffer, const uint16_t buf_size,
+ HttpFlowData* session_data_, SourceId source_id_, bool buf_owner, Flow* flow_,
+ const HttpParaList* params_) :
+ msg_text(buf_size, buffer, buf_owner),
+ session_data(session_data_),
+ flow(flow_),
+ params(params_),
+ transaction(nullptr),
+ trans_num(0),
+ status_code_num(STAT_NOT_PRESENT),
+ source_id(source_id_),
+ version_id(VERS__NOT_PRESENT),
+ method_id(METH__NOT_PRESENT),
+ tcp_close(false)
+{}
+void HttpMsgSection::clear(){}
+bool HttpMsgSection::run_detection(snort::Packet*) { return false; }
+void HttpMsgHeadShared::analyze() {}
+
THREAD_LOCAL PegCount HttpModule::peg_counts[PEG_COUNT_MAX] = { };
+//
+// get_classic_buffer mock
+//
+Field odd (3, (const uint8_t *)"odd", false);
+Field even (4, (const uint8_t *)"even", false);
+static uint32_t test_number = 0;
+const Field& HttpMsgSection::get_classic_buffer(unsigned, uint64_t, uint64_t)
+{
+ return ((test_number % 2) == 0) ? even : odd;
+}
+
TEST_GROUP(pub_sub_http_transaction_end_event_test)
{
Flow* const flow = new Flow;
}
};
-TEST(pub_sub_http_transaction_end_event_test, version_no_req_no_status)
+TEST(pub_sub_http_transaction_end_event_test, no_req_no_status)
{
section_type[SRC_CLIENT] = SEC_REQUEST;
HttpTransaction* trans = HttpTransaction::attach_my_transaction(flow_data, SRC_CLIENT, flow);
HttpTransactionEndEvent event(trans);
HttpEnums::VersionId version = event.get_version();
CHECK(version == HttpEnums::VERS__NOT_PRESENT);
+ uint64_t trans_depth = event.get_trans_depth();
+ CHECK(trans_depth == 0);
+}
+
+TEST(pub_sub_http_transaction_end_event_test, proxied_str_exists)
+{
+ section_type[SRC_CLIENT] = SEC_REQUEST;
+ HttpTransaction* trans = HttpTransaction::attach_my_transaction(flow_data, SRC_CLIENT, flow);
+ char buf[] = "something";
+ HttpMsgHeader* hdr = new HttpMsgHeader((uint8_t*)buf, sizeof(buf), flow_data, SRC_CLIENT, false, flow, ¶ms);
+ trans->set_header(hdr, SRC_CLIENT);
+ HttpTransactionEndEvent event(trans);
+ const std::string result = "FORWARDED -> odd,X-FORWARDED-FOR -> odd,X-FORWARDED-FROM -> odd,"
+ "CLIENT-IP -> odd,VIA -> odd,XROXY-CONNECTION -> odd,PROXY-CONNECTION -> odd";
+ test_number = 1;
+ std::string proxied = event.get_proxied();
+ CHECK(proxied == result);
+ test_number = 2;
+ proxied = event.get_proxied();
+ CHECK(proxied == result);
}
int main(int argc, char** argv)
{
return CommandLineTestRunner::RunAllTests(argc, argv);
}
-
HEAD_CONTENT_TYPE, HEAD_EXPIRES, HEAD_LAST_MODIFIED, HEAD_X_FORWARDED_FOR, HEAD_TRUE_CLIENT_IP,
HEAD_X_WORKING_WITH, HEAD_CONTENT_TRANSFER_ENCODING, HEAD_MIME_VERSION, HEAD_PROXY_AGENT,
HEAD_CONTENT_DISPOSITION, HEAD_HTTP2_SETTINGS, HEAD_RESTRICT_ACCESS_TO_TENANTS,
- HEAD_RESTRICT_ACCESS_CONTEXT, HEAD_ORIGIN, HEAD__MAX_VALUE };
+ HEAD_RESTRICT_ACCESS_CONTEXT, HEAD_ORIGIN, HEAD_FORWARDED, HEAD_X_FORWARDED_FROM,
+ HEAD_CLIENT_IP, HEAD_XROXY_CONNECTION, HEAD_PROXY_CONNECTION, HEAD__MAX_VALUE };
// All the infractions we might find while parsing and analyzing a message
enum Infraction
}
}
body_octets += msg_text.length();
+ if (!session_data->partial_flush[source_id])
+ transaction->add_body_len(source_id, detect_data.length());
partial_inspected_octets = session_data->partial_flush[source_id] ? msg_text.length() : 0;
}
filename_length, 0,
get_header(source_id)->get_multi_file_processing_id(), uri_buffer,
uri_length);
+ transaction->set_filename(source_id, (const char*) filename_buffer, filename_length);
}
}
}
void gen_events() override;
void update_flow() override;
- const Field& get_status_code() { return status_code; }
- const Field& get_reason_phrase() { return reason_phrase; }
+ const Field& get_status_code() const { return status_code; }
+ const Field& get_reason_phrase() const { return reason_phrase; }
#ifdef REG_TEST
void print_section(FILE* output) override;
&NORMALIZER_BASIC, // HEAD_RESTRICT_ACCESS_TO_TENANTS
&NORMALIZER_BASIC, // HEAD_RESTRICT_ACCESS_CONTEXT
&NORMALIZER_URI, // HEAD_ORIGIN
+ &NORMALIZER_BASIC, // HEAD_FORWARDED
+ &NORMALIZER_BASIC, // HEAD_X_FORWARDED_FROM
+ &NORMALIZER_BASIC, // HEAD_CLIENT_IP
+ &NORMALIZER_BASIC, // HEAD_XROXY_CONNECTION
+ &NORMALIZER_BASIC, // HEAD_PROXY_CONNECTION
&NORMALIZER_BASIC, // HEAD__MAX_VALUE
&NORMALIZER_BASIC, // HEAD_CUSTOM_XFF_HEADER
&NORMALIZER_BASIC, // HEAD_CUSTOM_XFF_HEADER
{ HEAD_RESTRICT_ACCESS_TO_TENANTS, "restrict-access-to-tenants" },
{ HEAD_RESTRICT_ACCESS_CONTEXT, "restrict-access-context" },
{ HEAD_ORIGIN, "origin" },
+ { HEAD_FORWARDED, "forwarded" },
+ { HEAD_X_FORWARDED_FROM, "x-forwarded-from" },
+ { HEAD_CLIENT_IP, "client-ip" },
+ { HEAD_XROXY_CONNECTION, "xroxy-connection" },
+ { HEAD_PROXY_CONNECTION, "proxy-connection" },
{ 0, nullptr }
};
delete infractions[k];
}
delete_section_list(body_list);
- delete_section_list(discard_list);
+ delete_section_list(archive_hdr_list);
+ delete_section_list(archive_status_list);
}
HttpTransaction* HttpTransaction::attach_my_transaction(HttpFlowData* session_data, SourceId
session_data->transaction[SRC_SERVER]->second_response_expected)
{
session_data->transaction[SRC_SERVER]->second_response_expected = false;
- session_data->transaction[SRC_SERVER]->discard_section(
+ session_data->transaction[SRC_SERVER]->archive_status(
session_data->transaction[SRC_SERVER]->status);
session_data->transaction[SRC_SERVER]->status = nullptr;
- session_data->transaction[SRC_SERVER]->discard_section(
+ session_data->transaction[SRC_SERVER]->archive_header(
session_data->transaction[SRC_SERVER]->header[SRC_SERVER]);
session_data->transaction[SRC_SERVER]->header[SRC_SERVER] = nullptr;
}
return session_data->transaction[source_id];
}
-void HttpTransaction::discard_section(HttpMsgSection* section)
+void HttpTransaction::archive_section(HttpMsgSection* section, HttpMsgSection** archive_list)
{
if (section != nullptr)
{
- section->next = discard_list;
- discard_list = section;
+ section->next = *archive_list;
+ *archive_list = section;
}
}
+void HttpTransaction::archive_status(HttpMsgStatus* status)
+{
+ archive_section(status, &archive_status_list);
+}
+
+void HttpTransaction::archive_header(HttpMsgHeader* hdr)
+{
+ archive_section(hdr, &archive_hdr_list);
+}
+
void HttpTransaction::clear_section()
{
assert(active_sections > 0);
one_hundred_response = true;
second_response_expected = true;
}
+
+inline bool is_info_code(int32_t code)
+{
+ return ((99 < code) and (code < 200));
+}
+
+uint8_t HttpTransaction::get_info_code() const
+{
+ if (status)
+ {
+ const int32_t code = status->get_status_code_num();
+ if (is_info_code(code))
+ return code;
+ }
+
+ const HttpMsgStatus* arch_status = (HttpMsgStatus*) archive_status_list;
+ while (arch_status)
+ {
+ const int32_t code = arch_status->get_status_code_num();
+ if (is_info_code(code))
+ return code;
+ arch_status = (HttpMsgStatus*)(arch_status->next);
+ }
+
+ return 0;
+}
+
+const Field& HttpTransaction::get_info_msg() const
+{
+ if (status)
+ {
+ const int32_t code = status->get_status_code_num();
+ if (is_info_code(code))
+ return status->get_reason_phrase();
+ }
+
+ const HttpMsgStatus* arch_status = (HttpMsgStatus*) archive_status_list;
+ while (arch_status)
+ {
+ const int32_t code = arch_status->get_status_code_num();
+ if (is_info_code(code))
+ return arch_status->get_reason_phrase();
+ arch_status = (HttpMsgStatus*)(arch_status->next);
+ }
+
+ return Field::FIELD_NULL;
+}
void set_one_hundred_response();
bool final_response() const { return !second_response_expected; }
+ void add_body_len(HttpCommon::SourceId source_id, uint64_t len)
+ { body_len[source_id] += len; }
+ uint64_t get_body_len(HttpCommon::SourceId source_id) const
+ { return body_len[source_id]; }
+ uint8_t get_info_code() const;
+ const Field& get_info_msg() const;
+ void set_filename(HttpCommon::SourceId source_id, const char* fname, uint32_t len)
+ { filename[source_id].assign(fname, len);}
+ const std::string& get_filename(HttpCommon::SourceId source_id) const
+ { return filename[source_id]; }
+
void clear_section();
bool is_clear() const { return active_sections == 0; }
void garbage_collect();
private:
HttpTransaction(HttpFlowData*, snort::Flow* const);
- void discard_section(HttpMsgSection*);
+ void archive_section(HttpMsgSection*, HttpMsgSection**);
+ void archive_status(HttpMsgStatus*);
+ void archive_header(HttpMsgHeader*);
void publish_end_of_transaction();
HttpFlowData* const session_data;
HttpMsgHeader* header[2] = { nullptr, nullptr };
HttpMsgTrailer* trailer[2] = { nullptr, nullptr };
HttpMsgBody* body_list = nullptr;
- HttpMsgSection* discard_list = nullptr;
+ HttpMsgSection* archive_status_list = nullptr;
+ HttpMsgSection* archive_hdr_list = nullptr;
HttpInfractions* infractions[2];
bool response_seen = false;
unsigned pub_id;
snort::Flow* const flow;
+ uint64_t body_len[2] = { 0, 0 };
+ std::string filename[2];
+
// Estimates of how much memory http_inspect uses to process a transaction
static const uint16_t small_things = 400; // minor memory costs not otherwise accounted for
static const uint16_t transaction_memory_usage_estimate;
void HttpStreamSplitter::prep_partial_flush(snort::Flow*, uint32_t) { }
THREAD_LOCAL PegCount HttpModule::peg_counts[PEG_COUNT_MAX] = { };
+const Field Field::FIELD_NULL { STAT_NO_SOURCE };
TEST_GROUP(http_transaction_test)
{