if ( context )
return context->get_context_data(id);
- return DetectionEngine::get_data(id);
+ ContextSwitcher* sw = Snort::get_switcher();
+
+ if ( !sw )
+ return nullptr;
+
+ return sw->get_context()->get_context_data(id);
}
void DetectionEngine::add_replacement(const std::string& s, unsigned off)
http_test_input.h
http_flow_data.cc
http_flow_data.h
+ http_context_data.cc
+ http_context_data.h
http_transaction.cc
http_transaction.h
http_test_manager.cc
#include "http_api.h"
+#include "http_context_data.h"
#include "http_inspect.h"
using namespace snort;
return new HttpInspect(http_mod->get_once_params());
}
+void HttpApi::http_init()
+{
+ HttpFlowData::init();
+ HttpContextData::init();
+}
+
const char* HttpApi::classic_buffer_names[] =
{
"http_client_body",
static void http_mod_dtor(snort::Module* m) { delete m; }
static const char* http_my_name;
static const char* http_help;
- static void http_init() { HttpFlowData::init(); }
+ static void http_init();
static void http_term() { }
static snort::Inspector* http_ctor(snort::Module* mod);
static void http_dtor(snort::Inspector* p) { delete p; }
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2018-2018 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+// http_context_data.cc author Bhagya Tholpady <bbantwal@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "http_context_data.h"
+
+#include "http_msg_section.h"
+#include "protocols/packet.h"
+
+using namespace snort;
+
+unsigned HttpContextData::ips_id = 0;
+
+HttpMsgSection* HttpContextData::get_snapshot(const Packet* p)
+{
+ IpsContext* context = p ? p->context : nullptr;
+ HttpContextData* hcd = (HttpContextData*)DetectionEngine::get_data(HttpContextData::ips_id,
+ context);
+
+ if ( !hcd )
+ return nullptr;
+
+ return hcd->current_section;
+}
+
+void HttpContextData::save_snapshot(HttpMsgSection* section)
+{
+ HttpContextData* hcd = (HttpContextData*)DetectionEngine::get_data(HttpContextData::ips_id);
+
+ if ( !hcd )
+ {
+ hcd = new HttpContextData;
+ DetectionEngine::set_data(HttpContextData::ips_id, hcd);
+ }
+
+ hcd->current_section = section;
+ section->add_ips_context(DetectionEngine::get_context());
+}
+
+HttpMsgSection* HttpContextData::clear_snapshot(IpsContext* context)
+{
+ HttpMsgSection* section = nullptr;
+ HttpContextData* hcd = (HttpContextData*)DetectionEngine::get_data(HttpContextData::ips_id,
+ context);
+
+ if ( hcd )
+ {
+ section = hcd->current_section;
+ hcd->clear();
+ }
+
+ return section;
+}
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2018-2018 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+// http_context_data.h author Bhagya Tholpady <bbantwal@cisco.com>
+
+#ifndef HTTP_CONTEXT_DATA_H
+#define HTTP_CONTEXT_DATA_H
+
+#include "detection/ips_context.h"
+
+class HttpMsgSection;
+
+class HttpContextData : public snort::IpsContextData
+{
+public:
+ void clear() override
+ { current_section = nullptr; }
+
+ static void init()
+ { ips_id = IpsContextData::get_ips_id(); }
+ static HttpMsgSection* get_snapshot(const snort::Packet* p);
+ static void save_snapshot(HttpMsgSection* section);
+ static HttpMsgSection* clear_snapshot(snort::IpsContext* context);
+ static unsigned ips_id;
+
+private:
+ HttpMsgSection* current_section = nullptr;
+};
+
+#endif
+
delete infractions[k];
delete events[k];
delete[] section_buffer[k];
- HttpTransaction::delete_transaction(transaction[k]);
+ HttpTransaction::delete_transaction(transaction[k], nullptr);
delete cutter[k];
if (compress_stream[k] != nullptr)
{
if (fd_state != nullptr)
File_Decomp_StopFree(fd_state);
delete_pipeline();
+
+ while (discard_list != nullptr)
+ {
+ HttpTransaction* tmp = discard_list;
+ discard_list = discard_list->next;
+ delete tmp;
+ }
}
void HttpFlowData::half_reset(SourceId source_id)
detection_status[source_id] = DET_REACTIVATING;
}
+void HttpFlowData::garbage_collect()
+{
+ HttpTransaction** current = &discard_list;
+ while (*current != nullptr)
+ {
+ if ((*current)->is_clear())
+ {
+ HttpTransaction* tmp = *current;
+ *current = (*current)->next;
+ delete tmp;
+ }
+ else
+ current = &(*current)->next;
+ }
+}
+
bool HttpFlowData::add_to_pipeline(HttpTransaction* latest)
{
if (pipeline == nullptr)
{
for (int k=pipeline_front; k != pipeline_back; k = (k+1) % MAX_PIPELINE)
{
- HttpTransaction::delete_transaction(pipeline[k]);
+ delete pipeline[k];
}
delete[] pipeline;
}
// Convenience routines
void half_reset(HttpEnums::SourceId source_id);
void trailer_prep(HttpEnums::SourceId source_id);
+ void garbage_collect();
// 0 element refers to client request, 1 element refers to server response
int64_t detect_depth_remaining[2] = { HttpEnums::STAT_NOT_PRESENT,
HttpEnums::STAT_NOT_PRESENT };
uint64_t expected_trans_num[2] = { 1, 1 };
+
// number of user data octets seen so far (regular body or chunks)
int64_t body_octets[2] = { HttpEnums::STAT_NOT_PRESENT, HttpEnums::STAT_NOT_PRESENT };
int32_t status_code_num = HttpEnums::STAT_NOT_PRESENT;
- HttpMsgSection* latest_section = nullptr;
HttpEnums::VersionId version_id[2] = { HttpEnums::VERS__NOT_PRESENT,
HttpEnums::VERS__NOT_PRESENT };
HttpEnums::MethodId method_id = HttpEnums::METH__NOT_PRESENT;
HttpTransaction* take_from_pipeline();
void delete_pipeline();
+ // Transactions with uncleared sections awaiting deletion
+ HttpTransaction* discard_list = nullptr;
+
#ifdef REG_TEST
static uint64_t instance_count;
uint64_t seq_num;
#include "protocols/packet.h"
#include "stream/stream.h"
+#include "http_context_data.h"
#include "http_js_norm.h"
#include "http_msg_body.h"
#include "http_msg_body_chunk.h"
InspectSection HttpInspect::get_latest_is(const Packet* p)
{
- const HttpFlowData* const session_data =
- (HttpFlowData*)p->flow->get_flow_data(HttpFlowData::inspector_id);
+ HttpMsgSection* current_section = HttpContextData::get_snapshot(p);
- if ((session_data == nullptr) || (session_data->latest_section == nullptr))
+ if (current_section == nullptr)
return HttpEnums::IS_NONE;
- return session_data->latest_section->get_inspection_section();
+ return current_section->get_inspection_section();
}
SourceId HttpInspect::get_latest_src(const Packet* p)
{
- const HttpFlowData* const session_data =
- (HttpFlowData*)p->flow->get_flow_data(HttpFlowData::inspector_id);
+ HttpMsgSection* current_section = HttpContextData::get_snapshot(p);
- if ((session_data == nullptr) || (session_data->latest_section == nullptr))
+ if (current_section == nullptr)
return HttpEnums::SRC__NOT_COMPUTE;
- return session_data->latest_section->get_source_id();
+ return current_section->get_source_id();
}
bool HttpInspect::get_buf(InspectionBuffer::Type ibt, Packet* p, InspectionBuffer& b)
bool HttpInspect::http_get_buf(unsigned id, uint64_t sub_id, uint64_t form, Packet* p,
InspectionBuffer& b)
{
- const HttpFlowData* const session_data =
- (HttpFlowData*)p->flow->get_flow_data(HttpFlowData::inspector_id);
+ HttpMsgSection* current_section = HttpContextData::get_snapshot(p);
- if ((session_data == nullptr) || (session_data->latest_section == nullptr))
+ if (current_section == nullptr)
return false;
- const Field& buffer = session_data->latest_section->get_classic_buffer(id, sub_id, form);
+ const Field& buffer = current_section->get_classic_buffer(id, sub_id, form);
if (buffer.length() <= 0)
return false;
return get_buf(ibt, p, b);
}
-int HttpInspect::get_xtra_trueip(Flow* flow, uint8_t** buf, uint32_t* len, uint32_t* type)
+int HttpInspect::get_xtra_trueip(Flow*, uint8_t** buf, uint32_t* len, uint32_t* type)
{
- const HttpFlowData* const session_data =
- (HttpFlowData*)flow->get_flow_data(HttpFlowData::inspector_id);
+ HttpMsgSection* current_section = HttpContextData::get_snapshot(nullptr);
- if ((session_data == nullptr) || (session_data->latest_section == nullptr))
+ if (current_section == nullptr)
return 0;
- const HttpTransaction* const transaction = session_data->latest_section->get_transaction();
- HttpMsgHeader* const req_header = transaction->get_header(SRC_CLIENT);
+ HttpMsgHeader* const req_header = current_section->get_header(SRC_CLIENT);
if (req_header == nullptr)
return 0;
const Field& true_ip = req_header->get_true_ip_addr();
return 1;
}
-int HttpInspect::get_xtra_uri(Flow* flow, uint8_t** buf, uint32_t* len, uint32_t* type)
+int HttpInspect::get_xtra_uri(Flow*, uint8_t** buf, uint32_t* len, uint32_t* type)
{
- const HttpFlowData* const session_data =
- (HttpFlowData*)flow->get_flow_data(HttpFlowData::inspector_id);
+ HttpMsgSection* current_section = HttpContextData::get_snapshot(nullptr);
- if ((session_data == nullptr) || (session_data->latest_section == nullptr))
+ if (current_section == nullptr)
return 0;
- const HttpTransaction* const transaction = session_data->latest_section->get_transaction();
- HttpMsgRequest* const request = transaction->get_request();
+ HttpMsgRequest* const request = current_section->get_request();
if (request == nullptr)
return 0;
const Field& uri = request->get_uri();
return 1;
}
-int HttpInspect::get_xtra_host(Flow* flow, uint8_t** buf, uint32_t* len, uint32_t* type)
+int HttpInspect::get_xtra_host(Flow*, uint8_t** buf, uint32_t* len, uint32_t* type)
{
- const HttpFlowData* const session_data =
- (HttpFlowData*)flow->get_flow_data(HttpFlowData::inspector_id);
+ HttpMsgSection* current_section = HttpContextData::get_snapshot(nullptr);
- if ((session_data == nullptr) || (session_data->latest_section == nullptr))
+ if (current_section == nullptr)
return 0;
- const HttpTransaction* const transaction = session_data->latest_section->get_transaction();
- HttpMsgHeader* const req_header = transaction->get_header(SRC_CLIENT);
+ HttpMsgHeader* const req_header = current_section->get_header(SRC_CLIENT);
if (req_header == nullptr)
return 0;
const Field& host = req_header->get_header_value_norm(HEAD_HOST);
// The name of this method reflects its legacy purpose. We actually return the normalized data
// from a response message body which may include other forms of normalization in addition to
// JavaScript normalization. But if you don't turn JavaScript normalization on you get nothing.
-int HttpInspect::get_xtra_jsnorm(Flow* flow, uint8_t** buf, uint32_t* len, uint32_t* type)
+int HttpInspect::get_xtra_jsnorm(Flow*, uint8_t** buf, uint32_t* len, uint32_t* type)
{
- const HttpFlowData* const session_data =
- (HttpFlowData*)flow->get_flow_data(HttpFlowData::inspector_id);
+ HttpMsgSection* current_section = HttpContextData::get_snapshot(nullptr);
- if ((session_data == nullptr) || (session_data->latest_section == nullptr) ||
- (session_data->latest_section->get_source_id() != SRC_SERVER) ||
- !session_data->latest_section->get_params()->js_norm_param.normalize_javascript)
+ if ((current_section == nullptr) ||
+ (current_section->get_source_id() != SRC_SERVER) ||
+ !current_section->get_params()->js_norm_param.normalize_javascript)
return 0;
- const HttpTransaction* const transaction = session_data->latest_section->get_transaction();
- HttpMsgBody* const body = transaction->get_body();
+ HttpMsgBody* const body = current_section->get_body();
if (body == nullptr)
return 0;
- assert((void*)body == (void*)session_data->latest_section);
+ assert((void*)body == (void*)current_section);
const Field& detect_data = body->get_detect_data();
if (detect_data.length() <= 0)
return 0;
{
DetectionEngine::disable_content(p);
}
+
#ifdef REG_TEST
else
{
}
#endif
+ // If current transaction is complete then we are done with it. This is strictly a memory
+ // optimization not necessary for correct operation.
+ if ((source_id == SRC_SERVER) && (session_data->type_expected[SRC_SERVER] == SEC_STATUS) &&
+ session_data->transaction[SRC_SERVER]->final_response())
+ {
+ HttpTransaction::delete_transaction(session_data->transaction[SRC_SERVER], session_data);
+ session_data->transaction[SRC_SERVER] = nullptr;
+ }
+
// Whenever we process a packet we set these flags. If someone asks for an extra data
// buffer the JIT code will figure out if we actually have it.
SetExtraData(p, xtra_trueip_id);
bool HttpInspect::process(const uint8_t* data, const uint16_t dsize, Flow* const flow,
SourceId source_id, bool buf_owner) const
{
+ HttpMsgSection* current_section;
HttpFlowData* session_data = (HttpFlowData*)flow->get_flow_data(HttpFlowData::inspector_id);
assert(session_data != nullptr);
switch (session_data->section_type[source_id])
{
case SEC_REQUEST:
- session_data->latest_section = new HttpMsgRequest(
+ current_section = new HttpMsgRequest(
data, dsize, session_data, source_id, buf_owner, flow, params);
break;
case SEC_STATUS:
- session_data->latest_section = new HttpMsgStatus(
+ current_section = new HttpMsgStatus(
data, dsize, session_data, source_id, buf_owner, flow, params);
break;
case SEC_HEADER:
- session_data->latest_section = new HttpMsgHeader(
+ current_section = new HttpMsgHeader(
data, dsize, session_data, source_id, buf_owner, flow, params);
break;
case SEC_BODY_CL:
- session_data->latest_section = new HttpMsgBodyCl(
+ current_section = new HttpMsgBodyCl(
data, dsize, session_data, source_id, buf_owner, flow, params);
break;
case SEC_BODY_OLD:
- session_data->latest_section = new HttpMsgBodyOld(
+ current_section = new HttpMsgBodyOld(
data, dsize, session_data, source_id, buf_owner, flow, params);
break;
case SEC_BODY_CHUNK:
- session_data->latest_section = new HttpMsgBodyChunk(
+ current_section = new HttpMsgBodyChunk(
data, dsize, session_data, source_id, buf_owner, flow, params);
break;
case SEC_TRAILER:
- session_data->latest_section = new HttpMsgTrailer(
+ current_section = new HttpMsgTrailer(
data, dsize, session_data, source_id, buf_owner, flow, params);
break;
default:
return false;
}
- session_data->latest_section->analyze();
- session_data->latest_section->gen_events();
- session_data->latest_section->update_flow();
+ current_section->analyze();
+ current_section->gen_events();
+ current_section->update_flow();
#ifdef REG_TEST
if (HttpTestManager::use_test_output())
{
- session_data->latest_section->print_section(HttpTestManager::get_output_file());
+ current_section->print_section(HttpTestManager::get_output_file());
fflush(HttpTestManager::get_output_file());
if (HttpTestManager::use_test_input())
{
}
#endif
- session_data->latest_section->publish();
- return session_data->latest_section->detection_required();
+ current_section->publish();
+ return current_section->detection_required();
}
void HttpInspect::clear(Packet* p)
HttpFlowData* const session_data =
(HttpFlowData*)p->flow->get_flow_data(HttpFlowData::inspector_id);
- if (session_data == nullptr)
+ if ( session_data == nullptr )
+ return;
+
+ HttpMsgSection* current_section = HttpContextData::clear_snapshot(p->context);
+
+ // FIXIT-M This test is necessary because sometimes we get extra clears
+ // Convert to assert when that gets fixed.
+ if ( current_section == nullptr )
return;
- session_data->latest_section = nullptr;
- const SourceId source_id = (p->is_from_client()) ? SRC_CLIENT : SRC_SERVER;
+ current_section->clear();
+ HttpTransaction* current_transaction = current_section->get_transaction();
+
+ const SourceId source_id = current_section->get_source_id();
+ //FIXIT-M This check may not apply to the transaction attached to the packet
+ //in case of offload.
if (session_data->detection_status[source_id] == DET_DEACTIVATING)
{
if (source_id == SRC_CLIENT)
session_data->detection_status[source_id] = DET_OFF;
}
- if (session_data->transaction[source_id] == nullptr)
- return;
-
- // If current transaction is complete then we are done with it and should reclaim the space
- if ((source_id == SRC_SERVER) && (session_data->type_expected[SRC_SERVER] == SEC_STATUS) &&
- session_data->transaction[SRC_SERVER]->final_response())
- {
- HttpTransaction::delete_transaction(session_data->transaction[SRC_SERVER]);
- session_data->transaction[SRC_SERVER] = nullptr;
- }
- else
- {
- // Get rid of most recent body section if present
- session_data->transaction[source_id]->set_body(nullptr);
- }
+ current_transaction->garbage_collect();
+ session_data->garbage_collect();
}
detection_section((body_octets == 0) && (session_data->detect_depth_remaining[source_id] > 0))
{
transaction->set_body(this);
+ get_related_sections();
}
void HttpMsgBody::analyze()
snort::FileFlows* file_flows = snort::FileFlows::get_file_flows(flow);
const bool download = (source_id == SRC_SERVER);
- HttpMsgRequest* request = transaction->get_request();
-
size_t file_index = 0;
if ((request != nullptr) and (request->get_http_uri() != nullptr))
HttpEnums::InspectSection get_inspection_section() const override
{ return detection_section ? HttpEnums::IS_DETECTION : HttpEnums::IS_BODY; }
bool detection_required() const override;
+ HttpMsgBody* get_body() override { return this; }
const Field& get_classic_client_body();
const Field& get_detect_data() { return detect_data; }
static void fd_event_callback(void* context, int event);
HttpMsgHeadShared(buffer, buf_size, session_data_, source_id_, buf_owner, flow_, params_)
{
transaction->set_header(this, source_id);
+ get_related_sections();
}
void HttpMsgHeader::publish()
return;
}
- if ((source_id == SRC_SERVER) && (transaction->get_request() != nullptr) &&
- (transaction->get_request()->get_method_id() == METH_HEAD))
+ if ((source_id == SRC_SERVER) && (request != nullptr) &&
+ (request->get_method_id() == METH_HEAD))
{
// No body allowed by RFC for response to HEAD method
session_data->half_reset(SRC_SERVER);
HttpMsgStart(buffer, buf_size, session_data_, source_id_, buf_owner, flow_, params_)
{
transaction->set_request(this);
+ get_related_sections();
}
void HttpMsgRequest::parse_start_line()
#include "http_msg_section.h"
+#include "http_context_data.h"
#include "http_msg_body.h"
#include "http_msg_head_shared.h"
#include "http_msg_header.h"
tcp_close(session_data->tcp_close[source_id])
{
assert((source_id == SRC_CLIENT) || (source_id == SRC_SERVER));
+ HttpContextData::save_snapshot(this);
+}
+
+HttpMsgSection::~HttpMsgSection()
+{
+ // FIXIT-L This clear call is to handle premature deletion (before offload completes)
+ // of message sections due to session deletion
+ if ( ips_context )
+ HttpContextData::clear_snapshot(ips_context);
}
bool HttpMsgSection::detection_required() const
{
if (source_id != SRC_CLIENT)
return Field::FIELD_NULL;
- HttpMsgBody* body = transaction->get_body();
- return (body != nullptr) ? body->get_classic_client_body() : Field::FIELD_NULL;
+ return (get_body() != nullptr) ? get_body()->get_classic_client_body() : Field::FIELD_NULL;
}
case HTTP_BUFFER_COOKIE:
case HTTP_BUFFER_RAW_COOKIE:
{
- HttpMsgHeader* header = transaction->get_header(buffer_side);
- if (header == nullptr)
+ if (header[buffer_side] == nullptr)
return Field::FIELD_NULL;
- return (id == HTTP_BUFFER_COOKIE) ? header->get_classic_norm_cookie() :
- header->get_classic_raw_cookie();
+ return (id == HTTP_BUFFER_COOKIE) ? header[buffer_side]->get_classic_norm_cookie() :
+ header[buffer_side]->get_classic_raw_cookie();
}
case HTTP_BUFFER_HEADER:
case HTTP_BUFFER_TRAILER:
{
// FIXIT-L Someday want to be able to return field name or raw field value
- HttpMsgHeadShared* const header = (id == HTTP_BUFFER_HEADER) ?
- (HttpMsgHeadShared*)transaction->get_header(buffer_side) :
- (HttpMsgHeadShared*)transaction->get_trailer(buffer_side);
- if (header == nullptr)
+ HttpMsgHeadShared* const head = (id == HTTP_BUFFER_HEADER) ?
+ (HttpMsgHeadShared*)header[buffer_side] : (HttpMsgHeadShared*)trailer[buffer_side];
+ if (head == nullptr)
return Field::FIELD_NULL;
if (sub_id == 0)
- return header->get_classic_norm_header();
- return header->get_header_value_norm((HeaderId)sub_id);
+ return head->get_classic_norm_header();
+ return head->get_header_value_norm((HeaderId)sub_id);
}
case HTTP_BUFFER_METHOD:
{
- HttpMsgRequest* request = transaction->get_request();
return (request != nullptr) ? request->get_method() : Field::FIELD_NULL;
}
case HTTP_BUFFER_RAW_BODY:
{
- HttpMsgBody* body = transaction->get_body();
- return (body != nullptr) ? body->msg_text : Field::FIELD_NULL;
+ return (get_body() != nullptr) ? get_body()->msg_text : Field::FIELD_NULL;
}
case HTTP_BUFFER_RAW_HEADER:
{
- HttpMsgHeader* header = transaction->get_header(buffer_side);
- return (header != nullptr) ? header->get_classic_raw_header() : Field::FIELD_NULL;
+ return (header[buffer_side] != nullptr) ? header[buffer_side]->get_classic_raw_header() :
+ Field::FIELD_NULL;
}
case HTTP_BUFFER_RAW_REQUEST:
{
- HttpMsgRequest* request = transaction->get_request();
return (request != nullptr) ? request->msg_text : Field::FIELD_NULL;
}
case HTTP_BUFFER_RAW_STATUS:
{
- HttpMsgStatus* status = transaction->get_status();
return (status != nullptr) ? status->msg_text : Field::FIELD_NULL;
}
case HTTP_BUFFER_RAW_TRAILER:
{
- HttpMsgTrailer* trailer = transaction->get_trailer(buffer_side);
- return (trailer != nullptr) ? trailer->get_classic_raw_header() : Field::FIELD_NULL;
+ return (trailer[buffer_side] != nullptr) ? trailer[buffer_side]->get_classic_raw_header() :
+ Field::FIELD_NULL;
}
case HTTP_BUFFER_STAT_CODE:
{
- HttpMsgStatus* status = transaction->get_status();
return (status != nullptr) ? status->get_status_code() : Field::FIELD_NULL;
}
case HTTP_BUFFER_STAT_MSG:
{
- HttpMsgStatus* status = transaction->get_status();
return (status != nullptr) ? status->get_reason_phrase() : Field::FIELD_NULL;
}
case HTTP_BUFFER_TRUE_IP:
{
- HttpMsgHeader* header = transaction->get_header(SRC_CLIENT);
- return (header != nullptr) ? header->get_true_ip() : Field::FIELD_NULL;
+ return (header[SRC_CLIENT] != nullptr) ? header[SRC_CLIENT]->get_true_ip() :
+ Field::FIELD_NULL;
}
case HTTP_BUFFER_URI:
case HTTP_BUFFER_RAW_URI:
{
const bool raw = (id == HTTP_BUFFER_RAW_URI);
- HttpMsgRequest* request = transaction->get_request();
if (request == nullptr)
return Field::FIELD_NULL;
if (sub_id == 0)
case HTTP_BUFFER_VERSION:
{
HttpMsgStart* start = (buffer_side == SRC_CLIENT) ?
- (HttpMsgStart*)transaction->get_request() : (HttpMsgStart*)transaction->get_status();
+ (HttpMsgStart*)request : (HttpMsgStart*)status;
return (start != nullptr) ? start->get_version() : Field::FIELD_NULL;
}
default:
}
}
+void HttpMsgSection::get_related_sections()
+{
+ // When a message section is created these relationships become fixed so we make copies for
+ // future reference.
+ request = transaction->get_request();
+ status = transaction->get_status();
+ header[SRC_CLIENT] = transaction->get_header(SRC_CLIENT);
+ header[SRC_SERVER] = transaction->get_header(SRC_SERVER);
+ trailer[SRC_CLIENT] = transaction->get_trailer(SRC_CLIENT);
+ trailer[SRC_SERVER] = transaction->get_trailer(SRC_SERVER);
+}
+
+void HttpMsgSection::clear()
+{
+ transaction->clear_section();
+ cleared = true;
+ ips_context = nullptr;
+}
+
#ifdef REG_TEST
void HttpMsgSection::print_section_title(FILE* output, const char* title) const
class HttpMsgSection
{
public:
- virtual ~HttpMsgSection() = default;
+ virtual ~HttpMsgSection();
virtual HttpEnums::InspectSection get_inspection_section() const
{ return HttpEnums::IS_NONE; }
virtual bool detection_required() const;
HttpTransaction* get_transaction() const { return transaction; }
const HttpParaList* get_params() const { return params; }
+ HttpMsgRequest* get_request() const { return request; }
+ HttpMsgStatus* get_status() const { return status; }
+ HttpMsgHeader* get_header(HttpEnums::SourceId source_id) const { return header[source_id]; }
+ HttpMsgTrailer* get_trailer(HttpEnums::SourceId source_id) const
+ { return trailer[source_id]; }
+ virtual HttpMsgBody* get_body() { return nullptr; }
+
+ void add_ips_context(snort::IpsContext* context) { ips_context = context; }
+
// Minimum necessary processing for every message
virtual void analyze() = 0;
// Publish an inspection event for other modules to consume.
virtual void publish() { }
+ void clear();
+ bool is_clear() { return cleared; }
+
+ HttpMsgSection* next = nullptr;
+
#ifdef REG_TEST
// Test tool prints all derived message parts
virtual void print_section(FILE* output) = 0;
HttpEnums::SourceId source_id_, bool buf_owner, snort::Flow* flow_, const HttpParaList*
params_);
+ void get_related_sections();
+
const Field msg_text;
HttpFlowData* const session_data;
snort::Flow* const flow;
HttpEnums::MethodId method_id;
const bool tcp_close;
+ snort::IpsContext* ips_context = nullptr;
+ // Pointers to related message sections in the same transaction
+ HttpMsgRequest* request;
+ HttpMsgStatus* status;
+ HttpMsgHeader* header[2];
+ HttpMsgTrailer* trailer[2];
+
+ bool cleared = false;
+
// Convenience methods shared by multiple subclasses
void add_infraction(int infraction);
void create_event(int sid);
HttpMsgStart(buffer, buf_size, session_data_, source_id_, buf_owner, flow_, params_)
{
transaction->set_status(this);
+ get_related_sections();
}
void HttpMsgStatus::parse_start_line()
}
}
- if (!transaction->get_request() && (trans_num == 1))
+ if (!request && (trans_num == 1))
{
if (flow->is_pdu_inorder(SSN_DIR_FROM_SERVER))
{
{
// Verify that 206 Partial Content is in response to a Range request. Unsolicited 206
// responses indicate content is being fragmented for no good reason.
- HttpMsgHeader* const req_header = transaction->get_header(SRC_CLIENT);
- if ((req_header != nullptr) && (req_header->get_header_count(HEAD_RANGE) == 0))
+ if ((header[SRC_CLIENT] != nullptr) &&
+ (header[SRC_CLIENT]->get_header_count(HEAD_RANGE) == 0))
{
add_infraction(INF_206_WITHOUT_RANGE);
create_event(EVENT_206_WITHOUT_RANGE);
if (status_code_num == 100)
{
// Were we "Expect"-ing this?
- HttpMsgHeader* const req_header = transaction->get_header(SRC_CLIENT);
- if ((req_header != nullptr) && (req_header->get_header_count(HEAD_EXPECT) == 0))
+ if ((header[SRC_CLIENT] != nullptr) &&
+ (header[SRC_CLIENT]->get_header_count(HEAD_EXPECT) == 0))
{
add_infraction(INF_UNEXPECTED_100_RESPONSE);
create_event(EVENT_UNEXPECTED_100_RESPONSE);
HttpMsgHeadShared(buffer, buf_size, session_data_, source_id_, buf_owner, flow_, params_)
{
transaction->set_trailer(this, source_id);
+ get_related_sections();
}
void HttpMsgTrailer::gen_events()
type = SEC_BODY_OLD;
prepare_flush(session_data, nullptr, SEC_STATUS, 14, 0, 0, false, 0, 14, true);
my_inspector->process((const uint8_t*)"HTTP/0.9 200 .", 14, flow, SRC_SERVER, false);
+ session_data->transaction[SRC_SERVER]->clear_section();
prepare_flush(session_data, nullptr, SEC_HEADER, 0, 0, 0, false, 0, 0, true);
my_inspector->process((const uint8_t*)"", 0, flow, SRC_SERVER, false);
+ session_data->transaction[SRC_SERVER]->clear_section();
}
HttpCutter*& cutter = session_data->cutter[source_id];
using namespace HttpEnums;
+static void delete_section_list(HttpMsgSection* section_list)
+{
+ while (section_list != nullptr)
+ {
+ HttpMsgSection* tmp = section_list;
+ section_list = section_list->next;
+ delete tmp;
+ }
+}
+
HttpTransaction::~HttpTransaction()
{
delete request;
delete infractions[k];
delete events[k];
}
- delete latest_body;
+ delete_section_list(body_list);
+ delete_section_list(discard_list);
}
HttpTransaction* HttpTransaction::attach_my_transaction(HttpFlowData* session_data, SourceId
// needed it. Instead the two sides have been sharing the transaction. This is a
// soft delete that eliminates our interest in this transaction without disturbing
// the possibly ongoing response processing.
- delete_transaction(session_data->transaction[SRC_CLIENT]);
+ delete_transaction(session_data->transaction[SRC_CLIENT], session_data);
}
else if ((session_data->pipeline_overflow) || (session_data->pipeline_underflow))
{
// Pipelining previously broke down and both sides are processed separately from
// now on. We just throw things away when we are done with them.
- delete_transaction(session_data->transaction[SRC_CLIENT]);
+ delete_transaction(session_data->transaction[SRC_CLIENT], session_data);
}
else if (!session_data->add_to_pipeline(session_data->transaction[SRC_CLIENT]))
{
// The pipeline is full and just overflowed.
*session_data->infractions[source_id] += INF_PIPELINE_OVERFLOW;
session_data->events[source_id]->create_event(EVENT_PIPELINE_MAX);
- delete_transaction(session_data->transaction[SRC_CLIENT]);
+ delete_transaction(session_data->transaction[SRC_CLIENT], session_data);
}
}
session_data->transaction[SRC_CLIENT] = new HttpTransaction;
session_data->transaction[SRC_SERVER]->second_response_expected)
{
session_data->transaction[SRC_SERVER]->second_response_expected = false;
- delete session_data->transaction[SRC_SERVER]->status;
+ session_data->transaction[SRC_SERVER]->discard_section(
+ session_data->transaction[SRC_SERVER]->status);
session_data->transaction[SRC_SERVER]->status = nullptr;
- delete session_data->transaction[SRC_SERVER]->header[SRC_SERVER];
+ session_data->transaction[SRC_SERVER]->discard_section(
+ session_data->transaction[SRC_SERVER]->header[SRC_SERVER]);
session_data->transaction[SRC_SERVER]->header[SRC_SERVER] = nullptr;
- delete session_data->transaction[SRC_SERVER]->trailer[SRC_SERVER];
- session_data->transaction[SRC_SERVER]->trailer[SRC_SERVER] = nullptr;
}
// Status section: delete the current transaction and get a new one from the pipeline. If the
// pipeline is empty check for a request transaction and take it. If there is no transaction
// response side.
else if (session_data->section_type[source_id] == SEC_STATUS)
{
- delete_transaction(session_data->transaction[SRC_SERVER]);
+ delete_transaction(session_data->transaction[SRC_SERVER], session_data);
if (session_data->pipeline_underflow)
{
// A previous underflow separated the two sides forever
}
assert(session_data->transaction[source_id] != nullptr);
+ session_data->transaction[source_id]->active_sections++;
return session_data->transaction[source_id];
}
-void HttpTransaction::delete_transaction(HttpTransaction* transaction)
+void HttpTransaction::discard_section(HttpMsgSection* section)
+{
+ if (section != nullptr)
+ {
+ section->next = discard_list;
+ discard_list = section;
+ }
+}
+
+void HttpTransaction::clear_section()
+{
+ assert(active_sections > 0);
+ active_sections--;
+}
+
+void HttpTransaction::garbage_collect()
+{
+ HttpMsgSection** current = (HttpMsgSection**)&body_list;
+ while (*current != nullptr)
+ {
+ if ((*current)->is_clear())
+ {
+ HttpMsgSection* tmp = *current;
+ *current = (*current)->next;
+ delete tmp;
+ }
+ else
+ current = &(*current)->next;
+ }
+}
+
+void HttpTransaction::delete_transaction(HttpTransaction* transaction, HttpFlowData* session_data)
{
if (transaction != nullptr)
{
if (!transaction->shared_ownership)
- delete transaction;
+ {
+ if ((transaction->active_sections > 0) && (session_data != nullptr))
+ {
+ transaction->next = session_data->discard_list;
+ session_data->discard_list = transaction;
+ }
+ else
+ delete transaction;
+ }
else
transaction->shared_ownership = false;
}
}
-void HttpTransaction::set_body(HttpMsgBody* latest_body_)
+void HttpTransaction::set_body(HttpMsgBody* latest_body)
{
- delete latest_body;
- latest_body = latest_body_;
+ latest_body->next = body_list;
+ body_list = latest_body;
}
HttpInfractions* HttpTransaction::get_infractions(HttpEnums::SourceId source_id)
class HttpTransaction
{
public:
+ ~HttpTransaction();
static HttpTransaction* attach_my_transaction(HttpFlowData* session_data,
HttpEnums::SourceId source_id);
- static void delete_transaction(HttpTransaction* transaction);
+ static void delete_transaction(HttpTransaction* transaction, HttpFlowData* session_data);
HttpMsgRequest* get_request() const { return request; }
void set_request(HttpMsgRequest* request_) { request = request_; }
{ return trailer[source_id]; }
void set_trailer(HttpMsgTrailer* trailer_, HttpEnums::SourceId source_id)
{ trailer[source_id] = trailer_; }
-
- HttpMsgBody* get_body() const { return latest_body; }
- void set_body(HttpMsgBody* latest_body_);
+ void set_body(HttpMsgBody* latest_body);
HttpInfractions* get_infractions(HttpEnums::SourceId source_id);
HttpEventGen* get_events(HttpEnums::SourceId source_id);
void set_one_hundred_response();
bool final_response() const { return !second_response_expected; }
+ void clear_section();
+ bool is_clear() const { return active_sections == 0; }
+ void garbage_collect();
+
+ HttpTransaction* next = nullptr;
+
private:
HttpTransaction() = default;
- ~HttpTransaction();
+ void discard_section(HttpMsgSection* section);
+
+ uint64_t active_sections = 0;
HttpMsgRequest* request = nullptr;
HttpMsgStatus* status = nullptr;
HttpMsgHeader* header[2] = { nullptr, nullptr };
HttpMsgTrailer* trailer[2] = { nullptr, nullptr };
- HttpMsgBody* latest_body = nullptr;
+ HttpMsgBody* body_list = nullptr;
+ HttpMsgSection* discard_list = nullptr;
HttpInfractions* infractions[2] = { nullptr, nullptr };
HttpEventGen* events[2] = { nullptr, nullptr };