]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #1120 in SNORT/snort3 from http2 to master
authorTom Peters (thopeter) <thopeter@cisco.com>
Mon, 5 Mar 2018 16:19:39 +0000 (11:19 -0500)
committerTom Peters (thopeter) <thopeter@cisco.com>
Mon, 5 Mar 2018 16:19:39 +0000 (11:19 -0500)
Squashed commit of the following:

commit a832c050c010f1f2ab728751a4779cc231c72137
Author: Tom Peters <thopeter@cisco.com>
Date:   Thu Jan 25 11:46:22 2018 -0500

    http2_inspect: initial prototype

31 files changed:
configure.ac
lua/snort.lua
lua/snort_defaults.lua
src/CMakeLists.txt
src/Makefile.am
src/service_inspectors/CMakeLists.txt
src/service_inspectors/Makefile.am
src/service_inspectors/http2_inspect/CMakeLists.txt [new file with mode: 0644]
src/service_inspectors/http2_inspect/Makefile.am [new file with mode: 0644]
src/service_inspectors/http2_inspect/dev_notes.txt [new file with mode: 0644]
src/service_inspectors/http2_inspect/http2_api.cc [new file with mode: 0644]
src/service_inspectors/http2_inspect/http2_api.h [new file with mode: 0644]
src/service_inspectors/http2_inspect/http2_enum.h [new file with mode: 0644]
src/service_inspectors/http2_inspect/http2_flow_data.cc [new file with mode: 0644]
src/service_inspectors/http2_inspect/http2_flow_data.h [new file with mode: 0644]
src/service_inspectors/http2_inspect/http2_inspect.cc [new file with mode: 0644]
src/service_inspectors/http2_inspect/http2_inspect.h [new file with mode: 0644]
src/service_inspectors/http2_inspect/http2_inspect_impl.cc [new file with mode: 0644]
src/service_inspectors/http2_inspect/http2_module.cc [new file with mode: 0644]
src/service_inspectors/http2_inspect/http2_module.h [new file with mode: 0644]
src/service_inspectors/http2_inspect/http2_stream_splitter.cc [new file with mode: 0644]
src/service_inspectors/http2_inspect/http2_stream_splitter.h [new file with mode: 0644]
src/service_inspectors/http2_inspect/http2_stream_splitter_impl.cc [new file with mode: 0644]
src/service_inspectors/http2_inspect/http2_tables.cc [new file with mode: 0644]
src/service_inspectors/http2_inspect/ips_http2.cc [new file with mode: 0644]
src/service_inspectors/http2_inspect/ips_http2.h [new file with mode: 0644]
src/service_inspectors/http2_inspect/test/CMakeLists.txt [new file with mode: 0644]
src/service_inspectors/http2_inspect/test/http2_flow_data_test.h [new file with mode: 0644]
src/service_inspectors/http2_inspect/test/http2_inspect_impl_test.cc [new file with mode: 0644]
src/service_inspectors/http2_inspect/test/http2_stream_splitter_impl_test.cc [new file with mode: 0644]
src/service_inspectors/service_inspectors.cc

index 34ce1723e2ab33925af224b5a21774bcd4437b2e..234c2da74d6b9a13ab7a135ff1d9a3c18cc9bfe0 100644 (file)
@@ -1256,6 +1256,7 @@ src/service_inspectors/imap/Makefile \
 src/service_inspectors/modbus/Makefile \
 src/service_inspectors/http_inspect/Makefile \
 src/service_inspectors/http_inspect/test/Makefile \
+src/service_inspectors/http2_inspect/Makefile \
 src/service_inspectors/pop/Makefile \
 src/service_inspectors/rpc_decode/Makefile \
 src/service_inspectors/sip/Makefile \
index 2e245dffd512809bba3490b0cf47ca5212b52ffd..2c4af5368a9454d8b79a753a4a823f1e34ea0587 100644 (file)
@@ -80,6 +80,7 @@ back_orifice = { }
 dnp3 = { }
 dns = { }
 http_inspect = { }
+http2_inspect = { }
 imap = { }
 modbus = { }
 normalizer = { }
@@ -153,6 +154,7 @@ binder =
     { when = { service = 'gtp' },              use = { type = 'gtp_inspect' } },
     { when = { service = 'imap' },             use = { type = 'imap' } },
     { when = { service = 'http' },             use = { type = 'http_inspect' } },
+    { when = { service = 'http2' },            use = { type = 'http2_inspect' } },
     { when = { service = 'modbus' },           use = { type = 'modbus' } },
     { when = { service = 'pop3' },             use = { type = 'pop' } },
     { when = { service = 'ssh' },              use = { type = 'ssh' } },
index 3115077e8801b813bbe05481580b396901ee4bc1..40554cbc22464d448e54b0f999b32efc0967369c 100644 (file)
@@ -324,12 +324,15 @@ default_wizard =
 
         { service = 'dce_http_proxy', proto = 'tcp', client_first = true,
           to_server = { 'RPC_CONNECT' } },
+
     },
     hexes =
     {
         { service = 'dnp3', proto = 'tcp', client_first = true, 
           to_server = { '|05 64|' }, to_client = { '|05 64|' } },
+
+        { service = 'http2', proto = 'tcp', client_first = true,
+          to_server = { '|50 52 49 20 2a 20 48 54 54 50 2f 32 2e 30 0d 0a 0d 0a 53 4d 0d 0a 0d 0a|' } },
 --[[
         { service = 'modbus', proto = 'tcp', client_first = true,
           to_server = { '??|0 0|' } },
index 9d55d65543e67c494aa1943d60dee88f75ca9500..6e9942f1a6133b954949a0cf7cb9b25d65dc3001 100644 (file)
@@ -89,6 +89,7 @@ target_link_libraries( snort
 # include only those with otherwise unresolved dependencies
     appid
     http_inspect
+    http2_inspect
     sip
     reputation
     stream
index e6cd839ec17975b603e94730e8ab9498e7301bde..548e34d3e265d240fc25e88f34f231bebc4ce52a 100644 (file)
@@ -58,6 +58,7 @@ network_inspectors/perf_monitor/libperf_monitor.a \
 network_inspectors/reputation/libreputation.a \
 service_inspectors/libservice_inspectors.a \
 service_inspectors/http_inspect/libhttp_inspect.a \
+service_inspectors/http2_inspect/libhttp2_inspect.a \
 service_inspectors/sip/libsip.a \
 $(lib_list) \
 pub_sub/libpub_sub.a \
index 6f68dc639e4faf6b3dfe0f278ff71baa8c7d221e..c86efe1b3b1cbba42568bb7f409f3067721ab954 100644 (file)
@@ -8,6 +8,7 @@ add_subdirectory(gtp)
 add_subdirectory(imap)
 add_subdirectory(modbus)
 add_subdirectory(http_inspect)
+add_subdirectory(http2_inspect)
 add_subdirectory(pop)
 add_subdirectory(rpc_decode)
 add_subdirectory(sip)
index dec03c2261d59550444f8c0a8483732e93bdca3e..5262912e38acb095b5241b1ab624b1b6ebb15a61 100644 (file)
@@ -10,6 +10,7 @@ service_inspectors.h
 #back_orifice/libback_orifice.a \
 #ftp_telnet/libftp_telnet.a \
 #http_inspect/libhttp_inspect.a \
+#http_inspect2/libhttp2_inspect.a \
 #rpc_decode/librpc_decode.a
 #wizard/libwizard.a
 
@@ -23,6 +24,7 @@ gtp \
 imap \
 modbus \
 http_inspect \
+http2_inspect \
 pop \
 rpc_decode \
 sip \
diff --git a/src/service_inspectors/http2_inspect/CMakeLists.txt b/src/service_inspectors/http2_inspect/CMakeLists.txt
new file mode 100644 (file)
index 0000000..593ce3d
--- /dev/null
@@ -0,0 +1,30 @@
+
+set (FILE_LIST
+    http2_api.cc
+    http2_api.h
+    http2_enum.h
+    http2_flow_data.cc
+    http2_flow_data.h
+    http2_inspect.cc
+    http2_inspect_impl.cc
+    http2_inspect.h
+    http2_module.cc
+    http2_module.h
+    http2_stream_splitter.cc
+    http2_stream_splitter_impl.cc
+    http2_stream_splitter.h
+    http2_tables.cc
+    ips_http2.cc
+    ips_http2.h
+)
+
+#if (STATIC_INSPECTORS)
+    add_library(http2_inspect STATIC ${FILE_LIST})
+
+#else(STATIC_INSPECTORS)
+    #add_dynamic_module(http2_inspect inspectors ${FILE_LIST})
+
+#endif(STATIC_INSPECTORS)
+
+add_subdirectory ( test )
+
diff --git a/src/service_inspectors/http2_inspect/Makefile.am b/src/service_inspectors/http2_inspect/Makefile.am
new file mode 100644 (file)
index 0000000..982b0ed
--- /dev/null
@@ -0,0 +1,13 @@
+file_list = \
+http2_api.cc http2_api.h \
+http2_enum.h \
+http2_flow_data.cc http2_flow_data.h \
+http2_inspect.cc http2_inspect_impl.cc http2_inspect.h \
+http2_module.cc http2_module.h \
+http2_stream_splitter.cc http2_stream_splitter_impl.cc http2_stream_splitter.h \
+http2_tables.cc \
+ips_http2.cc ips_http2.h
+
+noinst_LIBRARIES = libhttp2_inspect.a
+libhttp2_inspect_a_SOURCES = $(file_list)
+
diff --git a/src/service_inspectors/http2_inspect/dev_notes.txt b/src/service_inspectors/http2_inspect/dev_notes.txt
new file mode 100644 (file)
index 0000000..26c649e
--- /dev/null
@@ -0,0 +1,6 @@
+The HTTP/2 inspector (H2I) will convert HTTP/2 frames into HTTP/1.1 message sections and feed them
+to the new HTTP inspector (NHI) for further processing.
+
+The current implementation is the very first step. It splits an HTTP/2 stream into frames and
+forwards them for inspection. It does not interface with NHI, does not provide error detection and
+handling, and does not address the multiplexed nature of HTTP/2.
diff --git a/src/service_inspectors/http2_inspect/http2_api.cc b/src/service_inspectors/http2_inspect/http2_api.cc
new file mode 100644 (file)
index 0000000..d7c0b10
--- /dev/null
@@ -0,0 +1,86 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2018-2018 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// http2_api.cc author Tom Peters <thopeter@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "http2_api.h"
+
+#include "http2_inspect.h"
+
+const char* Http2Api::http2_my_name = HTTP2_NAME;
+const char* Http2Api::http2_help = "the HTTP/2 inspector";
+
+Inspector* Http2Api::http2_ctor(Module* mod)
+{
+    Http2Module* const http2_mod = (Http2Module*)mod;
+    return new Http2Inspect(http2_mod->get_once_params());
+}
+
+const char* Http2Api::classic_buffer_names[] =
+{
+    "http2_frame_type",
+    "http2_raw_frame",
+    nullptr
+};
+
+const InspectApi Http2Api::http2_api =
+{
+    {
+        PT_INSPECTOR,
+        sizeof(InspectApi),
+        INSAPI_VERSION,
+        0,
+        API_RESERVED,
+        API_OPTIONS,
+        Http2Api::http2_my_name,
+        Http2Api::http2_help,
+        Http2Api::http2_mod_ctor,
+        Http2Api::http2_mod_dtor
+    },
+    IT_SERVICE,
+    (uint16_t)PktType::PDU,
+    classic_buffer_names,
+    "http2",
+    Http2Api::http2_init,
+    Http2Api::http2_term,
+    Http2Api::http2_tinit,
+    Http2Api::http2_tterm,
+    Http2Api::http2_ctor,
+    Http2Api::http2_dtor,
+    nullptr,
+    nullptr
+};
+
+extern const BaseApi* ips_http2_frame_header;
+extern const BaseApi* ips_http2_frame_data;
+
+#ifdef BUILDING_SO
+SO_PUBLIC const BaseApi* snort_plugins[] =
+#else
+const BaseApi* sin_http2[] =
+#endif
+{
+    &Http2Api::http2_api.base,
+    ips_http2_frame_header,
+    ips_http2_frame_data,
+    nullptr
+};
+
diff --git a/src/service_inspectors/http2_inspect/http2_api.h b/src/service_inspectors/http2_inspect/http2_api.h
new file mode 100644 (file)
index 0000000..8f30378
--- /dev/null
@@ -0,0 +1,50 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2018-2018 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// http2_api.h author Tom Peters <thopeter@cisco.com>
+
+#ifndef HTTP2_API_H
+#define HTTP2_API_H
+
+#include "framework/inspector.h"
+#include "framework/module.h"
+
+#include "http2_flow_data.h"
+#include "http2_module.h"
+
+class Http2Api
+{
+public:
+    static const InspectApi http2_api;
+    static const char* classic_buffer_names[];
+
+private:
+    Http2Api() = delete;
+    static Module* http2_mod_ctor() { return new Http2Module; }
+    static void http2_mod_dtor(Module* m) { delete m; }
+    static const char* http2_my_name;
+    static const char* http2_help;
+    static void http2_init() { Http2FlowData::init(); }
+    static void http2_term() { }
+    static Inspector* http2_ctor(Module* mod);
+    static void http2_dtor(Inspector* p) { delete p; }
+    static void http2_tinit() { }
+    static void http2_tterm() { }
+};
+
+#endif
+
diff --git a/src/service_inspectors/http2_inspect/http2_enum.h b/src/service_inspectors/http2_inspect/http2_enum.h
new file mode 100644 (file)
index 0000000..6cbac02
--- /dev/null
@@ -0,0 +1,59 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2018-2018 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// http2_enum.h author Tom Peters <thopeter@cisco.com>
+
+#ifndef HTTP2_ENUM_H
+#define HTTP2_ENUM_H
+
+#include <cstdint>
+
+namespace Http2Enums
+{
+static const int MAX_OCTETS = 63780;
+static const int DATA_SECTION_SIZE = 16384;
+static const int FRAME_HEADER_LENGTH = 9;
+
+// FIXIT-M need to replace with a real number
+static const uint32_t HTTP2_GID = 219;
+
+// Message originator--client or server
+enum SourceId { SRC__NOT_COMPUTE=-14, SRC_CLIENT=0, SRC_SERVER=1 };
+
+// Frame type codes (fourth octet of frame header)
+enum FrameType { FT_DATA=0, FT_HEADERS=1, FT_PRIORITY=2, FT_RST_STREAM=3, FT_SETTINGS=4,
+    FT_PUSH_PROMISE=5, FT_PING=6, FT_GOAWAY=7, FT_WINDOW_UPDATE=8, FT_CONTINUATION=9 };
+
+// Message buffers available to clients
+// This enum must remain synchronized with Http2Api::classic_buffer_names[]
+enum HTTP2_BUFFER { HTTP2_BUFFER_FRAME_HEADER = 1, HTTP2_BUFFER_FRAME_DATA, HTTP2_BUFFER_MAX };
+
+// Peg counts
+// This enum must remain synchronized with Http2Module::peg_names[] in http2_tables.cc
+enum PEG_COUNT { PEG_CONCURRENT_SESSIONS = 0, PEG_MAX_CONCURRENT_SESSIONS, PEG_FLOW,
+    PEG_COUNT_MAX };
+
+enum EventSid
+{
+    EVENT__NONE = -1,
+    EVENT__MAX_VALUE
+};
+
+} // end namespace Http2Enums
+
+#endif
+
diff --git a/src/service_inspectors/http2_inspect/http2_flow_data.cc b/src/service_inspectors/http2_inspect/http2_flow_data.cc
new file mode 100644 (file)
index 0000000..72dc452
--- /dev/null
@@ -0,0 +1,52 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2018-2018 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// http2_flow_data.cc author Tom Peters <thopeter@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "http2_flow_data.h"
+
+#include "http2_enum.h"
+#include "http2_module.h"
+
+using namespace Http2Enums;
+
+unsigned Http2FlowData::inspector_id = 0;
+
+Http2FlowData::Http2FlowData() : FlowData(inspector_id)
+{
+    Http2Module::increment_peg_counts(PEG_CONCURRENT_SESSIONS);
+    if (Http2Module::get_peg_counts(PEG_MAX_CONCURRENT_SESSIONS) <
+        Http2Module::get_peg_counts(PEG_CONCURRENT_SESSIONS))
+        Http2Module::increment_peg_counts(PEG_MAX_CONCURRENT_SESSIONS);
+}
+
+Http2FlowData::~Http2FlowData()
+{
+    if (Http2Module::get_peg_counts(PEG_CONCURRENT_SESSIONS) > 0)
+        Http2Module::decrement_peg_counts(PEG_CONCURRENT_SESSIONS);
+
+    for (int k=0; k <= 1; k++)
+    {
+        delete[] frame_header[k];
+        delete[] frame[k];
+    }
+}
+
diff --git a/src/service_inspectors/http2_inspect/http2_flow_data.h b/src/service_inspectors/http2_inspect/http2_flow_data.h
new file mode 100644 (file)
index 0000000..9e6f1fd
--- /dev/null
@@ -0,0 +1,59 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2018-2018 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// http2_flow_data.h author Tom Peters <thopeter@cisco.com>
+
+#ifndef HTTP2_FLOW_DATA_H
+#define HTTP2_FLOW_DATA_H
+
+#include "flow/flow.h"
+#include "stream/stream_splitter.h"
+#include "http2_enum.h"
+
+class Http2FlowData : public FlowData
+{
+public:
+    Http2FlowData();
+    ~Http2FlowData() override;
+    static unsigned inspector_id;
+    static void init() { inspector_id = FlowData::create_flow_data_id(); }
+
+    friend class Http2Inspect;
+    friend class Http2StreamSplitter;
+    friend const StreamBuffer implement_reassemble(Http2FlowData*, unsigned, unsigned,
+        const uint8_t*, unsigned, uint32_t, unsigned&, Http2Enums::SourceId);
+    friend StreamSplitter::Status implement_scan(Http2FlowData*, const uint8_t*, uint32_t,
+        uint32_t*, Http2Enums::SourceId);
+    friend bool implement_get_buf(unsigned id, Http2FlowData*, Http2Enums::SourceId,
+        InspectionBuffer&);
+
+protected:
+    // 0 element refers to client frame, 1 element refers to server frame
+    bool preface[2] = { true, false };
+    bool header_coming[2]  = { false, false };
+    uint8_t* frame_header[2] = { nullptr, nullptr };
+    uint8_t* frame[2] = { nullptr, nullptr };
+    uint32_t frame_size[2] = { 0, 0 };
+    uint8_t* frame_data[2] = { nullptr, nullptr };
+    uint32_t frame_data_size[2] = { 0, 0 };
+    uint32_t leftover_data[2] = { 0, 0 };
+    uint32_t octets_seen[2] = { 0, 0 };
+    bool frame_in_detection = false;
+};
+
+#endif
+
diff --git a/src/service_inspectors/http2_inspect/http2_inspect.cc b/src/service_inspectors/http2_inspect/http2_inspect.cc
new file mode 100644 (file)
index 0000000..b7a4a07
--- /dev/null
@@ -0,0 +1,114 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2018-2018 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// http2_inspect.cc author Tom Peters <thopeter@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "http2_inspect.h"
+#include "detection/detection_engine.h"
+#include "protocols/packet.h"
+#include "service_inspectors/http_inspect/http_enum.h"
+#include "service_inspectors/http_inspect/http_field.h"
+#include "service_inspectors/http_inspect/http_test_manager.h"
+#include "stream/stream.h"
+
+using namespace Http2Enums;
+
+Http2Inspect::Http2Inspect(const Http2ParaList* params_) : params(params_)
+{
+}
+
+bool Http2Inspect::configure(SnortConfig* )
+{
+    return true;
+}
+
+bool Http2Inspect::get_buf(InspectionBuffer::Type /*ibt*/, Packet* /*p*/, InspectionBuffer& /*b*/)
+{
+    return false;
+}
+
+bool Http2Inspect::get_buf(unsigned id, Packet* p, InspectionBuffer& b)
+{
+    Http2FlowData* const session_data =
+        (Http2FlowData*)p->flow->get_flow_data(Http2FlowData::inspector_id);
+
+    if (session_data == nullptr)
+        return false;
+
+    // Otherwise we can return buffers for raw packets because frame header is available before
+    // frame is reassembled.
+    if (!session_data->frame_in_detection)
+        return false;
+
+    const SourceId source_id = p->is_from_client() ? SRC_CLIENT : SRC_SERVER;
+
+    return implement_get_buf(id, session_data, source_id, b);
+}
+
+bool Http2Inspect::get_fp_buf(InspectionBuffer::Type /*ibt*/, Packet* /*p*/,
+    InspectionBuffer& /*b*/)
+{
+    // No fast pattern buffers have been defined for HTTP/2
+    return false;
+}
+
+void Http2Inspect::eval(Packet* p)
+{
+    Profile profile(Http2Module::get_profile_stats());
+
+    const SourceId source_id = p->is_from_client() ? SRC_CLIENT : SRC_SERVER;
+
+    Http2FlowData* const session_data =
+        (Http2FlowData*)p->flow->get_flow_data(Http2FlowData::inspector_id);
+
+    set_file_data(session_data->frame_data[source_id], session_data->frame_data_size[source_id]);
+    session_data->frame_in_detection = true;
+
+#ifdef REG_TEST
+    if (HttpTestManager::use_test_output())
+    {
+        Field((session_data->frame_header[source_id] != nullptr) ? FRAME_HEADER_LENGTH :
+            HttpEnums::STAT_NOT_PRESENT,
+            session_data->frame_header[source_id]).print(stdout, "frame header");
+        Field((session_data->frame_data[source_id] != nullptr) ?
+            (int) session_data->frame_data_size[source_id] : HttpEnums::STAT_NOT_PRESENT,
+            session_data->frame_data[source_id]).print(stdout, "frame data");
+    }
+#endif
+}
+
+void Http2Inspect::clear(Packet* p)
+{
+    Http2FlowData* const session_data =
+        (Http2FlowData*)p->flow->get_flow_data(Http2FlowData::inspector_id);
+
+    if (session_data == nullptr)
+        return;
+
+    const SourceId source_id = (p->is_from_client()) ? SRC_CLIENT : SRC_SERVER;
+
+    delete[] session_data->frame_header[source_id];
+    session_data->frame_header[source_id] = nullptr;
+    delete[] session_data->frame[source_id];
+    session_data->frame[source_id] = nullptr;
+    session_data->frame_in_detection = false;
+}
+
diff --git a/src/service_inspectors/http2_inspect/http2_inspect.h b/src/service_inspectors/http2_inspect/http2_inspect.h
new file mode 100644 (file)
index 0000000..ff94aee
--- /dev/null
@@ -0,0 +1,66 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2018-2018 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// http2_inspect.h author Tom Peters <thopeter@cisco.com>
+
+#ifndef HTTP2_INSPECT_H
+#define HTTP2_INSPECT_H
+
+//-------------------------------------------------------------------------
+// Http2Inspect class
+//-------------------------------------------------------------------------
+
+#include "log/messages.h"
+
+#include "http2_enum.h"
+#include "http2_flow_data.h"
+#include "http2_module.h"
+#include "http2_stream_splitter.h"
+
+class Http2Api;
+
+class Http2Inspect : public Inspector
+{
+public:
+    Http2Inspect(const Http2ParaList* params_);
+    ~Http2Inspect() override { delete params; }
+
+    bool get_buf(InspectionBuffer::Type ibt, Packet* p, InspectionBuffer& b) override;
+    bool get_buf(unsigned id, Packet* p, InspectionBuffer& b) override;
+    bool get_fp_buf(InspectionBuffer::Type ibt, Packet* p, InspectionBuffer& b) override;
+    bool configure(SnortConfig*) override;
+    void show(SnortConfig*) override { LogMessage("Http2Inspect\n"); }
+    void eval(Packet* p) override;
+    void clear(Packet* p) override;
+    void tinit() override { }
+    void tterm() override { }
+    Http2StreamSplitter* get_splitter(bool is_client_to_server) override
+    {
+        return new Http2StreamSplitter(is_client_to_server);
+    }
+
+private:
+    friend Http2Api;
+
+    const Http2ParaList* const params;
+};
+
+bool implement_get_buf(unsigned id, Http2FlowData* session_data, Http2Enums::SourceId source_id,
+    InspectionBuffer& b);
+
+#endif
+
diff --git a/src/service_inspectors/http2_inspect/http2_inspect_impl.cc b/src/service_inspectors/http2_inspect/http2_inspect_impl.cc
new file mode 100644 (file)
index 0000000..9a7ab2a
--- /dev/null
@@ -0,0 +1,51 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2018-2018 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// http2_inspect_impl.cc author Tom Peters <thopeter@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "http2_inspect.h"
+#include "http2_flow_data.h"
+
+using namespace Http2Enums;
+
+bool implement_get_buf(unsigned id, Http2FlowData* session_data, SourceId source_id,
+    InspectionBuffer& b)
+{
+    switch (id)
+    {
+    case HTTP2_BUFFER_FRAME_HEADER:
+        if (session_data->frame_header[source_id] == nullptr)
+            return false;
+        b.data = session_data->frame_header[source_id];
+        b.len = FRAME_HEADER_LENGTH;
+        break;
+    case HTTP2_BUFFER_FRAME_DATA:
+        if (session_data->frame_data[source_id] == nullptr)
+            return false;
+        b.data = session_data->frame_data[source_id];
+        b.len = session_data->frame_data_size[source_id];
+        break;
+    default:
+        return false;
+    }
+    return true;
+}
+
diff --git a/src/service_inspectors/http2_inspect/http2_module.cc b/src/service_inspectors/http2_inspect/http2_module.cc
new file mode 100644 (file)
index 0000000..5a79c3e
--- /dev/null
@@ -0,0 +1,56 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2018-2018 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// http2_module.cc author Tom Peters <thopeter@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "http2_module.h"
+
+using namespace Http2Enums;
+
+const Parameter Http2Module::http2_params[] =
+{
+    { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
+};
+
+THREAD_LOCAL ProfileStats Http2Module::http2_profile;
+
+ProfileStats* Http2Module::get_profile() const
+{ return &http2_profile; }
+
+THREAD_LOCAL PegCount Http2Module::peg_counts[PEG_COUNT_MAX] = { 0 };
+
+bool Http2Module::begin(const char*, int, SnortConfig*)
+{
+    delete params;
+    params = new Http2ParaList;
+    return true;
+}
+
+bool Http2Module::set(const char*, Value& /*val*/, SnortConfig*)
+{
+    return false;
+}
+
+bool Http2Module::end(const char*, int, SnortConfig*)
+{
+    return true;
+}
+
diff --git a/src/service_inspectors/http2_inspect/http2_module.h b/src/service_inspectors/http2_inspect/http2_module.h
new file mode 100644 (file)
index 0000000..72ef065
--- /dev/null
@@ -0,0 +1,83 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2018-2018 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// http2_module.h author Tom Peters <thopeter@cisco.com>
+
+#ifndef HTTP2_MODULE_H
+#define HTTP2_MODULE_H
+
+#include <string>
+#include <bitset>
+
+#include "framework/module.h"
+#include "profiler/profiler.h"
+
+#include "http2_enum.h"
+
+#define HTTP2_NAME "http2_inspect"
+#define HTTP2_HELP "HTTP/2 inspector"
+
+struct Http2ParaList
+{
+public:
+};
+
+class Http2Module : public Module
+{
+public:
+    Http2Module() : Module(HTTP2_NAME, HTTP2_HELP, http2_params) { }
+    ~Http2Module() override { delete params; }
+    bool begin(const char*, int, SnortConfig*) override;
+    bool end(const char*, int, SnortConfig*) override;
+    bool set(const char*, Value&, SnortConfig*) override;
+    unsigned get_gid() const override { return Http2Enums::HTTP2_GID; }
+    const RuleMap* get_rules() const override { return http2_events; }
+    const Http2ParaList* get_once_params()
+    {
+        Http2ParaList* ret_val = params;
+        params = nullptr;
+        return ret_val;
+    }
+
+    const PegInfo* get_pegs() const override { return peg_names; }
+    PegCount* get_counts() const override { return peg_counts; }
+    static void increment_peg_counts(Http2Enums::PEG_COUNT counter)
+        { peg_counts[counter]++; }
+    static void decrement_peg_counts(Http2Enums::PEG_COUNT counter)
+        { peg_counts[counter]--; }
+    static PegCount get_peg_counts(Http2Enums::PEG_COUNT counter)
+        { return peg_counts[counter]; }
+
+    ProfileStats* get_profile() const override;
+
+    static ProfileStats& get_profile_stats()
+    { return http2_profile; }
+
+    Usage get_usage() const override
+    { return INSPECT; }
+
+private:
+    static const Parameter http2_params[];
+    static const RuleMap http2_events[];
+    Http2ParaList* params = nullptr;
+    static const PegInfo peg_names[];
+    static THREAD_LOCAL ProfileStats http2_profile;
+    static THREAD_LOCAL PegCount peg_counts[];
+};
+
+#endif
+
diff --git a/src/service_inspectors/http2_inspect/http2_stream_splitter.cc b/src/service_inspectors/http2_inspect/http2_stream_splitter.cc
new file mode 100644 (file)
index 0000000..12a180c
--- /dev/null
@@ -0,0 +1,61 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2018-2018 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// http2_stream_splitter.cc author Tom Peters <thopeter@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <cassert>
+
+#include "http2_stream_splitter.h"
+#include "http2_module.h"
+
+using namespace Http2Enums;
+
+// Mindless scan() that just flushes whatever it is given
+StreamSplitter::Status Http2StreamSplitter::scan(Flow* flow, const uint8_t* data, uint32_t length,
+    uint32_t, uint32_t* flush_offset)
+{
+    // This is the session state information we share with Http2Inspect 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.
+    Http2FlowData* session_data = (Http2FlowData*)flow->get_flow_data(Http2FlowData::inspector_id);
+
+    if (session_data == nullptr)
+    {
+        flow->set_flow_data(session_data = new Http2FlowData);
+        Http2Module::increment_peg_counts(PEG_FLOW);
+    }
+
+    return implement_scan(session_data, data, length, flush_offset, source_id);
+}
+
+// Generic reassemble() copies the inputs unchanged into a static buffer
+const StreamBuffer Http2StreamSplitter::reassemble(Flow* flow, unsigned total, unsigned offset,
+    const uint8_t* data, unsigned len, uint32_t flags, unsigned& copied)
+{
+    Http2FlowData* session_data = (Http2FlowData*)flow->get_flow_data(Http2FlowData::inspector_id);
+    assert(session_data != nullptr);
+
+    return implement_reassemble(session_data, total, offset, data, len, flags, copied, source_id);
+}
+
+// Eventually we will need to address unexpected connection closes
+bool Http2StreamSplitter::finish(Flow* /*flow*/) { return false; }
+
diff --git a/src/service_inspectors/http2_inspect/http2_stream_splitter.h b/src/service_inspectors/http2_inspect/http2_stream_splitter.h
new file mode 100644 (file)
index 0000000..5d9278b
--- /dev/null
@@ -0,0 +1,56 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2018-2018 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// http2_stream_splitter.h author Tom Peters <thopeter@cisco.com>
+
+#ifndef HTTP2_STREAM_SPLITTER_H
+#define HTTP2_STREAM_SPLITTER_H
+
+#include "stream/stream_splitter.h"
+
+#include "http2_enum.h"
+#include "http2_flow_data.h"
+
+class Http2Inspect;
+
+class Http2StreamSplitter : public StreamSplitter
+{
+public:
+    Http2StreamSplitter(bool is_client_to_server) : StreamSplitter(is_client_to_server),
+        source_id(is_client_to_server ? Http2Enums::SRC_CLIENT : Http2Enums::SRC_SERVER) { }
+    Status scan(Flow* flow, const uint8_t* data, uint32_t length, uint32_t not_used,
+        uint32_t* flush_offset) override;
+    const StreamBuffer reassemble(Flow* flow, unsigned total, unsigned offset, const
+        uint8_t* data, unsigned len, uint32_t flags, unsigned& copied) override;
+    bool finish(Flow* flow) override;
+    bool is_paf() override { return true; }
+
+    // FIXIT-M should return actual packet buffer size
+    unsigned max(Flow*) override { return Http2Enums::MAX_OCTETS; }
+
+private:
+    const Http2Enums::SourceId source_id;
+};
+
+StreamSplitter::Status implement_scan(Http2FlowData* session_data, const uint8_t* data,
+    uint32_t length, uint32_t* flush_offset, Http2Enums::SourceId source_id);
+const StreamBuffer implement_reassemble(Http2FlowData* session_data, unsigned total,
+    unsigned offset, const uint8_t* data, unsigned len, uint32_t flags, unsigned& copied,
+    Http2Enums::SourceId source_id);
+
+#endif
+
diff --git a/src/service_inspectors/http2_inspect/http2_stream_splitter_impl.cc b/src/service_inspectors/http2_inspect/http2_stream_splitter_impl.cc
new file mode 100644 (file)
index 0000000..cb455a6
--- /dev/null
@@ -0,0 +1,144 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2018-2018 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// http2_stream_splitter_impl.cc author Tom Peters <thopeter@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <cassert>
+
+#include "http2_stream_splitter.h"
+#include "protocols/packet.h"
+#include "http2_flow_data.h"
+
+using namespace Http2Enums;
+
+StreamSplitter::Status implement_scan(Http2FlowData* session_data, const uint8_t* data,
+    uint32_t length, uint32_t* flush_offset, Http2Enums::SourceId source_id)
+{
+    if (session_data->preface[source_id])
+    {
+        // 24-byte preface, not a real frame, no frame header
+        *flush_offset = 24;
+        session_data->header_coming[source_id] = false;
+        session_data->preface[source_id] = false;
+    }
+    else if (session_data->leftover_data[source_id] > 0)
+    {
+        // Continuation of ongoing data frame
+        session_data->header_coming[source_id] = false;
+        *flush_offset = (session_data->leftover_data[source_id] < DATA_SECTION_SIZE) ?
+            session_data->leftover_data[source_id] : DATA_SECTION_SIZE;
+        session_data->leftover_data[source_id] -= *flush_offset;
+    }
+    else
+    {
+        // frame with header
+        if (session_data->frame_header[source_id] == nullptr)
+        {
+            session_data->header_coming[source_id] = true;
+            session_data->frame_header[source_id] = new uint8_t[FRAME_HEADER_LENGTH];
+            session_data->octets_seen[source_id] = 0;
+        }
+
+        // The first nine bytes are the frame header. But all nine might not all be present in the
+        // first TCP segment we receive.
+        for (uint32_t k = 0; (k < length) && (session_data->octets_seen[source_id] <
+            FRAME_HEADER_LENGTH); k++, session_data->octets_seen[source_id]++)
+        {
+            session_data->frame_header[source_id][session_data->octets_seen[source_id]] = data[k];
+        }
+        if (session_data->octets_seen[source_id] < FRAME_HEADER_LENGTH)
+            return StreamSplitter::SEARCH;
+
+        uint32_t const frame_length = (session_data->frame_header[source_id][0] << 16) +
+                                      (session_data->frame_header[source_id][1] << 8) +
+                                       session_data->frame_header[source_id][2];
+        if ((session_data->frame_header[source_id][3] == FT_DATA) &&
+            (frame_length > DATA_SECTION_SIZE))
+        {
+            // Long data frame is cut into pieces
+            *flush_offset = DATA_SECTION_SIZE + FRAME_HEADER_LENGTH;
+            session_data->leftover_data[source_id] = frame_length - DATA_SECTION_SIZE;
+        }
+        else if (frame_length + FRAME_HEADER_LENGTH > MAX_OCTETS)
+        {
+            // FIXIT-M long non-data frame needs to be supported
+            return StreamSplitter::ABORT;
+        }
+        else
+        {
+            // Normal case
+            *flush_offset = frame_length + FRAME_HEADER_LENGTH;
+        }
+    }
+    return StreamSplitter::FLUSH;
+}
+
+const StreamBuffer implement_reassemble(Http2FlowData* session_data, unsigned total,
+    unsigned offset, const uint8_t* data, unsigned len, uint32_t flags, unsigned& copied,
+    Http2Enums::SourceId source_id)
+{
+    assert(offset+len <= total);
+    assert(total >= FRAME_HEADER_LENGTH);
+    assert(total <= Http2Enums::MAX_OCTETS);
+
+    StreamBuffer frame_buf { nullptr, 0 };
+
+    if (offset == 0)
+    {
+        session_data->frame[source_id] = new uint8_t[total];
+        session_data->frame_size[source_id] = total;
+    }
+    assert(session_data->frame_size[source_id] == total);
+
+    memcpy(session_data->frame[source_id]+offset, data, len);
+    copied = len;
+    if (flags & PKT_PDU_TAIL)
+    {
+        assert(offset+len == total);
+        if (!session_data->header_coming[source_id])
+        {
+            session_data->frame_data[source_id] = session_data->frame[source_id];
+            frame_buf.data = session_data->frame_data[source_id];
+            session_data->frame_data_size[source_id] = session_data->frame_size[source_id];
+            frame_buf.length = session_data->frame_data_size[source_id];
+        }
+        else if (session_data->frame_size[source_id] == FRAME_HEADER_LENGTH)
+        {
+            session_data->frame_data[source_id] = nullptr;
+            session_data->frame_data_size[source_id] = 0;
+            // Don't send empty frame body to detection, use header so there is something
+            frame_buf.data = session_data->frame[source_id];
+            frame_buf.length = session_data->frame_size[source_id];
+        }
+        else
+        {
+            // Adjust for frame header
+            session_data->frame_data[source_id] =
+                session_data->frame[source_id] + FRAME_HEADER_LENGTH;
+            frame_buf.data = session_data->frame_data[source_id];
+            session_data->frame_data_size[source_id] =
+                session_data->frame_size[source_id] - FRAME_HEADER_LENGTH;
+            frame_buf.length = session_data->frame_data_size[source_id];
+        }
+    }
+    return frame_buf;
+}
+
diff --git a/src/service_inspectors/http2_inspect/http2_tables.cc b/src/service_inspectors/http2_inspect/http2_tables.cc
new file mode 100644 (file)
index 0000000..3705c45
--- /dev/null
@@ -0,0 +1,43 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2018-2018 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// http2_tables.cc author Tom Peters <thopeter@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "framework/counts.h"
+
+#include "http2_enum.h"
+#include "http2_module.h"
+
+using namespace Http2Enums;
+
+const RuleMap Http2Module::http2_events[] =
+{
+    { 0, nullptr }
+};
+
+const PegInfo Http2Module::peg_names[PEG_COUNT_MAX+1] =
+{
+    { CountType::SUM, "flows", "HTTP connections inspected" },
+    { CountType::NOW, "concurrent_sessions", "total concurrent HTTP/2 sessions" },
+    { CountType::MAX, "max_concurrent_sessions", "maximum concurrent HTTP/2 sessions" },
+    { CountType::END, nullptr, nullptr }
+};
+
diff --git a/src/service_inspectors/http2_inspect/ips_http2.cc b/src/service_inspectors/http2_inspect/ips_http2.cc
new file mode 100644 (file)
index 0000000..b2d9ac3
--- /dev/null
@@ -0,0 +1,169 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2018-2018 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// ips_http2.cc author Tom Peters <thopeter@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ips_http2.h"
+
+#include "framework/cursor.h"
+#include "protocols/packet.h"
+
+#include "http2_flow_data.h"
+#include "http2_inspect.h"
+
+using namespace Http2Enums;
+
+THREAD_LOCAL std::array<ProfileStats, PSI_MAX> Http2CursorModule::http2_ps;
+
+bool Http2CursorModule::begin(const char*, int, SnortConfig*)
+{
+    para_list.reset();
+    return true;
+}
+
+bool Http2CursorModule::set(const char*, Value& /*v*/, SnortConfig*)
+{
+    return false;
+}
+
+bool Http2CursorModule::end(const char*, int, SnortConfig*)
+{
+    return true;
+}
+
+void Http2CursorModule::Http2RuleParaList::reset()
+{
+}
+
+uint32_t Http2IpsOption::hash() const
+{
+    return IpsOption::hash();
+}
+
+bool Http2IpsOption::operator==(const IpsOption& ips) const
+{
+    return IpsOption::operator==(ips);
+}
+
+IpsOption::EvalStatus Http2IpsOption::eval(Cursor& c, Packet* p)
+{
+    Profile profile(Http2CursorModule::http2_ps[psi]);
+
+    if (!p->flow || !p->flow->gadget)
+        return NO_MATCH;
+
+    InspectionBuffer hb;
+
+    if (! ((Http2Inspect*)(p->flow->gadget))->get_buf((unsigned)buffer_index, p, hb))
+        return NO_MATCH;
+
+    c.set(key, hb.data, hb.len);
+
+    return MATCH;
+}
+
+//-------------------------------------------------------------------------
+// http2_frame_data
+//-------------------------------------------------------------------------
+
+#undef IPS_OPT
+#define IPS_OPT "http2_frame_data"
+#undef IPS_HELP
+#define IPS_HELP "rule option to see HTTP/2 frame body"
+
+static Module* frame_data_mod_ctor()
+{
+    return new Http2CursorModule(IPS_OPT, IPS_HELP, HTTP2_BUFFER_FRAME_DATA, CAT_SET_OTHER,
+        PSI_FRAME_DATA);
+}
+
+static const IpsApi frame_data_api =
+{
+    {
+        PT_IPS_OPTION,
+        sizeof(IpsApi),
+        IPSAPI_VERSION,
+        1,
+        API_RESERVED,
+        API_OPTIONS,
+        IPS_OPT,
+        IPS_HELP,
+        frame_data_mod_ctor,
+        Http2CursorModule::mod_dtor
+    },
+    OPT_TYPE_DETECTION,
+    0, PROTO_BIT__TCP,
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    Http2IpsOption::opt_ctor,
+    Http2IpsOption::opt_dtor,
+    nullptr
+};
+
+//-------------------------------------------------------------------------
+// http2_frame_header
+//-------------------------------------------------------------------------
+
+#undef IPS_OPT
+#define IPS_OPT "http2_frame_header"
+#undef IPS_HELP
+#define IPS_HELP "rule option to see 9-octet HTTP/2 frame header"
+
+static Module* frame_header_mod_ctor()
+{
+    return new Http2CursorModule(IPS_OPT, IPS_HELP, HTTP2_BUFFER_FRAME_HEADER, CAT_SET_OTHER,
+        PSI_FRAME_HEADER);
+}
+
+static const IpsApi frame_header_api =
+{
+    {
+        PT_IPS_OPTION,
+        sizeof(IpsApi),
+        IPSAPI_VERSION,
+        1,
+        API_RESERVED,
+        API_OPTIONS,
+        IPS_OPT,
+        IPS_HELP,
+        frame_header_mod_ctor,
+        Http2CursorModule::mod_dtor
+    },
+    OPT_TYPE_DETECTION,
+    0, PROTO_BIT__TCP,
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    Http2IpsOption::opt_ctor,
+    Http2IpsOption::opt_dtor,
+    nullptr
+};
+
+//-------------------------------------------------------------------------
+// plugins
+//-------------------------------------------------------------------------
+
+const BaseApi* ips_http2_frame_data = &frame_data_api.base;
+const BaseApi* ips_http2_frame_header = &frame_header_api.base;
+
diff --git a/src/service_inspectors/http2_inspect/ips_http2.h b/src/service_inspectors/http2_inspect/ips_http2.h
new file mode 100644 (file)
index 0000000..c10fbaa
--- /dev/null
@@ -0,0 +1,87 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2018-2018 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// ips_http2.h author Tom Peters <thopeter@cisco.com>
+
+#ifndef IPS_HTTP2_H
+#define IPS_HTTP2_H
+
+#include <array>
+
+#include "profiler/profiler.h"
+#include "framework/ips_option.h"
+#include "framework/module.h"
+
+#include "http2_enum.h"
+
+enum PsIdx { PSI_FRAME_DATA, PSI_FRAME_HEADER, PSI_MAX };
+
+class Http2CursorModule : public Module
+{
+public:
+    Http2CursorModule(const char* key_, const char* help, Http2Enums::HTTP2_BUFFER buffer_index_,
+        CursorActionType cat_, PsIdx psi_) : Module(key_, help), key(key_),
+        buffer_index(buffer_index_), cat(cat_), psi(psi_) {}
+    ProfileStats* get_profile() const override { return &http2_ps[psi]; }
+    static void mod_dtor(Module* m) { delete m; }
+    bool begin(const char*, int, SnortConfig*) override;
+    bool set(const char*, Value&, SnortConfig*) override;
+    bool end(const char*, int, SnortConfig*) override;
+
+    Usage get_usage() const override
+    { return DETECT; }
+
+private:
+    friend class Http2IpsOption;
+    static THREAD_LOCAL std::array<ProfileStats, PsIdx::PSI_MAX> http2_ps;
+
+    struct Http2RuleParaList
+    {
+    public:
+        void reset();
+    };
+
+    const char* const key;
+    const Http2Enums::HTTP2_BUFFER buffer_index;
+    const CursorActionType cat;
+    const PsIdx psi;
+
+    Http2RuleParaList para_list;
+};
+
+class Http2IpsOption : public IpsOption
+{
+public:
+    Http2IpsOption(const Http2CursorModule* cm) :
+        IpsOption(cm->key, RULE_OPTION_TYPE_BUFFER_SET), key(cm->key),
+        buffer_index(cm->buffer_index), cat(cm->cat), psi(cm->psi) {}
+    CursorActionType get_cursor_type() const override { return cat; }
+    EvalStatus eval(Cursor&, Packet*) override;
+    uint32_t hash() const override;
+    bool operator==(const IpsOption& ips) const override;
+    static IpsOption* opt_ctor(Module* m, OptTreeNode*)
+        { return new Http2IpsOption((Http2CursorModule*)m); }
+    static void opt_dtor(IpsOption* p) { delete p; }
+private:
+    const char* const key;
+    const Http2Enums::HTTP2_BUFFER buffer_index;
+    const CursorActionType cat;
+    const PsIdx psi;
+};
+
+#endif
+
diff --git a/src/service_inspectors/http2_inspect/test/CMakeLists.txt b/src/service_inspectors/http2_inspect/test/CMakeLists.txt
new file mode 100644 (file)
index 0000000..724be9b
--- /dev/null
@@ -0,0 +1,3 @@
+add_cpputest(http2_inspect_impl_test http2_inspect framework)
+add_cpputest(http2_stream_splitter_impl_test http2_inspect framework)
+
diff --git a/src/service_inspectors/http2_inspect/test/http2_flow_data_test.h b/src/service_inspectors/http2_inspect/test/http2_flow_data_test.h
new file mode 100644 (file)
index 0000000..f4878d3
--- /dev/null
@@ -0,0 +1,64 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2018-2018 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// http2_flow_data_test.h author Tom Peters <thopeter@cisco.com>
+
+#ifndef HTTP2_FLOW_DATA_TEST_H
+#define HTTP2_FLOW_DATA_TEST_H
+
+#include "service_inspectors/http2_inspect/http2_enum.h"
+#include "service_inspectors/http2_inspect/http2_flow_data.h"
+#include "service_inspectors/http2_inspect/http2_module.h"
+
+// Stubs whose sole purpose is to make the test code link
+FlowData::FlowData(unsigned u, Inspector* ph) : next(nullptr), prev(nullptr), handler(ph), id(u) {}
+FlowData::~FlowData() {}
+void show_stats(PegCount*, const PegInfo*, unsigned, const char*) { }
+void show_stats(PegCount*, const PegInfo*, IndexVec&, const char*, FILE*) { }
+void show_stats(SimpleStats*, const char*) { }
+
+class Http2FlowDataTest : public Http2FlowData
+{
+public:
+    bool get_preface(Http2Enums::SourceId source_id) { return preface[source_id]; }
+    void set_preface(bool value, Http2Enums::SourceId source_id) { preface[source_id] = value; }
+    bool get_header_coming(Http2Enums::SourceId source_id) { return header_coming[source_id]; }
+    void set_header_coming(bool value, Http2Enums::SourceId source_id)
+        { header_coming[source_id] = value; }
+    uint8_t* get_frame_header(Http2Enums::SourceId source_id) { return frame_header[source_id]; }
+    void set_frame_header(uint8_t* value, Http2Enums::SourceId source_id)
+        { frame_header[source_id] = value; }
+    uint8_t* get_frame(Http2Enums::SourceId source_id) { return frame[source_id]; }
+    void set_frame(uint8_t* value, Http2Enums::SourceId source_id) { frame[source_id] = value; }
+    uint32_t get_frame_size(Http2Enums::SourceId source_id) { return frame_size[source_id]; }
+    void set_frame_size(uint32_t value, Http2Enums::SourceId source_id)
+        { frame_size[source_id] = value; }
+    uint8_t* get_frame_data(Http2Enums::SourceId source_id) { return frame_data[source_id]; }
+    void set_frame_data(uint8_t* value, Http2Enums::SourceId source_id)
+        { frame_data[source_id] = value; }
+    uint32_t get_frame_data_size(Http2Enums::SourceId source_id)
+        { return frame_data_size[source_id]; }
+    void set_frame_data_size(uint32_t value, Http2Enums::SourceId source_id)
+        { frame_data_size[source_id] = value; }
+    uint32_t get_leftover_data(Http2Enums::SourceId source_id) { return leftover_data[source_id]; }
+    void set_leftover_data(uint32_t value, Http2Enums::SourceId source_id)
+        { leftover_data[source_id] = value; }
+};
+
+#endif
+
diff --git a/src/service_inspectors/http2_inspect/test/http2_inspect_impl_test.cc b/src/service_inspectors/http2_inspect/test/http2_inspect_impl_test.cc
new file mode 100644 (file)
index 0000000..e94c9ab
--- /dev/null
@@ -0,0 +1,100 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2018-2018 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// http2_inspect_test.cc author Tom Peters <thopeter@cisco.com>
+// unit test main
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "service_inspectors/http2_inspect/http2_stream_splitter.h"
+
+#include "protocols/packet.h"
+#include "service_inspectors/http2_inspect/http2_enum.h"
+
+#include "http2_flow_data_test.h"
+
+#include <CppUTest/CommandLineTestRunner.h>
+#include <CppUTest/TestHarness.h>
+#include <CppUTestExt/MockSupport.h>
+
+using namespace Http2Enums;
+
+TEST_GROUP(http2_get_buf_test)
+{
+    Http2FlowDataTest* session_data = nullptr;
+    InspectionBuffer b;
+
+    void setup() override
+    {
+        session_data = new Http2FlowDataTest();
+        CHECK(session_data != nullptr);
+    }
+
+    void teardown() override
+    {
+        delete session_data;
+    }
+};
+
+TEST(http2_get_buf_test, frame_header)
+{
+    uint8_t* head_buf = new uint8_t[9];
+    memcpy(head_buf, "\x01\x02\x03\x04\x05\x06\x07\x08\x09", 9);
+    session_data->set_frame_header(head_buf, SRC_CLIENT);
+    const bool result = implement_get_buf(HTTP2_BUFFER_FRAME_HEADER, session_data, SRC_CLIENT, b);
+    CHECK(result == true);
+    CHECK(b.len == 9);
+    CHECK(memcmp(b.data, "\x01\x02\x03\x04\x05\x06\x07\x08\x09", 9) == 0);
+}
+
+TEST(http2_get_buf_test, frame_header_absent)
+{
+    const bool result = implement_get_buf(HTTP2_BUFFER_FRAME_HEADER, session_data, SRC_SERVER, b);
+    CHECK(result == false);
+    CHECK(b.len == 0);
+    CHECK(b.data == nullptr);
+}
+
+TEST(http2_get_buf_test, frame_data)
+{
+    uint8_t* data_buf = new uint8_t[26];
+    memcpy(data_buf, "zyxwvutsrqponmlkjihgfedcba", 26);
+    session_data->set_frame_data(data_buf, SRC_SERVER);
+    session_data->set_frame_data_size(26, SRC_SERVER);
+    const bool result = implement_get_buf(HTTP2_BUFFER_FRAME_DATA, session_data, SRC_SERVER, b);
+    CHECK(result == true);
+    CHECK(b.len == 26);
+    CHECK(memcmp(b.data, "zyxwvutsrqponmlkjihgfedcba", 26) == 0);
+    delete[] data_buf;
+}
+
+TEST(http2_get_buf_test, frame_data_absent)
+{
+    const bool result = implement_get_buf(HTTP2_BUFFER_FRAME_DATA, session_data, SRC_CLIENT, b);
+    CHECK(result == false);
+    CHECK(b.len == 0);
+    CHECK(b.data == nullptr);
+}
+
+int main(int argc, char** argv)
+{
+    return CommandLineTestRunner::RunAllTests(argc, argv);
+}
+
diff --git a/src/service_inspectors/http2_inspect/test/http2_stream_splitter_impl_test.cc b/src/service_inspectors/http2_inspect/test/http2_stream_splitter_impl_test.cc
new file mode 100644 (file)
index 0000000..ceec7e4
--- /dev/null
@@ -0,0 +1,278 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2018-2018 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// http2_stream_splitter_test.cc author Tom Peters <thopeter@cisco.com>
+// unit test main
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "service_inspectors/http2_inspect/http2_stream_splitter.h"
+
+#include "protocols/packet.h"
+#include "service_inspectors/http2_inspect/http2_enum.h"
+
+#include "http2_flow_data_test.h"
+
+#include <CppUTest/CommandLineTestRunner.h>
+#include <CppUTest/TestHarness.h>
+#include <CppUTestExt/MockSupport.h>
+
+using namespace Http2Enums;
+
+TEST_GROUP(http2_scan_test)
+{
+    Http2FlowDataTest* session_data = nullptr;
+    uint32_t flush_offset = 0;
+
+    void setup() override
+    {
+        session_data = new Http2FlowDataTest();
+        CHECK(session_data != nullptr);
+    }
+
+    void teardown() override
+    {
+        delete session_data;
+    }
+};
+
+TEST(http2_scan_test, basic_with_header)
+{
+    session_data->set_preface(false, SRC_CLIENT);
+    const StreamSplitter::Status result = implement_scan(session_data,
+        (const uint8_t*)"\x00\x00\x0A\x02\x00\x00\x00\x00\x00" "0123456789ABCDEFG",
+        26, &flush_offset, SRC_CLIENT);
+    CHECK(result == StreamSplitter::FLUSH);
+    CHECK(flush_offset == 19);
+    CHECK(session_data->get_header_coming(SRC_CLIENT));
+}
+
+TEST(http2_scan_test, basic_with_header_s2c)
+{
+    const StreamSplitter::Status result = implement_scan(session_data,
+        (const uint8_t*)"\x00\x00\x0A\x02\x00\x00\x00\x00\x00" "0123456789ABCDEFG",
+        26, &flush_offset, SRC_SERVER);
+    CHECK(result == StreamSplitter::FLUSH);
+    CHECK(flush_offset == 19);
+    CHECK(session_data->get_header_coming(SRC_SERVER));
+}
+
+TEST(http2_scan_test, header_without_data)
+{
+    session_data->set_preface(false, SRC_CLIENT);
+    const StreamSplitter::Status result = implement_scan(session_data,
+        (const uint8_t*)"\x00\x00\x0A\x02\x00\x00\x00\x00\x00",
+        9, &flush_offset, SRC_CLIENT);
+    CHECK(result == StreamSplitter::FLUSH);
+    CHECK(flush_offset == 19);
+    CHECK(session_data->get_header_coming(SRC_CLIENT));
+}
+
+TEST(http2_scan_test, preface_and_more)
+{
+    const StreamSplitter::Status result = implement_scan(session_data,
+        (const uint8_t*)"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\nABCDEFGHIJKLMNOPQRSTUVWXYZ",
+        50, &flush_offset, SRC_CLIENT);
+    CHECK(result == StreamSplitter::FLUSH);
+    CHECK(flush_offset == 24);
+    CHECK(!session_data->get_header_coming(SRC_CLIENT));
+}
+
+TEST(http2_scan_test, preface_exactly)
+{
+    const StreamSplitter::Status result = implement_scan(session_data,
+        (const uint8_t*)"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n",
+        24, &flush_offset, SRC_CLIENT);
+    CHECK(result == StreamSplitter::FLUSH);
+    CHECK(flush_offset == 24);
+    CHECK(!session_data->get_header_coming(SRC_CLIENT));
+}
+
+TEST(http2_scan_test, short_input)
+{
+    session_data->set_preface(false, SRC_SERVER);
+    StreamSplitter::Status result = implement_scan(session_data,
+        (const uint8_t*)"\x00\x00\x10",
+        3, &flush_offset, SRC_SERVER);
+    CHECK(result == StreamSplitter::SEARCH);
+    result = implement_scan(session_data, (const uint8_t*)"\x04\x05\x06", 3, &flush_offset,
+        SRC_SERVER);
+    CHECK(result == StreamSplitter::SEARCH);
+    result = implement_scan(session_data, (const uint8_t*)"\x07\x08\x09YZ", 5, &flush_offset,
+        SRC_SERVER);
+    CHECK(result == StreamSplitter::FLUSH);
+    CHECK(flush_offset == 25);
+    CHECK(session_data->get_header_coming(SRC_SERVER));
+    CHECK(memcmp(session_data->get_frame_header(SRC_SERVER),
+        "\x00\x00\x10\x04\x05\x06\x07\x08\x09", 9) == 0);
+}
+
+TEST(http2_scan_test, oversize_non_data_frame)
+{
+    session_data->set_preface(false, SRC_CLIENT);
+    const StreamSplitter::Status result = implement_scan(session_data,
+        (const uint8_t*)"\x00\xF9\x1C\x01" "12345678901234567",
+        21, &flush_offset, SRC_SERVER);
+    CHECK(result == StreamSplitter::ABORT);
+}
+
+TEST(http2_scan_test, maximum_frame)
+{
+    const StreamSplitter::Status result = implement_scan(session_data,
+        (const uint8_t*)"\x00\xF9\x1B" "12345678901234567",
+        20, &flush_offset, SRC_SERVER);
+    CHECK(result == StreamSplitter::FLUSH);
+    CHECK(flush_offset == 63780);
+    CHECK(session_data->get_header_coming(SRC_SERVER));
+}
+
+TEST(http2_scan_test, data_sections)
+{
+    StreamSplitter::Status result = implement_scan(session_data,
+        (const uint8_t*)"\x01\x21\x3C\x00\x00\x00\x00\x00\x00" "abcdefghij",
+        19, &flush_offset, SRC_SERVER);
+    CHECK(result == StreamSplitter::FLUSH);
+    CHECK(flush_offset == DATA_SECTION_SIZE + 9);
+    CHECK(session_data->get_header_coming(SRC_SERVER));
+    CHECK(session_data->get_leftover_data(SRC_SERVER) == 0xE13C);
+    result = implement_scan(session_data,
+        (const uint8_t*)"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstu"
+         "vwxyz+-",
+        80, &flush_offset, SRC_SERVER);
+    CHECK(result == StreamSplitter::FLUSH);
+    CHECK(flush_offset == DATA_SECTION_SIZE);
+    CHECK(!session_data->get_header_coming(SRC_SERVER));
+    result = implement_scan(session_data,
+        (const uint8_t*)"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstu"
+         "vwxyz+-=",
+        81, &flush_offset, SRC_SERVER);
+    CHECK(result == StreamSplitter::FLUSH);
+    CHECK(flush_offset == DATA_SECTION_SIZE);
+    CHECK(!session_data->get_header_coming(SRC_SERVER));
+    result = implement_scan(session_data,
+        (const uint8_t*)"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstu"
+         "vwxyz+-=*",
+        82, &flush_offset, SRC_SERVER);
+    CHECK(result == StreamSplitter::FLUSH);
+    CHECK(flush_offset == DATA_SECTION_SIZE);
+    CHECK(!session_data->get_header_coming(SRC_SERVER));
+    result = implement_scan(session_data,
+        (const uint8_t*)"!",
+        1, &flush_offset, SRC_SERVER);
+    CHECK(result == StreamSplitter::FLUSH);
+    CHECK(flush_offset == 0x213C);
+    CHECK(!session_data->get_header_coming(SRC_SERVER));
+}
+
+TEST_GROUP(http2_reassemble_test)
+{
+    Http2FlowDataTest* session_data = nullptr;
+    unsigned copied = 0;
+
+    void setup() override
+    {
+        session_data = new Http2FlowDataTest();
+        CHECK(session_data != nullptr);
+    }
+
+    void teardown() override
+    {
+        delete session_data;
+    }
+};
+
+TEST(http2_reassemble_test, basic_with_header)
+{
+    session_data->set_header_coming(true, SRC_CLIENT);
+    const StreamBuffer buffer = implement_reassemble(session_data, 19, 0,
+        (const uint8_t*)"\x00\x00\x0A\x02\x00\x00\x00\x00\x00" "0123456789",
+        19, PKT_PDU_TAIL, copied, SRC_CLIENT);
+    CHECK(copied == 19);
+    CHECK(buffer.length == 10);
+    CHECK(memcmp(buffer.data, "0123456789", 10) == 0);
+}
+
+TEST(http2_reassemble_test, basic_with_header_s2c)
+{
+    session_data->set_header_coming(true, SRC_SERVER);
+    const StreamBuffer buffer = implement_reassemble(session_data, 19, 0,
+        (const uint8_t*)"\x00\x00\x0A\x02\x00\x00\x00\x00\x00" "0123456789",
+        19, PKT_PDU_TAIL, copied, SRC_SERVER);
+    CHECK(copied == 19);
+    CHECK(buffer.length == 10);
+    CHECK(memcmp(buffer.data, "0123456789", 10) == 0);
+}
+
+TEST(http2_reassemble_test, basic_without_header)
+{
+    session_data->set_header_coming(false, SRC_CLIENT);
+    const StreamBuffer buffer = implement_reassemble(session_data, 24, 0,
+        (const uint8_t*)"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n",
+        24, PKT_PDU_TAIL, copied, SRC_CLIENT);
+    CHECK(copied == 24);
+    CHECK(buffer.length == 24);
+    CHECK(memcmp(buffer.data, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", 24) == 0);
+}
+
+TEST(http2_reassemble_test, basic_three_pieces)
+{
+    session_data->set_header_coming(true, SRC_CLIENT);
+    StreamBuffer buffer = implement_reassemble(session_data, 19, 0,
+        (const uint8_t*)"\x00\x00\x0A\x02\x00\x00",
+        6, 0, copied, SRC_CLIENT);
+    CHECK(copied == 6);
+    CHECK(buffer.length == 0);
+    CHECK(buffer.data == nullptr);
+    buffer = implement_reassemble(session_data, 19, 6,
+        (const uint8_t*)"\x00\x00\x00" "01234",
+        8, 0, copied, SRC_CLIENT);
+    CHECK(copied == 8);
+    CHECK(buffer.length == 0);
+    CHECK(buffer.data == nullptr);
+    buffer = implement_reassemble(session_data, 19, 14,
+        (const uint8_t*)"56789",
+        5, PKT_PDU_TAIL, copied, SRC_CLIENT);
+    CHECK(copied == 5);
+    CHECK(buffer.length == 10);
+    CHECK(memcmp(buffer.data, "0123456789", 10) == 0);
+}
+
+TEST(http2_reassemble_test, basic_without_header_two_pieces)
+{
+    session_data->set_header_coming(false, SRC_CLIENT);
+    StreamBuffer buffer = implement_reassemble(session_data, 24, 0,
+        (const uint8_t*)"P",
+        1, 0, copied, SRC_CLIENT);
+    CHECK(copied == 1);
+    CHECK(buffer.length == 0);
+    CHECK(buffer.data == nullptr);
+    buffer = implement_reassemble(session_data, 24, 1,
+        (const uint8_t*)"RI * HTTP/2.0\r\n\r\nSM\r\n\r\n",
+        23, PKT_PDU_TAIL, copied, SRC_CLIENT);
+    CHECK(copied == 23);
+    CHECK(buffer.length == 24);
+    CHECK(memcmp(buffer.data, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", 24) == 0);
+}
+
+int main(int argc, char** argv)
+{
+    return CommandLineTestRunner::RunAllTests(argc, argv);
+}
+
index a02013a5f5c13833b6791e2e3bc28d4d967f02fa..da496c42df092c84b94834f7082d26099e202c95 100644 (file)
@@ -31,6 +31,7 @@ extern const BaseApi* sin_smtp;
 
 extern const BaseApi* sin_file[];
 extern const BaseApi* sin_http[];
+extern const BaseApi* sin_http2[];
 extern const BaseApi* sin_sip[];
 extern const BaseApi* sin_ssl[];
 
@@ -78,6 +79,7 @@ void load_service_inspectors()
 
     PluginManager::load_plugins(sin_file);
     PluginManager::load_plugins(sin_http);
+    PluginManager::load_plugins(sin_http2);
     PluginManager::load_plugins(sin_sip);
     PluginManager::load_plugins(sin_ssl);