]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #1919 in SNORT/snort3 from ~MDAGON/snort3:integration to master
authorMike Stepanek (mstepane) <mstepane@cisco.com>
Mon, 13 Jan 2020 14:27:15 +0000 (14:27 +0000)
committerMike Stepanek (mstepane) <mstepane@cisco.com>
Mon, 13 Jan 2020 14:27:15 +0000 (14:27 +0000)
Squashed commit of the following:

commit 5135fa3bf40d3e6ae2e8e12ff92f4ed79d264fb6
Author: Mike Stepanek (mstepane) <mstepane@cisco.com>
Date:   Mon Dec 9 17:08:40 2019 +0000

    http2_inspect: integration with NHI

26 files changed:
src/service_inspectors/http2_inspect/http2_enum.h
src/service_inspectors/http2_inspect/http2_flow_data.cc
src/service_inspectors/http2_inspect/http2_flow_data.h
src/service_inspectors/http2_inspect/http2_frame.cc
src/service_inspectors/http2_inspect/http2_frame.h
src/service_inspectors/http2_inspect/http2_headers_frame.cc
src/service_inspectors/http2_inspect/http2_headers_frame.h
src/service_inspectors/http2_inspect/http2_hpack.cc
src/service_inspectors/http2_inspect/http2_hpack.h
src/service_inspectors/http2_inspect/http2_inspect.cc
src/service_inspectors/http2_inspect/http2_settings_frame.cc
src/service_inspectors/http2_inspect/http2_settings_frame.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.cc
src/service_inspectors/http2_inspect/http2_stream_splitter_impl.cc
src/service_inspectors/http_inspect/http_context_data.cc
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_inspect.h
src/service_inspectors/http_inspect/http_msg_section.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
src/service_inspectors/http_inspect/ips_http.cc

index 37c822c7f3ae43fb9d673b33f331304b8ce4da63..c305228bff3f2146edace7c74bafae993fed5f03 100644 (file)
@@ -27,6 +27,7 @@ namespace Http2Enums
 static const int MAX_OCTETS = 63780;
 static const int DATA_SECTION_SIZE = 16384;
 static const int FRAME_HEADER_LENGTH = 9;
+static const uint32_t NO_STREAM_ID = 0xFFFFFFFF;
 
 static const uint32_t HTTP2_GID = 121;
 
index 57540a803ae0a5029324682c925bd5c5a6290eaf..9e9cbee3715cfda55ebf97059f3261b1b8b126ff 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "http2_flow_data.h"
 
+#include "service_inspectors/http_inspect/http_inspect.h"
 #include "service_inspectors/http_inspect/http_test_manager.h"
 
 #include "http2_enum.h"
@@ -41,8 +42,17 @@ unsigned Http2FlowData::inspector_id = 0;
 uint64_t Http2FlowData::instance_count = 0;
 #endif
 
-Http2FlowData::Http2FlowData() : FlowData(inspector_id), stream(new Http2Stream(this))
+Http2FlowData::Http2FlowData(Flow* flow_) :
+    FlowData(inspector_id),
+    flow(flow_),
+    hi((HttpInspect*)(flow->assistant_gadget))
 {
+    if (hi != nullptr)
+    {
+        hi_ss[SRC_CLIENT] = hi->get_splitter(true);
+        hi_ss[SRC_SERVER] = hi->get_splitter(false);
+    }
+
 #ifdef REG_TEST
     seq_num = ++instance_count;
     if (HttpTestManager::use_test_output(HttpTestManager::IN_HTTP2) &&
@@ -75,8 +85,68 @@ Http2FlowData::~Http2FlowData()
     {
         delete infractions[k];
         delete events[k];
+        delete hi_ss[k];
     }
+}
+
+HttpFlowData* Http2FlowData::get_hi_flow_data() const
+{
+    assert(stream_in_hi != Http2Enums::NO_STREAM_ID);
+    Http2Stream* stream = get_hi_stream();
+    return stream->get_hi_flow_data();
+}
+
+void Http2FlowData::set_hi_flow_data(HttpFlowData* flow)
+{
+    assert(stream_in_hi != Http2Enums::NO_STREAM_ID);
+    Http2Stream* stream = get_hi_stream();
+    stream->set_hi_flow_data(flow);
+}
+
+HttpMsgSection* Http2FlowData::get_hi_msg_section() const
+{
+    Http2Stream* stream = get_hi_stream();
+    if (stream == nullptr)
+        return nullptr;
+    return stream->get_hi_msg_section();
+}
+
+void Http2FlowData::set_hi_msg_section(HttpMsgSection* section)
+{
+    assert(stream_in_hi != Http2Enums::NO_STREAM_ID);
+    Http2Stream* stream = get_hi_stream();
+    stream->set_hi_msg_section(section);
+}
 
-    delete stream;
+class Http2Stream* Http2FlowData::find_stream(uint32_t key) const
+{
+    for (const StreamInfo& stream_info : streams)
+    {
+        if (stream_info.id == key)
+            return stream_info.stream;
+    }
+
+    return nullptr;
+}
+
+class Http2Stream* Http2FlowData::get_stream(uint32_t key)
+{
+    class Http2Stream* stream = find_stream(key);
+    if (!stream)
+    {
+        stream = new Http2Stream(key, this);
+        streams.emplace_front(key, stream);
+    }
+    return stream;
+}
+
+class Http2Stream* Http2FlowData::get_hi_stream() const
+{
+    return find_stream(stream_in_hi);
+}
+
+class Http2Stream* Http2FlowData::get_current_stream(const HttpCommon::SourceId source_id)
+{
+    return get_stream(current_stream[source_id]);
 }
 
index 669319249433ad3936e97521d0d9bd57b3abc88c..9a239ba790cc9ff9de7f90df37a8010d726705ca 100644 (file)
 #include "http2_hpack_int_decode.h"
 #include "http2_hpack_string_decode.h"
 #include "http2_settings_frame.h"
+#include "http2_stream.h"
 
 using Http2Infractions = Infractions<Http2Enums::INF__MAX_VALUE, Http2Enums::INF__NONE>;
 
 using Http2EventGen = EventGen<Http2Enums::EVENT__MAX_VALUE, Http2Enums::EVENT__NONE,
     Http2Enums::HTTP2_GID>;
 
+class HttpFlowData;
+class HttpMsgSection;
+class HttpInspect;
+class HttpStreamSplitter;
+
 class Http2FlowData : public snort::FlowData
 {
 public:
-    Http2FlowData();
+    Http2FlowData(snort::Flow* flow_);
     ~Http2FlowData() override;
     static unsigned inspector_id;
     static void init() { inspector_id = snort::FlowData::create_flow_data_id(); }
 
+    // Used by http_inspect to store its stuff
+    HttpFlowData* get_hi_flow_data() const;
+    void set_hi_flow_data(HttpFlowData* flow);
+    HttpMsgSection* get_hi_msg_section() const;
+    void set_hi_msg_section(HttpMsgSection* section);
+
     friend class Http2Frame;
     friend class Http2HeadersFrame;
     friend class Http2Hpack;
@@ -67,9 +79,31 @@ public:
     size_t size_of() override
     { return sizeof(*this); }
 
+    // Stream access
+    class StreamInfo
+    {
+    public:
+        const uint32_t id;
+        class Http2Stream* stream;
+
+        StreamInfo(uint32_t _id, class Http2Stream* ptr) : id(_id), stream(ptr) { assert(ptr); }
+        ~StreamInfo() { delete stream; }
+    };
+    class Http2Stream* get_current_stream(const HttpCommon::SourceId source_id);
+
 protected:
+    snort::Flow* flow;
+    HttpInspect* const hi;
+    HttpStreamSplitter* hi_ss[2] = { nullptr, nullptr };
+
     // 0 element refers to client frame, 1 element refers to server frame
 
+    // Stream ID of the frame currently being read in and processed
+    uint32_t current_stream[2] = { Http2Enums::NO_STREAM_ID, Http2Enums::NO_STREAM_ID };
+    // At any given time there may be different streams going in each direction. But only one of
+    // them is the stream that http_inspect is actually processing at the moment.
+    uint32_t stream_in_hi = Http2Enums::NO_STREAM_ID;
+
     // Reassemble() data to eval()
     uint8_t* frame_header[2] = { nullptr, nullptr };
     uint32_t frame_header_size[2] = { 0, 0 };
@@ -80,7 +114,7 @@ protected:
     bool frame_in_detection = false;
     Http2ConnectionSettings connection_settings[2];
     Http2HpackDecoder hpack_decoder[2];
-    class Http2Stream* stream;
+    std::list<class StreamInfo> streams;
 
     // Internal to scan()
     bool preface[2] = { true, false };
@@ -115,6 +149,11 @@ protected:
     static uint64_t instance_count;
     uint64_t seq_num;
 #endif
+
+private:
+    class Http2Stream* find_stream(uint32_t key) const;
+    class Http2Stream* get_stream(uint32_t key);
+    class Http2Stream* get_hi_stream() const;
 };
 
 #endif
index bde7796fb78ad4e2ed7739f89c2147353930ace0..e4453148d9c455e8b743edf7a022a1af075c1882 100644 (file)
@@ -48,20 +48,17 @@ Http2Frame::Http2Frame(const uint8_t* header_buffer, const int32_t header_len,
 Http2Frame* Http2Frame::new_frame(const uint8_t* header, const int32_t header_len,
     const uint8_t* data, const int32_t data_len, Http2FlowData* session_data, SourceId source_id)
 {
-    //FIXIT-H call the appropriate frame subclass constructor based on the type
+    // FIXIT-H call the appropriate frame subclass constructor based on the type
     switch(session_data->frame_type[source_id])
     {
         case FT_HEADERS:
             return new Http2HeadersFrame(header, header_len, data, data_len, session_data,
                 source_id);
-            break;
         case FT_SETTINGS:
             return new Http2SettingsFrame(header, header_len, data, data_len, session_data,
                 source_id);
-            break;
         default:
             return new Http2Frame(header, header_len, data, data_len, session_data, source_id);
-            break;
     }
 }
 
index 5fe1f5d4d0102dc4f2d796c535acc231048f9d1a..d1748da8272b8bbbd276140a2b54b286d5e69f78 100644 (file)
@@ -40,7 +40,7 @@ public:
     static Http2Frame* new_frame(const uint8_t* header_buffer, const int32_t header_len,
         const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* session_data,
         HttpCommon::SourceId source_id);
-
+    virtual void clear() { }
     virtual const Field& get_buf(unsigned id);
 #ifdef REG_TEST
     virtual void print_frame(FILE* output);
index 4b8e23850524e4e5552b081aa6b5515fbd634d85..21b758b633f70c992888bc656a5bdb1f7a66df2e 100644 (file)
 
 #include "http2_headers_frame.h"
 
+#include "protocols/packet.h"
+
+#include "service_inspectors/http_inspect/http_inspect.h"
+#include "service_inspectors/http_inspect/http_stream_splitter.h"
+
 #include "http2_enum.h"
 #include "http2_flow_data.h"
 #include "http2_hpack.h"
 #include "http2_start_line.h"
 
+using namespace snort;
 using namespace HttpCommon;
 using namespace Http2Enums;
 
 Http2HeadersFrame::Http2HeadersFrame(const uint8_t* header_buffer, const int32_t header_len,
-    const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* ssn_data,
-    HttpCommon::SourceId src_id) : Http2Frame(header_buffer, header_len, data_buffer, data_len,
-    ssn_data, src_id)
+    const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* session_data_,
+    HttpCommon::SourceId source_id_) :
+    Http2Frame(header_buffer, header_len, data_buffer, data_len, session_data_, source_id_)
 {
     uint8_t hpack_headers_offset = 0;
 
@@ -43,41 +49,130 @@ Http2HeadersFrame::Http2HeadersFrame(const uint8_t* header_buffer, const int32_t
         hpack_headers_offset = 5;
 
     // Set up the decoding context
-    hpack_decoder = &session_data->hpack_decoder[source_id];
+    Http2HpackDecoder& hpack_decoder = session_data->hpack_decoder[source_id];
 
     // Allocate stuff
     decoded_headers = new uint8_t[MAX_OCTETS];
-    decoded_headers_size = 0;
 
     start_line_generator = Http2StartLine::new_start_line_generator(source_id,
         session_data->events[source_id], session_data->infractions[source_id]);
 
     // Decode headers
-    if (!hpack_decoder->decode_headers((data.start() + hpack_headers_offset), data.length() -
-            hpack_headers_offset, decoded_headers, &decoded_headers_size, start_line_generator,
-            session_data->events[source_id], session_data->infractions[source_id]))
+    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]))
     {
         session_data->frame_type[source_id] = FT__ABORT;
         error_during_decode = true;
     }
-    start_line = hpack_decoder->get_start_line();
-    http2_decoded_header = hpack_decoder->get_decoded_headers(decoded_headers);
+    start_line = hpack_decoder.get_start_line();
+    http1_header = hpack_decoder.get_decoded_headers(decoded_headers);
+
+    if ((error_during_decode) || (session_data->hi_ss[source_id] == nullptr))
+        return;
+
+    // http_inspect scan() of start line
+    session_data->stream_in_hi = session_data->current_stream[source_id];
+    {
+        uint32_t flush_offset;
+        Packet dummy_pkt(false);
+        dummy_pkt.flow = session_data->flow;
+        const uint32_t unused = 0;
+        const StreamSplitter::Status start_scan_result =
+            session_data->hi_ss[source_id]->scan(&dummy_pkt, start_line->start(),
+            start_line->length(), unused, &flush_offset);
+        assert(start_scan_result == StreamSplitter::FLUSH);
+        UNUSED(start_scan_result);
+        assert((int64_t)flush_offset == start_line->length());
+    }
+
+    StreamBuffer stream_buf;
+    // http_inspect reassemble() of start line
+    {
+        unsigned copied;
+        stream_buf = session_data->hi_ss[source_id]->reassemble(session_data->flow,
+            start_line->length(), 0, start_line->start(), start_line->length(), PKT_PDU_TAIL,
+            copied);
+        assert(stream_buf.data != nullptr);
+        assert(copied == (unsigned)start_line->length());
+    }
+
+    // http_inspect eval() and clear() of start line
+    {
+        Packet dummy_pkt(false);
+        dummy_pkt.flow = session_data->flow;
+        dummy_pkt.packet_flags = (source_id == SRC_CLIENT) ? PKT_FROM_CLIENT : PKT_FROM_SERVER;
+        dummy_pkt.dsize = stream_buf.length;
+        dummy_pkt.data = stream_buf.data;
+        session_data->hi->eval(&dummy_pkt);
+        session_data->hi->clear(&dummy_pkt);
+    }
+
+    // http_inspect scan() of headers
+    {
+        uint32_t flush_offset;
+        Packet dummy_pkt(false);
+        dummy_pkt.flow = session_data->flow;
+        const uint32_t unused = 0;
+        const StreamSplitter::Status header_scan_result =
+            session_data->hi_ss[source_id]->scan(&dummy_pkt, http1_header->start(),
+            http1_header->length(), unused, &flush_offset);
+        if (header_scan_result == StreamSplitter::ABORT)
+        {
+            // eval() aborted the start line?
+            hi_abort = true;
+            return;
+        }
+        assert(header_scan_result == StreamSplitter::FLUSH);
+        assert((int64_t)flush_offset == http1_header->length());
+    }
+
+    // http_inspect reassemble() of headers
+    {
+        unsigned copied;
+        stream_buf = session_data->hi_ss[source_id]->reassemble(session_data->flow,
+            http1_header->length(), 0, http1_header->start(), http1_header->length(), PKT_PDU_TAIL,
+            copied);
+        assert(stream_buf.data != nullptr);
+        assert(copied == (unsigned)http1_header->length());
+    }
+
+    // http_inspect eval() of headers
+    {
+        Packet dummy_pkt(false);
+        dummy_pkt.flow = session_data->flow;
+        dummy_pkt.packet_flags = (source_id == SRC_CLIENT) ? PKT_FROM_CLIENT : PKT_FROM_SERVER;
+        dummy_pkt.dsize = stream_buf.length;
+        dummy_pkt.data = stream_buf.data;
+        session_data->hi->eval(&dummy_pkt);
+    }
 }
 
 Http2HeadersFrame::~Http2HeadersFrame()
 {
     delete start_line;
     delete start_line_generator;
-    delete http2_decoded_header;
+    delete http1_header;
     delete[] decoded_headers;
 }
 
+void Http2HeadersFrame::clear()
+{
+    if (error_during_decode || hi_abort || (session_data->hi == nullptr))
+        return;
+    Packet dummy_pkt(false);
+    dummy_pkt.flow = session_data->flow;
+    session_data->hi->clear(&dummy_pkt);
+}
+
 const Field& Http2HeadersFrame::get_buf(unsigned id)
 {
     switch (id)
     {
+    // FIXIT-M need to add a buffer for the decoded start line
     case HTTP2_BUFFER_DECODED_HEADER:
-        return *http2_decoded_header;
+        return *http1_header;
     default:
         return Http2Frame::get_buf(id);
     }
@@ -86,12 +181,12 @@ const Field& Http2HeadersFrame::get_buf(unsigned id)
 #ifdef REG_TEST
 void Http2HeadersFrame::print_frame(FILE* output)
 {
-    fprintf(output, "\nHEADERS frame\n");
+    fprintf(output, "\nHeaders frame\n");
     if (error_during_decode)
         fprintf(output, "Error decoding headers.\n");
     if (start_line)
         start_line->print(output, "Decoded start-line");
-    http2_decoded_header->print(output, "Decoded header");
+    http1_header->print(output, "Decoded header");
     Http2Frame::print_frame(output);
 }
 #endif
index 2c67822d24281c00ef3b61810ab13aa2904b8426..da3f80f94a98465632e5840ac594c2cf77f71ca5 100644 (file)
@@ -31,12 +31,12 @@ class Http2HeadersFrame : public Http2Frame
 {
 public:
     ~Http2HeadersFrame() override;
+    void clear() override;    
 
     const Field& get_buf(unsigned id) override;
 
-    friend Http2Frame* Http2Frame::new_frame(const uint8_t* header_buffer, const int32_t header_len,
-        const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* session_data,
-        HttpCommon::SourceId source_id);
+    friend Http2Frame* Http2Frame::new_frame(const uint8_t*, const int32_t, const uint8_t*,
+        const int32_t, Http2FlowData*, HttpCommon::SourceId);
 
 #ifdef REG_TEST
     void print_frame(FILE* output) override;
@@ -50,9 +50,9 @@ private:
     Http2StartLine* start_line_generator = nullptr;
     uint8_t* decoded_headers = nullptr; // working buffer to store decoded headers
     uint32_t decoded_headers_size = 0;
-    const Field* http2_decoded_header = nullptr; // finalized headers to be passed to NHI
+    const Field* http1_header = nullptr; // finalized headers to be passed to NHI
     const Field* start_line = nullptr;
     bool error_during_decode = false;
-    Http2HpackDecoder* hpack_decoder= nullptr;
+    bool hi_abort = false;
 };
 #endif
index de250421ce6cd6ae6b64e39e6561e652b1f344cc..dc5f597769359ece4a50029f22880bc48a8e282b 100644 (file)
@@ -329,7 +329,7 @@ bool Http2HpackDecoder::decode_header_line(const uint8_t* encoded_header_buffer,
 // not output the start line or decoded headers - this function must be followed by calls to
 // get_start_line() and get_decoded_headers() to generate and obtain these fields.
 bool Http2HpackDecoder::decode_headers(const uint8_t* encoded_headers,
-    const uint32_t encoded_headers_length, uint8_t* decoded_headers, uint32_t* decoded_headers_len,
+    const uint32_t encoded_headers_length, uint8_t* decoded_headers,
     Http2StartLine *start_line_generator, Http2EventGen* stream_events,
     Http2Infractions* stream_infractions)
 {
@@ -338,7 +338,7 @@ bool Http2HpackDecoder::decode_headers(const uint8_t* encoded_headers,
     uint32_t line_bytes_written = 0;
     bool success = true;
     start_line = start_line_generator;
-    decoded_headers_size = decoded_headers_len;
+    decoded_headers_size = 0;
     events = stream_events;
     infractions = stream_infractions;
     pseudo_headers_fragment_size = 0;
@@ -347,10 +347,10 @@ bool Http2HpackDecoder::decode_headers(const uint8_t* encoded_headers,
     {
         success = decode_header_line(encoded_headers + total_bytes_consumed,
             encoded_headers_length - total_bytes_consumed, line_bytes_consumed,
-            decoded_headers + *decoded_headers_size, MAX_OCTETS - *decoded_headers_size,
+            decoded_headers + decoded_headers_size, MAX_OCTETS - decoded_headers_size,
             line_bytes_written);
         total_bytes_consumed  += line_bytes_consumed;
-        *decoded_headers_size += line_bytes_written;
+        decoded_headers_size += line_bytes_written;
     }
 
     // If there were only pseudo-headers, finalize never got called, so create the start-line
@@ -361,8 +361,8 @@ bool Http2HpackDecoder::decode_headers(const uint8_t* encoded_headers,
     if (success)
     {
         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;
+            decoded_headers_size, MAX_OCTETS - decoded_headers_size, line_bytes_written);
+        decoded_headers_size += line_bytes_written;
     }
     else
         decode_error = true;
@@ -373,7 +373,7 @@ bool Http2HpackDecoder::finalize_start_line()
 {
     // Save the current position in the decoded buffer so we can set the pointer to the start
     // of the regular headers
-    pseudo_headers_fragment_size = *decoded_headers_size;
+    pseudo_headers_fragment_size = decoded_headers_size;
 
     return start_line->finalize();
 }
@@ -388,6 +388,6 @@ const Field* Http2HpackDecoder::get_decoded_headers(const uint8_t* const decoded
     if (decode_error)
         return new Field(STAT_NO_SOURCE);
     else
-        return new Field(*decoded_headers_size - pseudo_headers_fragment_size, decoded_headers +
+        return new Field(decoded_headers_size - pseudo_headers_fragment_size, decoded_headers +
             pseudo_headers_fragment_size, false);
 }
index 97cc2e34b2cadec9721f5c19bec75709f321e1f8..cbe6a9e1c8b8817e8fe65ea871c13a2d4e4ec8ab 100644 (file)
@@ -37,7 +37,7 @@ class Http2HpackDecoder
 public:
     Http2HpackDecoder() { }
     bool decode_headers(const uint8_t* encoded_headers, const uint32_t encoded_headers_length,
-        uint8_t* decoded_headers, uint32_t* decoded_headers_size, Http2StartLine* start_line,
+        uint8_t* decoded_headers, Http2StartLine* start_line,
         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);
@@ -71,7 +71,7 @@ public:
 
 private:
     Http2StartLine* start_line = nullptr;
-    uint32_t* decoded_headers_size;
+    uint32_t decoded_headers_size;
     uint32_t pseudo_headers_fragment_size = 0;
     bool decode_error = false;
     Http2EventGen* events;
index ac04e98bfcfc5c951fe3896e76ac1e7565555f8e..44f6102d2ee45156183bb7fc8423097fcc0fd363 100644 (file)
@@ -58,7 +58,7 @@ Http2Inspect::Http2Inspect(const Http2ParaList* params_) : params(params_)
 #endif
 }
 
-bool Http2Inspect::configure(SnortConfig* )
+bool Http2Inspect::configure(SnortConfig*)
 {
     return true;
 }
@@ -81,7 +81,9 @@ bool Http2Inspect::get_buf(unsigned id, Packet* p, InspectionBuffer& b)
     if (!session_data->frame_in_detection)
         return false;
 
-    const Field& buffer = session_data->stream->get_buf(id);
+    const SourceId source_id = p->is_from_client() ? SRC_CLIENT : SRC_SERVER;
+    Http2Stream* const stream = session_data->get_current_stream(source_id);
+    const Field& buffer = stream->get_buf(id);
     if (buffer.length() <= 0)
         return false;
 
@@ -111,7 +113,9 @@ void Http2Inspect::eval(Packet* p)
     if (session_data->frame_type[source_id] == FT__NONE)
         return;
 
-    session_data->stream->eval_frame(session_data->frame_header[source_id],
+    Http2Stream* stream = session_data->get_current_stream(source_id);
+
+    stream->eval_frame(session_data->frame_header[source_id],
         session_data->frame_header_size[source_id], session_data->frame_data[source_id],
         session_data->frame_data_size[source_id], source_id);
 
@@ -124,7 +128,7 @@ void Http2Inspect::eval(Packet* p)
 #ifdef REG_TEST
     if (HttpTestManager::use_test_output(HttpTestManager::IN_HTTP2))
     {
-        session_data->stream->print_frame(HttpTestManager::get_output_file());
+        stream->print_frame(HttpTestManager::get_output_file());
         if (HttpTestManager::use_test_input(HttpTestManager::IN_HTTP2))
         {
             printf("Finished processing section from test %" PRIi64 "\n",
@@ -143,6 +147,9 @@ void Http2Inspect::clear(Packet* p)
         return;
 
     session_data->frame_in_detection = false;
-    session_data->stream->clear_frame();
+
+    const SourceId source_id = p->is_from_client() ? SRC_CLIENT : SRC_SERVER;
+    Http2Stream* stream = session_data->get_current_stream(source_id);
+    stream->clear_frame();
 }
 
index db2a7e9fdee2e9ec0cc9983ca55c137fe38f73cb..c6a5c815c1c847388d645925972b6516e4788af0 100644 (file)
@@ -103,7 +103,7 @@ bool Http2SettingsFrame::sanity_check()
 #ifdef REG_TEST
 void Http2SettingsFrame::print_frame(FILE* output)
 {
-    fprintf(output, "SETTINGS frame:");
+    fprintf(output, "Settings frame:");
 
     if (bad_frame)
         fprintf(output, " Error in settings frame.");
index 3fc2858713f4ba1ac2b41a92a2a3dc31cd1456af..55176f5079d63b888091ddd83524343d92be725d 100644 (file)
@@ -29,9 +29,8 @@ class Http2Frame;
 class Http2SettingsFrame : public Http2Frame
 {
 public:
-    friend Http2Frame* Http2Frame::new_frame(const uint8_t* header_buffer, const int32_t header_len,
-        const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* session_data,
-        HttpCommon::SourceId source_id);
+    friend Http2Frame* Http2Frame::new_frame(const uint8_t*, const int32_t, const uint8_t*,
+        const int32_t, Http2FlowData*, HttpCommon::SourceId);
 
 #ifdef REG_TEST
     void print_frame(FILE* output) override;
@@ -65,7 +64,7 @@ private:
               100, // Max concurrent Streams
             65535, // Window size
             16384, // Max frame size
-       4294967295 // Max header list size
+       4294967295  // Max header list size
     };
 };
 #endif
index 6fed50c9c4ca94b7949473218210694900ca2513..9f2b59deb5384cdf5d82c6212b3944e9872c0488 100644 (file)
 
 #include "http2_stream.h"
 
+#include "service_inspectors/http_inspect/http_flow_data.h"
+
 using namespace HttpCommon;
 
-Http2Stream::Http2Stream(Http2FlowData* session_data_) : session_data(session_data_)
+Http2Stream::Http2Stream(uint32_t stream_id_, Http2FlowData* session_data_) :
+    stream_id(stream_id_),
+    session_data(session_data_)
 {
 }
 
 Http2Stream::~Http2Stream()
 {
     delete current_frame;
+    delete hi_flow_data;
 }
 
 void Http2Stream::eval_frame(const uint8_t* header_buffer, int32_t header_len,
@@ -44,6 +49,8 @@ void Http2Stream::eval_frame(const uint8_t* header_buffer, int32_t header_len,
 
 void Http2Stream::clear_frame()
 {
+    if (current_frame != nullptr) // FIXIT-M why is this needed?
+        current_frame->clear();
     delete current_frame;
     current_frame = nullptr;
 }
index 9e82fef4bdcc9193357d350f50d2a898ed49c603..3e4ff6e133d2d45e46f2091e5f55a6fc6c21af56 100644 (file)
 #include "service_inspectors/http_inspect/http_common.h"
 #include "service_inspectors/http_inspect/http_field.h"
 
-#include "http2_flow_data.h"
 #include "http2_frame.h"
 
+class HttpFlowData;
+class Http2FlowData;
+class HttpMsgSection;
+
 class Http2Stream
 {
 public:
-    Http2Stream(Http2FlowData* session_data_);
+    Http2Stream(uint32_t stream_id, Http2FlowData* session_data_);
     ~Http2Stream();
+    uint32_t get_stream_id() { return stream_id; }
     void eval_frame(const uint8_t* header_buffer, int32_t header_len, const uint8_t* data_buffer,
         int32_t data_len, HttpCommon::SourceId source_id);
     void clear_frame();
     const Field& get_buf(unsigned id);
+    HttpFlowData* get_hi_flow_data() const { return hi_flow_data; }
+    void set_hi_flow_data(HttpFlowData* flow_data)
+        { assert(hi_flow_data == nullptr); hi_flow_data = flow_data; }
+    HttpMsgSection* get_hi_msg_section() const { return hi_msg_section; }
+    void set_hi_msg_section(HttpMsgSection* section) { hi_msg_section = section; }
 #ifdef REG_TEST
     void print_frame(FILE* output);
 #endif
 
 private:
+    const uint32_t stream_id;
     Http2FlowData* const session_data;
     Http2Frame* current_frame = nullptr;
+    HttpFlowData* hi_flow_data = nullptr;
+    HttpMsgSection* hi_msg_section = nullptr;
 };
 
 #endif
index c1b9388385596fa1c5765d13a43d7ce495f4f0c2..6344c101460023de54adf58041bd5c8c3ed3768c 100644 (file)
@@ -52,10 +52,10 @@ StreamSplitter::Status Http2StreamSplitter::scan(Packet* pkt, const uint8_t* dat
 
     if (session_data == nullptr)
     {
-        pkt->flow->set_flow_data(session_data = new Http2FlowData);
-        Http2Module::increment_peg_counts(PEG_FLOW);
         AssistantGadgetEvent event(pkt, "http");
         DataBus::publish(FLOW_ASSISTANT_GADGET_EVENT, event);
+        pkt->flow->set_flow_data(session_data = new Http2FlowData(pkt->flow));
+        Http2Module::increment_peg_counts(PEG_FLOW);
     }
 
     // General mechanism to abort using scan
index ff210c5c1d75f14dd1a3e15889605bbfba618ebc..9e42fa0b489489a7405b97ebc2f099ef34537b79 100644 (file)
@@ -59,6 +59,16 @@ static uint8_t get_frame_flags(const uint8_t* frame_buffer)
         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 implement_scan(Http2FlowData* session_data, const uint8_t* data,
     uint32_t length, uint32_t* flush_offset, HttpCommon::SourceId source_id)
 {
@@ -135,9 +145,9 @@ StreamSplitter::Status implement_scan(Http2FlowData* session_data, const uint8_t
 
             // The first nine bytes are the frame header. But all nine might not all be present in
             // the first TCP segment we receive.
-            uint32_t remaining_header = FRAME_HEADER_LENGTH -
+            const uint32_t remaining_header = FRAME_HEADER_LENGTH -
                 session_data->scan_octets_seen[source_id];
-            uint32_t remaining_header_in_data = remaining_header > length - data_offset ?
+            const uint32_t remaining_header_in_data = remaining_header > length - data_offset ?
                 length - data_offset : remaining_header;
             memcpy(session_data->scan_frame_header[source_id] +
                 session_data->scan_octets_seen[source_id], data + data_offset,
@@ -154,7 +164,9 @@ StreamSplitter::Status implement_scan(Http2FlowData* session_data, const uint8_t
             // We have the full frame header, compute some variables
             const uint32_t frame_length = get_frame_length(session_data->
                 scan_frame_header[source_id]);
-            uint8_t type = get_frame_type(session_data->scan_frame_header[source_id]);
+            const uint8_t type = get_frame_type(session_data->scan_frame_header[source_id]);
+            session_data->current_stream[source_id] =
+                get_stream_id(session_data->scan_frame_header[source_id]);
 
             // Compute frame section length once per frame
             if (session_data->scan_remaining_frame_octets[source_id] == 0)
index ff6a4ba7538c4346e53b71a07ceb9356e4a6b542..15f432ed83ac9e40d7bb815d79390d4caf4ffe16 100644 (file)
@@ -25,6 +25,7 @@
 
 #include "http_msg_section.h"
 #include "protocols/packet.h"
+#include "service_inspectors/http2_inspect/http2_flow_data.h"
 
 using namespace snort;
 
@@ -32,6 +33,14 @@ unsigned HttpContextData::ips_id = 0;
 
 HttpMsgSection* HttpContextData::get_snapshot(const Packet* p)
 {
+    if (Http2FlowData::inspector_id != 0)
+    {
+        const Http2FlowData* const h2i_flow_data =
+               (Http2FlowData*)p->flow->get_flow_data(Http2FlowData::inspector_id);
+        if (h2i_flow_data != nullptr)
+            return h2i_flow_data->get_hi_msg_section();
+    }
+
     IpsContext* context = p ? p->context : nullptr;
     HttpContextData* hcd = (HttpContextData*)DetectionEngine::get_data(HttpContextData::ips_id,
             context);
index ed494f2c5b2a47fc4eab2233bc917b952022a594..acbdd2f653ca28d7573c1caecdc8f1506f9d47f9 100644 (file)
@@ -49,8 +49,8 @@ HttpFlowData::HttpFlowData() : FlowData(inspector_id)
     if (HttpTestManager::use_test_output(HttpTestManager::IN_HTTP) &&
         !HttpTestManager::use_test_input(HttpTestManager::IN_HTTP))
     {
-        printf("Flow Data construct %" PRIu64 "\n", seq_num);
-        fflush(nullptr);
+        fprintf(HttpTestManager::get_output_file(), "Flow Data construct %" PRIu64 "\n", seq_num);
+        fflush(HttpTestManager::get_output_file());
     }
 #endif
     HttpModule::increment_peg_counts(PEG_CONCURRENT_SESSIONS);
@@ -65,8 +65,8 @@ HttpFlowData::~HttpFlowData()
     if (HttpTestManager::use_test_output(HttpTestManager::IN_HTTP) &&
         !HttpTestManager::use_test_input(HttpTestManager::IN_HTTP))
     {
-        printf("Flow Data destruct %" PRIu64 "\n", seq_num);
-        fflush(nullptr);
+        fprintf(HttpTestManager::get_output_file(), "Flow Data destruct %" PRIu64 "\n", seq_num);
+        fflush(HttpTestManager::get_output_file());
     }
 #endif
     if (HttpModule::get_peg_counts(PEG_CONCURRENT_SESSIONS) > 0)
index ca2d37d13cde5c5af41a55802dc52d144cf73bfa..90759f16f35b6ee5de7bc9dad1828c251acba0e4 100644 (file)
@@ -66,6 +66,8 @@ public:
 #endif
 
 private:
+    bool for_http2 = false;
+
     // Convenience routines
     void half_reset(HttpCommon::SourceId source_id);
     void trailer_prep(HttpCommon::SourceId source_id);
index 71d2946f9b55c235c316734495a227a1511160e7..5892bb6c4181727f124e5f12727d1c87044e4e48 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "detection/detection_engine.h"
 #include "detection/detection_util.h"
+#include "service_inspectors/http2_inspect/http2_flow_data.h"
 #include "log/unified2.h"
 #include "protocols/packet.h"
 #include "stream/stream.h"
@@ -310,14 +311,38 @@ int HttpInspect::get_xtra_jsnorm(Flow*, uint8_t** buf, uint32_t* len, uint32_t*
     return 1;
 }
 
+HttpFlowData* HttpInspect::http_get_flow_data(const Flow* flow)
+{
+    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)
+        return (HttpFlowData*)flow->get_flow_data(HttpFlowData::inspector_id);
+    else
+        return h2i_flow_data->get_hi_flow_data();
+}
+
+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)
+        flow->set_flow_data(flow_data);
+    else
+    {
+        flow_data->for_http2 = true;
+        h2i_flow_data->set_hi_flow_data(flow_data);
+    }
+}
+
 void HttpInspect::eval(Packet* p)
 {
     Profile profile(HttpModule::get_profile_stats());
 
     const SourceId source_id = p->is_from_client() ? SRC_CLIENT : SRC_SERVER;
 
-    HttpFlowData* session_data =
-        (HttpFlowData*)p->flow->get_flow_data(HttpFlowData::inspector_id);
+    HttpFlowData* session_data = http_get_flow_data(p->flow);
 
     // FIXIT-H Workaround for unexpected eval() calls
     if (session_data->section_type[source_id] == SEC__NOT_COMPUTE)
@@ -340,7 +365,8 @@ void HttpInspect::eval(Packet* p)
     const bool buf_owner = !session_data->partial_flush[source_id];
     if (!process(p->data, p->dsize, p->flow, source_id, buf_owner))
     {
-        DetectionEngine::disable_content(p);
+        if (!session_data->for_http2)
+            DetectionEngine::disable_content(p);
     }
 
 #ifdef REG_TEST
@@ -376,7 +402,7 @@ bool HttpInspect::process(const uint8_t* data, const uint16_t dsize, Flow* const
     SourceId source_id, bool buf_owner) const
 {
     HttpMsgSection* current_section;
-    HttpFlowData* session_data = (HttpFlowData*)flow->get_flow_data(HttpFlowData::inspector_id);
+    HttpFlowData* session_data = http_get_flow_data(flow);
     assert(session_data != nullptr);
 
     if (!session_data->partial_flush[source_id])
@@ -452,13 +478,25 @@ void HttpInspect::clear(Packet* p)
 {
     Profile profile(HttpModule::get_profile_stats());
 
-    HttpFlowData* const session_data =
-        (HttpFlowData*)p->flow->get_flow_data(HttpFlowData::inspector_id);
+    HttpFlowData* const session_data = http_get_flow_data(p->flow);
 
     if ( session_data == nullptr )
         return;
 
-    HttpMsgSection* current_section = HttpContextData::clear_snapshot(p->context);
+    Http2FlowData* h2i_flow_data = nullptr;
+    if (Http2FlowData::inspector_id != 0)
+    {
+        h2i_flow_data = (Http2FlowData*)p->flow->get_flow_data(Http2FlowData::inspector_id);
+    }
+
+    HttpMsgSection* current_section = nullptr;
+    if (h2i_flow_data != nullptr)
+    {
+        current_section = h2i_flow_data->get_hi_msg_section();
+        h2i_flow_data->set_hi_msg_section(nullptr);
+    }
+    else
+        current_section = HttpContextData::clear_snapshot(p->context);
 
     // FIXIT-M This test is necessary because sometimes we get extra clears
     // Convert to assert when that gets fixed.
index db64b9eb055080bca1ffbc282698fdd21bd6ebd2..4d276a60e885e6190c7173b70c5b807ad5161277 100644 (file)
@@ -69,6 +69,8 @@ private:
 
     bool process(const uint8_t* data, const uint16_t dsize, snort::Flow* const flow,
         HttpCommon::SourceId source_id_, bool buf_owner) const;
+    static HttpFlowData* http_get_flow_data(const snort::Flow* flow);
+    static void http_set_flow_data(snort::Flow* flow, HttpFlowData* flow_data);
 
     const HttpParaList* const params;
 
index d99577e7b3ec612d61fb7d707528838952736945..cfe8c8b7c94a7c2718d50e8f70cb81c4b224cb16 100644 (file)
@@ -21,6 +21,8 @@
 #include "config.h"
 #endif
 
+#include "service_inspectors/http2_inspect/http2_flow_data.h"
+
 #include "http_msg_section.h"
 
 #include "http_context_data.h"
@@ -55,6 +57,17 @@ HttpMsgSection::HttpMsgSection(const uint8_t* buffer, const uint16_t buf_size,
     tcp_close(session_data->tcp_close[source_id])
 {
     assert((source_id == SRC_CLIENT) || (source_id == SRC_SERVER));
+
+    if (Http2FlowData::inspector_id != 0)
+    {
+        Http2FlowData* const h2i_flow_data = (Http2FlowData*)flow->get_flow_data(Http2FlowData::inspector_id);
+        if (h2i_flow_data != nullptr)
+        {
+            h2i_flow_data->set_hi_msg_section(this);
+            return;
+        }
+    }
+
     HttpContextData::save_snapshot(this);
 }
 
index 46b2c63fc2893605f7f463f9f7d614c79a64dfb6..5c631bfc852e30613b47d1cba22d394e9acdebf6 100644 (file)
@@ -26,6 +26,7 @@
 #include "http_common.h"
 #include "http_cutter.h"
 #include "http_enum.h"
+#include "http_inspect.h"
 #include "http_module.h"
 #include "http_msg_request.h"
 #include "http_stream_splitter.h"
@@ -39,7 +40,7 @@ bool HttpStreamSplitter::finish(Flow* flow)
 {
     Profile profile(HttpModule::get_profile_stats());
 
-    HttpFlowData* session_data = (HttpFlowData*)flow->get_flow_data(HttpFlowData::inspector_id);
+    HttpFlowData* session_data = HttpInspect::http_get_flow_data(flow);
     // FIXIT-M - this assert has been changed to check for null session data and return false if so
     //           due to lack of reliable feedback to stream that scan has been called...if that is
     //           addressed in stream reassembly rewrite this can be reverted to an assert
@@ -57,9 +58,10 @@ bool HttpStreamSplitter::finish(Flow* flow)
         }
         else
         {
-            printf("Finish from flow data %" PRIu64 " direction %d\n", session_data->seq_num,
+            fprintf(HttpTestManager::get_output_file(),
+                "Finish from flow data %" PRIu64 " direction %d\n", session_data->seq_num,
                 source_id);
-            fflush(stdout);
+            fflush(HttpTestManager::get_output_file());
         }
     }
 #endif
@@ -166,7 +168,7 @@ bool HttpStreamSplitter::init_partial_flush(Flow* flow)
         return false;
     }
 
-    HttpFlowData* session_data = (HttpFlowData*)flow->get_flow_data(HttpFlowData::inspector_id);
+    HttpFlowData* session_data = HttpInspect::http_get_flow_data(flow);
     assert(session_data != nullptr);
     if ((session_data->type_expected[source_id] != SEC_BODY_CL)      &&
         (session_data->type_expected[source_id] != SEC_BODY_OLD)     &&
@@ -180,8 +182,9 @@ bool HttpStreamSplitter::init_partial_flush(Flow* flow)
     if (HttpTestManager::use_test_output(HttpTestManager::IN_HTTP) &&
         !HttpTestManager::use_test_input(HttpTestManager::IN_HTTP))
     {
-        printf("Partial flush from flow data %" PRIu64 "\n", session_data->seq_num);
-        fflush(stdout);
+        fprintf(HttpTestManager::get_output_file(), "Partial flush from flow data %" PRIu64 "\n",
+            session_data->seq_num);
+        fflush(HttpTestManager::get_output_file());
     }
 #endif
 
index 6b1880de8120a79d446171e4f634fab7b31a0f92..f69dc1ebb49edf6dba39c19890e0564e4b8ec255 100644 (file)
@@ -231,7 +231,7 @@ const StreamBuffer HttpStreamSplitter::reassemble(Flow* flow, unsigned total,
 
     copied = len;
 
-    HttpFlowData* session_data = (HttpFlowData*)flow->get_flow_data(HttpFlowData::inspector_id);
+    HttpFlowData* session_data = HttpInspect::http_get_flow_data(flow);
     assert(session_data != nullptr);
 
 #ifdef REG_TEST
@@ -268,10 +268,10 @@ const StreamBuffer HttpStreamSplitter::reassemble(Flow* flow, unsigned total,
         }
         else
         {
-            printf("Reassemble from flow data %" PRIu64
+            fprintf(HttpTestManager::get_output_file(), "Reassemble from flow data %" PRIu64
                 " direction %d total %u length %u partial %d\n", session_data->seq_num, source_id,
                 total, len, session_data->partial_flush[source_id]);
-            fflush(stdout);
+            fflush(HttpTestManager::get_output_file());
         }
     }
 #endif
index 1860518c3ca92f0e2dc225b723428458c21c38e3..5f3abef59188731b962498a8c2b0d0b2a8d6743e 100644 (file)
@@ -131,11 +131,11 @@ StreamSplitter::Status HttpStreamSplitter::scan(Packet* pkt, const uint8_t* data
     // This is the session state information we share with HttpInspect and store with stream. A
     // session is defined by a TCP connection. Since scan() is the first to see a new TCP
     // connection the new flow data object is created here.
-    HttpFlowData* session_data = (HttpFlowData*)flow->get_flow_data(HttpFlowData::inspector_id);
+    HttpFlowData* session_data = HttpInspect::http_get_flow_data(flow);
 
     if (session_data == nullptr)
     {
-        flow->set_flow_data(session_data = new HttpFlowData);
+        HttpInspect::http_set_flow_data(flow, session_data = new HttpFlowData);
         HttpModule::increment_peg_counts(PEG_FLOW);
     }
 
@@ -163,13 +163,13 @@ StreamSplitter::Status HttpStreamSplitter::scan(Packet* pkt, const uint8_t* data
     if (HttpTestManager::use_test_output(HttpTestManager::IN_HTTP) &&
         !HttpTestManager::use_test_input(HttpTestManager::IN_HTTP))
     {
-        printf("Scan from flow data %" PRIu64
+        fprintf(HttpTestManager::get_output_file(), "Scan from flow data %" PRIu64
             " direction %d length %u client port %hu server port %hu\n", session_data->seq_num,
             source_id, length, flow->client_port, flow->server_port);
-        fflush(stdout);
+        fflush(HttpTestManager::get_output_file());
         if (HttpTestManager::get_show_scan())
         {
-            Field(length, data).print(stdout, "Scan segment");
+            Field(length, data).print(HttpTestManager::get_output_file(), "Scan segment");
         }
     }
 #endif
index 4307a73f64fe13e35849fd2a2c66a4d7a750939a..be28e7afed148f632e5250e694c0270ba27caa09 100644 (file)
@@ -27,6 +27,7 @@
 #include "hash/hashfcn.h"
 #include "log/messages.h"
 #include "protocols/packet.h"
+#include "service_inspectors/http2_inspect/http2_flow_data.h"
 
 #include "http_common.h"
 #include "http_enum.h"
@@ -223,10 +224,15 @@ IpsOption::EvalStatus HttpIpsOption::eval(Cursor& c, Packet* p)
     if (!section_match)
         return NO_MATCH;
 
+    const Http2FlowData* const h2i_flow_data =
+       (Http2FlowData*)p->flow->get_flow_data(Http2FlowData::inspector_id);
+
+    HttpInspect* const hi = (h2i_flow_data != nullptr) ?
+        (HttpInspect*)(p->flow->assistant_gadget) : (HttpInspect*)(p->flow->gadget);
+
     InspectionBuffer hb;
 
-    if (! ((HttpInspect*)(p->flow->gadget))->
-           http_get_buf((unsigned)buffer_index, sub_id, form, p, hb))
+    if (! (hi->http_get_buf((unsigned)buffer_index, sub_id, form, p, hb)))
         return NO_MATCH;
 
     c.set(key, hb.data, hb.len);