]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #2088 in SNORT/snort3 from ~KATHARVE/snort3:nhi_h2 to master
authorMike Stepanek (mstepane) <mstepane@cisco.com>
Mon, 23 Mar 2020 19:26:48 +0000 (19:26 +0000)
committerMike Stepanek (mstepane) <mstepane@cisco.com>
Mon, 23 Mar 2020 19:26:48 +0000 (19:26 +0000)
Squashed commit of the following:

commit eada91f3303497cbb76e33cc1dc6e54c5c34e5fd
Author: Katura Harvey <katharve@cisco.com>
Date:   Wed Mar 18 10:08:59 2020 -0400

    http_inspect: create http2 message body type

commit 242bff1e4f0c717a184f213a342ade9192b895de
Author: mdagon <mdagon@cisco.com>
Date:   Mon Mar 9 15:39:02 2020 -0400

    http2_inspect: refactor data cutter - preparation for multi packet processing

25 files changed:
src/service_inspectors/http2_inspect/CMakeLists.txt
src/service_inspectors/http2_inspect/http2_data_cutter.cc
src/service_inspectors/http2_inspect/http2_data_cutter.h
src/service_inspectors/http2_inspect/http2_flow_data.h
src/service_inspectors/http2_inspect/http2_headers_frame.cc
src/service_inspectors/http2_inspect/http2_hpack.cc
src/service_inspectors/http2_inspect/http2_hpack.h
src/service_inspectors/http2_inspect/http2_stream.cc
src/service_inspectors/http2_inspect/http2_stream.h
src/service_inspectors/http2_inspect/http2_stream_splitter_impl.cc
src/service_inspectors/http2_inspect/http2_utils.cc [new file with mode: 0644]
src/service_inspectors/http2_inspect/http2_utils.h [new file with mode: 0644]
src/service_inspectors/http_inspect/CMakeLists.txt
src/service_inspectors/http_inspect/dev_notes.txt
src/service_inspectors/http_inspect/http_cutter.cc
src/service_inspectors/http_inspect/http_cutter.h
src/service_inspectors/http_inspect/http_enum.h
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_h2.cc [new file with mode: 0644]
src/service_inspectors/http_inspect/http_msg_body_h2.h [new file with mode: 0644]
src/service_inspectors/http_inspect/http_msg_header.cc
src/service_inspectors/http_inspect/http_stream_splitter_finish.cc
src/service_inspectors/http_inspect/http_stream_splitter_reassemble.cc
src/service_inspectors/http_inspect/http_stream_splitter_scan.cc

index 4e81773037004cbea2bcf962c8486c6710a288ef..5fd32c3328919e68deadf5b830d1bf4f77b1e9e8 100644 (file)
@@ -43,6 +43,8 @@ set (FILE_LIST
     http2_stream_splitter_impl.cc
     http2_stream_splitter.h
     http2_tables.cc
+    http2_utils.cc
+    http2_utils.h
     ips_http2.cc
     ips_http2.h
 )
index 87f3b013cb6d949d27f9737b98032452f99c92f0..6f6852db87a89b7e568cc70d7bfbe1cf75954d79 100644 (file)
 #include "service_inspectors/http_inspect/http_stream_splitter.h"
 
 #include "http2_dummy_packet.h"
+#include "http2_utils.h"
 
 using namespace snort;
 using namespace HttpCommon;
 using namespace Http2Enums;
 
-static std::string create_chunk_hdr(uint32_t len)
-{
-    std::stringstream stream;
-    stream<<std::hex<< len;
-    return stream.str() + "\r\n";
-}
-
-Http2DataCutter::Http2DataCutter(Http2FlowData* _session_data, uint32_t len,
-    HttpCommon::SourceId src_id, bool is_padded) :
-    session_data(_session_data),  source_id(src_id), frame_length(len), data_len(len)
-{
-    data_state = (is_padded) ? PADDING_LENGTH : DATA;
-}
+Http2DataCutter::Http2DataCutter(Http2FlowData* _session_data, HttpCommon::SourceId src_id) :
+    session_data(_session_data),  source_id(src_id)
+{ }
 
 // Scan data frame, extract information needed for http scan.
 // http scan will need the data only, stripped of padding and header.
 bool Http2DataCutter::http2_scan(const uint8_t* data, uint32_t length,
-    uint32_t* flush_offset)
+    uint32_t* flush_offset, uint32_t frame_len, uint8_t flags)
 {
     *flush_offset = cur_data_offset = cur_data = cur_padding = 0;
 
     if (frame_bytes_seen == 0)
     {
+        frame_length = data_len = frame_len;
+        padding_len = data_bytes_read = padding_read = 0;
+        frame_flags = flags;
         frame_bytes_seen = cur_data_offset = FRAME_HEADER_LENGTH;
         length -= FRAME_HEADER_LENGTH;
         *flush_offset = FRAME_HEADER_LENGTH;
+        data_state = ((frame_flags & PADDED) !=0) ? PADDING_LENGTH : DATA;
     }
 
     uint32_t cur_pos = leftover_bytes;
@@ -117,7 +112,8 @@ bool Http2DataCutter::http2_scan(const uint8_t* data, uint32_t length,
     return true;
 }
 
-// Call http scan. Wrap data with chunk header and end of chunk.
+// Call http scan. After all data in first frame has been sent, set http2_end_stream flag and send
+// zero-length buffer to flush through detection
 StreamSplitter::Status Http2DataCutter::http_scan(const uint8_t* data, uint32_t* flush_offset)
 {
     StreamSplitter::Status scan_result = StreamSplitter::SEARCH;
@@ -126,59 +122,47 @@ StreamSplitter::Status Http2DataCutter::http_scan(const uint8_t* data, uint32_t*
     dummy_pkt.flow = session_data->flow;
     uint32_t unused = 0;
 
-    // first phase supports only flush of full packet
-    switch (http_state)
+    if (cur_data || leftover_bytes)
     {
-    case NONE_SENT:
-      {
-        if (cur_data)
+        scan_result = session_data->hi_ss[source_id]->scan(&dummy_pkt, data + cur_data_offset,
+            cur_data + leftover_bytes, unused, &http_flush_offset);
+
+        if (scan_result == StreamSplitter::FLUSH)
         {
-            std::string chunk_hdr = create_chunk_hdr(data_len);
-            scan_result = session_data->hi_ss[source_id]->scan(&dummy_pkt,
-                (const unsigned char*)chunk_hdr.c_str(),
-                chunk_hdr.length(), unused, &http_flush_offset);
-            bytes_sent_http += chunk_hdr.length();
-            http_state = HEADER_SENT;
-            if (scan_result != StreamSplitter::SEARCH)
-                return StreamSplitter::ABORT;
+            bytes_sent_http += http_flush_offset;
+            leftover_bytes = cur_data + leftover_bytes - http_flush_offset;
+            *flush_offset -= leftover_bytes;
+            session_data->mid_packet[source_id] = ( leftover_bytes > 0 ) ? true : false;
         }
-      }     // fallthrough
-    case HEADER_SENT:
-      {
-        if (cur_data || leftover_bytes)
+        else if (scan_result == StreamSplitter::SEARCH)
         {
-            scan_result = session_data->hi_ss[source_id]->scan(&dummy_pkt, data + cur_data_offset,
-                cur_data + leftover_bytes, unused, &http_flush_offset);
-
-            if (scan_result != StreamSplitter::SEARCH)
-            {
-                if (scan_result == StreamSplitter::FLUSH)
-                {
-                    bytes_sent_http += http_flush_offset;
-                    leftover_bytes = cur_data + leftover_bytes - http_flush_offset;
-                    *flush_offset -= leftover_bytes;
-                    session_data->mid_packet[source_id] = true;
-                    return scan_result;
-                }
-                else
-                    return StreamSplitter::ABORT;
-            }
-
             bytes_sent_http += (cur_data + leftover_bytes);
             leftover_bytes = 0;
         }
-        if (data_state == FULL_FRAME)
+        else if (scan_result == StreamSplitter::ABORT)
+            return StreamSplitter::ABORT;
+    }
+    if (data_state == FULL_FRAME)
+    {
+        if (leftover_bytes == 0)
         {
-            scan_result = session_data->hi_ss[source_id]->scan(&dummy_pkt, (const unsigned
-                char*)"\r\n0\r\n",
-                5, unused, &http_flush_offset);
-            bytes_sent_http +=5;
+            session_data->get_current_stream(source_id)->get_hi_flow_data()->
+                set_http2_end_stream(source_id);
+            scan_result = session_data->hi_ss[source_id]->scan(&dummy_pkt, nullptr, 0, unused,
+                &http_flush_offset);
             assert(scan_result == StreamSplitter::FLUSH);
+
+            // FIXIT-H for now only a single data frame is processed
+            Http2Stream* const stream = session_data->find_stream(
+                session_data->current_stream[source_id]);
+            stream->set_abort_data_processing(source_id);
+
+            // Done with this frame, cleanup
             session_data->mid_packet[source_id] = false;
             session_data->scan_octets_seen[source_id] = 0;
             session_data->scan_remaining_frame_octets[source_id] = 0;
+            frame_bytes_seen = 0;
         }
-      }
     }
 
     if (scan_result != StreamSplitter::FLUSH)
@@ -188,9 +172,14 @@ StreamSplitter::Status Http2DataCutter::http_scan(const uint8_t* data, uint32_t*
 }
 
 StreamSplitter::Status Http2DataCutter::scan(const uint8_t* data, uint32_t length,
-    uint32_t* flush_offset)
+    uint32_t* flush_offset, uint32_t frame_len, uint8_t frame_flags)
 {
-    if (!http2_scan(data, length, flush_offset))
+    // FIXIT-H temporary, until more than 1 data frame sent to http inspect is supported
+    Http2Stream* const stream = session_data->find_stream(session_data->current_stream[source_id]);
+    if (stream->get_abort_data_processing(source_id))
+        return StreamSplitter::ABORT;
+
+    if (!http2_scan(data, length, flush_offset, frame_len, frame_flags))
         return StreamSplitter::ABORT;
 
     return http_scan(data, flush_offset);
@@ -208,13 +197,15 @@ const StreamBuffer Http2DataCutter::reassemble(unsigned, const uint8_t* data,
     {
         switch (reassemble_state)
         {
-        case SKIP_FRAME_HDR:
+        case GET_FRAME_HDR:
           {
             if (reassemble_hdr_bytes_read == 0)
             {
                 session_data->frame_header[source_id] = new uint8_t[FRAME_HEADER_LENGTH];
                 session_data->frame_header_size[source_id] = FRAME_HEADER_LENGTH;
+                padding_len = 0;
             }
+
             const uint32_t missing = FRAME_HEADER_LENGTH - reassemble_hdr_bytes_read;
             const uint32_t cur_frame = ((len - cur_pos) < missing) ? (len - cur_pos) : missing;
             memcpy(session_data->frame_header[source_id] + reassemble_hdr_bytes_read, data +
@@ -222,30 +213,24 @@ const StreamBuffer Http2DataCutter::reassemble(unsigned, const uint8_t* data,
                 cur_frame);
             reassemble_hdr_bytes_read += cur_frame;
             cur_pos += cur_frame;
+
             if (reassemble_hdr_bytes_read == FRAME_HEADER_LENGTH)
             {
+                data_len = frame_length = get_frame_length(session_data->frame_header[source_id]);
+                frame_flags = get_frame_flags(session_data->frame_header[source_id]);
                 cur_data_offset = cur_pos;
-                reassemble_state = (padding_len) ? SKIP_PADDING_LEN : SEND_CHUNK_HDR;
+                reassemble_state = ((frame_flags & PADDED) !=0) ? GET_PADDING_LEN : SEND_DATA;
             }
 
             break;
           }
-        case SKIP_PADDING_LEN:
+        case GET_PADDING_LEN:
+            padding_len = *(data + cur_pos);
+            data_len -= (padding_len + 1);
             cur_pos++;
             cur_data_offset++;
-            reassemble_state = SEND_CHUNK_HDR;
-            break;
-        case SEND_CHUNK_HDR:
-          {
-            std::string chunk_hdr = create_chunk_hdr(data_len);
-            unsigned copied;
-            session_data->hi_ss[source_id]->reassemble(session_data->flow,
-                bytes_sent_http, 0, (const uint8_t*)chunk_hdr.c_str(), chunk_hdr.length(), 0,
-                copied);
-            assert(copied == (unsigned)chunk_hdr.length());
-            reassemble_bytes_sent += copied;
             reassemble_state = SEND_DATA;
-          }     // fallthrough
+            break;
         case SEND_DATA:
           {
             const uint32_t missing = data_len - reassemble_data_bytes_read;
@@ -263,7 +248,7 @@ const StreamBuffer Http2DataCutter::reassemble(unsigned, const uint8_t* data,
             reassemble_bytes_sent += copied;
 
             if (reassemble_data_bytes_read == data_len)
-                reassemble_state = (padding_len) ? SKIP_PADDING : SEND_CRLF;
+                reassemble_state = (padding_len) ? SKIP_PADDING : CLEANUP;
 
             break;
           }
@@ -275,7 +260,7 @@ const StreamBuffer Http2DataCutter::reassemble(unsigned, const uint8_t* data,
             cur_pos += cur_padding;
             reassemble_padding_read += cur_padding;
             if (reassemble_padding_read == padding_len)
-                reassemble_state = SEND_CRLF;
+                reassemble_state = CLEANUP;
             break;
           }
 
@@ -284,12 +269,11 @@ const StreamBuffer Http2DataCutter::reassemble(unsigned, const uint8_t* data,
         }
     }
 
-    if (reassemble_state == SEND_CRLF)
+    if (reassemble_state == CLEANUP)
     {
-        unsigned copied;
-        frame_buf = session_data->hi_ss[source_id]->reassemble(session_data->flow,
-            bytes_sent_http, 0,(const unsigned char*)"\r\n0\r\n", 5, PKT_PDU_TAIL, copied);
-        assert(copied == 5);
+        // Done with this packet
+        reassemble_state = GET_FRAME_HDR;
+        reassemble_hdr_bytes_read = reassemble_data_bytes_read = reassemble_padding_read = 0;
     }
 
     if (frame_buf.data != nullptr)
index 1e79cbfb6fadb30fbf236d1b08989d41191dcba2..fcdb34bd4db5b78dc8765b0440f0f7a03051f8ea 100644 (file)
 class Http2DataCutter
 {
 public:
-    Http2DataCutter(Http2FlowData* flow_data, uint32_t len, HttpCommon::SourceId
-        src_id, bool is_padded);
+    Http2DataCutter(Http2FlowData* flow_data, HttpCommon::SourceId src_id);
     snort::StreamSplitter::Status scan(const uint8_t* data, uint32_t length,
-        uint32_t* flush_offset);
+        uint32_t* flush_offset, uint32_t frame_len =0, uint8_t frame_flags =0);
     const snort::StreamBuffer reassemble(unsigned total, const uint8_t* data,
         unsigned len);
 
@@ -42,9 +41,10 @@ private:
     const HttpCommon::SourceId source_id;
 
     // total per frame
-    const uint32_t frame_length;
+    uint32_t frame_length;
     uint32_t data_len;
     uint32_t padding_len = 0;
+    uint8_t frame_flags;
     // accumulating - scan
     uint32_t frame_bytes_seen = 0;
     uint32_t bytes_sent_http = 0;
@@ -70,16 +70,12 @@ private:
     enum DataState { PADDING_LENGTH, DATA, PADDING, FULL_FRAME };
     enum DataState data_state;
 
-    // http scan
-    enum HttpScanState { NONE_SENT, HEADER_SENT };
-    enum HttpScanState http_state = NONE_SENT;
-
     // reassemble
-    enum ReassembleState { SKIP_FRAME_HDR, SKIP_PADDING_LEN, SEND_CHUNK_HDR, SEND_DATA,
-                           SKIP_PADDING, SEND_CRLF };
-    enum ReassembleState reassemble_state = SKIP_FRAME_HDR;
+    enum ReassembleState { GET_FRAME_HDR, GET_PADDING_LEN, SEND_DATA, SKIP_PADDING, CLEANUP };
+    enum ReassembleState reassemble_state = GET_FRAME_HDR;
 
-    bool http2_scan(const uint8_t* data, uint32_t length, uint32_t* flush_offset);
+    bool http2_scan(const uint8_t* data, uint32_t length, uint32_t* flush_offset,
+       uint32_t frame_len, uint8_t frame_flags);
     snort::StreamSplitter::Status http_scan(const uint8_t* data, uint32_t* flush_offset);
 };
 
index d0b6895cdc56b34f4b7df3481d8b3d7dfe3daf60..b5b2900d8f26b22db589b73ba821561c43826040 100644 (file)
@@ -74,8 +74,8 @@ public:
     friend class Http2Stream;
     friend class Http2StreamSplitter;
     friend snort::StreamSplitter::Status data_scan(Http2FlowData* session_data, const uint8_t* data,
-       uint32_t length, uint32_t* flush_offset, HttpCommon::SourceId source_id,
-       uint32_t frame_length, bool is_padded);
+        uint32_t length, uint32_t* flush_offset, HttpCommon::SourceId source_id,
+        uint32_t frame_length, uint8_t frame_flags);
     friend const snort::StreamBuffer implement_reassemble(Http2FlowData*, unsigned, unsigned,
         const uint8_t*, unsigned, uint32_t, HttpCommon::SourceId);
     friend snort::StreamSplitter::Status implement_scan(Http2FlowData*, const uint8_t*, uint32_t,
index 46254299d2fa8aff7f0d9f645a28ea488f86375b..c2e4743057e9f09f015df4447dace8ad1903d10a 100644 (file)
@@ -50,9 +50,6 @@ Http2HeadersFrame::Http2HeadersFrame(const uint8_t* header_buffer, const int32_t
     if (get_flags() & PRIORITY)
         hpack_headers_offset = 5;
 
-    // No message body after stream bit is set
-    bool no_message_body = (get_flags() & END_STREAM);
-
     // Set up the decoding context
     Http2HpackDecoder& hpack_decoder = session_data->hpack_decoder[source_id];
 
@@ -64,9 +61,8 @@ Http2HeadersFrame::Http2HeadersFrame(const uint8_t* header_buffer, const int32_t
 
     // Decode headers
     if (!hpack_decoder.decode_headers((data.start() + hpack_headers_offset), data.length() -
-        hpack_headers_offset, decoded_headers,
-        start_line_generator, session_data->events[source_id],
-        session_data->infractions[source_id], no_message_body))
+        hpack_headers_offset, decoded_headers, start_line_generator,
+        session_data->events[source_id], session_data->infractions[source_id]))
     {
         session_data->frame_type[source_id] = FT__ABORT;
         error_during_decode = true;
index ac47b781828ad7bea910e0a0e5150c6482e0f333..9691261e53ab8e11e9b34aeeaaa777e7e68a9021 100644 (file)
@@ -354,7 +354,7 @@ bool Http2HpackDecoder::decode_header_line(const uint8_t* encoded_header_buffer,
 bool Http2HpackDecoder::decode_headers(const uint8_t* encoded_headers,
     const uint32_t encoded_headers_length, uint8_t* decoded_headers,
     Http2StartLine *start_line_generator, Http2EventGen* stream_events,
-    Http2Infractions* stream_infractions, bool no_message_body)
+    Http2Infractions* stream_infractions)
 {
     uint32_t total_bytes_consumed = 0;
     uint32_t line_bytes_consumed = 0;
@@ -390,15 +390,8 @@ bool Http2HpackDecoder::decode_headers(const uint8_t* encoded_headers,
        frame boundaries to http_inspect and http_inspect can expect chunked data during inspection */
     if (success)
     {
-        if (no_message_body)
-            success = write_decoded_headers((const uint8_t*)"\r\n", 2, decoded_headers +
-                decoded_headers_size, MAX_OCTETS - decoded_headers_size, line_bytes_written);
-        else
-        {
-            const uint8_t chunk_hdr[] = "transfer-encoding: chunked\r\n\r\n";
-            success = write_decoded_headers(chunk_hdr, sizeof(chunk_hdr) - 1, decoded_headers +
-                decoded_headers_size, MAX_OCTETS - decoded_headers_size, line_bytes_written);
-        }
+        success = write_decoded_headers((const uint8_t*)"\r\n", 2, decoded_headers +
+            decoded_headers_size, MAX_OCTETS - decoded_headers_size, line_bytes_written);
         decoded_headers_size += line_bytes_written;
     }
     else
index 5fa92e3d956bedc39e785c99317fe84fe77fdf1a..f2d2e9407bc586a92765207047c303a06d232a83 100644 (file)
@@ -39,7 +39,7 @@ public:
         decode_table(flow_data, src_id) { }
     bool decode_headers(const uint8_t* encoded_headers, const uint32_t encoded_headers_length,
         uint8_t* decoded_headers, Http2StartLine* start_line,
-        Http2EventGen* stream_events, Http2Infractions* stream_infractions, bool no_message_body);
+        Http2EventGen* stream_events, Http2Infractions* stream_infractions);
     bool write_decoded_headers(const uint8_t* in_buffer, const uint32_t in_length,
         uint8_t* decoded_header_buffer, uint32_t decoded_header_length, uint32_t& bytes_written);
     bool decode_header_line(const uint8_t* encoded_header_buffer,
index bd212718f5a6a1708a38567a57bcf1223a7ef5cd..ea96b2734fe56a491c1559e3432c7f212d2afd95 100644 (file)
@@ -74,9 +74,9 @@ void Http2Stream::print_frame(FILE* output)
 }
 #endif
 
-Http2DataCutter* Http2Stream::get_data_cutter(HttpCommon::SourceId source_id, uint32_t len, bool is_padded)
+Http2DataCutter* Http2Stream::get_data_cutter(HttpCommon::SourceId source_id)
 {
     if (!data_cutter[source_id])
-        data_cutter[source_id] = new Http2DataCutter(session_data, len, source_id, is_padded);
+        data_cutter[source_id] = new Http2DataCutter(session_data, source_id);
     return data_cutter[source_id];
 }
index 4476c1055375b1e5e59de4b5bf6bb60fe6ee463d..d1215480ea877854afd778be306a64296991119e 100644 (file)
@@ -48,10 +48,8 @@ public:
     uint32_t get_xtradata_mask() { return (current_frame != nullptr) ?
         current_frame->get_xtradata_mask() : 0; }
     Http2Frame *get_current_frame() { return current_frame; }
-
-    Http2DataCutter* get_data_cutter(
-        HttpCommon::SourceId source_id, uint32_t len=0, bool is_padded=false);
-
+    
+    Http2DataCutter* get_data_cutter(HttpCommon::SourceId source_id);
     void set_data_cutter(Http2DataCutter* cutter, HttpCommon::SourceId source_id)
     { data_cutter[source_id] = cutter; }
 
index 16e35dfde1388e85e4228f391eb8f8794be8a815..213f81689cca26d21e250e52dea2a8e04af164c7 100644 (file)
 
 #include "http2_data_cutter.h"
 #include "http2_flow_data.h"
+#include "http2_utils.h"
 
 using namespace snort;
 using namespace HttpCommon;
 using namespace Http2Enums;
 
-static uint32_t get_frame_length(const uint8_t* frame_buffer)
-{
-    return (frame_buffer[0] << 16) + (frame_buffer[1] << 8) + frame_buffer[2];
-}
-
-static uint8_t get_frame_type(const uint8_t* frame_buffer)
-{
-    const uint8_t frame_type_index = 3;
-    if (frame_buffer)
-        return frame_buffer[frame_type_index];
-    // If there was no frame header, this must be a piece of a long data frame
-    else
-        return FT_DATA;
-}
-
-static uint8_t get_frame_flags(const uint8_t* frame_buffer)
-{
-    const uint8_t frame_flags_index = 4;
-    if (frame_buffer)
-        return frame_buffer[frame_flags_index];
-    else
-        return NO_HEADER;
-}
-
-static uint8_t get_stream_id(const uint8_t* frame_buffer)
-{
-    const uint8_t stream_id_index = 5;
-    assert(frame_buffer != nullptr);
-    return ((frame_buffer[stream_id_index] & 0x7f) << 24) +
-           (frame_buffer[stream_id_index + 1] << 16) +
-           (frame_buffer[stream_id_index + 2] << 8) +
-           frame_buffer[stream_id_index + 3];
-}
 
 StreamSplitter::Status data_scan(Http2FlowData* session_data, const uint8_t* data,
     uint32_t length, uint32_t* flush_offset, HttpCommon::SourceId source_id,
-    uint32_t frame_length, bool is_padded)
+    uint32_t frame_length, uint8_t frame_flags)
 {
     Http2Stream* const stream = session_data->find_stream(session_data->current_stream[source_id]);
     HttpFlowData* http_flow = nullptr;
@@ -87,7 +55,7 @@ StreamSplitter::Status data_scan(Http2FlowData* session_data, const uint8_t* dat
     }
 
     if (!stream || !http_flow || (frame_length > 0 and
-        (http_flow->get_type_expected(source_id) != HttpEnums::SEC_BODY_CHUNK)))
+        (http_flow->get_type_expected(source_id) != HttpEnums::SEC_BODY_H2)))
     {
         *session_data->infractions[source_id] += INF_FRAME_SEQUENCE;
         session_data->events[source_id]->create_event(EVENT_FRAME_SEQUENCE);
@@ -97,8 +65,8 @@ StreamSplitter::Status data_scan(Http2FlowData* session_data, const uint8_t* dat
     if (frame_length == 0 or frame_length > MAX_OCTETS)
         return StreamSplitter::ABORT;
 
-    Http2DataCutter* data_cutter = stream->get_data_cutter(source_id, frame_length, is_padded);
-    return data_cutter->scan(data, length, flush_offset);
+    Http2DataCutter* data_cutter = stream->get_data_cutter(source_id);
+    return data_cutter->scan(data, length, flush_offset, frame_length, frame_flags);
 }
 
 StreamSplitter::Status non_data_scan(Http2FlowData* session_data,
@@ -253,7 +221,7 @@ StreamSplitter::Status implement_scan(Http2FlowData* session_data, const uint8_t
 
             if (type == FT_DATA)
                 return data_scan(session_data, data, length, flush_offset, source_id,
-                    frame_length, ((frame_flags & PADDED) !=0));
+                    frame_length, frame_flags);
             else
                 status = non_data_scan(session_data, length, flush_offset, source_id,
                     frame_length, type, frame_flags, data_offset);
@@ -271,7 +239,6 @@ const StreamBuffer implement_reassemble(Http2FlowData* session_data, unsigned to
     HttpCommon::SourceId source_id)
 {
     assert(offset+len <= total);
-    assert(total >= FRAME_HEADER_LENGTH);
     assert(total <= MAX_OCTETS);
 
     StreamBuffer frame_buf { nullptr, 0 };
@@ -284,13 +251,13 @@ const StreamBuffer implement_reassemble(Http2FlowData* session_data, unsigned to
         StreamBuffer http_frame_buf = data_cutter->reassemble(total, data, len);
         if (http_frame_buf.data)
         {
-            stream->set_abort_data_processing(source_id);
             session_data->frame_data[source_id] = const_cast<uint8_t*>(http_frame_buf.data);
             session_data->frame_data_size[source_id] = http_frame_buf.length;
         }
     }
     else
     {
+        assert(total >= FRAME_HEADER_LENGTH);
         uint32_t data_offset = 0;
 
         if (offset == 0)
diff --git a/src/service_inspectors/http2_inspect/http2_utils.cc b/src/service_inspectors/http2_inspect/http2_utils.cc
new file mode 100644 (file)
index 0000000..620803c
--- /dev/null
@@ -0,0 +1,60 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 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.
+//--------------------------------------------------------------------------
+// http2_utils.cc author Maya Dagon <mdagon@cisco.com>
+
+#include "http2_utils.h"
+
+#include <cassert>
+
+#include "http2_enum.h"
+
+using namespace Http2Enums;
+
+uint32_t get_frame_length(const uint8_t* frame_header_buffer)
+{
+    return (frame_header_buffer[0] << 16) + (frame_header_buffer[1] << 8) + frame_header_buffer[2];
+}
+
+uint8_t get_frame_type(const uint8_t* frame_header_buffer)
+{
+    const uint8_t frame_type_index = 3;
+    if (frame_header_buffer)
+        return frame_header_buffer[frame_type_index];
+    // If there was no frame header, this must be a piece of a long data frame
+    else
+        return FT_DATA;
+}
+
+uint8_t get_frame_flags(const uint8_t* frame_header_buffer)
+{
+    const uint8_t frame_flags_index = 4;
+    if (frame_header_buffer)
+        return frame_header_buffer[frame_flags_index];
+    else
+        return NO_HEADER;
+}
+
+uint8_t get_stream_id(const uint8_t* frame_header_buffer)
+{
+    const uint8_t stream_id_index = 5;
+    assert(frame_header_buffer != nullptr);
+    return ((frame_header_buffer[stream_id_index] & 0x7f) << 24) +
+           (frame_header_buffer[stream_id_index + 1] << 16) +
+           (frame_header_buffer[stream_id_index + 2] << 8) +
+           frame_header_buffer[stream_id_index + 3];
+}
diff --git a/src/service_inspectors/http2_inspect/http2_utils.h b/src/service_inspectors/http2_inspect/http2_utils.h
new file mode 100644 (file)
index 0000000..6a88f1e
--- /dev/null
@@ -0,0 +1,37 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 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.
+//--------------------------------------------------------------------------
+// http2_utils.h author Maya Dagon <mdagon@cisco.com>
+
+#ifndef HTTP2_UTILS_H
+#define HTTP2_UTILS_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main/snort_types.h"
+
+// Frame header parsing utils.
+// Assumption is that if input isn't null, it contains full frame header
+
+uint32_t get_frame_length(const uint8_t* frame_header_buffer);
+uint8_t get_frame_type(const uint8_t* frame_header_buffer);
+uint8_t get_frame_flags(const uint8_t* frame_header_buffer);
+uint8_t get_stream_id(const uint8_t* frame_header_buffer);
+
+#endif
index 7e0a49055c93eff2c9c1f3921ec8aac371455674..57c1d52db3862ecb1c75476151a7130f271d289f 100644 (file)
@@ -25,6 +25,8 @@ set (FILE_LIST
     http_msg_body_chunk.h
     http_msg_body_cl.cc
     http_msg_body_cl.h
+    http_msg_body_h2.cc
+    http_msg_body_h2.h
     http_msg_body_old.cc
     http_msg_body_old.h
     http_msg_trailer.cc
index 44031bbe0b17f900052e20db3e40f64316aa969d..f0f1ccbf03a4e008fbe4fadf11a49c1b560e045d 100644 (file)
@@ -50,7 +50,7 @@ processed together. There are seven types of message section:
 7. Trailers (all header lines following a chunked body as a group)
 
 Message sections are represented by message section objects that contain and process them. There
-are eleven message section classes that inherit as follows. An asterisk denotes a virtual class.
+are twelve message section classes that inherit as follows. An asterisk denotes a virtual class.
 
 1. HttpMsgSection* - top level with all common elements
 2. HttpMsgStart* : HttpMsgSection - common elements of request and status
@@ -63,6 +63,7 @@ are eleven message section classes that inherit as follows. An asterisk denotes
 9. HttpMsgBodyCl : HttpMsgBody
 10. HttpMsgBodyChunk : HttpMsgBody
 11. HttpMsgBodyOld : HttpMsgBody
+12. HttpMsgBodyH2 : HttpMsgBody
 
 An HttpTransaction is a container that keeps all the sections of a message together and associates
 the request message with the response message. Transactions may be organized into pipelines when an
index da17cc38bdcd4c0d97d030e5d28b259232712c9d..30c9bc2734cdb6523b61f0285be423a768258dde 100644 (file)
@@ -27,7 +27,7 @@
 using namespace HttpEnums;
 
 ScanResult HttpStartCutter::cut(const uint8_t* buffer, uint32_t length,
-    HttpInfractions* infractions, HttpEventGen* events, uint32_t, bool)
+    HttpInfractions* infractions, HttpEventGen* events, uint32_t, bool, bool)
 {
     for (uint32_t k = 0; k < length; k++)
     {
@@ -155,7 +155,7 @@ HttpStartCutter::ValidationResult HttpStatusCutter::validate(uint8_t octet,
 }
 
 ScanResult HttpHeaderCutter::cut(const uint8_t* buffer, uint32_t length,
-    HttpInfractions* infractions, HttpEventGen* events, uint32_t, bool)
+    HttpInfractions* infractions, HttpEventGen* events, uint32_t, bool, bool)
 {
     // Header separators: leading \r\n, leading \n, nonleading \r\n\r\n, nonleading \n\r\n,
     // nonleading \r\n\n, and nonleading \n\n. The separator itself becomes num_excess which is
@@ -282,7 +282,7 @@ HttpBodyCutter::~HttpBodyCutter()
 }
 
 ScanResult HttpBodyClCutter::cut(const uint8_t* buffer, uint32_t length, HttpInfractions*,
-    HttpEventGen*, uint32_t flow_target, bool stretch)
+    HttpEventGen*, uint32_t flow_target, bool stretch, bool)
 {
     assert(remaining > octets_seen);
 
@@ -358,7 +358,7 @@ ScanResult HttpBodyClCutter::cut(const uint8_t* buffer, uint32_t length, HttpInf
 }
 
 ScanResult HttpBodyOldCutter::cut(const uint8_t* buffer, uint32_t length, HttpInfractions*,
-    HttpEventGen*, uint32_t flow_target, bool stretch)
+    HttpEventGen*, uint32_t flow_target, bool stretch, bool)
 {
     if (flow_target == 0)
     {
@@ -395,7 +395,7 @@ ScanResult HttpBodyOldCutter::cut(const uint8_t* buffer, uint32_t length, HttpIn
 }
 
 ScanResult HttpBodyChunkCutter::cut(const uint8_t* buffer, uint32_t length,
-    HttpInfractions* infractions, HttpEventGen* events, uint32_t flow_target, bool stretch)
+    HttpInfractions* infractions, HttpEventGen* events, uint32_t flow_target, bool stretch, bool)
 {
     // Are we skipping through the rest of this chunked body to the trailers and the next message?
     const bool discard_mode = (flow_target == 0);
@@ -687,6 +687,42 @@ ScanResult HttpBodyChunkCutter::cut(const uint8_t* buffer, uint32_t length,
     return detain_this_packet ? SCAN_NOT_FOUND_DETAIN : SCAN_NOT_FOUND;
 }
 
+ScanResult HttpBodyH2Cutter::cut(const uint8_t* buffer, uint32_t length, HttpInfractions*,
+    HttpEventGen*, uint32_t flow_target, bool stretch, bool h2_end_stream)
+{
+    //FIXIT-M detained inspection not yet supported for http2
+    UNUSED(buffer);
+
+    // FIXIT-M stretch not yet supported for http2 message bodies
+    UNUSED(stretch);
+
+    if (flow_target == 0)
+    {
+        num_flush = length;
+        return SCAN_DISCARD_PIECE;
+    }
+    if (!h2_end_stream)
+    {
+        if (octets_seen + length < flow_target)
+        {
+            // Not enough data yet to create a message section
+            octets_seen += length;
+            return SCAN_NOT_FOUND;
+        }
+        else
+        {
+            num_flush = flow_target - octets_seen;
+            return SCAN_FOUND_PIECE;
+        }
+    }
+    else
+    {
+        // For now if end_stream is set for scan, a zero-length buffer is always sent to flush
+        num_flush = 0;
+        return SCAN_FOUND;
+    }
+}
+
 // This method searches the input stream looking for the beginning of a script or other dangerous
 // content that requires detained inspection. Exactly what we are looking for is encapsulated in
 // dangerous().
index a83b7f81e466f9a94a0d4b7885fd76ecbee65755..1ea351b241ca1d8248b99b8bdce29eb00c9452d6 100644 (file)
@@ -35,8 +35,8 @@ class HttpCutter
 public:
     virtual ~HttpCutter() = default;
     virtual HttpEnums::ScanResult cut(const uint8_t* buffer, uint32_t length,
-        HttpInfractions* infractions, HttpEventGen* events, uint32_t flow_target, bool stretch)
-        = 0;
+        HttpInfractions* infractions, HttpEventGen* events, uint32_t flow_target, bool stretch,
+        bool h2_end_stream) = 0;
     uint32_t get_num_flush() const { return num_flush; }
     uint32_t get_octets_seen() const { return octets_seen; }
     uint32_t get_num_excess() const { return num_crlf; }
@@ -56,7 +56,7 @@ class HttpStartCutter : public HttpCutter
 {
 public:
     HttpEnums::ScanResult cut(const uint8_t* buffer, uint32_t length,
-        HttpInfractions* infractions, HttpEventGen* events, uint32_t, bool) override;
+        HttpInfractions* infractions, HttpEventGen* events, uint32_t, bool, bool) override;
 
 protected:
     enum ValidationResult { V_GOOD, V_BAD, V_TBD };
@@ -85,7 +85,7 @@ class HttpHeaderCutter : public HttpCutter
 {
 public:
     HttpEnums::ScanResult cut(const uint8_t* buffer, uint32_t length,
-        HttpInfractions* infractions, HttpEventGen* events, uint32_t, bool) override;
+        HttpInfractions* infractions, HttpEventGen* events, uint32_t, bool, bool) override;
     uint32_t get_num_head_lines() const override { return num_head_lines; }
 
 private:
@@ -124,7 +124,7 @@ public:
         HttpBodyCutter(detained_inspection, compression), remaining(expected_length)
         { assert(remaining > 0); }
     HttpEnums::ScanResult cut(const uint8_t*, uint32_t length, HttpInfractions*, HttpEventGen*,
-        uint32_t flow_target, bool stretch) override;
+        uint32_t flow_target, bool stretch, bool) override;
 
 private:
     int64_t remaining;
@@ -136,7 +136,7 @@ public:
     explicit HttpBodyOldCutter(bool detained_inspection, HttpEnums::CompressId compression) :
         HttpBodyCutter(detained_inspection, compression) {}
     HttpEnums::ScanResult cut(const uint8_t*, uint32_t, HttpInfractions*, HttpEventGen*,
-        uint32_t flow_target, bool stretch) override;
+        uint32_t flow_target, bool stretch, bool) override;
 };
 
 class HttpBodyChunkCutter : public HttpBodyCutter
@@ -145,8 +145,8 @@ public:
     explicit HttpBodyChunkCutter(bool detained_inspection, HttpEnums::CompressId compression) :
         HttpBodyCutter(detained_inspection, compression) {}
     HttpEnums::ScanResult cut(const uint8_t* buffer, uint32_t length,
-        HttpInfractions* infractions, HttpEventGen* events, uint32_t flow_target, bool stretch)
-        override;
+        HttpInfractions* infractions, HttpEventGen* events, uint32_t flow_target, bool stretch,
+        bool) override;
     bool get_is_broken_chunk() const override { return curr_state == HttpEnums::CHUNK_BAD; }
     uint32_t get_num_good_chunks() const override { return num_good_chunks; }
     void soft_reset() override { num_good_chunks = 0; HttpBodyCutter::soft_reset(); }
@@ -161,5 +161,15 @@ private:
     uint32_t num_good_chunks = 0;  // that end in the current section
 };
 
+class HttpBodyH2Cutter : public HttpBodyCutter
+{
+public:
+    explicit HttpBodyH2Cutter(bool detained_inspection, HttpEnums::CompressId compression) :
+        HttpBodyCutter(detained_inspection, compression) {}
+    HttpEnums::ScanResult cut(const uint8_t*, uint32_t, HttpInfractions*, HttpEventGen*,
+        uint32_t flow_target, bool stretch, bool h2_end_stream) override;
+};
+
+
 #endif
 
index 495943beeee5ab1e14260c276f92b56a24069037..cbfce493f2d7781d6813fdb8855093f336d95bfe 100644 (file)
@@ -39,7 +39,7 @@ static const uint64_t FORM_REQUEST = 0x1;
 // Type of message section
 enum SectionType { SEC_DISCARD = -19, SEC_ABORT = -18, SEC__NOT_COMPUTE=-14, SEC__NOT_PRESENT=-11,
     SEC_REQUEST = 2, SEC_STATUS, SEC_HEADER, SEC_BODY_CL, SEC_BODY_CHUNK, SEC_TRAILER,
-    SEC_BODY_OLD };
+    SEC_BODY_OLD, SEC_BODY_H2 };
 
 enum DetectionStatus { DET_REACTIVATING = 1, DET_ON, DET_DEACTIVATING, DET_OFF };
 
index 249bb9343f264eb00c0d083e0c396d58c181b880..a4527e472e323f714e75e4da7a96f4aa58ad1615 100644 (file)
@@ -59,6 +59,7 @@ public:
     friend class HttpMsgBody;
     friend class HttpMsgBodyChunk;
     friend class HttpMsgBodyCl;
+    friend class HttpMsgBodyH2;
     friend class HttpMsgBodyOld;
     friend class HttpQueryParser;
     friend class HttpStreamSplitter;
@@ -70,8 +71,13 @@ public:
     HttpEnums::SectionType get_type_expected(HttpCommon::SourceId source_id)
     { return type_expected[source_id]; }
 
+    void set_http2_end_stream(HttpCommon::SourceId source_id)
+    { http2_end_stream[source_id] = true; }
+
 private:
+    // HTTP/2 handling
     bool for_http2 = false;
+    bool http2_end_stream[2] = { false, false };
 
     // Convenience routines
     void half_reset(HttpCommon::SourceId source_id);
index e2f43fd85aea3f3db4426ad427c5a13b4442b10a..8d30233feafb5f2418fbf621b6d8d3fd75bac136 100644 (file)
@@ -40,6 +40,7 @@
 #include "http_msg_body.h"
 #include "http_msg_body_chunk.h"
 #include "http_msg_body_cl.h"
+#include "http_msg_body_h2.h"
 #include "http_msg_body_old.h"
 #include "http_msg_header.h"
 #include "http_msg_request.h"
@@ -453,6 +454,10 @@ bool HttpInspect::process(const uint8_t* data, const uint16_t dsize, Flow* const
         current_section = new HttpMsgBodyChunk(
             data, dsize, session_data, source_id, buf_owner, flow, params);
         break;
+    case SEC_BODY_H2:
+        current_section = new HttpMsgBodyH2(
+            data, dsize, session_data, source_id, buf_owner, flow, params);
+        break;
     case SEC_TRAILER:
         current_section = new HttpMsgTrailer(
             data, dsize, session_data, source_id, buf_owner, flow, params);
diff --git a/src/service_inspectors/http_inspect/http_msg_body_h2.cc b/src/service_inspectors/http_inspect/http_msg_body_h2.cc
new file mode 100644 (file)
index 0000000..ea0a875
--- /dev/null
@@ -0,0 +1,51 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 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_msg_body_h2.cc author Katura Harvey <katharve@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "http_msg_body_h2.h"
+
+void HttpMsgBodyH2::update_flow()
+{
+    session_data->body_octets[source_id] = body_octets;
+    if (session_data->http2_end_stream[source_id])
+    {
+        // FIXIT-H check content length header against bytes received
+
+        session_data->trailer_prep(source_id);
+        session_data->http2_end_stream[source_id] = false;
+    }
+    else
+    {
+        //FIXIT-H check have not exceeded content length
+        update_depth();
+    }
+}
+
+#ifdef REG_TEST
+void HttpMsgBodyH2::print_section(FILE* output)
+{
+    HttpMsgSection::print_section_title(output, "HTTP/2 body");
+    fprintf(output, "octets seen %" PRIi64 "\n", body_octets);
+    print_body_section(output);
+}
+#endif
+
diff --git a/src/service_inspectors/http_inspect/http_msg_body_h2.h b/src/service_inspectors/http_inspect/http_msg_body_h2.h
new file mode 100644 (file)
index 0000000..ecc9b88
--- /dev/null
@@ -0,0 +1,45 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 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_msg_body_h2.h author Katura Harvey <katharve@cisco.com>
+
+#ifndef HTTP_MSG_BODY_H2_H
+#define HTTP_MSG_BODY_H2_H
+
+#include "http_common.h"
+#include "http_msg_body.h"
+
+//-------------------------------------------------------------------------
+// HttpMsgBodyH2 class
+//-------------------------------------------------------------------------
+
+class HttpMsgBodyH2 : public HttpMsgBody
+{
+public:
+    HttpMsgBodyH2(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_)
+        : HttpMsgBody(buffer, buf_size, session_data_, source_id_, buf_owner, flow_, params_) {}
+    void update_flow() override;
+
+#ifdef REG_TEST
+    void print_section(FILE* output) override;
+#endif
+};
+
+#endif
+
index 7cab6bef3d7011372e563e8a3b10554f49d5f8d5..855c745072d6bdc07cf0322aab17730e7c42273d 100644 (file)
@@ -194,6 +194,14 @@ void HttpMsgHeader::update_flow()
         return;
     }
 
+    if (session_data->for_http2)
+    {
+        // FIXIT-H check for transfer-encoding and content-length headers
+        session_data->type_expected[source_id] = SEC_BODY_H2;
+        prepare_body();
+        return;
+    }
+
     const Field& te_header = get_header_value_norm(HEAD_TRANSFER_ENCODING);
     if ((te_header.length() > 0) && (version_id == VERS_1_0))
     {
index 6f4bc4aacbc3f93b28e5a8b7cf255ec39a70eb33..702775cdccbd0caa68aa83ef70e85ffd036b1e48 100644 (file)
@@ -102,17 +102,17 @@ bool HttpStreamSplitter::finish(Flow* flow)
         return true;
     }
 
-    // FIXIT-M No longer necessary to send an empty body section because the header section is
+    // FIXIT-H No longer necessary to send an empty body section because the header section is
     // always forwarded to detection.
     // If the message has been truncated immediately following the start line or immediately
     // following the headers (a body was expected) then we need to process an empty section to
     // provide an inspection section. Otherwise the start line and headers won't go through
     // detection.
     if (((session_data->type_expected[source_id] == SEC_HEADER)     ||
-         (session_data->type_expected[source_id] == SEC_BODY_CL)    ||
-         (session_data->type_expected[source_id] == SEC_BODY_CHUNK) ||
-         (session_data->type_expected[source_id] == SEC_BODY_OLD))     &&
-        (session_data->cutter[source_id] == nullptr)                   &&
+        (session_data->type_expected[source_id] == SEC_BODY_CL)     ||
+        (session_data->type_expected[source_id] == SEC_BODY_CHUNK)  ||
+        (session_data->type_expected[source_id] == SEC_BODY_OLD))   &&
+        (session_data->cutter[source_id] == nullptr)                &&
         (session_data->section_type[source_id] == SEC__NOT_COMPUTE))
     {
         // Set up to process empty message section
index 7b21953aad59dd4db473ab3470b3464ae5c324eb..fde9e491b32b7d7fbd19a8538cde926d86b2d72a 100644 (file)
@@ -350,6 +350,7 @@ const StreamBuffer HttpStreamSplitter::reassemble(Flow* flow, unsigned total,
                 {
                     session_data->half_reset(source_id);
                 }
+                // FIXIT-M update this to include H2 message once H2I supports trailers and finish()
                 else if (session_data->type_expected[source_id] == SEC_BODY_CHUNK)
                 {
                     session_data->trailer_prep(source_id);
@@ -363,7 +364,8 @@ const StreamBuffer HttpStreamSplitter::reassemble(Flow* flow, unsigned total,
 
     const bool is_body = (session_data->section_type[source_id] == SEC_BODY_CHUNK) ||
                          (session_data->section_type[source_id] == SEC_BODY_CL) ||
-                         (session_data->section_type[source_id] == SEC_BODY_OLD);
+                         (session_data->section_type[source_id] == SEC_BODY_OLD) ||
+                         (session_data->section_type[source_id] == SEC_BODY_H2);
     uint8_t*& buffer = session_data->section_buffer[source_id];
     if (buffer == nullptr)
     {
index 51fa9216555b638991cdb21d7b1d6266ddee1cdc..587fd7eafd4bfcce8e93af380cf140a7d7b07153 100644 (file)
@@ -84,6 +84,10 @@ HttpCutter* HttpStreamSplitter::get_cutter(SectionType type,
         return (HttpCutter*)new HttpBodyOldCutter(
             session_data->detained_inspection[source_id],
             session_data->compression[source_id]);
+    case SEC_BODY_H2:
+        return (HttpCutter*)new HttpBodyH2Cutter(
+            session_data->detained_inspection[source_id],
+            session_data->compression[source_id]);
     default:
         assert(false);
         return nullptr;
@@ -222,7 +226,7 @@ StreamSplitter::Status HttpStreamSplitter::scan(Packet* pkt, const uint8_t* data
     const ScanResult cut_result = cutter->cut(data, (length <= max_length) ? length :
         max_length, session_data->get_infractions(source_id), session_data->events[source_id],
         session_data->section_size_target[source_id],
-        session_data->stretch_section_to_packet[source_id]);
+        session_data->stretch_section_to_packet[source_id], session_data->http2_end_stream[source_id]);
     switch (cut_result)
     {
     case SCAN_NOT_FOUND: