]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Pull request #4915: http_inspect: partial inpection on start line
authorAdrian Mamolea (admamole) <admamole@cisco.com>
Mon, 13 Oct 2025 18:05:18 +0000 (18:05 +0000)
committerRayen Mohanty (ramohant) <ramohant@cisco.com>
Mon, 13 Oct 2025 18:05:18 +0000 (18:05 +0000)
Merge in SNORT/snort3 from ~ADMAMOLE/snort3:part_rl to master

Squashed commit of the following:

commit 0499b6ce50885ba6544ddf8202cf52a25b57a9ee
Author: Adrian Mamolea <admamole@cisco.com>
Date:   Mon Sep 15 12:45:22 2025 -0400

    http_inspect: partial inpection on start line

doc/user/http_inspect.txt
src/service_inspectors/http_inspect/http_msg_request.cc
src/service_inspectors/http_inspect/http_msg_request.h
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
src/service_inspectors/http_inspect/test/http_transaction_test.cc
src/service_inspectors/http_inspect/test/http_unit_test_helpers.h

index 66c319e0ed9cc0bf2c0ab3c0fb22588bc0aff5fc..65a3e4dab186e582514a705ae7eb9e50789999cb 100755 (executable)
@@ -153,7 +153,7 @@ This feature is off by default. script_detection = true will activate it.
 Partial depth detection enables faster threat detection by immediately forwarding
 partial message data to the detection engine before the complete message arrives.
 This feature can be configured independently for HTTP request bodies
-(partial_depth_body) and headers (partial_depth_header).
+(partial_depth_body) and headers and request line (partial_depth_header).
 
     Configuration options:
     0 (default):    Feature disabled
@@ -164,6 +164,10 @@ For HTTP request bodies only, the maximum configurable value is 16,384 bytes. Us
 partial_depth_body = -1 when early detection is needed beyond this limit. HTTP
 headers have no such limitation.
 
+Partially inspected request lines are not parsed, the raw content is available in the
+http_raw_request buffer. Incomplete lines in partially inspected headers are not parsed.
+The raw content of the partial header is available in the http_raw_header buffer.
+
 This feature is turned off by default by setting partial_depth_body = 0
 and partial_depth_header = 0. To activate it, set the corresponding parameter to
 the desired value.
index 9d3275e39cc8d57f29d0828420a47581fdda1041..c0dbb3195de4d52bc8419823041e4803062a545a 100644 (file)
@@ -52,8 +52,18 @@ HttpMsgRequest::~HttpMsgRequest()
     delete body_params;
 }
 
+bool HttpMsgRequest::detection_required() const
+{
+    if (params->partial_depth_header != 0)
+        return true;
+    return version_id == HttpEnums::VERS_0_9;
+}
+
 void HttpMsgRequest::parse_start_line()
 {
+    if (session_data->partial_flush[source_id])
+        return;
+
     // Version field
     if ((start_line.length() < 10) || !is_sp_tab[start_line.start()[start_line.length()-9]] ||
         memcmp(start_line.start() + start_line.length() - 8, "HTTP/", 5))
@@ -219,6 +229,9 @@ void HttpMsgRequest::clear_body_params()
 
 void HttpMsgRequest::gen_events()
 {
+    if (session_data->partial_flush[source_id])
+        return;
+
     if (*transaction->get_infractions(source_id) & INF_BAD_REQ_LINE)
         return;
 
@@ -351,6 +364,9 @@ void HttpMsgRequest::update_flow()
 
 void HttpMsgRequest::publish(unsigned)
 {
+    if (session_data->partial_flush[source_id])
+        return;
+
     if (!session_data->ssl_search_abandoned && trans_num > 1 &&
         !flow->flags.data_decrypted && get_method_id() != METH_CONNECT)
     {
index 5591e987654aeeae819e1cf169e33a682edfd011..1144ecdac1127bce42cccfc771ab9def07e09b8a 100644 (file)
@@ -39,8 +39,7 @@ public:
         HttpCommon::SourceId source_id_, bool buf_owner, snort::Flow* flow_,
         const HttpParaList* params_);
     ~HttpMsgRequest() override;
-    bool detection_required() const override
-        { return version_id == HttpEnums::VERS_0_9; }
+    bool detection_required() const override;
     snort::PduSection get_inspection_section() const override
         { return snort::PS_HEADER; }
     void gen_events() override;
index 0a9df8095f8f0e81d6b02ce3cd2fa33924fa5952..2e3da6bce4b6bdd1bfd9a20708b996e29e2e17a7 100644 (file)
@@ -222,7 +222,7 @@ StreamSplitter::Status HttpStreamSplitter::call_cutter(Flow* flow, HttpFlowData*
             int64_t partial_depth = 0;
             auto params = my_inspector->params;
 
-            if (type == SEC_HEADER)
+            if (type == SEC_HEADER || type == SEC_REQUEST)
             {
                 if (params->partial_depth_header != 0 && !session_data->for_httpx)
                     partial_depth = params->partial_depth_header;
index ace0fcbbdc42c3eadb911ac6f0a498ba630b6791..d6741dad5e336e7603f1fec8e38e4b8bf0f44342 100644 (file)
@@ -95,7 +95,8 @@ HttpTransaction* HttpTransaction::attach_my_transaction(HttpFlowData* session_da
     // 4. returns the current transaction
 
     // Request section: replace the old request transaction with a new transaction.
-    if (session_data->section_type[source_id] == SEC_REQUEST)
+    if (session_data->section_type[source_id] == SEC_REQUEST &&
+        session_data->infractions[SRC_CLIENT] != nullptr)
     {
         // If the HTTP request and response messages are alternating (usual situation) the old
         // request transaction will have been moved to the server side when the last response
@@ -278,6 +279,14 @@ void HttpTransaction::delete_transaction(HttpTransaction* transaction, HttpFlowD
     }
 }
 
+void HttpTransaction::set_request(HttpMsgRequest* request_)
+{
+    // section is already cleared
+    // check when clear_section() needs to be called
+    delete request;
+    request = request_;
+}
+
 void HttpTransaction::set_header(HttpMsgHeader* header_, HttpCommon::SourceId source_id)
 {
     delete (header[source_id]);
index 70944b59069d256eb96340f44a864518103c0753..ee9e85505e865b2183f144cd55dca8af2b379532 100644 (file)
@@ -42,7 +42,7 @@ public:
     static void delete_transaction(HttpTransaction*, HttpFlowData*);
 
     HttpMsgRequest* get_request() const { return request; }
-    void set_request(HttpMsgRequest* request_) { request = request_; }
+    void set_request(HttpMsgRequest* request);
 
     HttpMsgStatus* get_status() const { return status; }
     void set_status(HttpMsgStatus* status_) { status = status_; }
index 96f36ec55c82b72a7da6cc49884f45dd17704236..1519e56c478c3a4a27c5c47797bad06cd429b203 100644 (file)
@@ -204,6 +204,7 @@ TEST(http_transaction_test, simple_pipeline)
         type_expected[SRC_CLIENT] = SEC_HEADER;
         section_type[SRC_CLIENT] = SEC_HEADER;
         CHECK(trans[k] == HttpTransaction::attach_my_transaction(flow_data, SRC_CLIENT, flow));
+        HttpUnitTestSetup::half_reset(flow_data, SRC_CLIENT);
         for (unsigned j=0; j < k; j++)
         {
             CHECK(trans[k] != trans[j]);
@@ -268,6 +269,7 @@ TEST(http_transaction_test, pipeline_underflow)
     type_expected[SRC_CLIENT] = SEC_HEADER;
     section_type[SRC_CLIENT] = SEC_HEADER;
     CHECK(trans == HttpTransaction::attach_my_transaction(flow_data, SRC_CLIENT, flow));
+    HttpUnitTestSetup::half_reset(flow_data, SRC_CLIENT);
     type_expected[SRC_CLIENT] = SEC_REQUEST;
 
     section_type[SRC_SERVER] = SEC_STATUS;
@@ -287,6 +289,7 @@ TEST(http_transaction_test, pipeline_underflow)
     type_expected[SRC_CLIENT] = SEC_HEADER;
     section_type[SRC_CLIENT] = SEC_HEADER;
     CHECK(trans2 == HttpTransaction::attach_my_transaction(flow_data, SRC_CLIENT, flow));
+    HttpUnitTestSetup::half_reset(flow_data, SRC_CLIENT);
     type_expected[SRC_CLIENT] = SEC_REQUEST;
 
     section_type[SRC_SERVER] = SEC_STATUS;
@@ -482,6 +485,7 @@ TEST(http_transaction_test, pipeline_continue_pipeline)
         type_expected[SRC_CLIENT] = SEC_HEADER;
         section_type[SRC_CLIENT] = SEC_HEADER;
         CHECK(trans[k] == HttpTransaction::attach_my_transaction(flow_data, SRC_CLIENT, flow));
+        HttpUnitTestSetup::half_reset(flow_data, SRC_CLIENT);
         for (unsigned j=0; j < k; j++)
         {
             CHECK(trans[k] != trans[j]);
@@ -521,6 +525,7 @@ TEST(http_transaction_test, pipeline_continue_pipeline)
         type_expected[SRC_CLIENT] = SEC_HEADER;
         section_type[SRC_CLIENT] = SEC_HEADER;
         CHECK(trans[k] == HttpTransaction::attach_my_transaction(flow_data, SRC_CLIENT, flow));
+        HttpUnitTestSetup::half_reset(flow_data, SRC_CLIENT);
         for (unsigned j=5; j < k; j++)
         {
             CHECK(trans[k] != trans[j]);
index 2828c76a162fe4b633bf3cd7aa0c700285158ce9..78ae8a3cf69aa99affd16f4a69e1d2c04a250e0c 100644 (file)
@@ -31,6 +31,8 @@ public:
         { assert(flow_data!=nullptr); return flow_data->section_type; }
     static HttpCommon::SectionType* get_type_expected(HttpFlowData* flow_data)
         { assert(flow_data!=nullptr); return flow_data->type_expected; }
+    static void half_reset(HttpFlowData* flow_data, HttpCommon::SourceId source_id)
+        { assert(flow_data!=nullptr); flow_data->half_reset(source_id); }
 };
 
 #endif