From: Tom Peters (thopeter) Date: Mon, 5 Mar 2018 16:19:39 +0000 (-0500) Subject: Merge pull request #1120 in SNORT/snort3 from http2 to master X-Git-Tag: 3.0.0-244~15 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=fd0a9b7c38d119ecdc7d647f48d5a114ac48ca4a;p=thirdparty%2Fsnort3.git Merge pull request #1120 in SNORT/snort3 from http2 to master Squashed commit of the following: commit a832c050c010f1f2ab728751a4779cc231c72137 Author: Tom Peters Date: Thu Jan 25 11:46:22 2018 -0500 http2_inspect: initial prototype --- diff --git a/configure.ac b/configure.ac index 34ce1723e..234c2da74 100644 --- a/configure.ac +++ b/configure.ac @@ -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 \ diff --git a/lua/snort.lua b/lua/snort.lua index 2e245dffd..2c4af5368 100644 --- a/lua/snort.lua +++ b/lua/snort.lua @@ -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' } }, diff --git a/lua/snort_defaults.lua b/lua/snort_defaults.lua index 3115077e8..40554cbc2 100644 --- a/lua/snort_defaults.lua +++ b/lua/snort_defaults.lua @@ -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|' } }, diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9d55d6554..6e9942f1a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -89,6 +89,7 @@ target_link_libraries( snort # include only those with otherwise unresolved dependencies appid http_inspect + http2_inspect sip reputation stream diff --git a/src/Makefile.am b/src/Makefile.am index e6cd839ec..548e34d3e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/service_inspectors/CMakeLists.txt b/src/service_inspectors/CMakeLists.txt index 6f68dc639..c86efe1b3 100644 --- a/src/service_inspectors/CMakeLists.txt +++ b/src/service_inspectors/CMakeLists.txt @@ -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) diff --git a/src/service_inspectors/Makefile.am b/src/service_inspectors/Makefile.am index dec03c226..5262912e3 100644 --- a/src/service_inspectors/Makefile.am +++ b/src/service_inspectors/Makefile.am @@ -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 index 000000000..593ce3df6 --- /dev/null +++ b/src/service_inspectors/http2_inspect/CMakeLists.txt @@ -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 index 000000000..982b0ed73 --- /dev/null +++ b/src/service_inspectors/http2_inspect/Makefile.am @@ -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 index 000000000..26c649e90 --- /dev/null +++ b/src/service_inspectors/http2_inspect/dev_notes.txt @@ -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 index 000000000..d7c0b1021 --- /dev/null +++ b/src/service_inspectors/http2_inspect/http2_api.cc @@ -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 + +#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 index 000000000..8f30378a8 --- /dev/null +++ b/src/service_inspectors/http2_inspect/http2_api.h @@ -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 + +#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 index 000000000..6cbac02c5 --- /dev/null +++ b/src/service_inspectors/http2_inspect/http2_enum.h @@ -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 + +#ifndef HTTP2_ENUM_H +#define HTTP2_ENUM_H + +#include + +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 index 000000000..72dc45273 --- /dev/null +++ b/src/service_inspectors/http2_inspect/http2_flow_data.cc @@ -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 + +#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 index 000000000..9e6f1fd15 --- /dev/null +++ b/src/service_inspectors/http2_inspect/http2_flow_data.h @@ -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 + +#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 index 000000000..b7a4a078d --- /dev/null +++ b/src/service_inspectors/http2_inspect/http2_inspect.cc @@ -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 + +#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 index 000000000..ff94aeec9 --- /dev/null +++ b/src/service_inspectors/http2_inspect/http2_inspect.h @@ -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 + +#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 index 000000000..9a7ab2a56 --- /dev/null +++ b/src/service_inspectors/http2_inspect/http2_inspect_impl.cc @@ -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 + +#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 index 000000000..5a79c3e18 --- /dev/null +++ b/src/service_inspectors/http2_inspect/http2_module.cc @@ -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 + +#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 index 000000000..72ef06577 --- /dev/null +++ b/src/service_inspectors/http2_inspect/http2_module.h @@ -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 + +#ifndef HTTP2_MODULE_H +#define HTTP2_MODULE_H + +#include +#include + +#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 index 000000000..12a180c03 --- /dev/null +++ b/src/service_inspectors/http2_inspect/http2_stream_splitter.cc @@ -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 + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#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 index 000000000..5d9278be0 --- /dev/null +++ b/src/service_inspectors/http2_inspect/http2_stream_splitter.h @@ -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 + +#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 index 000000000..cb455a61e --- /dev/null +++ b/src/service_inspectors/http2_inspect/http2_stream_splitter_impl.cc @@ -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 + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#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 index 000000000..3705c4504 --- /dev/null +++ b/src/service_inspectors/http2_inspect/http2_tables.cc @@ -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 + +#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 index 000000000..b2d9ac327 --- /dev/null +++ b/src/service_inspectors/http2_inspect/ips_http2.cc @@ -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 + +#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 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 index 000000000..c10fbaa99 --- /dev/null +++ b/src/service_inspectors/http2_inspect/ips_http2.h @@ -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 + +#ifndef IPS_HTTP2_H +#define IPS_HTTP2_H + +#include + +#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 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 index 000000000..724be9b71 --- /dev/null +++ b/src/service_inspectors/http2_inspect/test/CMakeLists.txt @@ -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 index 000000000..f4878d3f0 --- /dev/null +++ b/src/service_inspectors/http2_inspect/test/http2_flow_data_test.h @@ -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 + +#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 index 000000000..e94c9aba1 --- /dev/null +++ b/src/service_inspectors/http2_inspect/test/http2_inspect_impl_test.cc @@ -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 +// 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 +#include +#include + +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 index 000000000..ceec7e467 --- /dev/null +++ b/src/service_inspectors/http2_inspect/test/http2_stream_splitter_impl_test.cc @@ -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 +// 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 +#include +#include + +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); +} + diff --git a/src/service_inspectors/service_inspectors.cc b/src/service_inspectors/service_inspectors.cc index a02013a5f..da496c42d 100644 --- a/src/service_inspectors/service_inspectors.cc +++ b/src/service_inspectors/service_inspectors.cc @@ -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);