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
#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"
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;
}
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.
--- /dev/null
+//--------------------------------------------------------------------------
+// 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 <katharve@cisco.com>
+
+#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
--- /dev/null
+//--------------------------------------------------------------------------
+// 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 <katharve@cisco.com>
+
+#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
--- /dev/null
+//--------------------------------------------------------------------------
+// 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 <katharve@cisco.com>
+
+#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
--- /dev/null
+//--------------------------------------------------------------------------
+// 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 <katharve@cisco.com>
+
+#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
#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;
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;
}
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)
{
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;
}
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;
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);
// 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))
{
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;
}
// 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;
// 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)
// 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))
{
// 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))
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;
}
}
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 -
}
// 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;
}
// 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)
{
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;
}
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);
// 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);
}
#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
#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;
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",
#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
{
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
#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;
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;
+}
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())
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;
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;
// 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
{
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 +
// 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 +
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;
}
bytes_written += 2;
assert(bytes_written == start_line_length);
- start_line.set(start_line_length, start_line_buffer, true);
-
return true;
}
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;
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;
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);
+}
#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<Http2Enums::INF__MAX_VALUE, Http2Enums::INF__NONE>;
+using Http2EventGen = EventGen<Http2Enums::EVENT__MAX_VALUE, Http2Enums::EVENT__NONE,
+ Http2Enums::HTTP2_GID>;
+
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] == ':'; }
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;
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)
{
// 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;
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;
}
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*)"";
-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
)
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
+++ /dev/null
-//--------------------------------------------------------------------------
-// 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 <thopeter@cisco.com>
-// unit test main
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "service_inspectors/http2_inspect/http2_stream_splitter.h"
-
-#include "protocols/packet.h"
-#include "service_inspectors/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 <CppUTest/CommandLineTestRunner.h>
-#include <CppUTest/TestHarness.h>
-#include <CppUTestExt/MockSupport.h>
-
-#include <cstdio>
-
-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);
-}
-
#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"
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)
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)
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)
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)
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)
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);
}
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)
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);
}
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);
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);
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);
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);
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);