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 \
dnp3 = { }
dns = { }
http_inspect = { }
+http2_inspect = { }
imap = { }
modbus = { }
normalizer = { }
{ 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' } },
{ 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|' } },
# include only those with otherwise unresolved dependencies
appid
http_inspect
+ http2_inspect
sip
reputation
stream
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 \
add_subdirectory(imap)
add_subdirectory(modbus)
add_subdirectory(http_inspect)
+add_subdirectory(http2_inspect)
add_subdirectory(pop)
add_subdirectory(rpc_decode)
add_subdirectory(sip)
#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
imap \
modbus \
http_inspect \
+http2_inspect \
pop \
rpc_decode \
sip \
--- /dev/null
+
+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 )
+
--- /dev/null
+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)
+
--- /dev/null
+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.
--- /dev/null
+//--------------------------------------------------------------------------
+// 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
+};
+
--- /dev/null
+//--------------------------------------------------------------------------
+// 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
+
--- /dev/null
+//--------------------------------------------------------------------------
+// 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
+
--- /dev/null
+//--------------------------------------------------------------------------
+// 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];
+ }
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// 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
+
--- /dev/null
+//--------------------------------------------------------------------------
+// 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;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// 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
+
--- /dev/null
+//--------------------------------------------------------------------------
+// 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;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// 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;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// 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
+
--- /dev/null
+//--------------------------------------------------------------------------
+// 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; }
+
--- /dev/null
+//--------------------------------------------------------------------------
+// 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
+
--- /dev/null
+//--------------------------------------------------------------------------
+// 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;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// 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 }
+};
+
--- /dev/null
+//--------------------------------------------------------------------------
+// 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;
+
--- /dev/null
+//--------------------------------------------------------------------------
+// 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
+
--- /dev/null
+add_cpputest(http2_inspect_impl_test http2_inspect framework)
+add_cpputest(http2_stream_splitter_impl_test http2_inspect framework)
+
--- /dev/null
+//--------------------------------------------------------------------------
+// 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
+
--- /dev/null
+//--------------------------------------------------------------------------
+// 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);
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// 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);
+}
+
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[];
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);