From: Mike Stepanek (mstepane) Date: Wed, 20 Nov 2019 13:46:44 +0000 (+0000) Subject: Merge pull request #1849 in SNORT/snort3 from ~KATHARVE/snort3:h2i_refactor to master X-Git-Tag: 3.0.0-265~5 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d1baa7fa5d3409c4041dcd5f12462d45ae23f74a;p=thirdparty%2Fsnort3.git Merge pull request #1849 in SNORT/snort3 from ~KATHARVE/snort3:h2i_refactor to master Squashed commit of the following: commit df563843b3c042052d395ee46e72536545875718 Author: Katura Harvey Date: Mon Nov 18 10:40:09 2019 -0500 http2_inspect: add frame class and refactor stream splitter --- diff --git a/src/service_inspectors/http2_inspect/CMakeLists.txt b/src/service_inspectors/http2_inspect/CMakeLists.txt index f115997ed..53ff980a8 100644 --- a/src/service_inspectors/http2_inspect/CMakeLists.txt +++ b/src/service_inspectors/http2_inspect/CMakeLists.txt @@ -5,6 +5,10 @@ set (FILE_LIST http2_enum.h http2_flow_data.cc http2_flow_data.h + http2_frame.cc + http2_frame.h + http2_headers_frame.cc + http2_headers_frame.h http2_hpack.cc http2_hpack.h http2_hpack_int_decode.cc diff --git a/src/service_inspectors/http2_inspect/http2_flow_data.cc b/src/service_inspectors/http2_inspect/http2_flow_data.cc index a7a611088..66be2c2d9 100644 --- a/src/service_inspectors/http2_inspect/http2_flow_data.cc +++ b/src/service_inspectors/http2_inspect/http2_flow_data.cc @@ -26,6 +26,7 @@ #include "service_inspectors/http_inspect/http_test_manager.h" #include "http2_enum.h" +#include "http2_frame.h" #include "http2_module.h" #include "http2_start_line.h" @@ -71,33 +72,19 @@ Http2FlowData::~Http2FlowData() for (int k=0; k <= 1; k++) { - delete[] frame_header[k]; - delete[] frame_data[k]; - delete[] raw_decoded_header[k]; delete infractions[k]; delete events[k]; - delete http2_decoded_header[k]; + delete current_frame[k]; } } void Http2FlowData::clear_frame_data(HttpCommon::SourceId source_id) { - // If there is more data to be inspected in the frame, leave the frame_header - if (leftover_data[source_id] == 0) - { - delete[] frame_header[source_id]; - frame_header[source_id] = nullptr; - } - delete[] frame_data[source_id]; - frame_data[source_id] = nullptr; frame_in_detection = false; - delete[] raw_decoded_header[source_id]; - raw_decoded_header[source_id] = nullptr; continuation_expected[source_id] = false; - frames_aggregated[source_id] = 0; - scan_header_octets_seen[source_id] = 0; - delete header_start_line[source_id]; - header_start_line[source_id] = nullptr; - delete http2_decoded_header[source_id]; - http2_decoded_header[source_id] = nullptr; + num_frame_headers[source_id] = 0; + scan_octets_seen[source_id] = 0; + total_bytes_in_split[source_id] = 0; + delete current_frame[source_id]; + current_frame[source_id] = nullptr; } diff --git a/src/service_inspectors/http2_inspect/http2_flow_data.h b/src/service_inspectors/http2_inspect/http2_flow_data.h index 9cfa258a8..6d324c471 100644 --- a/src/service_inspectors/http2_inspect/http2_flow_data.h +++ b/src/service_inspectors/http2_inspect/http2_flow_data.h @@ -53,53 +53,56 @@ public: friend class Http2Hpack; friend class Http2StartLine; friend class Http2RequestLine; + friend class Http2Frame; + friend class Http2HeadersFrame; friend const snort::StreamBuffer implement_reassemble(Http2FlowData*, unsigned, unsigned, const uint8_t*, unsigned, uint32_t, HttpCommon::SourceId); friend snort::StreamSplitter::Status implement_scan(Http2FlowData*, const uint8_t*, uint32_t, uint32_t*, HttpCommon::SourceId); friend bool implement_get_buf(unsigned id, Http2FlowData*, HttpCommon::SourceId, snort::InspectionBuffer&); + friend void implement_eval(Http2FlowData* session_data, HttpCommon::SourceId source_id); size_t size_of() override { return sizeof(*this); } protected: // 0 element refers to client frame, 1 element refers to server frame - bool preface[2] = { true, false }; + + // Reassemble() signals to eval() uint8_t* frame_header[2] = { nullptr, nullptr }; uint32_t frame_header_size[2] = { 0, 0 }; uint8_t* frame_data[2] = { nullptr, nullptr }; uint32_t frame_data_size[2] = { 0, 0 }; - uint8_t* raw_decoded_header[2] = { nullptr, nullptr }; - uint32_t raw_decoded_header_size[2] = { 0, 0 }; - uint32_t pseudo_header_fragment_size[2] = { 0, 0 }; - Field* http2_decoded_header[2] = { nullptr, nullptr }; + + // Used in eval() bool frame_in_detection = false; + class Http2Frame* current_frame[2] = { nullptr, nullptr }; + Http2HpackDecoder hpack_decoder[2]; // Internal to scan() + bool preface[2] = { true, false }; bool continuation_expected[2] = { false, false }; - uint8_t currently_processing_frame_header[2][Http2Enums::FRAME_HEADER_LENGTH]; - uint32_t inspection_section_length[2] = { 0, 0 }; + uint8_t scan_frame_header[2][Http2Enums::FRAME_HEADER_LENGTH]; + uint32_t scan_remaining_frame_octets[2] = { 0, 0 }; + uint32_t scan_octets_seen[2] = { 0, 0 }; uint32_t leftover_data[2] = { 0, 0 }; - uint32_t octets_seen[2] = { 0, 0 }; - uint8_t scan_header_octets_seen[2] = { 0, 0 }; // Scan signals to reassemble() - bool header_coming[2] = { false, false }; bool payload_discard[2] = { false, false }; - uint32_t frames_aggregated[2] = { 0, 0 }; + uint32_t num_frame_headers[2] = { 0, 0 }; + uint32_t total_bytes_in_split[2] = { 0, 0 }; + uint32_t octets_before_first_header[2] = { 0, 0 }; // Used by scan, reassemble and eval to communicate uint8_t frame_type[2] = { Http2Enums::FT__NONE, Http2Enums::FT__NONE }; // Internal to reassemble() - Http2Hpack hpack[2]; - class Http2StartLine* header_start_line[2] = { nullptr, nullptr }; - uint32_t remaining_octets_to_next_header[2] = { 0, 0 }; - uint32_t remaining_frame_data_octets[2] = { 0, 0 }; - uint32_t remaining_frame_data_offset[2] = { 0, 0 }; uint32_t frame_header_offset[2] = { 0, 0 }; - uint8_t reassemble_header_octets_seen[2] = { 0, 0 }; + uint32_t frame_data_offset[2] = { 0, 0 }; + uint32_t remaining_frame_octets[2] = { 0, 0 }; + uint8_t padding_octets_in_frame[2] = { 0, 0 }; + bool get_padding_len[2] = { false, false }; // These will eventually be moved over to the frame/stream object, as they are moved to the // transaction in NHI. Also as in NHI accessor methods will need to be added. diff --git a/src/service_inspectors/http2_inspect/http2_frame.cc b/src/service_inspectors/http2_inspect/http2_frame.cc new file mode 100644 index 000000000..aeca87988 --- /dev/null +++ b/src/service_inspectors/http2_inspect/http2_frame.cc @@ -0,0 +1,92 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2019-2019 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_frame.cc author Katura Harvey + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "http2_frame.h" + +#include "detection/detection_engine.h" +#include "http2_enum.h" +#include "http2_flow_data.h" +#include "http2_headers_frame.h" +#include "service_inspectors/http_inspect/http_field.h" + +using namespace HttpCommon; +using namespace Http2Enums; +using namespace snort; + +Http2Frame::Http2Frame(const uint8_t* header_buffer, const int32_t header_len, + const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* session_data, + SourceId source_id) : session_data(session_data), source_id(source_id) +{ + if (header_len > 0) + header.set(header_len, header_buffer, true); + if (data_len > 0) + { + data.set(data_len, data_buffer, true); + set_file_data(data.start(), data.length()); + } +} + +Http2Frame* Http2Frame::new_frame(const uint8_t* header, const int32_t header_len, + const uint8_t* data, const int32_t data_len, Http2FlowData* session_data, SourceId source_id) +{ + //FIXIT-H call the appropriate frame subclass constructor based on the type + switch(session_data->frame_type[source_id]) + { + case FT_HEADERS: + return new Http2HeadersFrame(header, header_len, data, data_len, session_data, + source_id); + break; + default: + return new Http2Frame(header, header_len, data, data_len, session_data, source_id); + break; + } +} + +const Field& Http2Frame::get_buf(unsigned id) +{ + switch (id) + { + case HTTP2_BUFFER_FRAME_HEADER: + return header; + case HTTP2_BUFFER_FRAME_DATA: + return data; + default: + return Field::FIELD_NULL; + } +} + +uint8_t Http2Frame::get_flags() +{ + if (header.length() > 0) + return header.start()[flags_index]; + else + return 0; +} + +#ifdef REG_TEST +void Http2Frame::print_frame(FILE* output) +{ + header.print(output, "Frame Header"); + data.print(output, "Frame Data"); +} +#endif diff --git a/src/service_inspectors/http2_inspect/http2_frame.h b/src/service_inspectors/http2_inspect/http2_frame.h new file mode 100644 index 000000000..dc33efb16 --- /dev/null +++ b/src/service_inspectors/http2_inspect/http2_frame.h @@ -0,0 +1,62 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2019-2019 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_frame.h author Katura Harvey + +#ifndef HTTP2_FRAME_H +#define HTTP2_FRAME_H + +#include "service_inspectors/http_inspect/http_common.h" +#include "service_inspectors/http_inspect/http_field.h" + +/* This class is called Http2Frame, but an object of this class may not represent exactly one HTTP/2 + * frame as received on the wire. For HEADERS frames, the Http2Frame object contains the initial + * HEADERS frame plus any following CONTINUATION frames grouped together. For DATA frames, the + * Http2Frame object represents approximately 16kb of data to be inspected. This may consist of + * part of a larger DATA frame cut into 16kb-sized pieces, or several smaller DATA frames aggregated + * together. + */ + +class Http2FlowData; + +class Http2Frame +{ +public: + virtual ~Http2Frame() { } + static Http2Frame* new_frame(const uint8_t* header_buffer, const int32_t header_len, + const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* session_data, + HttpCommon::SourceId source_id); + + virtual const Field& get_buf(unsigned id); +#ifdef REG_TEST + virtual void print_frame(FILE* output); +#endif + +protected: + Http2Frame(const uint8_t* header_buffer, const int32_t header_len, + const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* session_data, + HttpCommon::SourceId source_id); + uint8_t get_flags(); + + Field header; + Field data; + Http2FlowData* session_data; + HttpCommon::SourceId source_id; + + const static uint8_t flags_index = 4; +}; +#endif diff --git a/src/service_inspectors/http2_inspect/http2_headers_frame.cc b/src/service_inspectors/http2_inspect/http2_headers_frame.cc new file mode 100644 index 000000000..9b22f4f12 --- /dev/null +++ b/src/service_inspectors/http2_inspect/http2_headers_frame.cc @@ -0,0 +1,102 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2019-2019 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_headers_frame.cc author Katura Harvey + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "http2_headers_frame.h" + +#include "http2_enum.h" +#include "http2_flow_data.h" +#include "http2_hpack.h" +#include "http2_request_line.h" +#include "http2_start_line.h" + +using namespace HttpCommon; +using namespace Http2Enums; + +Http2HeadersFrame::Http2HeadersFrame(const uint8_t* header_buffer, const int32_t header_len, + const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* session_data, + HttpCommon::SourceId source_id) : Http2Frame(header_buffer, header_len, data_buffer, data_len, + session_data, source_id) +{ + uint8_t hpack_headers_offset = 0; + + // Remove stream dependency if present + if (get_flags() & PRIORITY) + hpack_headers_offset = 5; + + // Set up the decoding context + hpack_decoder = &session_data->hpack_decoder[source_id]; + + // Allocate stuff + decoded_headers = new uint8_t[MAX_OCTETS]; + decoded_headers_size = 0; + + // FIXIT-H Implement response start line + if (source_id == SRC_CLIENT) + start_line_generator = new Http2RequestLine(session_data->events[source_id], + session_data->infractions[source_id]); + + // Decode headers + if (!hpack_decoder->decode_headers((data.start() + hpack_headers_offset), data.length() - + hpack_headers_offset, decoded_headers, &decoded_headers_size, start_line_generator, + session_data->events[source_id], session_data->infractions[source_id])) + { + session_data->frame_type[source_id] = FT__ABORT; + error_during_decode = true; + } + //FIXIT-H remove condition once status lines implemented + if (source_id == SRC_CLIENT) + start_line = hpack_decoder->get_start_line(); + http2_decoded_header = hpack_decoder->get_decoded_headers(decoded_headers); +} + +Http2HeadersFrame::~Http2HeadersFrame() +{ + delete start_line; + delete start_line_generator; + delete http2_decoded_header; + delete[] decoded_headers; +} + +const Field& Http2HeadersFrame::get_buf(unsigned id) +{ + switch (id) + { + case HTTP2_BUFFER_DECODED_HEADER: + return *http2_decoded_header; + default: + return Http2Frame::get_buf(id); + } +} + +#ifdef REG_TEST +void Http2HeadersFrame::print_frame(FILE* output) +{ + fprintf(output, "\nHEADERS frame\n"); + if (error_during_decode) + fprintf(output, "Error decoding headers.\n"); + if (start_line) + start_line->print(output, "Decoded start-line"); + http2_decoded_header->print(output, "Decoded header"); + Http2Frame::print_frame(output); +} +#endif diff --git a/src/service_inspectors/http2_inspect/http2_headers_frame.h b/src/service_inspectors/http2_inspect/http2_headers_frame.h new file mode 100644 index 000000000..e9ca1110c --- /dev/null +++ b/src/service_inspectors/http2_inspect/http2_headers_frame.h @@ -0,0 +1,58 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2019-2019 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_headers_frame.h author Katura Harvey + +#ifndef HTTP2_HEADERS_FRAME_H +#define HTTP2_HEADERS_FRAME_H + +#include "http2_frame.h" + +class Field; +class Http2HpackDecoder; +class Http2StartLine; +class Http2Frame; + +class Http2HeadersFrame : public Http2Frame +{ +public: + ~Http2HeadersFrame() override; + + const Field& get_buf(unsigned id) override; + + friend Http2Frame* Http2Frame::new_frame(const uint8_t* header_buffer, const int32_t header_len, + const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* session_data, + HttpCommon::SourceId source_id); + +#ifdef REG_TEST + void print_frame(FILE* output) override; +#endif + +private: + Http2HeadersFrame(const uint8_t* header_buffer, const int32_t header_len, + const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* session_data, + HttpCommon::SourceId source_id); + + Http2StartLine* start_line_generator = nullptr; + uint8_t* decoded_headers = nullptr; // working buffer to store decoded headers + uint32_t decoded_headers_size = 0; + const Field* http2_decoded_header = nullptr; // finalized headers to be passed to NHI + const Field* start_line = nullptr; + bool error_during_decode = false; + Http2HpackDecoder* hpack_decoder= nullptr; +}; +#endif diff --git a/src/service_inspectors/http2_inspect/http2_hpack.cc b/src/service_inspectors/http2_inspect/http2_hpack.cc index 043abbae6..004b37a73 100644 --- a/src/service_inspectors/http2_inspect/http2_hpack.cc +++ b/src/service_inspectors/http2_inspect/http2_hpack.cc @@ -27,23 +27,20 @@ #include "service_inspectors/http_inspect/http_test_manager.h" #include "http2_enum.h" -#include "http2_flow_data.h" #include "http2_request_line.h" using namespace HttpCommon; using namespace Http2Enums; -Http2HpackIntDecode Http2Hpack::decode_int7(7); -Http2HpackIntDecode Http2Hpack::decode_int6(6); -Http2HpackIntDecode Http2Hpack::decode_int5(5); -Http2HpackIntDecode Http2Hpack::decode_int4(4); -Http2HpackStringDecode Http2Hpack::decode_string; -Http2HpackTable Http2Hpack::table; +Http2HpackIntDecode Http2HpackDecoder::decode_int7(7); +Http2HpackIntDecode Http2HpackDecoder::decode_int6(6); +Http2HpackIntDecode Http2HpackDecoder::decode_int5(5); +Http2HpackIntDecode Http2HpackDecoder::decode_int4(4); +Http2HpackStringDecode Http2HpackDecoder::decode_string; +Http2HpackTable Http2HpackDecoder::table; -bool Http2Hpack::write_decoded_headers(Http2FlowData* session_data, HttpCommon::SourceId source_id, - const uint8_t* in_buffer, const uint32_t in_length, - uint8_t* decoded_header_buffer, uint32_t decoded_header_length, - uint32_t &bytes_written) +bool Http2HpackDecoder::write_decoded_headers(const uint8_t* in_buffer, const uint32_t in_length, + uint8_t* decoded_header_buffer, uint32_t decoded_header_length, uint32_t &bytes_written) { bool ret = true; uint32_t length = in_length; @@ -52,8 +49,8 @@ bool Http2Hpack::write_decoded_headers(Http2FlowData* session_data, HttpCommon:: if (in_length > decoded_header_length) { length = decoded_header_length; - *session_data->infractions[source_id] += INF_DECODED_HEADER_BUFF_OUT_OF_SPACE; - session_data->events[source_id]->create_event(EVENT_MISFORMATTED_HTTP2); + *infractions += INF_DECODED_HEADER_BUFF_OUT_OF_SPACE; + events->create_event(EVENT_MISFORMATTED_HTTP2); ret = false; } @@ -62,9 +59,8 @@ bool Http2Hpack::write_decoded_headers(Http2FlowData* session_data, HttpCommon:: return ret; } -bool Http2Hpack::decode_string_literal(Http2FlowData* session_data, HttpCommon::SourceId source_id, - const uint8_t* encoded_header_buffer, const uint32_t encoded_header_length, - bool is_field_name, uint32_t &bytes_consumed, +bool Http2HpackDecoder::decode_string_literal(const uint8_t* encoded_header_buffer, + const uint32_t encoded_header_length, bool is_field_name, uint32_t &bytes_consumed, uint8_t* decoded_header_buffer, const uint32_t decoded_header_length, uint32_t &bytes_written) { @@ -84,7 +80,7 @@ bool Http2Hpack::decode_string_literal(Http2FlowData* session_data, HttpCommon:: if (!decode_string.translate(encoded_header_buffer + encoded_header_offset, encoded_header_length - encoded_header_offset, encoded_bytes_consumed, decoded_header_buffer, decoded_header_length, decoded_bytes_written, - session_data->events[source_id], session_data->infractions[source_id])) + events, infractions)) { return false; } @@ -94,14 +90,14 @@ bool Http2Hpack::decode_string_literal(Http2FlowData* session_data, HttpCommon:: if (is_field_name) { - if (!Http2Hpack::write_decoded_headers(session_data, source_id, (const uint8_t*)": ", 2, + if (!write_decoded_headers((const uint8_t*)": ", 2, decoded_header_buffer + bytes_written, decoded_header_length - bytes_written, decoded_bytes_written)) return false; } else { - if (!Http2Hpack::write_decoded_headers(session_data, source_id, (const uint8_t*)"\r\n", 2, + if (!write_decoded_headers((const uint8_t*)"\r\n", 2, decoded_header_buffer + bytes_written, decoded_header_length - bytes_written, decoded_bytes_written)) return false; @@ -112,10 +108,8 @@ bool Http2Hpack::decode_string_literal(Http2FlowData* session_data, HttpCommon:: return true; } -bool Http2Hpack::decode_static_table_index(Http2FlowData* session_data, - HttpCommon::SourceId source_id, const uint64_t index, const bool decode_full_line, - uint8_t* decoded_header_buffer, const uint32_t decoded_header_length, - uint32_t& bytes_written) +bool Http2HpackDecoder::decode_static_table_index(const uint64_t index, const bool decode_full_line, + uint8_t* decoded_header_buffer, const uint32_t decoded_header_length, uint32_t& bytes_written) { uint32_t local_bytes_written = 0; const Http2HpackTable::TableEntry* const entry = table.lookup(index); @@ -126,28 +120,27 @@ bool Http2Hpack::decode_static_table_index(Http2FlowData* session_data, // If this is a pseudo-header, pass it to the start line // Remove second condition after response start line implemented - if (index < PSEUDO_HEADER_MAX_INDEX and session_data->header_start_line[source_id]) + if (index < PSEUDO_HEADER_MAX_INDEX and start_line) { - if (!session_data->header_start_line[source_id]->process_pseudo_header_name(index)) + if (start_line and !start_line->process_pseudo_header_name(index)) return false; } // If this is a regular header, write header name + ': ' to decoded headers else { - if (session_data->header_start_line[source_id] and - !session_data->header_start_line[source_id]->is_finalized()) + if (start_line and !start_line->is_finalized()) { - if (!session_data->header_start_line[source_id]->finalize()) + if (!finalize_start_line()) return false; } - if (!write_decoded_headers(session_data, source_id, (const uint8_t*) entry->name, + if (!write_decoded_headers((const uint8_t*) entry->name, strlen(entry->name), decoded_header_buffer, decoded_header_length, local_bytes_written)) return false; bytes_written += local_bytes_written; - if (!write_decoded_headers(session_data, source_id, (const uint8_t*)": ", 2, + if (!write_decoded_headers((const uint8_t*)": ", 2, decoded_header_buffer + bytes_written, decoded_header_length - bytes_written, local_bytes_written)) @@ -159,25 +152,25 @@ bool Http2Hpack::decode_static_table_index(Http2FlowData* session_data, { if (strlen(entry->value) == 0) { - *session_data->infractions[source_id] += INF_LOOKUP_EMPTY_VALUE; - session_data->events[source_id]->create_event(EVENT_MISFORMATTED_HTTP2); + *infractions += INF_LOOKUP_EMPTY_VALUE; + events->create_event(EVENT_MISFORMATTED_HTTP2); return false; } // Remove second condition after response start line implemented - if (index < PSEUDO_HEADER_MAX_INDEX and session_data->header_start_line[source_id]) + if (index < PSEUDO_HEADER_MAX_INDEX and start_line) { - session_data->header_start_line[source_id]->process_pseudo_header_value( + start_line->process_pseudo_header_value( (const uint8_t*)entry->value, strlen(entry->value)); } else { - if (!write_decoded_headers(session_data, source_id, (const uint8_t*)entry->value, + if (!write_decoded_headers((const uint8_t*)entry->value, strlen(entry->value), decoded_header_buffer + bytes_written, decoded_header_length - bytes_written, local_bytes_written)) return false; bytes_written += local_bytes_written; - if (!write_decoded_headers(session_data, source_id, (const uint8_t*)"\r\n", 2, + if (!write_decoded_headers((const uint8_t*)"\r\n", 2, decoded_header_buffer + bytes_written, decoded_header_length - bytes_written, local_bytes_written)) return false; @@ -189,23 +182,21 @@ bool Http2Hpack::decode_static_table_index(Http2FlowData* session_data, } // FIXIT-H Implement dynamic table. Currently copies encoded index to decoded headers -bool Http2Hpack::decode_dynamic_table_index(Http2FlowData* session_data, - HttpCommon::SourceId source_id, const uint64_t index, const bool decode_full_line, - uint32_t &bytes_consumed, const uint8_t* encoded_header_buffer, +bool Http2HpackDecoder::decode_dynamic_table_index(const uint64_t index, + const bool decode_full_line, uint32_t &bytes_consumed, const uint8_t* encoded_header_buffer, uint8_t* decoded_header_buffer, const uint32_t decoded_header_length, uint32_t& bytes_written) { UNUSED(index); UNUSED(decode_full_line); //FIXIT-H finalize header_start_line only for regular headers - if (session_data->header_start_line[source_id] and - !session_data->header_start_line[source_id]->is_finalized()) + if (start_line and !start_line->is_finalized()) { - if (!session_data->header_start_line[source_id]->finalize()) + if (!finalize_start_line()) return false; } - if(!Http2Hpack::write_decoded_headers(session_data, source_id, encoded_header_buffer, + if(!write_decoded_headers(encoded_header_buffer, bytes_consumed, decoded_header_buffer + bytes_written, decoded_header_length, bytes_written)) return false; @@ -215,33 +206,31 @@ bool Http2Hpack::decode_dynamic_table_index(Http2FlowData* session_data, // FIXIT-H Will be incrementally updated to actually decode indexes. For now just copies encoded // index directly to decoded_header_buffer -bool Http2Hpack::decode_index(Http2FlowData* session_data, HttpCommon::SourceId source_id, - const uint8_t* encoded_header_buffer, const uint32_t encoded_header_length, - const Http2HpackIntDecode &decode_int, const bool decode_full_line, uint32_t &bytes_consumed, - uint8_t* decoded_header_buffer, const uint32_t decoded_header_length, uint32_t &bytes_written) +bool Http2HpackDecoder::decode_index(const uint8_t* encoded_header_buffer, + const uint32_t encoded_header_length, const Http2HpackIntDecode &decode_int, + const bool decode_full_line, uint32_t &bytes_consumed, uint8_t* decoded_header_buffer, + const uint32_t decoded_header_length, uint32_t &bytes_written) { uint64_t index; bytes_written = 0; bytes_consumed = 0; if (!decode_int.translate(encoded_header_buffer, encoded_header_length, - bytes_consumed, index, session_data->events[source_id], - session_data->infractions[source_id])) + bytes_consumed, index, events, infractions)) { return false; } if (index <= STATIC_TABLE_MAX_INDEX) - return decode_static_table_index(session_data, source_id, index, decode_full_line, + return decode_static_table_index(index, decode_full_line, decoded_header_buffer, decoded_header_length, bytes_written); else - return decode_dynamic_table_index(session_data, source_id, index, decode_full_line, + return decode_dynamic_table_index(index, decode_full_line, bytes_consumed, encoded_header_buffer, decoded_header_buffer, decoded_header_length, bytes_written); } -bool Http2Hpack::decode_literal_header_line(Http2FlowData* session_data, - HttpCommon::SourceId source_id, const uint8_t* encoded_header_buffer, +bool Http2HpackDecoder::decode_literal_header_line(const uint8_t* encoded_header_buffer, const uint32_t encoded_header_length, const uint8_t name_index_mask, const Http2HpackIntDecode &decode_int, uint32_t &bytes_consumed, uint8_t* decoded_header_buffer, const uint32_t decoded_header_length, uint32_t &bytes_written) @@ -254,7 +243,7 @@ bool Http2Hpack::decode_literal_header_line(Http2FlowData* session_data, // indexed field name if (encoded_header_buffer[0] & name_index_mask) { - if (!Http2Hpack::decode_index(session_data, source_id, encoded_header_buffer, + if (!Http2HpackDecoder::decode_index(encoded_header_buffer, encoded_header_length, decode_int, false, partial_bytes_consumed, decoded_header_buffer, decoded_header_length, partial_bytes_written)) { @@ -265,7 +254,7 @@ bool Http2Hpack::decode_literal_header_line(Http2FlowData* session_data, // literal field name else { - if (!Http2Hpack::decode_string_literal(session_data, source_id, encoded_header_buffer, + if (!Http2HpackDecoder::decode_string_literal(encoded_header_buffer, encoded_header_length, true, partial_bytes_consumed, decoded_header_buffer, decoded_header_length, partial_bytes_written)) @@ -274,22 +263,20 @@ bool Http2Hpack::decode_literal_header_line(Http2FlowData* session_data, return false; } // If this was a pseudo-header value, give it to the start-line. - if (session_data->header_start_line[source_id] and - session_data->header_start_line[source_id]->is_pseudo_name( + if (start_line and start_line->is_pseudo_name( (const char*) decoded_header_buffer)) { // don't include the ': ' that was written following the header name - if (!session_data->header_start_line[source_id]->process_pseudo_header_name( + if (!start_line->process_pseudo_header_name( decoded_header_buffer, partial_bytes_written - 2)) return false; } // If not a pseudo-header value, keep it in the decoded headers else { - if (session_data->header_start_line[source_id] and - !session_data->header_start_line[source_id]->is_finalized()) + if (start_line and !start_line->is_finalized()) { - if (!session_data->header_start_line[source_id]->finalize()) + if (!finalize_start_line()) return false; } } @@ -298,7 +285,7 @@ bool Http2Hpack::decode_literal_header_line(Http2FlowData* session_data, bytes_consumed += partial_bytes_consumed; // value is always literal - if (!Http2Hpack::decode_string_literal(session_data, source_id, encoded_header_buffer + + if (!Http2HpackDecoder::decode_string_literal(encoded_header_buffer + partial_bytes_consumed, encoded_header_length - partial_bytes_consumed, false, partial_bytes_consumed, decoded_header_buffer + bytes_written, decoded_header_length - @@ -309,11 +296,10 @@ bool Http2Hpack::decode_literal_header_line(Http2FlowData* session_data, } // If this was a pseudo-header value, give it to the start-line. - if (session_data->header_start_line[source_id] and - session_data->header_start_line[source_id]->is_pseudo_value()) + if (start_line and start_line->is_pseudo_value()) { // Subtract 2 from the length to remove the trailing CRLF before passing to the start line - session_data->header_start_line[source_id]->process_pseudo_header_value( + start_line->process_pseudo_header_value( decoded_header_buffer + bytes_written, partial_bytes_written - 2); } bytes_written += partial_bytes_written; @@ -323,8 +309,7 @@ bool Http2Hpack::decode_literal_header_line(Http2FlowData* session_data, } // FIXIT-M Will be updated to actually update dynamic table size. For now just skips over -bool Http2Hpack::handle_dynamic_size_update(Http2FlowData* session_data, - HttpCommon::SourceId source_id, const uint8_t* encoded_header_buffer, +bool Http2HpackDecoder::handle_dynamic_size_update(const uint8_t* encoded_header_buffer, const uint32_t encoded_header_length, const Http2HpackIntDecode &decode_int, uint32_t &bytes_consumed, uint32_t &bytes_written) { @@ -334,8 +319,7 @@ bool Http2Hpack::handle_dynamic_size_update(Http2FlowData* session_data, bytes_written = 0; if (!decode_int.translate(encoded_header_buffer, encoded_header_length, - encoded_bytes_consumed, decoded_int, session_data->events[source_id], - session_data->infractions[source_id])) + encoded_bytes_consumed, decoded_int, events, infractions)) { return false; } @@ -352,27 +336,19 @@ bool Http2Hpack::handle_dynamic_size_update(Http2FlowData* session_data, return true; } -bool Http2Hpack::decode_header_line(Http2FlowData* session_data, HttpCommon::SourceId source_id, - const uint8_t* encoded_header_buffer, const uint32_t encoded_header_length, - uint32_t& bytes_consumed, uint8_t* decoded_header_buffer, +bool Http2HpackDecoder::decode_header_line(const uint8_t* encoded_header_buffer, + const uint32_t encoded_header_length, uint32_t& bytes_consumed, uint8_t* decoded_header_buffer, const uint32_t decoded_header_length, uint32_t& bytes_written) { - const uint8_t index_mask = 0x80; - const uint8_t literal_index_mask = 0x40; - const uint8_t literal_index_name_index_mask = 0x3f; - const uint8_t literal_no_index_mask = 0xf0; - const uint8_t literal_never_index_pattern = 0x10; - const uint8_t literal_no_index_name_index_mask = 0x0f; - // indexed header representation if (encoded_header_buffer[0] & index_mask) - return Http2Hpack::decode_index(session_data, source_id, encoded_header_buffer, + return decode_index(encoded_header_buffer, encoded_header_length, decode_int7, true, bytes_consumed, decoded_header_buffer, decoded_header_length, bytes_written); // literal header representation to be added to dynamic table else if (encoded_header_buffer[0] & literal_index_mask) - return Http2Hpack::decode_literal_header_line(session_data, source_id, + return decode_literal_header_line( encoded_header_buffer, encoded_header_length, literal_index_name_index_mask, decode_int6, bytes_consumed, decoded_header_buffer, decoded_header_length, bytes_written); @@ -382,94 +358,81 @@ bool Http2Hpack::decode_header_line(Http2FlowData* session_data, HttpCommon::Sou // literal never index. From a decoding standpoint these are identical. else if ((encoded_header_buffer[0] & literal_no_index_mask) == 0 or (encoded_header_buffer[0] & literal_no_index_mask) == literal_never_index_pattern) - return Http2Hpack::decode_literal_header_line(session_data, source_id, + return decode_literal_header_line( encoded_header_buffer, encoded_header_length, literal_no_index_name_index_mask, decode_int4, bytes_consumed, decoded_header_buffer, decoded_header_length, bytes_written); else // FIXIT-M dynamic table size update not yet supported, just skip - return handle_dynamic_size_update(session_data, source_id, encoded_header_buffer, + return handle_dynamic_size_update(encoded_header_buffer, encoded_header_length, decode_int5, bytes_consumed, bytes_written); } // FIXIT-H This will eventually be the decoded header buffer. String literals and static table // indexes are decoded. Dynamic table indexes are not yet decoded. Both the start-line and // http2_decoded_header need to be sent to NHI -bool Http2Hpack::decode_headers(Http2FlowData* session_data, HttpCommon::SourceId source_id, - const uint8_t* encoded_header_buffer, const uint32_t header_length) +bool Http2HpackDecoder::decode_headers(const uint8_t* encoded_headers, + const uint32_t encoded_headers_length, uint8_t* decoded_headers, uint32_t* decoded_headers_len, + Http2StartLine *start_line_generator, Http2EventGen* stream_events, + Http2Infractions* stream_infractions) { uint32_t total_bytes_consumed = 0; uint32_t line_bytes_consumed = 0; uint32_t line_bytes_written = 0; bool success = true; - session_data->raw_decoded_header[source_id] = new uint8_t[MAX_OCTETS]; - session_data->raw_decoded_header_size[source_id] = 0; - - // FIXIT-H Implement response start line - if (source_id == SRC_CLIENT) - session_data->header_start_line[source_id] = new Http2RequestLine(session_data, source_id); + start_line = start_line_generator; + decoded_headers_size = decoded_headers_len; + events = stream_events; + infractions = stream_infractions; + pseudo_headers_fragment_size = 0; - while (success and total_bytes_consumed < header_length) + while (success and total_bytes_consumed < encoded_headers_length) { - success = Http2Hpack::decode_header_line(session_data, source_id, - encoded_header_buffer + total_bytes_consumed, header_length - total_bytes_consumed, - line_bytes_consumed, session_data->raw_decoded_header[source_id] + - session_data->raw_decoded_header_size[source_id], MAX_OCTETS - - session_data->raw_decoded_header_size[source_id], line_bytes_written); + success = decode_header_line(encoded_headers + total_bytes_consumed, + encoded_headers_length - total_bytes_consumed, line_bytes_consumed, + decoded_headers + *decoded_headers_size, MAX_OCTETS - *decoded_headers_size, + line_bytes_written); total_bytes_consumed += line_bytes_consumed; - session_data->raw_decoded_header_size[source_id] += line_bytes_written; + *decoded_headers_size += line_bytes_written; } // If there were only pseudo-headers, finalize never got called, so create the start-line - if (session_data->header_start_line[source_id] and - !session_data->header_start_line[source_id]->is_finalized()) + if (start_line and !start_line->is_finalized()) { - success &= session_data->header_start_line[source_id]->finalize(); + success &= finalize_start_line(); } // write the last CRLF to end the header if (success) { - success = Http2Hpack::write_decoded_headers(session_data, source_id, - (const uint8_t*)"\r\n", 2, session_data->raw_decoded_header[source_id] + - session_data->raw_decoded_header_size[source_id], MAX_OCTETS - - session_data->raw_decoded_header_size[source_id], line_bytes_written); - session_data->raw_decoded_header_size[source_id] += line_bytes_written; + success = write_decoded_headers((const uint8_t*)"\r\n", 2, decoded_headers + + *decoded_headers_size, MAX_OCTETS - *decoded_headers_size, line_bytes_written); + *decoded_headers_size += line_bytes_written; } + else + decode_error = true; + return success; +} - // set http2_decoded_header to send to NHI - session_data->http2_decoded_header[source_id] = new Field( - session_data->raw_decoded_header_size[source_id] - - session_data->pseudo_header_fragment_size[source_id], - session_data->raw_decoded_header[source_id] + - session_data->pseudo_header_fragment_size[source_id], false); +bool Http2HpackDecoder::finalize_start_line()//const uint32_t decoded_headers_size) +{ + // Save the current position in the decoded buffer so we can set the pointer to the start + // of the regular headers + pseudo_headers_fragment_size = *decoded_headers_size; + return start_line->finalize(); +} -#ifdef REG_TEST - if (HttpTestManager::use_test_output(HttpTestManager::IN_HTTP2)) - { - if (success) - { - if (session_data->header_start_line[source_id]) - session_data->header_start_line[source_id]->get_start_line(). - print(HttpTestManager::get_output_file(), "Decoded start-line"); - session_data->http2_decoded_header[source_id]-> - print(HttpTestManager::get_output_file(), "Decoded header"); - } - else - { - fprintf(HttpTestManager::get_output_file(), "Error decoding headers.\n"); - if (session_data->header_start_line[source_id] and - session_data->header_start_line[source_id]->get_start_line().length() > 0) - session_data->header_start_line[source_id]->get_start_line(). - print(HttpTestManager::get_output_file(), "Decoded start-line"); - if (session_data->raw_decoded_header_size[source_id] > 0) - Field(session_data->raw_decoded_header_size[source_id], - session_data->raw_decoded_header[source_id]).print( - HttpTestManager::get_output_file(), "Partially decoded raw header"); - } - } -#endif +const Field* Http2HpackDecoder::get_start_line() +{ + return start_line->get_start_line(); +} - return success; +const Field* Http2HpackDecoder::get_decoded_headers(const uint8_t* const decoded_headers) +{ + if (decode_error) + return new Field(STAT_NO_SOURCE); + else + return new Field(*decoded_headers_size - pseudo_headers_fragment_size, decoded_headers + + pseudo_headers_fragment_size, false); } diff --git a/src/service_inspectors/http2_inspect/http2_hpack.h b/src/service_inspectors/http2_inspect/http2_hpack.h index 09c1e31ce..97cea5972 100644 --- a/src/service_inspectors/http2_inspect/http2_hpack.h +++ b/src/service_inspectors/http2_inspect/http2_hpack.h @@ -27,63 +27,76 @@ #include "http2_hpack_table.h" class Http2FlowData; +class Field; +class Http2StartLine; // This class implements HPACK decompression. One instance is required in each direction for each // HTTP/2 flow -class Http2Hpack +class Http2HpackDecoder { public: - Http2Hpack() {} - static bool decode_headers(Http2FlowData* session_data, HttpCommon::SourceId source_id, - const uint8_t* raw_header_buffer, const uint32_t header_length); - static bool write_decoded_headers(Http2FlowData* session_data, HttpCommon::SourceId source_id, - const uint8_t* in_buffer, const uint32_t in_length, uint8_t* decoded_header_buffer, - uint32_t decoded_header_length, uint32_t &bytes_written); - static bool decode_header_line(Http2FlowData* session_data, HttpCommon::SourceId source_id, - const uint8_t* encoded_header_buffer, const uint32_t encoded_header_length, - uint32_t& bytes_consumed, uint8_t* decoded_header_buffer, - const uint32_t decoded_header_length, uint32_t& bytes_written); - static bool decode_literal_header_line(Http2FlowData* session_data, - HttpCommon::SourceId source_id, const uint8_t* encoded_header_buffer, + Http2HpackDecoder() { } + bool decode_headers(const uint8_t* encoded_headers, const uint32_t encoded_headers_length, + uint8_t* decoded_headers, uint32_t* decoded_headers_size, Http2StartLine* start_line, + Http2EventGen* stream_events, Http2Infractions* stream_infractions); + bool write_decoded_headers(const uint8_t* in_buffer, const uint32_t in_length, + uint8_t* decoded_header_buffer, uint32_t decoded_header_length, uint32_t &bytes_written); + bool decode_header_line(const uint8_t* encoded_header_buffer, + const uint32_t encoded_header_length, uint32_t& bytes_consumed, + uint8_t* decoded_header_buffer, const uint32_t decoded_header_length, + uint32_t& bytes_written); + bool decode_literal_header_line(const uint8_t* encoded_header_buffer, const uint32_t encoded_header_length, const uint8_t name_index_mask, const Http2HpackIntDecode &decode_int, uint32_t &bytes_consumed, uint8_t* decoded_header_buffer, const uint32_t decoded_header_length, uint32_t &bytes_written); - static bool decode_string_literal(Http2FlowData* session_data, HttpCommon::SourceId source_id, - const uint8_t* encoded_header_buffer, const uint32_t encoded_header_length, - bool is_field_name, uint32_t &bytes_consumed, - uint8_t* decoded_header_buffer, const uint32_t decoded_header_length, - uint32_t &bytes_written); - static bool decode_index(Http2FlowData* session_data, HttpCommon::SourceId source_id, - const uint8_t* encoded_header_buffer, const uint32_t encoded_header_length, - const Http2HpackIntDecode &decode_int,const bool decode_full_line, uint32_t &bytes_consumed, + bool decode_string_literal(const uint8_t* encoded_header_buffer, + const uint32_t encoded_header_length, bool is_field_name, uint32_t &bytes_consumed, uint8_t* decoded_header_buffer, const uint32_t decoded_header_length, uint32_t &bytes_written); - static bool handle_dynamic_size_update(Http2FlowData* session_data, - HttpCommon::SourceId source_id, const uint8_t* encoded_header_buffer, + bool decode_index(const uint8_t* encoded_header_buffer, + const uint32_t encoded_header_length, const Http2HpackIntDecode &decode_int, + const bool decode_full_line, uint32_t &bytes_consumed, uint8_t* decoded_header_buffer, + const uint32_t decoded_header_length, uint32_t &bytes_written); + bool handle_dynamic_size_update(const uint8_t* encoded_header_buffer, const uint32_t encoded_header_length, const Http2HpackIntDecode &decode_int, uint32_t &bytes_consumed, uint32_t &bytes_written); - static bool decode_static_table_index(Http2FlowData* session_data, - HttpCommon::SourceId source_id, const uint64_t index, const bool decode_full_line, + bool decode_static_table_index(const uint64_t index, const bool decode_full_line, uint8_t* decoded_header_buffer, const uint32_t decoded_header_length, uint32_t& bytes_written); - static bool decode_dynamic_table_index(Http2FlowData* session_data, - HttpCommon::SourceId source_id, const uint64_t index, const bool decode_full_line, + bool decode_dynamic_table_index(const uint64_t index, const bool decode_full_line, uint32_t &bytes_consumed, const uint8_t* encoded_header_buffer, uint8_t* decoded_header_buffer, const uint32_t decoded_header_length, uint32_t& bytes_written); + bool finalize_start_line(); + const Field* get_start_line(); + const Field* get_decoded_headers(const uint8_t* const decoded_headers); static const int STATIC_TABLE_MAX_INDEX = 61; private: + Http2StartLine* start_line = nullptr; + uint32_t* decoded_headers_size; + uint32_t pseudo_headers_fragment_size = 0; + bool decode_error = false; + Http2EventGen* events; + Http2Infractions* infractions; + static Http2HpackIntDecode decode_int7; static Http2HpackIntDecode decode_int6; static Http2HpackIntDecode decode_int5; static Http2HpackIntDecode decode_int4; static Http2HpackStringDecode decode_string; -// FIXIT-H Dictionary class and object go here static Http2HpackTable table; //static until dynamic table is implemented + + const static uint8_t index_mask = 0x80; + const static uint8_t literal_index_mask = 0x40; + const static uint8_t literal_index_name_index_mask = 0x3f; + const static uint8_t literal_no_index_mask = 0xf0; + const static uint8_t literal_never_index_pattern = 0x10; + const static uint8_t literal_no_index_name_index_mask = 0x0f; + }; #endif diff --git a/src/service_inspectors/http2_inspect/http2_inspect.cc b/src/service_inspectors/http2_inspect/http2_inspect.cc index 5508afd6b..a241bdc1b 100644 --- a/src/service_inspectors/http2_inspect/http2_inspect.cc +++ b/src/service_inspectors/http2_inspect/http2_inspect.cc @@ -29,6 +29,8 @@ #include "service_inspectors/http_inspect/http_test_manager.h" #include "stream/stream.h" +#include "http2_frame.h" + using namespace snort; using namespace HttpCommon; using namespace Http2Enums; @@ -103,23 +105,14 @@ void Http2Inspect::eval(Packet* p) if (session_data->frame_type[source_id] == FT__NONE) return; - session_data->frame_type[source_id] = FT__NONE; - - set_file_data(session_data->frame_data[source_id], - session_data->frame_data_size[source_id]); + implement_eval(session_data, source_id); + session_data->frame_in_detection = true; #ifdef REG_TEST if (HttpTestManager::use_test_output(HttpTestManager::IN_HTTP2)) { - Field((session_data->frame_header[source_id] != nullptr) ? - (int) session_data->frame_header_size[source_id] : HttpCommon::STAT_NOT_PRESENT, - session_data->frame_header[source_id]).print(HttpTestManager::get_output_file(), - "Frame Header"); - Field((session_data->frame_data[source_id] != nullptr) ? - (int) session_data->frame_data_size[source_id] : HttpCommon::STAT_NOT_PRESENT, - session_data->frame_data[source_id]).print(HttpTestManager::get_output_file(), - "Frame Data"); + session_data->current_frame[source_id]->print_frame(HttpTestManager::get_output_file()); if (HttpTestManager::use_test_input(HttpTestManager::IN_HTTP2)) { printf("Finished processing section from test %" PRIi64 "\n", diff --git a/src/service_inspectors/http2_inspect/http2_inspect.h b/src/service_inspectors/http2_inspect/http2_inspect.h index 0cabdb9a2..8249654f5 100644 --- a/src/service_inspectors/http2_inspect/http2_inspect.h +++ b/src/service_inspectors/http2_inspect/http2_inspect.h @@ -28,11 +28,11 @@ #include "service_inspectors/http_inspect/http_common.h" #include "http2_enum.h" -#include "http2_flow_data.h" #include "http2_module.h" #include "http2_stream_splitter.h" class Http2Api; +class Http2FlowData; class Http2Inspect : public snort::Inspector { @@ -61,6 +61,7 @@ private: bool implement_get_buf(unsigned id, Http2FlowData* session_data, HttpCommon::SourceId source_id, snort::InspectionBuffer& b); +void implement_eval(Http2FlowData* session_data, HttpCommon::SourceId source_id); #endif diff --git a/src/service_inspectors/http2_inspect/http2_inspect_impl.cc b/src/service_inspectors/http2_inspect/http2_inspect_impl.cc index 9109b7b07..82a1e362a 100644 --- a/src/service_inspectors/http2_inspect/http2_inspect_impl.cc +++ b/src/service_inspectors/http2_inspect/http2_inspect_impl.cc @@ -24,6 +24,7 @@ #include "http2_inspect.h" #include "http2_enum.h" #include "http2_flow_data.h" +#include "http2_frame.h" #include "service_inspectors/http_inspect/http_common.h" using namespace snort; @@ -33,30 +34,22 @@ 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 = session_data->frame_header_size[source_id]; - 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; - case HTTP2_BUFFER_DECODED_HEADER: - if (!session_data->http2_decoded_header[source_id] or - session_data->http2_decoded_header[source_id]->length() <= 0) - return false; - b.data = session_data->http2_decoded_header[source_id]->start(); - b.len = session_data->http2_decoded_header[source_id]->length(); - break; - default: + const Field& buffer = session_data->current_frame[source_id]->get_buf(id); + if (buffer.length() <= 0) return false; - } + b.data = buffer.start(); + b.len = buffer.length(); return true; } +void implement_eval(Http2FlowData* session_data, SourceId source_id) +{ + // Construct the appropriate frame class + session_data->current_frame[source_id] = Http2Frame::new_frame( + session_data->frame_header[source_id], session_data->frame_header_size[source_id], + session_data->frame_data[source_id], session_data->frame_data_size[source_id], + session_data, source_id); + // The current frame now owns these buffers, clear them from the flow data + session_data->frame_header[source_id] = nullptr; + session_data->frame_data[source_id] = nullptr; +} diff --git a/src/service_inspectors/http2_inspect/http2_request_line.cc b/src/service_inspectors/http2_inspect/http2_request_line.cc index 1db8d2d35..61473ad0d 100644 --- a/src/service_inspectors/http2_inspect/http2_request_line.cc +++ b/src/service_inspectors/http2_inspect/http2_request_line.cc @@ -42,13 +42,6 @@ const char* Http2RequestLine::SCHEME_NAME = ":scheme"; const char* Http2RequestLine::OPTIONS = "OPTIONS"; const char* Http2RequestLine::CONNECT = "CONNECT"; -Http2RequestLine::Http2RequestLine(Http2FlowData* _session_data, SourceId _source_id) - : Http2StartLine(_session_data, _source_id) -{ } - -Http2RequestLine::~Http2RequestLine() -{ } - bool Http2RequestLine::process_pseudo_header_name(const uint64_t index) { if (!process_pseudo_header_precheck()) @@ -64,8 +57,8 @@ bool Http2RequestLine::process_pseudo_header_name(const uint64_t index) value_coming = SCHEME; else { - *session_data->infractions[source_id] += INF_INVALID_PSEUDO_HEADER; - session_data->events[source_id]->create_event(EVENT_MISFORMATTED_HTTP2); + infractions += INF_INVALID_PSEUDO_HEADER; + events->create_event(EVENT_MISFORMATTED_HTTP2); return false; } return true; @@ -90,8 +83,8 @@ bool Http2RequestLine::process_pseudo_header_name(const uint8_t* const& name, ui value_coming = SCHEME; else { - *session_data->infractions[source_id] += INF_INVALID_PSEUDO_HEADER; - session_data->events[source_id]->create_event(EVENT_MISFORMATTED_HTTP2); + infractions += INF_INVALID_PSEUDO_HEADER; + events->create_event(EVENT_MISFORMATTED_HTTP2); return false; } return true; @@ -124,7 +117,6 @@ void Http2RequestLine::process_pseudo_header_value(const uint8_t* const& value, // provided pseudo-headers and generate the start line bool Http2RequestLine::generate_start_line() { - uint8_t *start_line_buffer; uint32_t bytes_written = 0; // Asterisk form - used for OPTIONS requests @@ -132,8 +124,8 @@ bool Http2RequestLine::generate_start_line() { if (method.length() <= 0) { - *session_data->infractions[source_id] += INF_PSEUDO_HEADER_URI_FORM_MISMATCH; - session_data->events[source_id]->create_event(EVENT_MISFORMATTED_HTTP2); + infractions += INF_PSEUDO_HEADER_URI_FORM_MISMATCH; + events->create_event(EVENT_MISFORMATTED_HTTP2); return false; } start_line_length = method.length() + path.length() + http_version_length + @@ -159,8 +151,8 @@ bool Http2RequestLine::generate_start_line() // FIXIT-L May want to be more lenient than RFC on generating start line if ( scheme.length() > 0 or path.length() > 0 or authority.length() <= 0) { - *session_data->infractions[source_id] += INF_PSEUDO_HEADER_URI_FORM_MISMATCH; - session_data->events[source_id]->create_event(EVENT_MISFORMATTED_HTTP2); + infractions += INF_PSEUDO_HEADER_URI_FORM_MISMATCH; + events->create_event(EVENT_MISFORMATTED_HTTP2); return false; } start_line_length = method.length() + authority.length() + http_version_length + @@ -228,8 +220,8 @@ bool Http2RequestLine::generate_start_line() else { // FIXIT-L May want to be more lenient than RFC on generating start line - *session_data->infractions[source_id] += INF_PSEUDO_HEADER_URI_FORM_MISMATCH; - session_data->events[source_id]->create_event(EVENT_MISFORMATTED_HTTP2); + infractions += INF_PSEUDO_HEADER_URI_FORM_MISMATCH; + events->create_event(EVENT_MISFORMATTED_HTTP2); return false; } @@ -237,7 +229,5 @@ bool Http2RequestLine::generate_start_line() bytes_written += 2; assert(bytes_written == start_line_length); - start_line.set(start_line_length, start_line_buffer, true); - return true; } diff --git a/src/service_inspectors/http2_inspect/http2_request_line.h b/src/service_inspectors/http2_inspect/http2_request_line.h index 088f41ad2..3ef8e35d4 100644 --- a/src/service_inspectors/http2_inspect/http2_request_line.h +++ b/src/service_inspectors/http2_inspect/http2_request_line.h @@ -29,8 +29,8 @@ class Http2RequestLine : public Http2StartLine { public: - Http2RequestLine(Http2FlowData* session_data, HttpCommon::SourceId source_id); - ~Http2RequestLine() override; + Http2RequestLine(Http2EventGen* events, Http2Infractions* infractions) : Http2StartLine(events, + infractions) { } bool process_pseudo_header_name(const uint64_t index) override; bool process_pseudo_header_name(const uint8_t* const& name, uint32_t length) override; diff --git a/src/service_inspectors/http2_inspect/http2_start_line.cc b/src/service_inspectors/http2_inspect/http2_start_line.cc index 2515e1342..04c95c9cb 100644 --- a/src/service_inspectors/http2_inspect/http2_start_line.cc +++ b/src/service_inspectors/http2_inspect/http2_start_line.cc @@ -34,16 +34,17 @@ using namespace Http2Enums; const char* Http2StartLine::http_version_string = "HTTP/1.1"; -Http2StartLine::Http2StartLine(Http2FlowData* _session_data, HttpCommon::SourceId _source_id) : - session_data(_session_data), source_id(_source_id) -{ } +Http2StartLine::~Http2StartLine() +{ + delete[] start_line_buffer; +} bool Http2StartLine::process_pseudo_header_precheck() { if (finalized) { - *session_data->infractions[source_id] += INF_PSEUDO_HEADER_AFTER_REGULAR_HEADER; - session_data->events[source_id]->create_event(EVENT_MISFORMATTED_HTTP2); + infractions += INF_PSEUDO_HEADER_AFTER_REGULAR_HEADER; + events->create_event(EVENT_MISFORMATTED_HTTP2); return false; } return true; @@ -52,11 +53,10 @@ bool Http2StartLine::process_pseudo_header_precheck() bool Http2StartLine::finalize() { finalized = true; - - // Save the current position in the raw decoded buffer so we can set the pointer to the start - // of the regular headers - session_data->pseudo_header_fragment_size[source_id] = - session_data->raw_decoded_header_size[source_id]; - return generate_start_line(); } + +const Field* Http2StartLine::get_start_line() +{ + return new Field(start_line_length, start_line_buffer, false); +} diff --git a/src/service_inspectors/http2_inspect/http2_start_line.h b/src/service_inspectors/http2_inspect/http2_start_line.h index df5a92f2c..08927b66e 100644 --- a/src/service_inspectors/http2_inspect/http2_start_line.h +++ b/src/service_inspectors/http2_inspect/http2_start_line.h @@ -22,26 +22,33 @@ #include "service_inspectors/http_inspect/http_common.h" #include "service_inspectors/http_inspect/http_field.h" +#include "utils/event_gen.h" +#include "utils/infractions.h" #include "http2_enum.h" +using Http2Infractions = Infractions; +using Http2EventGen = EventGen; + class Http2FlowData; class Http2StartLine { public: - Http2StartLine(Http2FlowData* _session_data, HttpCommon::SourceId _source_id); - virtual ~Http2StartLine() = default; + Http2StartLine(Http2EventGen* events, Http2Infractions* infractions) : events(events), + infractions(infractions) { } + + virtual ~Http2StartLine(); friend class Http2Hpack; - const Field& get_start_line() { return start_line; } + const Field* get_start_line(); virtual bool process_pseudo_header_name(const uint64_t index) = 0; virtual bool process_pseudo_header_name(const uint8_t* const& name, uint32_t length) = 0; virtual void process_pseudo_header_value(const uint8_t* const& value, const uint32_t length) = 0; bool finalize(); bool is_finalized() { return finalized; } - uint32_t get_start_line_length() { return start_line_length; } bool is_pseudo_value() { return value_coming != Http2Enums::HEADER_NONE; } bool is_pseudo_name(const char* const& name) { return name[0] == ':'; } @@ -49,12 +56,13 @@ protected: bool process_pseudo_header_precheck(); virtual bool generate_start_line() = 0; - Field start_line; - Http2FlowData* session_data; - HttpCommon::SourceId source_id; + Http2EventGen* events; + Http2Infractions* infractions; bool finalized = false; uint32_t start_line_length = 0; + uint8_t *start_line_buffer = nullptr; Http2Enums::PseudoHeaders value_coming = Http2Enums::HEADER_NONE; + uint32_t pseudo_header_fragment_size = 0; // Version string is HTTP/1.1 static const char* http_version_string; diff --git a/src/service_inspectors/http2_inspect/http2_stream_splitter_impl.cc b/src/service_inspectors/http2_inspect/http2_stream_splitter_impl.cc index 9120a3259..8f3142b7e 100644 --- a/src/service_inspectors/http2_inspect/http2_stream_splitter_impl.cc +++ b/src/service_inspectors/http2_inspect/http2_stream_splitter_impl.cc @@ -45,8 +45,9 @@ static uint8_t get_frame_type(const uint8_t* frame_buffer) const uint8_t frame_type_index = 3; if (frame_buffer) return frame_buffer[frame_type_index]; + // If there was no frame header, this must be a piece of a long data frame else - return FT__NONE; + return FT_DATA; } static uint8_t get_frame_flags(const uint8_t* frame_buffer) @@ -66,7 +67,7 @@ StreamSplitter::Status implement_scan(Http2FlowData* session_data, const uint8_t { // 24-byte preface, not a real frame, no frame header // Verify preface is correct, else generate loss of sync event and abort - switch (validate_preface(data, length, session_data->octets_seen[source_id])) + switch (validate_preface(data, length, session_data->scan_octets_seen[source_id])) { case V_GOOD: break; @@ -74,168 +75,171 @@ StreamSplitter::Status implement_scan(Http2FlowData* session_data, const uint8_t session_data->events[source_id]->create_event(EVENT_PREFACE_MATCH_FAILURE); return StreamSplitter::ABORT; case V_TBD: - session_data->octets_seen[source_id] += length; + session_data->scan_octets_seen[source_id] += length; return StreamSplitter::SEARCH; } - *flush_offset = 24 - session_data->octets_seen[source_id]; - session_data->header_coming[source_id] = false; + *flush_offset = 24 - session_data->scan_octets_seen[source_id]; session_data->preface[source_id] = false; session_data->payload_discard[source_id] = true; + session_data->scan_octets_seen[source_id] = 0; } + //FIXIT-M This should get split points from NHI else if (session_data->leftover_data[source_id] > 0) { // Continuation of ongoing data frame - session_data->header_coming[source_id] = false; + session_data->num_frame_headers[source_id] = 0; - // If this is a new section, update next inspection_section_length - if (session_data->octets_seen[source_id] == 0) + // If this is a new frame section, update next frame section length + if (session_data->scan_remaining_frame_octets[source_id] == 0) { if (session_data->leftover_data[source_id] > DATA_SECTION_SIZE) - session_data->inspection_section_length[source_id] = DATA_SECTION_SIZE; + session_data->scan_remaining_frame_octets[source_id] = DATA_SECTION_SIZE; else - session_data->inspection_section_length[source_id] = + session_data->scan_remaining_frame_octets[source_id] = session_data->leftover_data[source_id]; + session_data->total_bytes_in_split[source_id] = 0; } - // Don't have full inspection section, keep scanning - if (session_data->octets_seen[source_id] + length < - session_data->inspection_section_length[source_id]) + // Don't have full frame section, keep scanning + if (session_data->scan_remaining_frame_octets[source_id] > length) { - session_data->octets_seen[source_id] += length; + session_data->scan_remaining_frame_octets[source_id] -= length; + session_data->total_bytes_in_split[source_id] += length; return status = StreamSplitter::SEARCH; } - // Have full inspection section, flush and update leftover - *flush_offset = session_data->inspection_section_length[source_id] - - session_data->octets_seen[source_id]; + // Have full frame section, flush and update leftover + session_data->total_bytes_in_split[source_id] += + session_data->scan_remaining_frame_octets[source_id]; + *flush_offset = session_data->scan_remaining_frame_octets[source_id]; session_data->leftover_data[source_id] -= - session_data->inspection_section_length[source_id]; - session_data->octets_seen[source_id] = 0; + session_data->total_bytes_in_split[source_id]; + session_data->octets_before_first_header[source_id] = + session_data->total_bytes_in_split[source_id]; + session_data->scan_remaining_frame_octets[source_id] = 0; } else { - // frame with header + // Frame with header + *flush_offset = 0; + uint32_t data_offset = 0; + session_data->octets_before_first_header[source_id] = 0; // If there is a header frame followed by a continuation frame in the same tcp segment, // need to process multiple frames in a single scan - *flush_offset = 0; - uint32_t remaining_length = length; - const uint8_t *data_pos = data; do { - if (session_data->scan_header_octets_seen[source_id] == 0) - { - session_data->header_coming[source_id] = true; - session_data->octets_seen[source_id] = 0; - } + // Scanning a new frame + if (session_data->scan_octets_seen[source_id] == 0) + session_data->num_frame_headers[source_id] += 1; // The first nine bytes are the frame header. But all nine might not all be present in // the first TCP segment we receive. - if (session_data->scan_header_octets_seen[source_id] < FRAME_HEADER_LENGTH) - { - uint32_t remaining_header = FRAME_HEADER_LENGTH - - session_data->scan_header_octets_seen[source_id]; - uint32_t remaining_header_in_data = remaining_header > remaining_length ? - remaining_length : remaining_header; - memcpy(session_data->currently_processing_frame_header[source_id] + - session_data->scan_header_octets_seen[source_id], - data_pos, remaining_header_in_data); - session_data->scan_header_octets_seen[source_id] += remaining_header_in_data; - if (session_data->scan_header_octets_seen[source_id] < FRAME_HEADER_LENGTH) - { - session_data->octets_seen[source_id] += remaining_header_in_data; - status = StreamSplitter::SEARCH; - break; - } - session_data->frames_aggregated[source_id] += 1; - } - - uint8_t type = get_frame_type(session_data->currently_processing_frame_header[source_id]); - - if (session_data->continuation_expected[source_id] && type != FT_CONTINUATION) + uint32_t remaining_header = FRAME_HEADER_LENGTH - + session_data->scan_octets_seen[source_id]; + uint32_t remaining_header_in_data = remaining_header > length - data_offset ? + length - data_offset : remaining_header; + memcpy(session_data->scan_frame_header[source_id] + + session_data->scan_octets_seen[source_id], data + data_offset, + remaining_header_in_data); + session_data->scan_octets_seen[source_id] += remaining_header_in_data; + data_offset += remaining_header_in_data; + + if (session_data->scan_octets_seen[source_id] < FRAME_HEADER_LENGTH) { - *session_data->infractions[source_id] += INF_MISSING_CONTINUATION; - session_data->events[source_id]->create_event(EVENT_MISSING_CONTINUATION); - status = StreamSplitter::ABORT; + status = StreamSplitter::SEARCH; break; } - // Frame length does not include the frame header - uint32_t const frame_length = get_frame_length(session_data-> - currently_processing_frame_header[source_id]); - - // For non-data frames, send a full frame to detection - session_data->inspection_section_length[source_id] = frame_length + FRAME_HEADER_LENGTH; + // We have the full frame header, compute some variables + const uint32_t frame_length = get_frame_length(session_data-> + scan_frame_header[source_id]); + uint8_t type = get_frame_type(session_data->scan_frame_header[source_id]); - if ((type == FT_DATA) && (frame_length > DATA_SECTION_SIZE)) + // Compute frame section length once per frame + if (session_data->scan_remaining_frame_octets[source_id] == 0) { - // Break up long data frames into pieces for detection - session_data->inspection_section_length[source_id] = DATA_SECTION_SIZE + - FRAME_HEADER_LENGTH; - } - else if (frame_length + FRAME_HEADER_LENGTH > MAX_OCTETS) - { - // FIXIT-M long non-data frame needs to be supported - status = StreamSplitter::ABORT; - break; + if (session_data->continuation_expected[source_id] && type != FT_CONTINUATION) + { + *session_data->infractions[source_id] += INF_MISSING_CONTINUATION; + session_data->events[source_id]->create_event(EVENT_MISSING_CONTINUATION); + status = StreamSplitter::ABORT; + break; + } + if ((type == FT_DATA) && (frame_length > DATA_SECTION_SIZE)) + { + // Break up long data frames into pieces for detection + session_data->scan_remaining_frame_octets[source_id] = DATA_SECTION_SIZE; + session_data->total_bytes_in_split[source_id] = DATA_SECTION_SIZE + + FRAME_HEADER_LENGTH; + } + else if (frame_length + FRAME_HEADER_LENGTH > MAX_OCTETS) + { + // FIXIT-M long non-data frame needs to be supported + status = StreamSplitter::ABORT; + break; + } + else + { + session_data->scan_remaining_frame_octets[source_id] = frame_length; + session_data->total_bytes_in_split[source_id] += FRAME_HEADER_LENGTH + + frame_length; + } } - if (length + session_data->octets_seen[source_id] < - session_data->inspection_section_length[source_id]) + // If we don't have the full frame, keep scanning + if (length - data_offset < session_data->scan_remaining_frame_octets[source_id]) { - // If we don't have the full inspection length, keep scanning - session_data->octets_seen[source_id] += length; + session_data->scan_remaining_frame_octets[source_id] -= (length - data_offset); status = StreamSplitter::SEARCH; break; } - else - { - // we have the full frame section to flush to detection - *flush_offset += session_data->inspection_section_length[source_id] - - session_data->octets_seen[source_id]; - session_data->leftover_data[source_id] = frame_length + FRAME_HEADER_LENGTH - - session_data->inspection_section_length[source_id]; - session_data->octets_seen[source_id] = 0; - } - // Process all header frames as one unit - if the END_HEADERS flag is not set and scan - // is out of data, tell stream to keep searching - uint8_t frame_flags = get_frame_flags(session_data-> - currently_processing_frame_header[source_id]); - if (type == FT_HEADERS && !(frame_flags & END_HEADERS)) + // Have the full frame + uint8_t frame_flags = get_frame_flags(session_data->scan_frame_header[source_id]); + switch(type) { - session_data->continuation_expected[source_id] = true; - - session_data->scan_header_octets_seen[source_id] = 0; - status = StreamSplitter::SEARCH; - data_pos = data + *flush_offset; - remaining_length = length - *flush_offset; - } - else if ( type == FT_CONTINUATION && session_data->continuation_expected[source_id]) - { - if (!(frame_flags & END_HEADERS)) - { - session_data->scan_header_octets_seen[source_id] = 0; - status = StreamSplitter::SEARCH; - data_pos = data + *flush_offset; - remaining_length = length - *flush_offset; - } - else - { - // continuation frame ending headers - status = StreamSplitter::FLUSH; - session_data->continuation_expected[source_id] = false; - } - } - //FIXIT-M CONTINUATION frames can also follow PUSH_PROMISE frames, which is not - //currently supported - else if (type == FT_CONTINUATION) - { - *session_data->infractions[source_id] += INF_UNEXPECTED_CONTINUATION; - session_data->events[source_id]->create_event(EVENT_UNEXPECTED_CONTINUATION); - status = StreamSplitter::ABORT; + case FT_HEADERS: + if (!(frame_flags & END_HEADERS)) + { + session_data->continuation_expected[source_id] = true; + status = StreamSplitter::SEARCH; + } + break; + case FT_CONTINUATION: + if (session_data->continuation_expected[source_id]) + { + if (!(frame_flags & END_HEADERS)) + status = StreamSplitter::SEARCH; + else + { + // continuation frame ending headers + status = StreamSplitter::FLUSH; + session_data->continuation_expected[source_id] = false; + } + } + else + { + // FIXIT-M CONTINUATION frames can also follow PUSH_PROMISE frames, which is + // not currently supported + *session_data->infractions[source_id] += INF_UNEXPECTED_CONTINUATION; + session_data->events[source_id]->create_event( + EVENT_UNEXPECTED_CONTINUATION); + status = StreamSplitter::ABORT; + } + break; + case FT_DATA: + session_data->leftover_data[source_id] = frame_length - + (session_data->total_bytes_in_split[source_id] - FRAME_HEADER_LENGTH); + break; } - } while (status == StreamSplitter::SEARCH && remaining_length > 0); + + data_offset += session_data->scan_remaining_frame_octets[source_id]; + *flush_offset = data_offset; + session_data->scan_octets_seen[source_id] = 0; + session_data->scan_remaining_frame_octets[source_id] = 0; + + } while (status == StreamSplitter::SEARCH && data_offset < length); } return status; } @@ -249,175 +253,105 @@ const StreamBuffer implement_reassemble(Http2FlowData* session_data, unsigned to assert(offset+len <= total); assert(total >= FRAME_HEADER_LENGTH); assert(total <= MAX_OCTETS); + assert(total == session_data->total_bytes_in_split[source_id]); StreamBuffer frame_buf { nullptr, 0 }; + uint32_t data_offset = 0; + if (offset == 0) { // This is the first reassemble() for this frame and we need to allocate some buffers - if (!session_data->header_coming[source_id]) - session_data->frame_data[source_id] = new uint8_t[total]; - else - { - const uint32_t header_length = FRAME_HEADER_LENGTH * - session_data->frames_aggregated[source_id]; - session_data->frame_header[source_id] = new uint8_t[header_length]; - session_data->frame_header_size[source_id] = header_length; - if (total > FRAME_HEADER_LENGTH) - session_data->frame_data[source_id] = new uint8_t[total - header_length]; - } - session_data->reassemble_header_octets_seen[source_id] = 0; - session_data->frame_data_size[source_id] = 0; + session_data->frame_header_size[source_id] = FRAME_HEADER_LENGTH * + session_data->num_frame_headers[source_id]; + if (session_data->frame_header_size[source_id] > 0) + session_data->frame_header[source_id] = new uint8_t[ + session_data->frame_header_size[source_id]]; + + session_data->frame_data_size[source_id]= total - + session_data->frame_header_size[source_id]; + if (session_data->frame_data_size[source_id] > 0) + session_data->frame_data[source_id] = new uint8_t[ + session_data->frame_data_size[source_id]]; + session_data->frame_header_offset[source_id] = 0; + session_data->frame_data_offset[source_id] = 0; + session_data->remaining_frame_octets[source_id] = + session_data->octets_before_first_header[source_id]; + session_data->padding_octets_in_frame[source_id] = 0; } - if (!session_data->header_coming[source_id]) - { - memcpy(session_data->frame_data[source_id] + offset, data, len); - session_data->frame_data_size[source_id] += len; - } - else + do { - uint32_t data_pos = 0; - do - { - // Each pass through the loop handles one frame - // Multiple frames occur when a Headers frame and Continuation frame(s) are flushed - // together - uint32_t remaining_len = len - data_pos; + uint32_t octets_to_copy; - // Process the frame header - if (session_data->reassemble_header_octets_seen[source_id] < FRAME_HEADER_LENGTH) - { - uint8_t remaining_header = FRAME_HEADER_LENGTH - - session_data->reassemble_header_octets_seen[source_id]; - if (remaining_header > remaining_len) - { - memcpy(session_data->frame_header[source_id] + - session_data->frame_header_offset[source_id] + - session_data->reassemble_header_octets_seen[source_id], data + data_pos, - remaining_len); - session_data->reassemble_header_octets_seen[source_id] += remaining_len; - break; - } - memcpy(session_data->frame_header[source_id] + - session_data->frame_header_offset[source_id] + - session_data->reassemble_header_octets_seen[source_id], data + data_pos, - remaining_header); - session_data->reassemble_header_octets_seen[source_id] += remaining_header; - data_pos += remaining_header; - remaining_len -= remaining_header; - } - - // done once per frame after we have the entire header - if (session_data->remaining_frame_data_octets[source_id] == 0) - { - uint32_t frame_length = 0; - uint32_t frame_data_offset = 0; - uint8_t pad_len = 0; - uint8_t frame_flags = 0; - - frame_length = get_frame_length(session_data->frame_header[source_id] + - session_data->frame_header_offset[source_id]); - - if (frame_length == 0) - break; - if (remaining_len == 0) - return frame_buf; - - // handle split long data frames - if (frame_length > total) - frame_length = total - data_pos; - frame_flags = get_frame_flags(session_data->frame_header[source_id] + - session_data->frame_header_offset[source_id]); - - if (frame_flags & PADDED) - { - frame_data_offset += 1; - pad_len = *(data + data_pos); - } - //FIXIT-M handle stream dependency and weight. For now just skip over - if (frame_flags & PRIORITY) - { - frame_data_offset += 5; - } - session_data->remaining_octets_to_next_header[source_id] = frame_length; - session_data->remaining_frame_data_octets[source_id] = - frame_length - pad_len - frame_data_offset; - session_data->remaining_frame_data_offset[source_id] = frame_data_offset; - } + // Read the padding length if necessary + if (session_data->get_padding_len[source_id]) + { + session_data->get_padding_len[source_id] = false; + session_data->padding_octets_in_frame[source_id] = *(data + data_offset); + data_offset += 1; + session_data->remaining_frame_octets[source_id] -= 1; + // Subtract the padding and padding length from the frame data size + session_data->frame_data_size[source_id] -= + (session_data->padding_octets_in_frame[source_id] + 1); + } - if (remaining_len >= session_data->remaining_octets_to_next_header[source_id]) - { - // have the remainder of the full frame - memcpy(session_data->frame_data[source_id] + session_data->frame_data_size[source_id], - data + data_pos + session_data->remaining_frame_data_offset[source_id], - session_data->remaining_frame_data_octets[source_id]); - session_data->frame_data_size[source_id] += - session_data->remaining_frame_data_octets[source_id]; - data_pos += session_data->remaining_octets_to_next_header[source_id]; - session_data->remaining_octets_to_next_header[source_id] = 0; - session_data->remaining_frame_data_octets[source_id] = 0; - session_data->remaining_frame_data_offset[source_id] = 0; - session_data->reassemble_header_octets_seen[source_id] = 0; - session_data->frame_header_offset[source_id] += FRAME_HEADER_LENGTH; - } - else if (remaining_len < session_data->remaining_frame_data_offset[source_id]) - { - // don't have the full stream dependency/weight, which precedes frame data - session_data->remaining_frame_data_offset[source_id] -= remaining_len; - session_data->remaining_octets_to_next_header[source_id] -= remaining_len; - return frame_buf; - } - else if (remaining_len < session_data->remaining_frame_data_octets[source_id]) - { - // don't have the full frame data - uint32_t data_len = remaining_len - session_data->remaining_frame_data_offset[source_id]; - memcpy(session_data->frame_data[source_id] + session_data->frame_data_size[source_id], - data + data_pos + session_data->remaining_frame_data_offset[source_id], - data_len); - session_data->frame_data_size[source_id] += data_len; - session_data->remaining_octets_to_next_header[source_id] -= remaining_len; - session_data->remaining_frame_data_octets[source_id] -= data_len; - session_data->remaining_frame_data_offset[source_id] = 0; - return frame_buf; - } - else - { - // have all the data but not all the padding following the data - memcpy(session_data->frame_data[source_id] + session_data->frame_data_size[source_id], - data + data_pos + session_data->remaining_frame_data_offset[source_id], - session_data->remaining_frame_data_octets[source_id]); - session_data->frame_data_size[source_id] += - session_data->remaining_frame_data_octets[source_id]; - session_data->remaining_octets_to_next_header[source_id] -= remaining_len; - session_data->remaining_frame_data_octets[source_id] = 0; - session_data->remaining_frame_data_offset[source_id] = 0; - } - } while (data_pos < len); - } + // Copy data into the frame buffer until we run out of data or reach the end of the current + // frame's data + const uint32_t remaining_frame_payload = + session_data->remaining_frame_octets[source_id] - + session_data->padding_octets_in_frame[source_id]; + octets_to_copy = remaining_frame_payload > len - data_offset ? len - data_offset : + remaining_frame_payload; + if (octets_to_copy > 0) + { + memcpy(session_data->frame_data[source_id] + session_data->frame_data_offset[source_id], + data + data_offset, octets_to_copy); + } + session_data->frame_data_offset[source_id] += octets_to_copy; + session_data->remaining_frame_octets[source_id] -= octets_to_copy; + data_offset += octets_to_copy; + + if (data_offset == len) + break; + + // Skip over any padding + uint32_t padding_bytes_to_skip = session_data->padding_octets_in_frame[source_id] > + len - data_offset ? len - data_offset : + session_data->padding_octets_in_frame[source_id]; + session_data->remaining_frame_octets[source_id] -= padding_bytes_to_skip; + data_offset += padding_bytes_to_skip; + + if (data_offset == len) + break; + + // Copy headers + const uint32_t remaining_frame_header = FRAME_HEADER_LENGTH - + (session_data->frame_header_offset[source_id] % FRAME_HEADER_LENGTH); + octets_to_copy = remaining_frame_header > len - data_offset ? len - data_offset : + remaining_frame_header; + memcpy(session_data->frame_header[source_id] + session_data->frame_header_offset[source_id], + data + data_offset, octets_to_copy); + session_data->frame_header_offset[source_id] += octets_to_copy; + data_offset += octets_to_copy; + + if (session_data->frame_header_offset[source_id] % FRAME_HEADER_LENGTH != 0) + break; + + // If we just finished copying a header, parse and update frame variables + session_data->remaining_frame_octets[source_id] = + get_frame_length(session_data->frame_header[source_id] + + session_data->frame_header_offset[source_id] - FRAME_HEADER_LENGTH); + + uint8_t frame_flags = get_frame_flags(session_data->frame_header[source_id] + + session_data->frame_header_offset[source_id] - FRAME_HEADER_LENGTH); + if (frame_flags & PADDED) + session_data->get_padding_len[source_id] = true; + } while (data_offset < len); if (flags & PKT_PDU_TAIL) { - if (session_data->header_coming[source_id]) - { - if (get_frame_type(session_data->frame_header[source_id]) == FT_HEADERS) - { - assert(session_data->raw_decoded_header[source_id] == nullptr); - - // FIXIT-H This will eventually be the decoded header buffer. Under development. - if (!Http2Hpack::decode_headers(session_data, source_id, - session_data->frame_data[source_id], session_data->frame_data_size[source_id])) - { - // Since this doesn't go to detection, clear() doesn't get called, so need to - // clear frame data from flow data directly - session_data->clear_frame_data(source_id); - - session_data->frame_type[source_id] = FT__ABORT; - return frame_buf; - } - } - } // Return 0-length non-null buffer to stream which signals detection required, but don't // create pkt_data buffer frame_buf.data = (const uint8_t*)""; diff --git a/src/service_inspectors/http2_inspect/test/CMakeLists.txt b/src/service_inspectors/http2_inspect/test/CMakeLists.txt index c3882abd7..87b644e8f 100644 --- a/src/service_inspectors/http2_inspect/test/CMakeLists.txt +++ b/src/service_inspectors/http2_inspect/test/CMakeLists.txt @@ -1,23 +1,8 @@ -add_cpputest( http2_inspect_impl_test - SOURCES - ../http2_flow_data.cc - ../http2_inspect_impl.cc - ../http2_module.cc - ../http2_tables.cc - ../../../framework/module.cc -) add_cpputest( http2_stream_splitter_impl_test SOURCES ../http2_flow_data.cc - ../http2_hpack_int_decode.cc - ../http2_hpack_table.cc - ../http2_hpack_string_decode.cc - ../http2_huffman_state_machine.cc ../http2_stream_splitter_impl.cc - ../http2_hpack.cc ../http2_module.cc - ../http2_request_line.cc - ../http2_start_line.cc ../http2_tables.cc ../../../framework/module.cc ) 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 index 818d25081..ab902d2cc 100644 --- a/src/service_inspectors/http2_inspect/test/http2_flow_data_test.h +++ b/src/service_inspectors/http2_inspect/test/http2_flow_data_test.h @@ -38,26 +38,36 @@ class Http2FlowDataTest : public Http2FlowData public: bool get_preface(HttpCommon::SourceId source_id) { return preface[source_id]; } void set_preface(bool value, HttpCommon::SourceId source_id) { preface[source_id] = value; } - bool get_header_coming(HttpCommon::SourceId source_id) { return header_coming[source_id]; } - void set_header_coming(bool value, HttpCommon::SourceId source_id) - { header_coming[source_id] = value; } + bool get_num_frame_headers(HttpCommon::SourceId source_id) { return num_frame_headers[source_id]; } + void set_num_frame_headers(bool value, HttpCommon::SourceId source_id) + { num_frame_headers[source_id] = value; } uint8_t* get_frame_header(HttpCommon::SourceId source_id) { return frame_header[source_id]; } void set_frame_header(uint8_t* value, uint32_t length, HttpCommon::SourceId source_id) { frame_header[source_id] = value; frame_header_size[source_id] = length; } - uint8_t* get_currently_processing_frame_header(HttpCommon::SourceId source_id) - { return currently_processing_frame_header[source_id]; } - void set_aggregated_frames(uint32_t value, HttpCommon::SourceId source_id) - { frames_aggregated[source_id] = value; } + uint8_t* get_scan_frame_header(HttpCommon::SourceId source_id) + { return scan_frame_header[source_id]; } uint8_t* get_frame_data(HttpCommon::SourceId source_id) { return frame_data[source_id]; } void set_frame_data(uint8_t* value, HttpCommon::SourceId source_id) { frame_data[source_id] = value; } uint32_t get_frame_data_size(HttpCommon::SourceId source_id) - { return frame_data_size[source_id]; } + { return frame_data_offset[source_id]; } void set_frame_data_size(uint32_t value, HttpCommon::SourceId source_id) { frame_data_size[source_id] = value; } uint32_t get_leftover_data(HttpCommon::SourceId source_id) { return leftover_data[source_id]; } void set_leftover_data(uint32_t value, HttpCommon::SourceId source_id) { leftover_data[source_id] = value; } + void set_total_bytes_in_split(uint32_t value, HttpCommon::SourceId source_id) + { total_bytes_in_split[source_id] = value; } + void set_octets_before_first_header(uint32_t value, HttpCommon::SourceId source_id) + { octets_before_first_header[source_id] = value; } + void cleanup() + { + for (int k = 0; k < 2; k++) + { + delete[] frame_header[k]; + delete[] frame_data[k]; + } + } }; #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 deleted file mode 100644 index 6a9e9993a..000000000 --- a/src/service_inspectors/http2_inspect/test/http2_inspect_impl_test.cc +++ /dev/null @@ -1,116 +0,0 @@ -//-------------------------------------------------------------------------- -// Copyright (C) 2018-2019 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/http_inspect/http_common.h" -#include "service_inspectors/http_inspect/http_field.h" -#include "service_inspectors/http_inspect/http_test_manager.h" -#include "service_inspectors/http2_inspect/http2_enum.h" - -#include "http2_flow_data_test.h" - -#include -#include -#include - -#include - -using namespace snort; -using namespace HttpCommon; -using namespace Http2Enums; - -// Stubs whose sole purpose is to make the test code link -unsigned HttpTestManager::test_input = IN_NONE; -unsigned HttpTestManager::test_output = IN_NONE; -int DetectionEngine::queue_event(unsigned int, unsigned int, Actions::Type) { return 0; } - -#ifdef REG_TEST -void Field::print(FILE*, char const*) const {} -#endif - -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, 9, SRC_CLIENT); - session_data->set_aggregated_frames (1, 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); -} - -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 index 2e1227c47..1b825720a 100644 --- 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 @@ -29,9 +29,6 @@ #include "service_inspectors/http_inspect/http_test_manager.h" #include "service_inspectors/http2_inspect/http2_enum.h" #include "service_inspectors/http2_inspect/http2_flow_data.h" -#include "service_inspectors/http2_inspect/http2_hpack_int_decode.h" -#include "service_inspectors/http2_inspect/http2_hpack_table.h" -#include "service_inspectors/http2_inspect/http2_hpack_string_decode.h" #include "service_inspectors/http2_inspect/http2_stream_splitter.h" #include "http2_flow_data_test.h" @@ -80,7 +77,7 @@ TEST(http2_scan_test, basic_with_header) 26, &flush_offset, SRC_CLIENT); CHECK(result == StreamSplitter::FLUSH); CHECK(flush_offset == 19); - CHECK(session_data->get_header_coming(SRC_CLIENT)); + CHECK(session_data->get_num_frame_headers(SRC_CLIENT) == 1); } TEST(http2_scan_test, basic_with_header_s2c) @@ -90,7 +87,7 @@ TEST(http2_scan_test, basic_with_header_s2c) 26, &flush_offset, SRC_SERVER); CHECK(result == StreamSplitter::FLUSH); CHECK(flush_offset == 19); - CHECK(session_data->get_header_coming(SRC_SERVER)); + CHECK(session_data->get_num_frame_headers(SRC_SERVER) == 1); } TEST(http2_scan_test, header_without_data) @@ -101,7 +98,7 @@ TEST(http2_scan_test, header_without_data) 9, &flush_offset, SRC_CLIENT); CHECK(result == StreamSplitter::SEARCH); CHECK(flush_offset == 0); - CHECK(session_data->get_header_coming(SRC_CLIENT)); + CHECK(session_data->get_num_frame_headers(SRC_CLIENT) == 1); } TEST(http2_scan_test, preface_and_more) @@ -111,7 +108,7 @@ TEST(http2_scan_test, preface_and_more) 50, &flush_offset, SRC_CLIENT); CHECK(result == StreamSplitter::FLUSH); CHECK(flush_offset == 24); - CHECK(!session_data->get_header_coming(SRC_CLIENT)); + CHECK(session_data->get_num_frame_headers(SRC_CLIENT) == 0); } TEST(http2_scan_test, preface_exactly) @@ -121,7 +118,7 @@ TEST(http2_scan_test, preface_exactly) 24, &flush_offset, SRC_CLIENT); CHECK(result == StreamSplitter::FLUSH); CHECK(flush_offset == 24); - CHECK(!session_data->get_header_coming(SRC_CLIENT)); + CHECK(session_data->get_num_frame_headers(SRC_CLIENT) == 0); } TEST(http2_scan_test, short_input) @@ -138,8 +135,8 @@ TEST(http2_scan_test, short_input) SRC_SERVER); CHECK(result == StreamSplitter::FLUSH); CHECK(flush_offset == 19); - CHECK(session_data->get_header_coming(SRC_SERVER)); - CHECK(memcmp(session_data->get_currently_processing_frame_header(SRC_SERVER), + CHECK(session_data->get_num_frame_headers(SRC_SERVER) == 1); + CHECK(memcmp(session_data->get_scan_frame_header(SRC_SERVER), "\x00\x00\x10\x04\x05\x06\x07\x08\x09", 9) == 0); } @@ -160,7 +157,7 @@ TEST(http2_scan_test, maximum_frame) data, 63780, &flush_offset, SRC_SERVER); CHECK(result == StreamSplitter::FLUSH); CHECK(flush_offset == 63780); - CHECK(session_data->get_header_coming(SRC_SERVER)); + CHECK(session_data->get_num_frame_headers(SRC_SERVER) == 1); } TEST(http2_scan_test, data_sections) @@ -171,28 +168,28 @@ TEST(http2_scan_test, data_sections) data, DATA_SECTION_SIZE + 9, &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_num_frame_headers(SRC_SERVER) == 1); CHECK(session_data->get_leftover_data(SRC_SERVER) == 0xE13C); result = implement_scan(session_data, data, DATA_SECTION_SIZE, &flush_offset, SRC_SERVER); CHECK(result == StreamSplitter::FLUSH); CHECK(flush_offset == DATA_SECTION_SIZE); - CHECK(!session_data->get_header_coming(SRC_SERVER)); + CHECK(session_data->get_num_frame_headers(SRC_SERVER) == 0); result = implement_scan(session_data, data, DATA_SECTION_SIZE, &flush_offset, SRC_SERVER); CHECK(result == StreamSplitter::FLUSH); CHECK(flush_offset == DATA_SECTION_SIZE); - CHECK(!session_data->get_header_coming(SRC_SERVER)); + CHECK(session_data->get_num_frame_headers(SRC_SERVER) == 0); result = implement_scan(session_data, data, DATA_SECTION_SIZE, &flush_offset, SRC_SERVER); CHECK(result == StreamSplitter::FLUSH); CHECK(flush_offset == DATA_SECTION_SIZE); - CHECK(!session_data->get_header_coming(SRC_SERVER)); + CHECK(session_data->get_num_frame_headers(SRC_SERVER) == 0); result = implement_scan(session_data, data, 8508, &flush_offset, SRC_SERVER); CHECK(result == StreamSplitter::FLUSH); CHECK(flush_offset == 8508); - CHECK(!session_data->get_header_coming(SRC_SERVER)); + CHECK(session_data->get_num_frame_headers(SRC_SERVER) == 0); CHECK(session_data->get_leftover_data(SRC_SERVER) == 0); } @@ -208,14 +205,15 @@ TEST_GROUP(http2_reassemble_test) void teardown() override { + session_data->cleanup(); delete session_data; } }; TEST(http2_reassemble_test, basic_with_header) { - session_data->set_header_coming(true, SRC_CLIENT); - session_data->set_aggregated_frames (1, SRC_CLIENT); + session_data->set_num_frame_headers(1, SRC_CLIENT); + session_data->set_total_bytes_in_split(19, SRC_CLIENT); implement_reassemble(session_data, 19, 0, (const uint8_t*)"\x00\x00\x0A\x02\x00\x00\x00\x00\x00" "0123456789", 19, PKT_PDU_TAIL, SRC_CLIENT); @@ -225,8 +223,8 @@ TEST(http2_reassemble_test, basic_with_header) TEST(http2_reassemble_test, basic_with_header_s2c) { - session_data->set_header_coming(true, SRC_SERVER); - session_data->set_aggregated_frames (1, SRC_SERVER); + session_data->set_num_frame_headers(1, SRC_SERVER); + session_data->set_total_bytes_in_split(19, SRC_SERVER); implement_reassemble(session_data, 19, 0, (const uint8_t*)"\x00\x00\x0A\x02\x00\x00\x00\x00\x00" "0123456789", 19, PKT_PDU_TAIL, SRC_SERVER); @@ -236,7 +234,9 @@ TEST(http2_reassemble_test, basic_with_header_s2c) TEST(http2_reassemble_test, basic_without_header) { - session_data->set_header_coming(false, SRC_CLIENT); + session_data->set_num_frame_headers(0, SRC_CLIENT); + session_data->set_octets_before_first_header(24, SRC_CLIENT); + session_data->set_total_bytes_in_split(24, SRC_CLIENT); 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, SRC_CLIENT); @@ -246,8 +246,8 @@ TEST(http2_reassemble_test, basic_without_header) TEST(http2_reassemble_test, basic_three_pieces) { - session_data->set_header_coming(true, SRC_CLIENT); - session_data->set_aggregated_frames (1, SRC_CLIENT); + session_data->set_num_frame_headers(1, SRC_CLIENT); + session_data->set_total_bytes_in_split(19, SRC_CLIENT); StreamBuffer buffer = implement_reassemble(session_data, 19, 0, (const uint8_t*)"\x00\x00\x0A\x02\x00\x00", 6, 0, SRC_CLIENT); @@ -266,7 +266,9 @@ TEST(http2_reassemble_test, basic_three_pieces) TEST(http2_reassemble_test, basic_without_header_two_pieces) { - session_data->set_header_coming(false, SRC_CLIENT); + session_data->set_num_frame_headers(0, SRC_CLIENT); + session_data->set_octets_before_first_header(24, SRC_CLIENT); + session_data->set_total_bytes_in_split(24, SRC_CLIENT); implement_reassemble(session_data, 24, 0, (const uint8_t*)"P", 1, 0, SRC_CLIENT);