]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #1386 in SNORT/snort3 from cisco-wip to master
authorMichael Altizer (mialtize) <mialtize@cisco.com>
Mon, 15 Oct 2018 21:22:46 +0000 (17:22 -0400)
committerMichael Altizer (mialtize) <mialtize@cisco.com>
Mon, 15 Oct 2018 21:22:46 +0000 (17:22 -0400)
Squashed commit of the following:

commit b30a30a659ca307a784bc47d41c815f19e505e2a
Author: Bhagya Tholpady <bbantwal@cisco.com>
Date:   Thu Jul 19 11:27:50 2018 -0400

    detection/http_inspect: Save a snapshot HTTP buffers in the IPS context to support offload of HTTP flows

20 files changed:
src/detection/detection_engine.cc
src/service_inspectors/http_inspect/CMakeLists.txt
src/service_inspectors/http_inspect/http_api.cc
src/service_inspectors/http_inspect/http_api.h
src/service_inspectors/http_inspect/http_context_data.cc [new file with mode: 0644]
src/service_inspectors/http_inspect/http_context_data.h [new file with mode: 0644]
src/service_inspectors/http_inspect/http_flow_data.cc
src/service_inspectors/http_inspect/http_flow_data.h
src/service_inspectors/http_inspect/http_inspect.cc
src/service_inspectors/http_inspect/http_msg_body.cc
src/service_inspectors/http_inspect/http_msg_body.h
src/service_inspectors/http_inspect/http_msg_header.cc
src/service_inspectors/http_inspect/http_msg_request.cc
src/service_inspectors/http_inspect/http_msg_section.cc
src/service_inspectors/http_inspect/http_msg_section.h
src/service_inspectors/http_inspect/http_msg_status.cc
src/service_inspectors/http_inspect/http_msg_trailer.cc
src/service_inspectors/http_inspect/http_stream_splitter_scan.cc
src/service_inspectors/http_inspect/http_transaction.cc
src/service_inspectors/http_inspect/http_transaction.h

index 44d764040e7e29049b8eab2ae2c0ec63aa848c97..c8a6c210e97ebe4fa70acbeae3377649b96d6a7e 100644 (file)
@@ -208,7 +208,12 @@ IpsContextData* DetectionEngine::get_data(unsigned id, IpsContext* context)
     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)
index bd3a7001b44e71ab3a7323a65f1e5a079adf16e2..2950654703ed87c368368b6b8b5f2ddbdf59d5fd 100644 (file)
@@ -46,6 +46,8 @@ set (FILE_LIST
     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
index 2c77c4cf69a9568f6e352d1ab529f00e1fda0be3..bc5da95b13357808420ac62cff34a097d0fdd645 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "http_api.h"
 
+#include "http_context_data.h"
 #include "http_inspect.h"
 
 using namespace snort;
@@ -36,6 +37,12 @@ Inspector* HttpApi::http_ctor(Module* mod)
     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",
index 2116e07112923bf53525a4c6509236f85979bdd5..04d4ba97f614a46e387554baadaa920f6490a786 100644 (file)
@@ -38,7 +38,7 @@ private:
     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; }
diff --git a/src/service_inspectors/http_inspect/http_context_data.cc b/src/service_inspectors/http_inspect/http_context_data.cc
new file mode 100644 (file)
index 0000000..8cde97e
--- /dev/null
@@ -0,0 +1,72 @@
+//--------------------------------------------------------------------------
+// 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;
+}
diff --git a/src/service_inspectors/http_inspect/http_context_data.h b/src/service_inspectors/http_inspect/http_context_data.h
new file mode 100644 (file)
index 0000000..9396d0e
--- /dev/null
@@ -0,0 +1,45 @@
+//--------------------------------------------------------------------------
+// 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
+
index 610ea5e187bc3f1f274638271b9bbd94ecde54d1..12edf371fbc48198e1d79c48c7fbc631272a1a06 100644 (file)
@@ -74,7 +74,7 @@ HttpFlowData::~HttpFlowData()
         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)
         {
@@ -91,6 +91,13 @@ HttpFlowData::~HttpFlowData()
     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)
@@ -161,6 +168,22 @@ void HttpFlowData::trailer_prep(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)
@@ -195,7 +218,7 @@ void HttpFlowData::delete_pipeline()
 {
     for (int k=pipeline_front; k != pipeline_back; k = (k+1) % MAX_PIPELINE)
     {
-        HttpTransaction::delete_transaction(pipeline[k]);
+        delete pipeline[k];
     }
     delete[] pipeline;
 }
index 11011f229410c9dbfe42b1936875bd84ac10afeb..2a3fc13908dc7dcc129ef10b4b90138d611925c3 100644 (file)
@@ -67,6 +67,7 @@ private:
     // 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
 
@@ -133,10 +134,10 @@ private:
     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;
@@ -154,6 +155,9 @@ private:
     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;
index 2dcd91fd096d6687d49c6d67efa2a5a8d9872896..545a161419faffbbfa611410d9b536ba6c32c960 100644 (file)
@@ -29,6 +29,7 @@
 #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"
@@ -81,24 +82,22 @@ bool HttpInspect::configure(SnortConfig* )
 
 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)
@@ -127,13 +126,12 @@ bool HttpInspect::get_buf(unsigned id, 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;
@@ -166,16 +164,14 @@ bool HttpInspect::get_fp_buf(InspectionBuffer::Type ibt, Packet* p, InspectionBu
     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();
@@ -188,16 +184,14 @@ int HttpInspect::get_xtra_trueip(Flow* flow, uint8_t** buf, uint32_t* len, uint3
     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();
@@ -211,16 +205,14 @@ int HttpInspect::get_xtra_uri(Flow* flow, uint8_t** buf, uint32_t* len, uint32_t
     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);
@@ -237,21 +229,19 @@ int HttpInspect::get_xtra_host(Flow* flow, uint8_t** buf, uint32_t* len, uint32_
 // 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;
@@ -288,6 +278,7 @@ void HttpInspect::eval(Packet* p)
     {
         DetectionEngine::disable_content(p);
     }
+
 #ifdef REG_TEST
     else
     {
@@ -300,6 +291,15 @@ void HttpInspect::eval(Packet* p)
     }
 #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);
@@ -311,6 +311,7 @@ void HttpInspect::eval(Packet* p)
 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);
 
@@ -319,31 +320,31 @@ bool HttpInspect::process(const uint8_t* data, const uint16_t dsize, Flow* const
     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:
@@ -355,14 +356,14 @@ bool HttpInspect::process(const uint8_t* data, const uint16_t dsize, Flow* const
         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())
         {
@@ -373,8 +374,8 @@ bool HttpInspect::process(const uint8_t* data, const uint16_t dsize, Flow* const
     }
 #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)
@@ -384,12 +385,23 @@ 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)
@@ -403,20 +415,7 @@ void HttpInspect::clear(Packet* p)
         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();
 }
 
index a654c4d6fa5a4e4e568c76b558e1134472ac50d6..8bc383e64e2fb91ea5ea94247e3b327cef47e990 100644 (file)
@@ -40,6 +40,7 @@ HttpMsgBody::HttpMsgBody(const uint8_t* buffer, const uint16_t buf_size,
     detection_section((body_octets == 0) && (session_data->detect_depth_remaining[source_id] > 0))
 {
     transaction->set_body(this);
+    get_related_sections();
 }
 
 void HttpMsgBody::analyze()
@@ -223,8 +224,6 @@ void HttpMsgBody::do_file_processing(Field& file_data)
         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))
index 8d52893d6709a66a9f4729e79204c96820558239..b884c28a354211c3ac177c4870877694d19a5f9b 100644 (file)
@@ -34,6 +34,7 @@ public:
     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);
index 63190753bbc9b589717627272fdf59162c780ccb..e2ee8aa5337c453f369d6c975e38f9f430f82bb9 100644 (file)
@@ -41,6 +41,7 @@ HttpMsgHeader::HttpMsgHeader(const uint8_t* buffer, const uint16_t buf_size,
     HttpMsgHeadShared(buffer, buf_size, session_data_, source_id_, buf_owner, flow_, params_)
 {
     transaction->set_header(this, source_id);
+    get_related_sections();
 }
 
 void HttpMsgHeader::publish()
@@ -172,8 +173,8 @@ void HttpMsgHeader::update_flow()
         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);
index 5a9c0049c8469f300f19fbc180934ceff9f1cb93..2d263e68fc6e3f28412d3707c9e6f144efcaf60e 100644 (file)
@@ -33,6 +33,7 @@ HttpMsgRequest::HttpMsgRequest(const uint8_t* buffer, const uint16_t buf_size,
     HttpMsgStart(buffer, buf_size, session_data_, source_id_, buf_owner, flow_, params_)
 {
     transaction->set_request(this);
+    get_related_sections();
 }
 
 void HttpMsgRequest::parse_start_line()
index e6ee696068e3d2ce7f07f1149a1939db7b2cac46..05caac50224b1d345ffb397a9f9682abf447f46c 100644 (file)
@@ -23,6 +23,7 @@
 
 #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"
@@ -50,6 +51,15 @@ HttpMsgSection::HttpMsgSection(const uint8_t* buffer, const uint16_t buf_size,
     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
@@ -141,81 +151,71 @@ const Field& HttpMsgSection::get_classic_buffer(unsigned id, uint64_t sub_id, ui
       {
         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)
@@ -244,7 +244,7 @@ const Field& HttpMsgSection::get_classic_buffer(unsigned id, uint64_t sub_id, ui
     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:
@@ -253,6 +253,25 @@ const Field& HttpMsgSection::get_classic_buffer(unsigned id, uint64_t sub_id, ui
     }
 }
 
+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
index 7d7ee692edb7cea7895f2fc09b4f7747e4a70ff5..f14077f2c55d2021505fb5a5e60ffe2b1459dee9 100644 (file)
@@ -34,7 +34,7 @@
 class HttpMsgSection
 {
 public:
-    virtual ~HttpMsgSection() = default;
+    virtual ~HttpMsgSection();
     virtual HttpEnums::InspectSection get_inspection_section() const
         { return HttpEnums::IS_NONE; }
     virtual bool detection_required() const;
@@ -42,6 +42,15 @@ public:
     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;
 
@@ -63,6 +72,11 @@ public:
     // 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;
@@ -73,6 +87,8 @@ protected:
         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;
@@ -85,6 +101,15 @@ protected:
     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);
index 3bb5a5bff8b8fababa8c5c7afb769194dd48506e..376b889fa429b9a027a671512f263b668a681c3f 100644 (file)
@@ -35,6 +35,7 @@ HttpMsgStatus::HttpMsgStatus(const uint8_t* buffer, const uint16_t buf_size,
     HttpMsgStart(buffer, buf_size, session_data_, source_id_, buf_owner, flow_, params_)
 {
     transaction->set_status(this);
+    get_related_sections();
 }
 
 void HttpMsgStatus::parse_start_line()
@@ -148,7 +149,7 @@ void HttpMsgStatus::gen_events()
         }
     }
 
-    if (!transaction->get_request() && (trans_num == 1))
+    if (!request && (trans_num == 1))
     {
         if (flow->is_pdu_inorder(SSN_DIR_FROM_SERVER))
         {
@@ -162,8 +163,8 @@ void HttpMsgStatus::gen_events()
     {
         // 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);
@@ -190,8 +191,8 @@ void HttpMsgStatus::update_flow()
         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);
index d711acc997bc45bf4f8c9a80f427c400d06f8171..908cf1b5aecc041d66946537019d9bd58dd01d98 100644 (file)
@@ -33,6 +33,7 @@ HttpMsgTrailer::HttpMsgTrailer(const uint8_t* buffer, const uint16_t buf_size,
     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()
index 97fc984e972d967cb0e51e42595434a579e917cd..25f760aee418f0951b16a58aa834dfb6aaa45db2 100644 (file)
@@ -155,8 +155,10 @@ StreamSplitter::Status HttpStreamSplitter::scan(Flow* flow, const uint8_t* data,
         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];
index 905a78feea0238491fba9b3064afd253ab7a2563..62caa7cd77e7cda232b606c4ca23e3f1ed349f79 100644 (file)
 
 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;
@@ -44,7 +54,8 @@ HttpTransaction::~HttpTransaction()
         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
@@ -76,20 +87,20 @@ HttpTransaction* HttpTransaction::attach_my_transaction(HttpFlowData* session_da
                 // 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;
@@ -111,12 +122,12 @@ HttpTransaction* HttpTransaction::attach_my_transaction(HttpFlowData* session_da
               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
@@ -124,7 +135,7 @@ HttpTransaction* HttpTransaction::attach_my_transaction(HttpFlowData* session_da
     // 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
@@ -170,24 +181,64 @@ HttpTransaction* HttpTransaction::attach_my_transaction(HttpFlowData* session_da
     }
 
     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)
index c092267ff3c4b624998b9f7035466e9fc28b70ea..241116b89fdd22c618ae0900da3d59437bd58efd 100644 (file)
@@ -36,9 +36,10 @@ class HttpEventGen;
 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_; }
@@ -54,9 +55,7 @@ public:
         { 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);
@@ -64,15 +63,24 @@ public:
     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 };