]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #2906 in SNORT/snort3 from ~KAMURTHI/snort3:strm_id_h2i_publish_re...
authorShravan Rangarajuvenkata (shrarang) <shrarang@cisco.com>
Wed, 2 Jun 2021 17:31:30 +0000 (17:31 +0000)
committerShravan Rangarajuvenkata (shrarang) <shrarang@cisco.com>
Wed, 2 Jun 2021 17:31:30 +0000 (17:31 +0000)
Squashed commit of the following:

commit 4d1cffd596d448faaa47076d2f2182587122310d
Author: Kanimozhi Murthi <kamurthi@cisco.com>
Date:   Mon May 24 16:16:12 2021 -0400

    appid: perform detection on request body for HTTP2 traffic.

commit de11bcc69069ec43181e32a7cb91bcc60ba657d7
Author: Katura Harvey <katharve@cisco.com>
Date:   Mon Apr 12 16:29:09 2021 -0400

    http_inspect: publish event for http/2 request bodies

25 files changed:
src/network_inspectors/appid/CMakeLists.txt
src/network_inspectors/appid/appid_http2_req_body_event_handler.h [new file with mode: 0644]
src/network_inspectors/appid/appid_http_session.cc
src/network_inspectors/appid/appid_http_session.h
src/network_inspectors/appid/appid_inspector.cc
src/pub_sub/CMakeLists.txt
src/pub_sub/http_events.h
src/pub_sub/http_request_body_event.cc [new file with mode: 0644]
src/pub_sub/http_request_body_event.h [new file with mode: 0644]
src/pub_sub/test/CMakeLists.txt
src/pub_sub/test/pub_sub_http_request_body_event_test.cc [new file with mode: 0644]
src/service_inspectors/http_inspect/http_enum.h
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_module.cc
src/service_inspectors/http_inspect/http_module.h
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_section.cc
src/service_inspectors/http_inspect/http_msg_section.h
src/service_inspectors/http_inspect/http_stream_splitter_finish.cc
src/service_inspectors/http_inspect/http_stream_splitter_scan.cc
src/service_inspectors/http_inspect/test/http_transaction_test.cc

index c02b9a637bfd20da727cc38daf8cbf099370c8c6..095521b2f80ed8b384d20f9e3c39a35954799e30 100644 (file)
@@ -154,6 +154,7 @@ set ( APPID_SOURCES
     appid_ha.h
     appid_http_session.cc
     appid_http_session.h
+    appid_http2_req_body_event_handler.h
     appid_opportunistic_tls_event_handler.h
     appid_peg_counts.h
     appid_peg_counts.cc
diff --git a/src/network_inspectors/appid/appid_http2_req_body_event_handler.h b/src/network_inspectors/appid/appid_http2_req_body_event_handler.h
new file mode 100644 (file)
index 0000000..115d542
--- /dev/null
@@ -0,0 +1,73 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2021-2021 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.
+//--------------------------------------------------------------------------
+
+// appid_http2_req_body_event_handler.h
+// author Kani<kamurthi@cisco.com>
+
+#ifndef APPID_HTTP2_REQ_BODY_EVENT_HANDLER_H
+#define APPID_HTTP2_REQ_BODY_EVENT_HANDLER_H
+
+#include "pub_sub/http_request_body_event.h"
+
+class AppIdHttp2ReqBodyEventHandler : public snort::DataHandler
+{
+public:
+    AppIdHttp2ReqBodyEventHandler() : DataHandler(MOD_NAME){ }
+    void handle(snort::DataEvent& event, snort::Flow* flow) override
+    {
+        if (!pkt_thread_odp_ctxt)
+            return;
+        assert(flow);
+        snort::Packet* p = snort::DetectionEngine::get_current_packet();
+        assert(p);
+        AppIdSession* asd = snort::appid_api.get_appid_session(*flow);
+
+        if (!asd or
+            !asd->get_session_flags(APPID_SESSION_DISCOVER_APP | APPID_SESSION_SPECIAL_MONITORED))
+            return;
+        // Skip sessions using old odp context after reload detectors
+        if (pkt_thread_odp_ctxt->get_version() != asd->get_odp_ctxt_version())
+            return;
+        snort::HttpRequestBodyEvent* http_req_body = (snort::HttpRequestBodyEvent*)&event;
+        AppIdHttpSession* hsession = asd->get_matching_http_session(
+            http_req_body->get_http2_stream_id());
+
+        if (!hsession)
+            return;
+
+        const uint8_t* header_start;
+        int32_t header_length;
+        int32_t offset;
+        AppidChangeBits change_bits;
+        header_start = http_req_body->get_request_body_data(header_length, offset);
+        if (hsession->get_field(REQ_BODY_FID) and
+            !asd->get_session_flags(APPID_SESSION_APP_REINSPECT))
+            hsession->set_chp_finished(false);
+
+        hsession->set_req_body_field(REQ_BODY_FID, header_start, header_length, change_bits);
+        hsession->process_http_packet(APP_ID_FROM_INITIATOR, change_bits,
+            asd->get_odp_ctxt().get_http_matchers());
+        asd->publish_appid_event(change_bits, *p, true, asd->get_api().get_hsessions_size() - 1);
+
+        bool last_req_rcvd = http_req_body->is_last_request_body_piece();
+        if (last_req_rcvd)
+            hsession->set_rcvd_full_req_body(last_req_rcvd);
+    }
+};
+#endif
+
index 4855fe01124cdeddf0dfb0a41930e8ccb51421e0..45bd1fc608aeb3e2ad26f99241fdb304cf781db5 100644 (file)
@@ -827,6 +827,34 @@ void AppIdHttpSession::set_field(HttpFieldIds id, const uint8_t* str, int32_t le
     }
 }
 
+void AppIdHttpSession::set_req_body_field(HttpFieldIds id, const uint8_t* str, int32_t len,
+    AppidChangeBits& change_bits)
+{
+    if (str and len)
+    {
+        if (rcvd_full_req_body)
+        {
+            delete meta_data[id];
+            meta_data[id] = nullptr;
+            rcvd_full_req_body = false;
+        }
+
+        if (!meta_data[id])
+            meta_data[id] = new std::string((const char*)str, len);
+        else
+        {
+            std::string *req_body = new std::string(*meta_data[id]);
+            delete meta_data[id];
+            req_body->append((const char*)str);
+            meta_data[id] = req_body;
+        }
+        set_http_change_bits(change_bits, id);
+        set_scan_flags(id);
+
+        if (appidDebug->is_active())
+            print_field(id, meta_data[id]);
+    }
+}
 void AppIdHttpSession::print_field(HttpFieldIds id, const std::string* field)
 {
     string field_name;
index cd79b28b8fb3939050b878d213c22559aa4bd317..97537ddc3e081c3c56b7679b92c7476807d70e76 100644 (file)
@@ -73,6 +73,8 @@ public:
     void update_url(AppidChangeBits& change_bits);
     void set_field(HttpFieldIds id, const std::string* str, AppidChangeBits& change_bits);
     void set_field(HttpFieldIds id, const uint8_t* str, int32_t len, AppidChangeBits& change_bits);
+    void set_req_body_field(HttpFieldIds id, const uint8_t* str, int32_t len,
+        AppidChangeBits& change_bits);
 
     const std::string* get_field(HttpFieldIds id) const
     { return meta_data[id]; }
@@ -158,6 +160,14 @@ public:
     {
         return http2_stream_id;
     }
+    void set_rcvd_full_req_body(bool req_full_body)
+    {
+        rcvd_full_req_body = req_full_body;
+    }
+    bool get_rcvd_full_req_body()
+    {
+        return rcvd_full_req_body;
+    }
 
 protected:
 
@@ -199,6 +209,7 @@ protected:
 #endif
     uint32_t http2_stream_id = 0;
     bool is_payload_processed = false;
+    bool rcvd_full_req_body = false;
 };
 
 #endif
index 48a0ff73895881ffa19c01360d8f387fa1fea0b0..fe8c7fdf0f37aad0030afb6b582ee3ff7f631dd7 100644 (file)
@@ -40,6 +40,7 @@
 #include "appid_discovery.h"
 #include "appid_ha.h"
 #include "appid_http_event_handler.h"
+#include "appid_http2_req_body_event_handler.h"
 #include "appid_opportunistic_tls_event_handler.h"
 #include "appid_peg_counts.h"
 #include "appid_session.h"
@@ -139,6 +140,9 @@ bool AppIdInspector::configure(SnortConfig* sc)
     DataBus::subscribe_global(HTTP_RESPONSE_HEADER_EVENT_KEY, new HttpEventHandler(
         HttpEventHandler::RESPONSE_EVENT, *this), sc);
 
+    DataBus::subscribe_global(HTTP2_REQUEST_BODY_EVENT_KEY, new AppIdHttp2ReqBodyEventHandler(),
+        sc);
+
     DataBus::subscribe_global(DATA_DECRYPT_EVENT, new DataDecryptEventHandler(), sc);
 
     DataBus::subscribe_global(DCERPC_EXP_SESSION_EVENT_KEY, new DceExpSsnEventHandler(), sc);
index d49ddd3119e03516971ec48f666464639e7eadb1..40b0e9e8b7813ccd75feaacfbe1de4564618236f 100644 (file)
@@ -9,6 +9,7 @@ set (PUB_SUB_INCLUDES
     expect_events.h
     finalize_packet_event.h
     http_events.h
+    http_request_body_event.h
     opportunistic_tls_event.h
     sip_events.h
     smb_events.h
@@ -18,6 +19,7 @@ add_library( pub_sub OBJECT
     ${PUB_SUB_INCLUDES}
     cip_events.cc
     http_events.cc
+    http_request_body_event.cc
     sip_events.cc
 )
 
index f4b81c560a1792497096b489b3566adbf137744b..b869c74a5a91ba362e0b93bd1fae9a5de98cd27e 100644 (file)
@@ -66,4 +66,3 @@ private:
 };
 }
 #endif
-
diff --git a/src/pub_sub/http_request_body_event.cc b/src/pub_sub/http_request_body_event.cc
new file mode 100644 (file)
index 0000000..42300ba
--- /dev/null
@@ -0,0 +1,57 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2021-2021 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_request_body_event.cc author Katura Harvey <katharve@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "http_request_body_event.h"
+
+#include "service_inspectors/http_inspect/http_flow_data.h"
+
+using namespace snort;
+
+const uint8_t* HttpRequestBodyEvent::get_request_body_data(int32_t& length, int32_t& offset)
+{
+    offset = msg_offset;
+
+    if (http_msg_body)
+    {
+        const Field& body = http_msg_body->get_msg_text_new();
+        length = http_msg_body->get_publish_length();
+        if (length > 0)
+        {
+            assert(body.length() >= length);
+            return body.start();
+        }
+    }
+
+    length = 0;
+    return nullptr;
+}
+
+bool HttpRequestBodyEvent::is_last_request_body_piece()
+{
+    return last_piece;
+}
+
+uint32_t HttpRequestBodyEvent::get_http2_stream_id() const
+{
+    return http_flow_data->get_h2_stream_id();
+}
diff --git a/src/pub_sub/http_request_body_event.h b/src/pub_sub/http_request_body_event.h
new file mode 100644 (file)
index 0000000..ad1c1c4
--- /dev/null
@@ -0,0 +1,56 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2021-2021 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_request_body_event.h author Katura Harvey <katharve@cisco.com>
+
+#ifndef HTTP_REQUEST_BODY_EVENT_H
+#define HTTP_REQUEST_BODY_EVENT_H
+
+#include "framework/data_bus.h"
+#include "service_inspectors/http_inspect/http_enum.h"
+#include "service_inspectors/http_inspect/http_field.h"
+#include "service_inspectors/http_inspect/http_msg_body.h"
+
+// These are common values between the HTTP inspector and the subscribers.
+#define HTTP2_REQUEST_BODY_EVENT_KEY "http2_request_body_event"
+
+class HttpFlowData;
+
+namespace snort
+{
+// This event is published each time new request body data is received by http_inspect for HTTP/2
+// traffic, up to the publish depth. The full request body may be sent in several pieces
+class SO_PUBLIC HttpRequestBodyEvent : public snort::DataEvent
+{
+public:
+    HttpRequestBodyEvent(HttpMsgBody* msg_body, int32_t offset, bool last, HttpFlowData* flow_data)
+        : http_msg_body(msg_body), msg_offset(offset), last_piece(last), http_flow_data(flow_data)
+        { }
+
+    const uint8_t* get_request_body_data(int32_t& length, int32_t& offset);
+    bool is_last_request_body_piece();
+    uint32_t get_http2_stream_id() const;
+
+private:
+    const HttpMsgBody* const http_msg_body;
+    const int32_t msg_offset;
+    const bool last_piece;
+    HttpFlowData* const http_flow_data;
+};
+}
+#endif
+
index b7c36c96999af851aa9a8e2dda02f59ec05a4ee9..d2c5642b9e65da77db2d733ead1246a78c084ddc 100644 (file)
@@ -2,3 +2,8 @@ add_cpputest( pub_sub_http_event_test
     SOURCES
         ../http_events.cc
 )
+add_cpputest( pub_sub_http_request_body_event_test
+    SOURCES
+        ../http_request_body_event.cc
+        ../../service_inspectors/http_inspect/http_msg_body_cl.cc
+)
diff --git a/src/pub_sub/test/pub_sub_http_request_body_event_test.cc b/src/pub_sub/test/pub_sub_http_request_body_event_test.cc
new file mode 100644 (file)
index 0000000..0af6cb2
--- /dev/null
@@ -0,0 +1,180 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2021-2021 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.
+//--------------------------------------------------------------------------
+// pub_sub_http_request_body_event_test.cc author Katura Harvey <katharve@cisco.com>
+
+// Unit test for the HttpRequestBodyEvent methods for HTTP/2 request bodies
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <cstring>
+
+#include "pub_sub/http_request_body_event.h"
+#include "service_inspectors/http_inspect/http_common.h"
+#include "service_inspectors/http_inspect/http_enum.h"
+#include "service_inspectors/http_inspect/http_field.h"
+#include "service_inspectors/http_inspect/http_msg_body_cl.h"
+
+#include <CppUTest/CommandLineTestRunner.h>
+#include <CppUTest/TestHarness.h>
+#include <CppUTestExt/MockSupport.h>
+
+using namespace snort;
+using namespace HttpCommon;
+using namespace HttpEnums;
+
+// Stubs to make the code link
+HttpMsgBody::HttpMsgBody(const uint8_t* buffer, const uint16_t buf_size,
+    HttpFlowData* session_data_, SourceId source_id_, bool buf_owner, Flow* flow_,
+    const HttpParaList* params_):
+    HttpMsgSection(buffer, buf_size, session_data_, source_id_, buf_owner, flow_, params_),
+    body_octets(0),
+    first_body(0)
+{
+    msg_text_new.set(buf_size, buffer, buf_owner);
+    publish_length = buf_size;
+}
+void HttpMsgBody::analyze() {}
+void HttpMsgBody::publish() {}
+void HttpMsgBody::do_file_processing(const Field&) {}
+void HttpMsgBody::do_utf_decoding(const Field&, Field&) {}
+void HttpMsgBody::do_file_decompression(const Field&, Field&) {}
+void HttpMsgBody::do_js_normalization(const Field&, Field&, bool) {}
+void HttpMsgBody::clean_partial(uint32_t&, uint32_t&, uint8_t*&, uint32_t&, int32_t) {}
+void HttpMsgBody::bookkeeping_regular_flush(uint32_t&, uint8_t*&, uint32_t&, int32_t) {}
+#ifdef REG_TEST
+void HttpMsgBody::print_body_section(FILE*, const char*) {}
+#endif
+
+HttpMsgSection::HttpMsgSection(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_):
+    msg_text(buf_size, buffer, buf_owner),
+    session_data(session_data_),
+    flow(flow_),
+    params(params_),
+    transaction(HttpTransaction::attach_my_transaction(session_data, source_id_)),
+    trans_num(STAT_NOT_PRESENT),
+    status_code_num(STAT_NOT_PRESENT),
+    source_id(source_id_),
+    version_id(VERS__NO_SOURCE),
+    method_id(METH__NOT_PRESENT),
+    tcp_close(false)
+{}
+void HttpMsgSection::update_depth() const{}
+
+HttpTransaction*HttpTransaction::attach_my_transaction(HttpFlowData*, HttpCommon::SourceId)
+    { return nullptr; }
+Field::Field(int32_t length, const uint8_t* start, bool own_the_buffer_) :
+    strt(start), len(length), own_the_buffer(own_the_buffer_)
+{}
+void Field::set(int32_t length, const uint8_t* start, bool own_the_buffer_)
+{
+    assert(len == STAT_NOT_COMPUTE);
+    assert(strt == nullptr);
+    assert(start != nullptr);
+    assert(length >= 0);
+    assert(length <= MAX_OCTETS);
+    strt = start;
+    len = length;
+    own_the_buffer = own_the_buffer_;
+}
+
+void HttpFlowData::half_reset(HttpCommon::SourceId) {}
+
+int32_t HttpMsgBody::get_publish_length() const
+{
+    return mock().getData("pub_length").getIntValue();
+}
+
+uint32_t HttpFlowData::get_h2_stream_id() const
+{
+    return  mock().getData("stream_id").getUnsignedIntValue();
+}
+
+
+TEST_GROUP(pub_sub_http_request_body_event_test)
+{
+    void teardown() override
+    {
+        mock().clear();
+    }
+};
+
+TEST(pub_sub_http_request_body_event_test, first_event)
+{
+    int32_t msg_len = 500;
+    int32_t length, offset;
+    uint32_t stream_id = 1;
+    std::string msg(msg_len, 'A');
+    mock().setData("pub_length", msg_len);
+    mock().setData("stream_id", stream_id);
+    HttpMsgBody* body = new HttpMsgBodyCl((const uint8_t*)msg.c_str(), msg_len, nullptr,
+        HttpCommon::SRC_CLIENT, false, nullptr, nullptr);
+    HttpRequestBodyEvent event(body, 0, false, nullptr);
+    const uint8_t* data = event.get_request_body_data(length, offset);
+    CHECK(memcmp(data, msg.data(), length) == 0);
+    CHECK(length == msg_len);
+    CHECK(offset == 0);
+    CHECK(event.get_http2_stream_id() == stream_id);
+    CHECK_FALSE(event.is_last_request_body_piece());
+    delete body;
+}
+
+TEST(pub_sub_http_request_body_event_test, last_event)
+{
+    int32_t msg_len = 500;
+    int32_t in_offset = REQUEST_PUBLISH_DEPTH - msg_len;
+    int32_t length, offset;
+    uint32_t stream_id = 3;
+    mock().setData("stream_id", stream_id);
+    std::string msg(msg_len, 'A');
+    mock().setData("pub_length", msg_len);
+    HttpMsgBody* body = new HttpMsgBodyCl((const uint8_t*)msg.c_str(), msg_len, nullptr,
+        HttpCommon::SRC_CLIENT, false, nullptr, nullptr);
+    HttpRequestBodyEvent event(body, in_offset, true, nullptr);
+    const uint8_t* data = event.get_request_body_data(length, offset);
+    CHECK(memcmp(data, msg.data(), length) == 0);
+    CHECK(length == msg_len);
+    CHECK(offset == 1500);
+    CHECK(event.get_http2_stream_id() == stream_id);
+    CHECK(event.is_last_request_body_piece());
+    delete body;
+}
+
+TEST(pub_sub_http_request_body_event_test, empty_data_last_event)
+{
+    int32_t in_offset = 1500;
+    int32_t length, offset;
+    uint32_t stream_id = 5;
+    mock().setData("stream_id", stream_id);
+    HttpRequestBodyEvent event(nullptr, in_offset, true, nullptr);
+    const uint8_t* data = event.get_request_body_data(length, offset);
+    CHECK(data == nullptr);
+    CHECK(length == 0);
+    CHECK(offset == 1500);
+    CHECK(event.get_http2_stream_id() == stream_id);
+    CHECK(event.is_last_request_body_piece());
+}
+
+int main(int argc, char** argv)
+{
+    return CommandLineTestRunner::RunAllTests(argc, argv);
+}
+
index e3af9347ea3cde4406448f5ea55312f47de37826..14a76521bdf975bcedfe8f4995ab3cae5fae4fda 100755 (executable)
@@ -27,6 +27,7 @@ namespace HttpEnums
 static const int MAX_OCTETS = 63780;
 static const int GZIP_BLOCK_SIZE = 2048;
 static const int MAX_SECTION_STRETCH = 1460;
+static const int REQUEST_PUBLISH_DEPTH = 2000;
 
 static const uint32_t HTTP_GID = 119;
 static const int GZIP_WINDOW_BITS = 31;
index 9fd7e2eb5ebf628d3e5f7e318a0d36c4c3edd5da..89a6fc7c1d53eec3900f2d117568ce3e075ccd7d 100644 (file)
@@ -24,6 +24,7 @@
 #include "http_flow_data.h"
 
 #include "decompress/file_decomp.h"
+#include "service_inspectors/http2_inspect/http2_flow_data.h"
 #include "utils/js_normalizer.h"
 
 #include "http_cutter.h"
@@ -47,7 +48,7 @@ unsigned HttpFlowData::inspector_id = 0;
 uint64_t HttpFlowData::instance_count = 0;
 #endif
 
-HttpFlowData::HttpFlowData() : FlowData(inspector_id)
+HttpFlowData::HttpFlowData(Flow* flow) : FlowData(inspector_id)
 {
 #ifdef REG_TEST
     if (HttpTestManager::use_test_output(HttpTestManager::IN_HTTP))
@@ -65,6 +66,15 @@ HttpFlowData::HttpFlowData() : FlowData(inspector_id)
     if (HttpModule::get_peg_counts(PEG_MAX_CONCURRENT_SESSIONS) <
         HttpModule::get_peg_counts(PEG_CONCURRENT_SESSIONS))
         HttpModule::increment_peg_counts(PEG_MAX_CONCURRENT_SESSIONS);
+
+    Http2FlowData* h2i_flow_data = nullptr;
+    if (Http2FlowData::inspector_id != 0)
+        h2i_flow_data = (Http2FlowData*)flow->get_flow_data(Http2FlowData::inspector_id);
+    if (h2i_flow_data != nullptr)
+    {
+        for_http2 = true;
+        h2_stream_id = h2i_flow_data->get_processing_stream_id();
+    }
 }
 
 HttpFlowData::~HttpFlowData()
@@ -138,12 +148,14 @@ void HttpFlowData::half_reset(SourceId source_id)
     data_length[source_id] = STAT_NOT_PRESENT;
     body_octets[source_id] = STAT_NOT_PRESENT;
     file_octets[source_id] = STAT_NOT_PRESENT;
+    publish_octets[source_id] = STAT_NOT_PRESENT;
     partial_inspected_octets[source_id] = 0;
     section_size_target[source_id] = 0;
     stretch_section_to_packet[source_id] = false;
     accelerated_blocking[source_id] = false;
     file_depth_remaining[source_id] = STAT_NOT_PRESENT;
     detect_depth_remaining[source_id] = STAT_NOT_PRESENT;
+    publish_depth_remaining[source_id] = STAT_NOT_PRESENT;
     detection_status[source_id] = DET_REACTIVATING;
 
     compression[source_id] = CMP_NONE;
@@ -320,6 +332,11 @@ void HttpFlowData::finish_h2_body(HttpCommon::SourceId source_id, HttpEnums::H2B
     }
 }
 
+uint32_t HttpFlowData::get_h2_stream_id() const
+{
+    return h2_stream_id;
+}
+
 #ifdef REG_TEST
 void HttpFlowData::show(FILE* out_file) const
 {
index ffcb7aece8e1b2a9befd52ae4923e80083d6c0db..eb86e790d0358bdf583e917da5f17845bfbd5ab2 100644 (file)
@@ -47,7 +47,7 @@ class JSNormalizer;
 class HttpFlowData : public snort::FlowData
 {
 public:
-    HttpFlowData();
+    HttpFlowData(snort::Flow* flow);
     ~HttpFlowData() override;
     static unsigned inspector_id;
     static void init() { inspector_id = snort::FlowData::create_flow_data_id(); }
@@ -86,11 +86,14 @@ public:
     void reset_partial_flush(HttpCommon::SourceId source_id)
     { partial_flush[source_id] = false; }
 
+    uint32_t get_h2_stream_id() const;
+
 private:
     // HTTP/2 handling
     bool for_http2 = false;
     HttpEnums::H2BodyState h2_body_state[2] = { HttpEnums::H2_BODY_NOT_COMPLETE,
          HttpEnums::H2_BODY_NOT_COMPLETE };
+    uint32_t h2_stream_id = 0;
 
     // Convenience routines
     void half_reset(HttpCommon::SourceId source_id);
@@ -165,12 +168,15 @@ private:
         HttpCommon::STAT_NOT_PRESENT };
     int64_t detect_depth_remaining[2] = { HttpCommon::STAT_NOT_PRESENT,
         HttpCommon::STAT_NOT_PRESENT };
+    int32_t publish_depth_remaining[2] = { HttpCommon::STAT_NOT_PRESENT,
+        HttpCommon::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] = { HttpCommon::STAT_NOT_PRESENT, HttpCommon::STAT_NOT_PRESENT };
     // normalized octets forwarded to file or MIME processing
     int64_t file_octets[2] = { HttpCommon::STAT_NOT_PRESENT, HttpCommon::STAT_NOT_PRESENT };
+    int32_t publish_octets[2] = { HttpCommon::STAT_NOT_PRESENT, HttpCommon::STAT_NOT_PRESENT };
     uint32_t partial_inspected_octets[2] = { 0, 0 };
     uint8_t* partial_detect_buffer[2] = { nullptr, nullptr };
     uint32_t partial_detect_length[2] = { 0, 0 };
index da9d7311566f814f2e9fce99968ad1b6b6d087aa..29b646158a0d24ba652b66c2774ebcd1a282a13d 100755 (executable)
@@ -174,6 +174,7 @@ void HttpInspect::show(const SnortConfig*) const
     ConfigLogger::log_flag("plus_to_space", params->uri_param.plus_to_space);
     ConfigLogger::log_flag("simplify_path", params->uri_param.simplify_path);
     ConfigLogger::log_value("xff_headers", xff_headers.c_str());
+    ConfigLogger::log_flag("request_body_app_detection", params->publish_request_body);
 }
 
 InspectSection HttpInspect::get_latest_is(const Packet* p)
@@ -428,14 +429,14 @@ HttpFlowData* HttpInspect::http_get_flow_data(const Flow* flow)
 
 void HttpInspect::http_set_flow_data(Flow* flow, HttpFlowData* flow_data)
 {
-    Http2FlowData* h2i_flow_data = nullptr;
-    if (Http2FlowData::inspector_id != 0)
-        h2i_flow_data = (Http2FlowData*)flow->get_flow_data(Http2FlowData::inspector_id);
-    if (h2i_flow_data == nullptr)
+    // for_http2 set in HttpFlowData constructor after checking for h2i_flow_data
+    if (!flow_data->for_http2)
         flow->set_flow_data(flow_data);
     else
     {
-        flow_data->for_http2 = true;
+        Http2FlowData* h2i_flow_data =
+            (Http2FlowData*)flow->get_flow_data(Http2FlowData::inspector_id);
+        assert(h2i_flow_data);
         h2i_flow_data->set_hi_flow_data(flow_data);
     }
 }
index cefc1c920668e1ffa65658ce78f78281568329ea..644d4d47a473cc96460bdaee57afaf2b79aa89ff 100755 (executable)
@@ -123,6 +123,11 @@ const Parameter HttpModule::http_params[] =
     { "xff_headers", Parameter::PT_STRING, nullptr, "x-forwarded-for true-client-ip",
       "specifies the xff type headers to parse and consider in the same order "
       "of preference as defined" },
+
+    { "request_body_app_detection", Parameter::PT_BOOL, nullptr, "false",
+      "make HTTP/2 request message bodies available for application detection "
+          "(detection requires AppId)" },
+
 #ifdef REG_TEST
     { "test_input", Parameter::PT_BOOL, nullptr, "false",
       "read HTTP messages from text file" },
@@ -308,6 +313,11 @@ bool HttpModule::set(const char*, Value& val, SnortConfig*)
         }
         params->xff_headers[hdr_idx] = end_header;
     }
+    else if (val.is("request_body_app_detection"))
+    {
+        params->publish_request_body = val.get_bool();
+    }
+
 #ifdef REG_TEST
     else if (val.is("test_input"))
     {
index d8318d6a209140220125ecdd0140a7c44c6f444f..2374d253ae22f323a8d07555e7d3ff83819e6bdb 100755 (executable)
@@ -47,6 +47,7 @@ public:
     bool decompress_zip = false;
     bool script_detection = false;
     snort::LiteralSearch::Handle* script_detection_handle = nullptr;
+    bool publish_request_body = false;
 
     struct JsNormParam
     {
index 26a18d48dacf56bbd4864cfbe5aaa334c93c1648..2ba5f71a631a5903020d24f98ecbfcb8b904ee91 100644 (file)
@@ -24,6 +24,7 @@
 #include "http_msg_body.h"
 
 #include "file_api/file_flows.h"
+#include "pub_sub/http_request_body_event.h"
 
 #include "http_api.h"
 #include "http_common.h"
@@ -31,6 +32,7 @@
 #include "http_js_norm.h"
 #include "http_msg_header.h"
 #include "http_msg_request.h"
+#include "http_test_manager.h"
 
 using namespace snort;
 using namespace HttpCommon;
@@ -47,6 +49,31 @@ HttpMsgBody::HttpMsgBody(const uint8_t* buffer, const uint16_t buf_size,
     get_related_sections();
 }
 
+void HttpMsgBody::publish()
+{
+    if (publish_length <= 0)
+        return;
+
+    const int32_t& pub_depth_remaining = session_data->publish_depth_remaining[source_id];
+    int32_t& publish_octets = session_data->publish_octets[source_id];
+    const bool last_piece = (session_data->cutter[source_id] == nullptr) || tcp_close ||
+        (pub_depth_remaining == 0);
+
+    HttpRequestBodyEvent http_request_body_event(this, publish_octets, last_piece, session_data);
+
+    DataBus::publish(HTTP2_REQUEST_BODY_EVENT_KEY, http_request_body_event, flow);
+    publish_octets += publish_length;
+#ifdef REG_TEST
+    if (HttpTestManager::use_test_output(HttpTestManager::IN_HTTP))
+    {
+        fprintf(HttpTestManager::get_output_file(),
+            "Published %" PRId32 " bytes of request body. last: %s\n", publish_length,
+            (last_piece ? "true" : "false"));
+        fflush(HttpTestManager::get_output_file());
+    }
+#endif
+}
+
 void HttpMsgBody::bookkeeping_regular_flush(uint32_t& partial_detect_length,
     uint8_t*& partial_detect_buffer, uint32_t& partial_js_detect_length, int32_t detect_length)
 {
@@ -70,8 +97,8 @@ void HttpMsgBody::clean_partial(uint32_t& partial_inspected_octets, uint32_t& pa
         delete[] partial_detect_buffer;
         session_data->update_deallocations(partial_detect_length);
         assert(detect_length <= session_data->detect_depth_remaining[source_id]);
-        bookkeeping_regular_flush(partial_detect_length, partial_detect_buffer, partial_js_detect_length,
-            detect_length);
+        bookkeeping_regular_flush(partial_detect_length, partial_detect_buffer,
+            partial_js_detect_length, detect_length);
     }
 }
 
@@ -96,72 +123,81 @@ void HttpMsgBody::analyze()
     else
         msg_text_new.set(msg_text);
 
-    do_utf_decoding(msg_text_new, decoded_body);
-
-    if (session_data->file_depth_remaining[source_id] > 0)
+    int32_t& pub_depth_remaining = session_data->publish_depth_remaining[source_id];
+    if (pub_depth_remaining > 0)
     {
-        do_file_processing(decoded_body);
+        publish_length = (pub_depth_remaining > msg_text_new.length()) ?
+            msg_text_new.length() : pub_depth_remaining;
+        pub_depth_remaining -= publish_length;
     }
 
-    if (session_data->detect_depth_remaining[source_id] > 0)
+    if (session_data->file_depth_remaining[source_id] > 0 or
+        session_data->detect_depth_remaining[source_id] > 0)
     {
-        do_file_decompression(decoded_body, decompressed_file_body);
+        do_utf_decoding(msg_text_new, decoded_body);
 
-        uint32_t& partial_detect_length = session_data->partial_detect_length[source_id];
-        uint8_t*& partial_detect_buffer = session_data->partial_detect_buffer[source_id];
-        uint32_t& partial_js_detect_length = session_data->partial_js_detect_length[source_id];
+        if (session_data->file_depth_remaining[source_id] > 0)
+        {
+            do_file_processing(decoded_body);
+        }
 
-        if (partial_detect_length > 0)
+        if (session_data->detect_depth_remaining[source_id] > 0)
         {
-            const int32_t total_length = partial_detect_length + decompressed_file_body.length();
-            uint8_t* const cumulative_buffer = new uint8_t[total_length];
-            memcpy(cumulative_buffer, partial_detect_buffer, partial_detect_length);
-            memcpy(cumulative_buffer + partial_detect_length, decompressed_file_body.start(),
-                decompressed_file_body.length());
-            cumulative_data.set(total_length, cumulative_buffer, true);
+            do_file_decompression(decoded_body, decompressed_file_body);
 
-            do_js_normalization(cumulative_data, js_norm_body, true);
+            uint32_t& partial_detect_length = session_data->partial_detect_length[source_id];
+            uint8_t*& partial_detect_buffer = session_data->partial_detect_buffer[source_id];
+            uint32_t& partial_js_detect_length = session_data->partial_js_detect_length[source_id];
 
-            if ((int32_t)partial_js_detect_length == js_norm_body.length())
+            if (partial_detect_length > 0)
             {
-                clean_partial(partial_inspected_octets, partial_detect_length,
-                    partial_detect_buffer, partial_js_detect_length, js_norm_body.length());
-                return;
+                const int32_t total_length = partial_detect_length + decompressed_file_body.length();
+                uint8_t* const cumulative_buffer = new uint8_t[total_length];
+                memcpy(cumulative_buffer, partial_detect_buffer, partial_detect_length);
+                memcpy(cumulative_buffer + partial_detect_length, decompressed_file_body.start(),
+                    decompressed_file_body.length());
+                cumulative_data.set(total_length, cumulative_buffer, true);
+                do_js_normalization(cumulative_data, js_norm_body, true);
+                if ((int32_t)partial_js_detect_length == js_norm_body.length())
+                {
+                    clean_partial(partial_inspected_octets, partial_detect_length,
+                        partial_detect_buffer, partial_js_detect_length, js_norm_body.length());
+                    return;
+                }
             }
-        }
-        else
-            do_js_normalization(decompressed_file_body, js_norm_body, false);
+            else
+                do_js_normalization(decompressed_file_body, js_norm_body, false);
 
-        const int32_t detect_length =
-            (js_norm_body.length() <= session_data->detect_depth_remaining[source_id]) ?
-            js_norm_body.length() : session_data->detect_depth_remaining[source_id];
+            const int32_t detect_length =
+                (js_norm_body.length() <= session_data->detect_depth_remaining[source_id]) ?
+                js_norm_body.length() : session_data->detect_depth_remaining[source_id];
 
-        detect_data.set(detect_length, js_norm_body.start());
+            detect_data.set(detect_length, js_norm_body.start());
 
-        delete[] partial_detect_buffer;
-        session_data->update_deallocations(partial_detect_length);
+            delete[] partial_detect_buffer;
+            session_data->update_deallocations(partial_detect_length);
 
-        if (!session_data->partial_flush[source_id])
-        {
-            bookkeeping_regular_flush(partial_detect_length, partial_detect_buffer,
-                partial_js_detect_length, detect_length);
-        }
-        else
-        {
-            Field* decompressed = (cumulative_data.length() > 0) ?
-                &cumulative_data : &decompressed_file_body;
-            uint8_t* const save_partial = new uint8_t[decompressed->length()];
-            memcpy(save_partial, decompressed->start(), decompressed->length());
-            partial_detect_buffer = save_partial;
-            partial_detect_length = decompressed->length();
-            partial_js_detect_length = js_norm_body.length();
-            session_data->update_allocations(partial_detect_length);
-        }
+            if (!session_data->partial_flush[source_id])
+            {
+                bookkeeping_regular_flush(partial_detect_length, partial_detect_buffer,
+                    partial_js_detect_length, detect_length);
+            }
+            else
+            {
+                Field* decompressed = (cumulative_data.length() > 0) ?
+                    &cumulative_data : &decompressed_file_body;
+                uint8_t* const save_partial = new uint8_t[decompressed->length()];
+                memcpy(save_partial, decompressed->start(), decompressed->length());
+                partial_detect_buffer = save_partial;
+                partial_detect_length = decompressed->length();
+                partial_js_detect_length = js_norm_body.length();
+                session_data->update_allocations(partial_detect_length);
+            }
 
-        set_file_data(const_cast<uint8_t*>(detect_data.start()),
-            (unsigned)detect_data.length());
+            set_file_data(const_cast<uint8_t*>(detect_data.start()),
+                (unsigned)detect_data.length());
+        }
     }
-
     body_octets += msg_text.length();
     partial_inspected_octets = session_data->partial_flush[source_id] ? msg_text.length() : 0;
 }
@@ -429,6 +465,11 @@ const Field& HttpMsgBody::get_classic_client_body()
     return classic_normalize(detect_data, classic_client_body, false, params->uri_param);
 }
 
+int32_t HttpMsgBody::get_publish_length() const
+{
+    return publish_length;
+}
+
 #ifdef REG_TEST
 // Common elements of print_section() for body sections
 void HttpMsgBody::print_body_section(FILE* output, const char* body_type_str)
index 689a9381db33c6cfac5b2155b373e0fd22c8fb62..81a81458afc7f172dff2927fec2bb725c21e0b36 100644 (file)
@@ -39,8 +39,11 @@ public:
     HttpMsgBody* get_body() override { return this; }
     const Field& get_classic_client_body();
     const Field& get_detect_data() { return detect_data; }
+    const Field& get_msg_text_new() const { return msg_text_new; }
     static void fd_event_callback(void* context, int event);
     bool is_first() { return first_body; }
+    void publish() override;
+    int32_t get_publish_length() const;
 
 protected:
     HttpMsgBody(const uint8_t* buffer, const uint16_t buf_size, HttpFlowData* session_data_,
@@ -75,6 +78,8 @@ private:
     Field detect_data;
     Field enhanced_js_norm_body;
     Field classic_client_body;   // URI normalization applied
+
+    int32_t publish_length = HttpCommon::STAT_NOT_PRESENT;
 };
 
 #endif
index 3fd2d3bb84d039a7785f73c7a3a836b14bb0a57d..e454bf6d40f2dc046587e1d19e93cdbe365c0379 100755 (executable)
@@ -30,6 +30,7 @@
 #include "file_api/file_service.h"
 #include "hash/hash_key_operations.h"
 #include "pub_sub/http_events.h"
+#include "pub_sub/http_request_body_event.h"
 #include "service_inspectors/http2_inspect/http2_flow_data.h"
 #include "sfip/sf_ip.h"
 
@@ -55,14 +56,14 @@ HttpMsgHeader::HttpMsgHeader(const uint8_t* buffer, const uint16_t buf_size,
 
 void HttpMsgHeader::publish()
 {
-    const uint32_t stream_id = get_h2_stream_id();
+    const uint32_t stream_id = session_data->get_h2_stream_id();
 
-    HttpEvent http_event(this, session_data->for_http2, stream_id);
+    HttpEvent http_header_event(this, session_data->for_http2, stream_id);
 
     const char* key = (source_id == SRC_CLIENT) ?
         HTTP_REQUEST_HEADER_EVENT_KEY : HTTP_RESPONSE_HEADER_EVENT_KEY;
 
-    DataBus::publish(key, http_event, flow);
+    DataBus::publish(key, http_header_event, flow);
 }
 
 const Field& HttpMsgHeader::get_true_ip()
@@ -429,6 +430,11 @@ void HttpMsgHeader::prepare_body()
     const int64_t& depth = (source_id == SRC_CLIENT) ? params->request_depth :
         params->response_depth;
     session_data->detect_depth_remaining[source_id] = (depth != -1) ? depth : INT64_MAX;
+    if ((source_id == SRC_CLIENT) and params->publish_request_body and session_data->for_http2)
+    {
+        session_data->publish_octets[source_id] = 0;
+        session_data->publish_depth_remaining[source_id] = REQUEST_PUBLISH_DEPTH;
+    }
     setup_file_processing();
     setup_encoding_decompression();
     setup_utf_decoding();
@@ -463,7 +469,7 @@ void HttpMsgHeader::setup_file_processing()
     }
 
     // Generate the unique file id for multi file processing
-    set_multi_file_processing_id(get_transaction_id(), get_h2_stream_id());
+    set_multi_file_processing_id(get_transaction_id(), session_data->get_h2_stream_id());
 
     // Do we meet all the conditions for MIME file processing?
     if (source_id == SRC_CLIENT)
index 1cfaf1a480f20cd263d7fd3994c503600fe76c21..e6277c6f5b32f29279c673f58b69832b3e362c62 100644 (file)
@@ -90,6 +90,7 @@ void HttpMsgSection::update_depth() const
 {
     const int64_t& file_depth_remaining = session_data->file_depth_remaining[source_id];
     const int64_t& detect_depth_remaining = session_data->detect_depth_remaining[source_id];
+    const int32_t& publish_depth_remaining = session_data->publish_depth_remaining[source_id];
 
     if ((detect_depth_remaining <= 0) &&
         (session_data->detection_status[source_id] == DET_ON) &&
@@ -103,16 +104,16 @@ void HttpMsgSection::update_depth() const
 
     if (detect_depth_remaining <= 0)
     {
-        if (file_depth_remaining <= 0)
+        if ((file_depth_remaining <= 0) && (publish_depth_remaining <= 0))
         {
             // Don't need any more of the body
             session_data->section_size_target[source_id] = 0;
         }
         else
         {
-            // Just for file processing.
-            session_data->section_size_target[source_id] = target_size;
+            // Need data for file processing or publishing
             session_data->stretch_section_to_packet[source_id] = true;
+            session_data->section_size_target[source_id] = target_size;
         }
         return;
     }
@@ -396,23 +397,6 @@ void HttpMsgSection::get_related_sections()
     trailer[SRC_SERVER] = transaction->get_trailer(SRC_SERVER);
 }
 
-uint32_t HttpMsgSection::get_h2_stream_id()
-{
-    if (h2_stream_id != STAT_NOT_COMPUTE)
-        return h2_stream_id;
-    
-    h2_stream_id = 0;
-    if (session_data->for_http2)
-    {
-        Http2FlowData* h2i_flow_data =
-            (Http2FlowData*)flow->get_flow_data(Http2FlowData::inspector_id);
-        assert(h2i_flow_data);
-        if (h2i_flow_data)
-            h2_stream_id = h2i_flow_data->get_processing_stream_id();
-    }
-    return h2_stream_id;
-}
-
 void HttpMsgSection::clear()
 {
     transaction->clear_section();
index a270f7fc6326bbc30c290c89da1f5c5a5b5c941f..8f41965c958ce305450c009f576a90e562639931 100644 (file)
@@ -107,9 +107,6 @@ protected:
     HttpEnums::MethodId method_id;
     const bool tcp_close;
 
-    int64_t h2_stream_id = HttpCommon::STAT_NOT_COMPUTE;
-    uint32_t get_h2_stream_id();
-
     // Pointers to related message sections in the same transaction
     HttpMsgRequest* request;
     HttpMsgStatus* status;
index d31bbe25f8e40779d6f901e0d69cc209834247fc..d0d799f12213125ad54c900ffdc6b46bfd3fc1f3 100644 (file)
 #endif
 
 #include "file_api/file_flows.h"
+#include "pub_sub/http_request_body_event.h"
 
 #include "http_common.h"
 #include "http_cutter.h"
 #include "http_enum.h"
+#include "http_field.h"
 #include "http_inspect.h"
 #include "http_module.h"
 #include "http_msg_header.h"
@@ -124,50 +126,73 @@ bool HttpStreamSplitter::finish(Flow* flow)
         return true;
     }
 
-    // If there is no more data to process we need to wrap up file processing right now
-    if ((session_data->file_depth_remaining[source_id] > 0)        &&
-        (session_data->cutter[source_id] != nullptr)               &&
+    // If there is no more data to process we may need to tell other components
+    if ((session_data->cutter[source_id] != nullptr) &&
         (session_data->cutter[source_id]->get_octets_seen() ==
             session_data->partial_raw_bytes[source_id]))
     {
-        Packet* packet = DetectionEngine::get_current_packet();
-        if (!session_data->mime_state[source_id])
+        // Wrap up file processing
+        if (session_data->file_depth_remaining[source_id] > 0)
         {
-            FileFlows* file_flows = FileFlows::get_file_flows(flow);
-            if (!file_flows)
-                return false;
-
-            const FileDirection dir = (source_id == SRC_SERVER) ? FILE_DOWNLOAD : FILE_UPLOAD;
-
-            assert(session_data->transaction[source_id] != nullptr);
-            HttpMsgHeader* header = session_data->transaction[source_id]->get_header(source_id);
-            assert(header);
-
-            uint64_t file_index = header->get_file_cache_index();
-            const uint64_t file_processing_id = header->get_multi_file_processing_id();
-            file_flows->file_process(packet, file_index, nullptr, 0,
-                session_data->file_octets[source_id], dir, file_processing_id, SNORT_FILE_END);
-#ifdef REG_TEST
-            if (HttpTestManager::use_test_output(HttpTestManager::IN_HTTP))
+            Packet* packet = DetectionEngine::get_current_packet();
+            if (!session_data->mime_state[source_id])
             {
-                fprintf(HttpTestManager::get_output_file(),
-                    "File processing finalization during finish()\n");
-                fflush(HttpTestManager::get_output_file());
+                FileFlows* file_flows = FileFlows::get_file_flows(flow);
+                if (!file_flows)
+                    return false;
+
+                const FileDirection dir = (source_id == SRC_SERVER) ? FILE_DOWNLOAD :
+                    FILE_UPLOAD;
+
+                assert(session_data->transaction[source_id] != nullptr);
+                HttpMsgHeader* header = session_data->transaction[source_id]->
+                    get_header(source_id);
+                assert(header);
+
+                uint64_t file_index = header->get_file_cache_index();
+                const uint64_t file_processing_id = header->get_multi_file_processing_id();
+                file_flows->file_process(packet, file_index, nullptr, 0,
+                    session_data->file_octets[source_id], dir, file_processing_id,
+                    SNORT_FILE_END);
+#ifdef REG_TEST
+                if (HttpTestManager::use_test_output(HttpTestManager::IN_HTTP))
+                {
+                    fprintf(HttpTestManager::get_output_file(),
+                        "File processing finalization during finish()\n");
+                    fflush(HttpTestManager::get_output_file());
+                }
+#endif
             }
+            else
+            {
+                // FIXIT-M The following call does not actually accomplish anything. The MIME
+                // interface needs to be enhanced so that we can communicate end-of-data
+                // without side effects.
+                session_data->mime_state[source_id]->process_mime_data(packet, nullptr, 0, true,
+                    SNORT_FILE_POSITION_UNKNOWN);
+                delete session_data->mime_state[source_id];
+                session_data->mime_state[source_id] = nullptr;
+#ifdef REG_TEST
+                if (HttpTestManager::use_test_output(HttpTestManager::IN_HTTP))
+                {
+                    fprintf(HttpTestManager::get_output_file(),
+                        "MIME finalization during finish()\n");
+                    fflush(HttpTestManager::get_output_file());
+                }
 #endif
+            }
         }
-        else
+        // If we were publishing a request body need to publish that body is complete
+        if (session_data->publish_depth_remaining[source_id] > 0)
         {
-            // FIXIT-M The following call does not actually accomplish anything. The MIME interface
-            // needs to be enhanced so that we can communicate end-of-data without side effects.
-            session_data->mime_state[source_id]->process_mime_data(packet, nullptr, 0, true,
-                SNORT_FILE_POSITION_UNKNOWN);
-            delete session_data->mime_state[source_id];
-            session_data->mime_state[source_id] = nullptr;
+            HttpRequestBodyEvent http_request_body_event(nullptr,
+                session_data->publish_octets[source_id], true, session_data);
+            DataBus::publish(HTTP2_REQUEST_BODY_EVENT_KEY, http_request_body_event, flow);
 #ifdef REG_TEST
             if (HttpTestManager::use_test_output(HttpTestManager::IN_HTTP))
             {
-                fprintf(HttpTestManager::get_output_file(), "MIME finalization during finish()\n");
+                fprintf(HttpTestManager::get_output_file(),
+                    "Request body event published during finish()\n");
                 fflush(HttpTestManager::get_output_file());
             }
 #endif
index 3b4992d92cdb0cab3240ec5db938a385ce71cc35..2373279b5ccadbfcef1d2a13f094a00c43c9ac37 100644 (file)
@@ -138,7 +138,7 @@ StreamSplitter::Status HttpStreamSplitter::scan(Packet* pkt, const uint8_t* data
 
     if (session_data == nullptr)
     {
-        HttpInspect::http_set_flow_data(flow, session_data = new HttpFlowData);
+        HttpInspect::http_set_flow_data(flow, session_data = new HttpFlowData(flow));
         HttpModule::increment_peg_counts(PEG_FLOW);
     }
 
index e88a662bd463f31abf69839068bf218c1347248a..a9b8acc576092051624b14b17f2386bd345ba9e8 100644 (file)
@@ -28,6 +28,7 @@
 #include "service_inspectors/http_inspect/http_flow_data.h"
 #include "service_inspectors/http_inspect/http_module.h"
 #include "service_inspectors/http_inspect/http_transaction.h"
+#include "service_inspectors/http2_inspect/http2_flow_data.h"
 
 #include <CppUTest/CommandLineTestRunner.h>
 #include <CppUTest/TestHarness.h>
@@ -48,8 +49,12 @@ fd_status_t File_Decomp_StopFree(fd_session_t*) { return File_Decomp_OK; }
 uint32_t str_to_hash(const uint8_t *, size_t) { return 0; }
 void FlowData::update_allocations(size_t) {}
 void FlowData::update_deallocations(size_t) {}
+FlowData* Flow::get_flow_data(uint32_t) const { return nullptr; }
 }
 
+unsigned Http2FlowData::inspector_id = 0;
+uint32_t Http2FlowData::get_processing_stream_id() const { return 0; }
+
 THREAD_LOCAL PegCount HttpModule::peg_counts[PEG_COUNT_MAX] = { };
 
 class HttpUnitTestSetup
@@ -63,7 +68,7 @@ public:
 
 TEST_GROUP(http_transaction_test)
 {
-    HttpFlowData* const flow_data = new HttpFlowData;
+    HttpFlowData* const flow_data = new HttpFlowData(nullptr);
     SectionType* const section_type = HttpUnitTestSetup::get_section_type(flow_data);
     SectionType* const type_expected = HttpUnitTestSetup::get_type_expected(flow_data);