The HTTP/2 inspector (H2I) will convert HTTP/2 frames into HTTP/1.1 message sections and feed them
to the new HTTP inspector (NHI) for further processing.
-The current implementation is the very first step. It splits an HTTP/2 stream into frames and
-forwards them for inspection. It does not interface with NHI, does not provide error detection and
-handling, and does not address the multiplexed nature of HTTP/2.
+The current implementation is the very first step. It splits an HTTP/2 stream into frame sections and
+forwards them for inspection. It does not interface with NHI and does not address the multiplexed
+nature of HTTP/2.
+
+The Http2StreamSplitter strips the frame headers from the frame data and stores them in separate
+buffers. As in NHI, long data frames are split into 16kb chunks for inspection. If a header frame is
+followed by continuation frames, all the header frames are flushed together for inspection. The
+frame headers from each frame are stored contiguously in the frame_header buffer. After cutting out
+the frame headers, the frame data is stored as a single block, consisting of the HPACK encoded
+HTTP/2 headers. HPACK decoding is under ongoing development. Decoded headers will be stored in the
+http2_decoded_header buffer.
EVENT_STRING_DECODE_FAILURE = 3,
EVENT_MISSING_CONTINUATION = 4,
EVENT_UNEXPECTED_CONTINUATION = 5,
+ EVENT_MISFORMATTED_HTTP2 = 6,
EVENT_PREFACE_MATCH_FAILURE = 7,
EVENT__MAX_VALUE
};
INF_INT_LEADING_ZEROS = 3,
INF_STRING_EMPTY_BUFF = 4,
INF_STRING_MISSING_BYTES = 5,
- INF_OUT_BUFF_OUT_OF_SPACE = 6,
+ INF_DECODED_HEADER_BUFF_OUT_OF_SPACE = 6,
INF_HUFFMAN_BAD_PADDING = 7,
INF_HUFFMAN_DECODED_EOS = 8,
INF_HUFFMAN_INCOMPLETE_CODE_PADDING = 9,
for (int k=0; k <= 1; k++)
{
delete[] frame_header[k];
- delete[] currently_processing_frame_header[k];
delete[] frame_data[k];
delete[] http2_decoded_header[k];
delete infractions[k];
#include "stream/stream_splitter.h"
#include "http2_enum.h"
+#include "http2_hpack_int_decode.h"
+#include "http2_hpack_string_decode.h"
using Http2Infractions = Infractions<Http2Enums::INF__MAX_VALUE, Http2Enums::INF__NONE>;
uint32_t*, HttpCommon::SourceId);
friend bool implement_get_buf(unsigned id, Http2FlowData*, HttpCommon::SourceId,
snort::InspectionBuffer&);
+ friend bool decode_headers(Http2FlowData* session_data, HttpCommon::SourceId source_id,
+ const uint8_t* raw_header_buffer, const uint32_t header_length);
+ friend bool decode_header_line(Http2FlowData* session_data, HttpCommon::SourceId source_id,
+ const uint8_t* encoded_header_buffer, const uint32_t encoded_header_buffer_length,
+ uint32_t& bytes_consumed, uint8_t* decoded_header_buffer,
+ const uint32_t decoded_header_buffer_length, uint32_t& bytes_written);
+ friend bool handle_dynamic_size_update(Http2FlowData* session_data,
+ HttpCommon::SourceId source_id, const uint8_t* encoded_header_buffer,
+ const uint32_t encoded_header_length, const Http2HpackIntDecode &decode_int,
+ uint32_t &bytes_consumed, uint32_t &bytes_written);
+ friend bool decode_literal_header_line(Http2FlowData* session_data,
+ HttpCommon::SourceId source_id, const uint8_t* encoded_header_buffer,
+ const uint32_t encoded_header_buffer_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_buffer_length, uint32_t &bytes_written);
+ friend 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, uint32_t &bytes_consumed,
+ uint8_t* decoded_header_buffer, const uint32_t decoded_header_length,
+ uint32_t &bytes_written);
+ friend bool decode_string_literal(Http2FlowData* session_data, HttpCommon::SourceId source_id,
+ const uint8_t* encoded_header_buffer, const uint32_t encoded_header_length,
+ const Http2HpackStringDecode &decode_string, bool is_field_name, uint32_t &bytes_consumed,
+ uint8_t* decoded_header_buffer, const uint32_t decoded_header_buffer_length,
+ uint32_t &bytes_written);
+ friend 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_buffer_length,
+ uint32_t &bytes_written);
size_t size_of() override
{ return sizeof(*this); }
// Internal to scan
bool continuation_expected[2] = { false, false };
- uint8_t* currently_processing_frame_header[2] = { nullptr, nullptr };
+ uint8_t currently_processing_frame_header[2][Http2Enums::FRAME_HEADER_LENGTH];
uint32_t inspection_section_length[2] = { 0, 0 };
uint32_t leftover_data[2] = { 0, 0 };
// transaction in NHI. Also as in NHI accessor methods will need to be added.
Http2Infractions* infractions[2] = { new Http2Infractions, new Http2Infractions };
Http2EventGen* events[2] = { new Http2EventGen, new Http2EventGen };
-
#ifdef REG_TEST
static uint64_t instance_count;
static const uint8_t VAL_MASK = 0x7F;
static const uint8_t FLAG_BIT = 0x80;
-Http2HpackIntDecode::Http2HpackIntDecode(uint8_t prefix, Http2EventGen* events,
- Http2Infractions* infractions) : prefix_mask(((uint16_t)1 << prefix) - 1), events(events),
- infractions(infractions)
+Http2HpackIntDecode::Http2HpackIntDecode(uint8_t prefix) : prefix_mask(((uint16_t)1 << prefix) - 1)
{
assert ((0 < prefix) && (prefix < 9));
}
bool Http2HpackIntDecode::translate(const uint8_t* in_buff, const uint32_t in_len,
- uint32_t& bytes_consumed, uint64_t& result)
+ uint32_t& bytes_consumed, uint64_t& result, Http2EventGen* events,
+ Http2Infractions* infractions) const
{
bytes_consumed = 0;
result = 0;
class Http2HpackIntDecode
{
public:
- Http2HpackIntDecode(uint8_t prefix, Http2EventGen* events, Http2Infractions* infractions);
+ Http2HpackIntDecode(uint8_t prefix);
bool translate(const uint8_t* in_buff, const uint32_t in_len, uint32_t& bytes_consumed,
- uint64_t& result);
+ uint64_t& result, Http2EventGen* events, Http2Infractions* infractions) const;
private:
const uint8_t prefix_mask;
- // FIXIT-M These will get merged into the corresponding frame/stream object infractions and
- // events
- Http2EventGen* const events;
- Http2Infractions* const infractions;
};
#endif
static const uint8_t min_decode_len[HUFFMAN_LOOKUP_MAX + 1] =
{5, 2, 2, 3, 5, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4};
-Http2HpackStringDecode::Http2HpackStringDecode(Http2EventGen* events,
- Http2Infractions* infractions) : decode7(new Http2HpackIntDecode(7, events, infractions)),
- events(events), infractions(infractions)
-{ }
-
bool Http2HpackStringDecode::translate(const uint8_t* in_buff, const uint32_t in_len,
- uint32_t& bytes_consumed, uint8_t* out_buff, const uint32_t out_len, uint32_t& bytes_written)
+ uint32_t& bytes_consumed, uint8_t* out_buff, const uint32_t out_len, uint32_t& bytes_written,
+ Http2EventGen* events, Http2Infractions* infractions) const
{
bytes_consumed = 0;
bytes_written = 0;
// Get length
uint64_t encoded_len;
- if (!decode7->translate(in_buff, in_len, bytes_consumed, encoded_len))
+ if (!decode7.translate(in_buff, in_len, bytes_consumed, encoded_len, events, infractions))
return false;
if (encoded_len > (uint64_t)(in_len - bytes_consumed))
return true;
if (!isHuffman)
- return get_string(in_buff, encoded_len, bytes_consumed, out_buff, out_len, bytes_written);
+ return get_string(in_buff, encoded_len, bytes_consumed, out_buff, out_len, bytes_written,
+ events, infractions);
return get_huffman_string(in_buff, encoded_len, bytes_consumed, out_buff, out_len,
- bytes_written);
-}
-
-Http2HpackStringDecode::~Http2HpackStringDecode()
-{
- assert(decode7 != nullptr);
- delete decode7;
+ bytes_written, events, infractions);
}
bool Http2HpackStringDecode::get_string(const uint8_t* in_buff, const uint32_t encoded_len,
- uint32_t& bytes_consumed, uint8_t* out_buff, const uint32_t out_len, uint32_t& bytes_written)
+ uint32_t& bytes_consumed, uint8_t* out_buff, const uint32_t out_len, uint32_t& bytes_written,
+ Http2EventGen* events, Http2Infractions* infractions) const
{
if (encoded_len > out_len)
{
- *infractions += INF_OUT_BUFF_OUT_OF_SPACE;
- events->create_event(EVENT_STRING_DECODE_FAILURE);
+ *infractions += INF_DECODED_HEADER_BUFF_OUT_OF_SPACE;
+ events->create_event(EVENT_MISFORMATTED_HTTP2);
return false;
}
// return is tail/padding
bool Http2HpackStringDecode::get_next_byte(const uint8_t* in_buff, const uint32_t last_byte,
uint32_t& bytes_consumed, uint8_t& cur_bit, uint8_t match_len, uint8_t& byte,
- bool& another_search)
+ bool& another_search) const
{
another_search = true;
cur_bit += match_len;
}
bool Http2HpackStringDecode::get_huffman_string(const uint8_t* in_buff, const uint32_t encoded_len,
- uint32_t& bytes_consumed, uint8_t* out_buff, const uint32_t out_len, uint32_t& bytes_written)
+ uint32_t& bytes_consumed, uint8_t* out_buff, const uint32_t out_len, uint32_t& bytes_written,
+ Http2EventGen* events, Http2Infractions* infractions) const
{
const uint32_t last_encoded_byte = bytes_consumed + encoded_len;
uint8_t byte;
const uint32_t max_length = floor(encoded_len * 8.0/5.0);
if (max_length > out_len)
{
- *infractions += INF_OUT_BUFF_OUT_OF_SPACE;
+ *infractions += INF_DECODED_HEADER_BUFF_OUT_OF_SPACE;
events->create_event(EVENT_STRING_DECODE_FAILURE);
return false;
}
class Http2HpackStringDecode
{
public:
- Http2HpackStringDecode(Http2EventGen* events, Http2Infractions* infractions);
+ Http2HpackStringDecode() : decode7(7) { }
bool translate(const uint8_t* in_buff, const uint32_t in_len, uint32_t& bytes_consumed,
- uint8_t* out_buff, const uint32_t out_len, uint32_t& bytes_written);
- ~Http2HpackStringDecode();
+ uint8_t* out_buff, const uint32_t out_len, uint32_t& bytes_written,
+ Http2EventGen* events, Http2Infractions* infractions) const;
private:
bool get_string(const uint8_t* in_buff, const uint32_t encoded_len, uint32_t& bytes_consumed,
- uint8_t* out_buff, const uint32_t out_len, uint32_t& bytes_written);
+ uint8_t* out_buff, const uint32_t out_len, uint32_t& bytes_written, Http2EventGen* events,
+ Http2Infractions* infractions) const;
bool get_huffman_string(const uint8_t* in_buff, const uint32_t encoded_len,
- uint32_t& bytes_consumed, uint8_t* out_buff, const uint32_t out_len, uint32_t& bytes_written);
+ uint32_t& bytes_consumed, uint8_t* out_buff, const uint32_t out_len, uint32_t&
+ bytes_written, Http2EventGen* events, Http2Infractions* infractions) const;
bool get_next_byte(const uint8_t* in_buff, const uint32_t last_byte,
- uint32_t& bytes_consumed, uint8_t& cur_bit, uint8_t match_len, uint8_t& byte, bool& another_search);
+ uint32_t& bytes_consumed, uint8_t& cur_bit, uint8_t match_len, uint8_t& byte,
+ bool& another_search) const;
- Http2HpackIntDecode* const decode7;
- Http2EventGen* const events;
- Http2Infractions* const infractions;
+ const Http2HpackIntDecode decode7;
};
#endif
delete[] session_data->http2_decoded_header[source_id];
session_data->http2_decoded_header[source_id] = nullptr;
session_data->continuation_expected[source_id] = false;
- delete[] session_data->currently_processing_frame_header[source_id];
- session_data->currently_processing_frame_header[source_id] = nullptr;
session_data->frames_aggregated[source_id] = 0;
+ session_data->header_octets_seen[source_id] = 0;
}
#include "http2_enum.h"
#include "http2_flow_data.h"
+#include "http2_hpack_int_decode.h"
+#include "http2_hpack_string_decode.h"
class Http2Inspect;
// FIXIT-M should return actual packet buffer size
unsigned max(snort::Flow*) override { return Http2Enums::MAX_OCTETS; }
+ friend bool decode_literal_header_line(Http2FlowData* session_data,
+ HttpCommon::SourceId source_id, 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);
+ friend 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);
+
private:
const HttpCommon::SourceId source_id;
+ static Http2HpackIntDecode decode_int7;
+ static Http2HpackIntDecode decode_int6;
+ static Http2HpackIntDecode decode_int5;
+ static Http2HpackIntDecode decode_int4;
+ static Http2HpackStringDecode decode_string;
};
snort::StreamSplitter::Status implement_scan(Http2FlowData* session_data, const uint8_t* data,
const snort::StreamBuffer implement_reassemble(Http2FlowData* session_data, unsigned total,
unsigned offset, const uint8_t* data, unsigned len, uint32_t flags,
HttpCommon::SourceId source_id);
+bool decode_headers(Http2FlowData* session_data, HttpCommon::SourceId source_id,
+ const uint8_t* raw_header_buffer, const uint32_t header_length, bool field_name);
enum ValidationResult { V_GOOD, V_BAD, V_TBD };
const uint32_t octets_seen);
#endif
-
#include <cassert>
-#include "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_input.h"
+#include "service_inspectors/http_inspect/http_test_manager.h"
#include "http2_flow_data.h"
+#include "http2_hpack_int_decode.h"
+#include "http2_hpack_string_decode.h"
+#include "http2_stream_splitter.h"
using namespace snort;
using namespace HttpCommon;
using namespace Http2Enums;
-static uint32_t get_frame_length(const uint8_t *frame_buffer)
+#define STATIC_TABLE_MAX_INDEX 61
+
+// FIXIT-H remove these declarations once implemented, for some reason this makes the compiler
+// happy for build alt
+bool decode_static_table_index(void);
+bool decode_dynamic_table_index(void);
+
+Http2HpackIntDecode Http2StreamSplitter::decode_int7(7);
+Http2HpackIntDecode Http2StreamSplitter::decode_int6(6);
+Http2HpackIntDecode Http2StreamSplitter::decode_int5(5);
+Http2HpackIntDecode Http2StreamSplitter::decode_int4(4);
+Http2HpackStringDecode Http2StreamSplitter::decode_string;
+
+static uint32_t get_frame_length(const uint8_t* frame_buffer)
{
return (frame_buffer[0] << 16) + (frame_buffer[1] << 8) + frame_buffer[2];
}
-static uint8_t get_frame_type(const uint8_t *frame_buffer)
+static uint8_t get_frame_type(const uint8_t* frame_buffer)
{
const uint8_t frame_type_index = 3;
if (frame_buffer)
return FT__NONE;
}
-static uint8_t get_frame_flags(const uint8_t *frame_buffer)
+static uint8_t get_frame_flags(const uint8_t* frame_buffer)
{
const uint8_t frame_flags_index = 4;
if (frame_buffer)
// Have full inspection section, flush and update leftover
*flush_offset = session_data->inspection_section_length[source_id];
- session_data->leftover_data[source_id] -= session_data->inspection_section_length[source_id];
+ session_data->leftover_data[source_id] -=
+ session_data->inspection_section_length[source_id];
session_data->octets_seen[source_id] = 0;
}
else
{
// frame with header
- // 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
+ // 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->currently_processing_frame_header[source_id] == nullptr)
+ if (session_data->header_octets_seen[source_id] == 0)
{
session_data->header_coming[source_id] = true;
- session_data->currently_processing_frame_header[source_id] = new uint8_t[FRAME_HEADER_LENGTH];
session_data->octets_seen[source_id] = 0;
session_data->header_octets_seen[source_id] = 0;
- session_data->frames_aggregated[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.
- for (uint32_t k = 0; (k < length) && (session_data->header_octets_seen[source_id] <
- FRAME_HEADER_LENGTH); k++, session_data->header_octets_seen[source_id]++)
+ // 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->header_octets_seen[source_id] < FRAME_HEADER_LENGTH)
{
- session_data->currently_processing_frame_header[source_id]
- [session_data->header_octets_seen[source_id]] = data[k];
+ uint32_t remaining_header = FRAME_HEADER_LENGTH -
+ session_data->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->header_octets_seen[source_id],
+ data_pos, remaining_header_in_data);
+ session_data->header_octets_seen[source_id] += remaining_header_in_data;
+ if (session_data->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;
}
- if (session_data->header_octets_seen[source_id] < FRAME_HEADER_LENGTH)
+
+ uint8_t type = get_frame_type(session_data->currently_processing_frame_header[source_id]);
+
+ if (session_data->continuation_expected[source_id] && type != FT_CONTINUATION)
{
- session_data->octets_seen[source_id] += length;
- status = StreamSplitter::SEARCH;
+ *session_data->infractions[source_id] += INF_MISSING_CONTINUATION;
+ session_data->events[source_id]->create_event(EVENT_MISSING_CONTINUATION);
+ status = StreamSplitter::ABORT;
break;
}
- uint8_t type = get_frame_type(session_data->currently_processing_frame_header[source_id]);
// Frame length does not include the frame header
uint32_t const frame_length = get_frame_length(session_data->
if ((type == FT_DATA) && (frame_length > DATA_SECTION_SIZE))
{
// Break up long data frames into pieces for detection
- session_data->inspection_section_length[source_id] = DATA_SECTION_SIZE + FRAME_HEADER_LENGTH;
+ session_data->inspection_section_length[source_id] = DATA_SECTION_SIZE +
+ FRAME_HEADER_LENGTH;
}
else if (frame_length + FRAME_HEADER_LENGTH > MAX_OCTETS)
{
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];
+ *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;
session_data->continuation_expected[source_id] = true;
session_data->header_octets_seen[source_id] = 0;
- session_data->frames_aggregated[source_id] += 1;
status = StreamSplitter::SEARCH;
- data += frame_length + FRAME_HEADER_LENGTH;
+ 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->header_octets_seen[source_id] = 0;
- session_data->frames_aggregated[source_id] += 1;
status = StreamSplitter::SEARCH;
- data += frame_length + FRAME_HEADER_LENGTH;
+ data_pos = data + *flush_offset;
+ remaining_length = length - *flush_offset;
}
else
{
session_data->events[source_id]->create_event(EVENT_UNEXPECTED_CONTINUATION);
status = StreamSplitter::ABORT;
}
- else if (session_data->continuation_expected[source_id])
- {
- *session_data->infractions[source_id] += INF_MISSING_CONTINUATION;
- session_data->events[source_id]->create_event(EVENT_MISSING_CONTINUATION);
- status = StreamSplitter::ABORT;
- }
- } while (status != StreamSplitter::FLUSH && *flush_offset < length);
+ } while (status == StreamSplitter::SEARCH && remaining_length > 0);
}
return status;
}
+// FIXIT-M If there are any errors in header decoding, this currently tells stream not to send
+// headers to detection. This behavior may need to be changed.
const StreamBuffer implement_reassemble(Http2FlowData* session_data, unsigned total,
unsigned offset, const uint8_t* data, unsigned len, uint32_t flags,
HttpCommon::SourceId source_id)
if (type == FT_HEADERS || type == FT_CONTINUATION)
{
assert(session_data->http2_decoded_header[source_id] == nullptr);
- session_data->http2_decoded_header[source_id] = new uint8_t[MAX_OCTETS];
//FIXIT-H This will eventually be the decoded header buffer. For now just copy
//directly
- memcpy(session_data->http2_decoded_header[source_id],
- session_data->frame_data[source_id], session_data->frame_data_size[source_id]);
- session_data->http2_decoded_header_size[source_id] = session_data->frame_data_size[source_id];
+ if (!decode_headers(session_data, source_id, session_data->frame_data[source_id],
+ session_data->frame_data_size[source_id]))
+ return frame_buf;
}
}
// Return 0-length non-null buffer to stream which signals detection required, but don't
return V_GOOD;
}
+
+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)
+{
+ bool ret = true;
+ uint32_t length = in_length;
+ bytes_written = 0;
+
+ if (in_length > decoded_header_length)
+ {
+ length = MAX_OCTETS - session_data->http2_decoded_header_size[source_id];
+ *session_data->infractions[source_id] += INF_DECODED_HEADER_BUFF_OUT_OF_SPACE;
+ session_data->events[source_id]->create_event(EVENT_MISFORMATTED_HTTP2);
+ ret = false;
+ }
+
+ memcpy((void*)decoded_header_buffer, (void*) in_buffer, length);
+ bytes_written = length;
+ return ret;
+}
+
+bool decode_string_literal(Http2FlowData* session_data, HttpCommon::SourceId source_id,
+ const uint8_t* encoded_header_buffer, const uint32_t encoded_header_length,
+ const Http2HpackStringDecode &decode_string, bool is_field_name, uint32_t &bytes_consumed,
+ uint8_t* decoded_header_buffer, const uint32_t decoded_header_length,
+ uint32_t &bytes_written)
+{
+ uint32_t decoded_bytes_written;
+ uint32_t encoded_bytes_consumed;
+ uint32_t encoded_header_offset = 0;
+ bytes_written = 0;
+ bytes_consumed = 0;
+
+ if (is_field_name)
+ {
+ // skip over parsed pattern and zeroed index
+ encoded_header_offset++;
+ bytes_consumed++;
+ }
+
+ if (!decode_string.translate(encoded_header_buffer + encoded_header_offset,
+ encoded_header_length, encoded_bytes_consumed, decoded_header_buffer,
+ decoded_header_length, decoded_bytes_written, session_data->events[source_id],
+ session_data->infractions[source_id]))
+ {
+ return false;
+ }
+
+ bytes_consumed += encoded_bytes_consumed;
+ bytes_written += decoded_bytes_written;
+
+ if (is_field_name)
+ {
+ if (!write_decoded_headers(session_data, source_id, (const uint8_t*)": ", 2,
+ decoded_header_buffer + bytes_written, decoded_header_length -
+ bytes_written, decoded_bytes_written))
+ return false;
+ }
+ else
+ {
+ if (!write_decoded_headers(session_data, source_id, (const uint8_t*)"\r\n", 2,
+ decoded_header_buffer + bytes_written, decoded_header_length -
+ bytes_written, decoded_bytes_written))
+ return false;
+ }
+
+ bytes_written += decoded_bytes_written;
+
+ return true;
+}
+
+// FIXIT-H implement
+bool decode_static_table_index(void)
+{
+ return false;
+}
+
+// FIXIT-H implement
+bool decode_dynamic_table_index(void)
+{
+ return false;
+}
+
+// FIXIT-H Will be incrementally updated to actually decode indexes. For now just copies encoded
+// index directly to decoded_header_buffer
+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, 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]))
+ {
+ return false;
+ }
+
+ if (index <= STATIC_TABLE_MAX_INDEX)
+ decode_static_table_index();
+ else
+ decode_dynamic_table_index();
+
+ if (!write_decoded_headers(session_data, source_id, encoded_header_buffer, bytes_consumed,
+ decoded_header_buffer, decoded_header_length, bytes_written))
+ return false;
+
+ return true;
+}
+
+bool decode_literal_header_line(Http2FlowData* session_data, HttpCommon::SourceId source_id,
+ 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)
+{
+ bytes_written = 0;
+ bytes_consumed = 0;
+ uint32_t partial_bytes_consumed;
+ uint32_t partial_bytes_written;
+
+ // indexed field name
+ if (encoded_header_buffer[0] & name_index_mask)
+ {
+ if (!decode_index(session_data, source_id, encoded_header_buffer,
+ encoded_header_length, decode_int, partial_bytes_consumed,
+ decoded_header_buffer, decoded_header_length, partial_bytes_written))
+ return false;
+ }
+ // literal field name
+ else
+ {
+ if (!decode_string_literal(session_data, source_id, encoded_header_buffer,
+ encoded_header_length, Http2StreamSplitter::decode_string, true,
+ partial_bytes_consumed, decoded_header_buffer, decoded_header_length,
+ partial_bytes_written))
+ return false;
+ }
+
+ bytes_consumed += partial_bytes_consumed;
+ bytes_written += partial_bytes_written;
+
+ // value is always literal
+ if (!decode_string_literal(session_data, source_id, encoded_header_buffer +
+ partial_bytes_consumed, encoded_header_length - partial_bytes_consumed,
+ Http2StreamSplitter::decode_string, false, partial_bytes_consumed,
+ decoded_header_buffer + partial_bytes_written, decoded_header_length -
+ partial_bytes_written, partial_bytes_written))
+ return false;
+
+ bytes_consumed += partial_bytes_consumed;
+ bytes_written += partial_bytes_written;
+
+ return true;
+}
+
+// FIXIT-M Will be updated to actually update dynamic table size. For now just skips over
+bool handle_dynamic_size_update(Http2FlowData* session_data, HttpCommon::SourceId source_id,
+ const uint8_t* encoded_header_buffer, const uint32_t encoded_header_length,
+ const Http2HpackIntDecode &decode_int, uint32_t &bytes_consumed, uint32_t &bytes_written)
+{
+ uint64_t decoded_int;
+ uint32_t encoded_bytes_consumed;
+ bytes_consumed = 0;
+ 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]))
+ {
+ return false;
+ }
+#ifdef REG_TEST
+ //FIXIT-M remove when dynamic size updates are handled
+ if (HttpTestManager::use_test_output(HttpTestManager::IN_HTTP2))
+ {
+ fprintf(HttpTestManager::get_output_file(),
+ "Skipping HPACK dynamic size update: %lu\n", decoded_int);
+ }
+#endif
+ bytes_consumed += encoded_bytes_consumed;
+
+ return true;
+}
+
+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)
+{
+ 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 decode_index(session_data, source_id, encoded_header_buffer, encoded_header_length,
+ Http2StreamSplitter::decode_int7, 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 decode_literal_header_line(session_data, source_id, encoded_header_buffer,
+ encoded_header_length, literal_index_name_index_mask,
+ Http2StreamSplitter::decode_int6, bytes_consumed, decoded_header_buffer,
+ decoded_header_length, bytes_written);
+
+ // literal header field representation not to be added to dynamic table
+ // Note that this includes two representation types from the RFC - literal without index and
+ // 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 decode_literal_header_line(session_data, source_id, encoded_header_buffer,
+ encoded_header_length, literal_no_index_name_index_mask,
+ Http2StreamSplitter::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,
+ encoded_header_length, Http2StreamSplitter::decode_int5, bytes_consumed, bytes_written);
+}
+
+//FIXIT-H This will eventually be the decoded header buffer. For now only string literals are
+//decoded
+bool decode_headers(Http2FlowData* session_data, HttpCommon::SourceId source_id,
+ const uint8_t* encoded_header_buffer, const uint32_t header_length)
+{
+
+ uint32_t total_bytes_consumed = 0;
+ uint32_t line_bytes_consumed = 0;
+ uint32_t line_bytes_written = 0;
+ bool success = true;
+ session_data->http2_decoded_header[source_id] = new uint8_t[MAX_OCTETS];
+ session_data->http2_decoded_header_size[source_id] = 0;
+
+ while (total_bytes_consumed < header_length)
+ {
+ if (!decode_header_line(session_data, source_id,
+ encoded_header_buffer + total_bytes_consumed, header_length - total_bytes_consumed,
+ line_bytes_consumed, session_data->http2_decoded_header[source_id] +
+ session_data->http2_decoded_header_size[source_id], MAX_OCTETS -
+ session_data->http2_decoded_header_size[source_id], line_bytes_written))
+ {
+ success = false;
+ break;
+ }
+ total_bytes_consumed += line_bytes_consumed;
+ session_data->http2_decoded_header_size[source_id] += line_bytes_written;
+ }
+
+ if (!success)
+ {
+#ifdef REG_TEST
+ if (HttpTestManager::use_test_output(HttpTestManager::IN_HTTP2))
+ {
+ fprintf(HttpTestManager::get_output_file(),
+ "Error decoding headers. ");
+ if(session_data->http2_decoded_header_size[source_id] > 0)
+ Field(session_data->http2_decoded_header_size[source_id],
+ session_data->http2_decoded_header[source_id]).print(
+ HttpTestManager::get_output_file(), "Partially Decoded Header");
+ }
+#endif
+ return false;
+ }
+
+ // write the last crlf to end the header
+ if (!write_decoded_headers(session_data, source_id, (const uint8_t*)"\r\n", 2,
+ session_data->http2_decoded_header[source_id] +
+ session_data->http2_decoded_header_size[source_id], MAX_OCTETS -
+ session_data->http2_decoded_header_size[source_id], line_bytes_written))
+ return false;
+ session_data->http2_decoded_header_size[source_id] += line_bytes_written;
+
+#ifdef REG_TEST
+ if (HttpTestManager::use_test_output(HttpTestManager::IN_HTTP2))
+ {
+ Field(session_data->http2_decoded_header_size[source_id],
+ session_data->http2_decoded_header[source_id]).print(HttpTestManager::get_output_file(),
+ "Decoded Header");
+ }
+#endif
+
+ return success;
+}
{ EVENT_STRING_DECODE_FAILURE, "error in HPACK string value" },
{ EVENT_MISSING_CONTINUATION, "missing continuation frame"},
{ EVENT_UNEXPECTED_CONTINUATION, "unexpected continuation frame"},
+ { EVENT_MISFORMATTED_HTTP2, "misformatted HTTP/2 traffic"},
{ EVENT_PREFACE_MATCH_FAILURE, "HTTP/2 connection preface does not match"},
{ 0, nullptr }
};
add_cpputest( http2_stream_splitter_impl_test
SOURCES
../http2_flow_data.cc
+ ../http2_hpack_int_decode.cc
+ ../http2_hpack_string_decode.cc
+ ../http2_huffman_state_machine.cc
../http2_stream_splitter_impl.cc
../http2_module.cc
../http2_tables.cc
{
Http2EventGen events;
Http2Infractions inf;
- Http2HpackIntDecode* const decode = new Http2HpackIntDecode(5, &events, &inf);
+ Http2HpackIntDecode* const decode = new Http2HpackIntDecode(5);
void teardown() override
{
// decode
uint32_t bytes_processed = 0;
uint64_t res = 0;
- bool success = decode->translate(&buf, 1, bytes_processed, res);
+ bool success = decode->translate(&buf, 1, bytes_processed, res, &events, &inf);
// check results
CHECK(success == true);
CHECK(res == 10);
// decode
uint32_t bytes_processed = 0;
uint64_t res = 0;
- bool success = decode->translate(buf, 2, bytes_processed, res);
+ bool success = decode->translate(buf, 2, bytes_processed, res, &events, &inf);
// check results
CHECK(success == true);
CHECK(res == 10);
// decode
uint32_t bytes_processed = 0;
uint64_t res = 0;
- bool success = decode->translate(buf, 3, bytes_processed, res);
+ bool success = decode->translate(buf, 3, bytes_processed, res, &events, &inf);
// check results
CHECK(success == true);
CHECK(res == 1337);
TEST(http2_hpack_int_decode_success, 42_using_8_bits)
{
// prepare decode object
- Http2HpackIntDecode decode_8(8, &events, &inf);
+ Http2HpackIntDecode decode_8(8);
// prepare buf to decode - example from RFC 7541 c.1.3
uint8_t buf = 42;
// decode
uint32_t bytes_processed = 0;
uint64_t res = 0;
- bool success = decode_8.translate(&buf, 1, bytes_processed, res);
+ bool success = decode_8.translate(&buf, 1, bytes_processed, res, &events, &inf);
// check results
CHECK(success == true);
CHECK(res == 42);
// decode
uint32_t bytes_processed = 0;
uint64_t res = 0;
- bool success = decode->translate(buf, 11, bytes_processed, res);
+ bool success = decode->translate(buf, 11, bytes_processed, res, &events, &inf);
// check results
CHECK(success == true);
CHECK(res == 0xFFFFFFFFFFFFFFFF);
// decode
uint32_t bytes_processed = 0;
uint64_t res = 0;
- bool success = decode->translate(buf, 2, bytes_processed, res);
+ bool success = decode->translate(buf, 2, bytes_processed, res, &events, &inf);
// check results
CHECK(success == true);
CHECK(res == 31);
// decode
uint32_t bytes_processed = 0;
uint64_t res = 0;
- bool success = decode->translate(&buf, 1, bytes_processed, res);
+ bool success = decode->translate(&buf, 1, bytes_processed, res, &events, &inf);
// check results
CHECK(success == true);
CHECK(res == 0);
// prepare decode object
Http2EventGen local_events;
Http2Infractions local_inf;
- Http2HpackIntDecode decode_8(8, &local_events, &local_inf);
+ Http2HpackIntDecode decode_8(8);
// prepare buf to decode - use buf length 0
uint8_t buf = 42;
// decode
uint32_t bytes_processed = 0;
uint64_t res = 0;
- bool success = decode_8.translate(&buf, 0, bytes_processed, res);
+ bool success = decode_8.translate(&buf, 0, bytes_processed, res, &local_events, &local_inf);
// check results
CHECK(success == false);
CHECK(bytes_processed == 0);
// prepare decode object
Http2EventGen local_events;
Http2Infractions local_inf;
- Http2HpackIntDecode local_decode(5, &local_events, &local_inf);
+ Http2HpackIntDecode local_decode(5);
// prepare buf to decode - buffer ends before decode finished
uint8_t buf[3] = { 31, 0x9a, 10 };
// decode
uint32_t bytes_processed = 0;
uint64_t res = 0;
- bool success = local_decode.translate(buf, 2, bytes_processed, res);
+ bool success = local_decode.translate(buf, 2, bytes_processed, res, &local_events, &local_inf);
// check results
CHECK(success == false);
CHECK(bytes_processed == 2);
// prepare decode object
Http2EventGen local_events;
Http2Infractions local_inf;
- Http2HpackIntDecode local_decode(5, &local_events, &local_inf);
+ Http2HpackIntDecode local_decode(5);
// prepare buf to decode - multiplier > 63
uint8_t buf[13] = { 31, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 1 };
// decode
uint32_t bytes_processed = 0;
uint64_t res = 0;
- bool success = local_decode.translate(buf, 13, bytes_processed, res);
+ bool success = local_decode.translate(buf, 13, bytes_processed, res, &local_events, &local_inf);
// check results
CHECK(success == false);
CHECK(bytes_processed == 11);
// prepare decode object
Http2EventGen local_events;
Http2Infractions local_inf;
- Http2HpackIntDecode local_decode(5, &local_events, &local_inf);
+ Http2HpackIntDecode local_decode(5);
// prepare buf to decode - value to add itself is already creating overflow
uint8_t buf[12] = { 31, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xFF, 1 };
// decode
uint32_t bytes_processed = 0;
uint64_t res = 0;
- bool success = local_decode.translate(buf, 12, bytes_processed, res);
+ bool success = local_decode.translate(buf, 12, bytes_processed, res, &local_events, &local_inf);
// check results
CHECK(success == false);
CHECK(bytes_processed == 11);
// prepare decode object
Http2EventGen local_events;
Http2Infractions local_inf;
- Http2HpackIntDecode local_decode(5, &local_events, &local_inf);
+ Http2HpackIntDecode local_decode(5);
// prepare buf to decode - adding value to result kept so far creates overflow
uint8_t buf[11] = { 31, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 1 };
// decode
uint32_t bytes_processed = 0;
uint64_t res = 0;
- bool success = local_decode.translate(buf, 11, bytes_processed, res);
+ bool success = local_decode.translate(buf, 11, bytes_processed, res, &local_events, &local_inf);
// check results
CHECK(success == false);
CHECK(bytes_processed == 11);
// prepare decode object
Http2EventGen local_events;
Http2Infractions local_inf;
- Http2HpackIntDecode local_decode(5, &local_events, &local_inf);
+ Http2HpackIntDecode local_decode(5);
// prepare buf to decode - 2^64
uint8_t buf[11] = { 31, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 1 };
// decode
uint32_t bytes_processed = 0;
uint64_t res = 0;
- bool success = local_decode.translate(buf, 11, bytes_processed, res);
+ bool success = local_decode.translate(buf, 11, bytes_processed, res, &local_events, &local_inf);
// check results
CHECK(success == false);
CHECK(bytes_processed == 11);
// prepare decode object
Http2EventGen local_events;
Http2Infractions local_inf;
- Http2HpackIntDecode local_decode(5, &local_events, &local_inf);
+ Http2HpackIntDecode local_decode(5);
// prepare buf to decode - MSB is zero
uint8_t buf[3] = { 31, 0x80, 0 };
// decode
uint32_t bytes_processed = 0;
uint64_t res = 0;
- bool success = local_decode.translate(buf, 3, bytes_processed, res);
+ bool success = local_decode.translate(buf, 3, bytes_processed, res, &local_events, &local_inf);
// check results
CHECK(success == true);
CHECK(res == 31);
// prepare decode object
Http2EventGen local_events;
Http2Infractions local_inf;
- Http2HpackIntDecode local_decode(5, &local_events, &local_inf);
+ Http2HpackIntDecode local_decode(5);
// prepare buf to decode - multiplier 63 doesn't create overflow, should alert on
// leading 0
uint8_t buf[11] = { 31, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0 };
// decode
uint32_t bytes_processed = 0;
uint64_t res = 0;
- bool success = local_decode.translate(buf, 11, bytes_processed, res);
+ bool success = local_decode.translate(buf, 11, bytes_processed, res, &local_events, &local_inf);
// check results
CHECK(success == true);
CHECK(res == 0x7FFFFFFFFFFFFFFF);
{
Http2EventGen events;
Http2Infractions inf;
- Http2HpackStringDecode* const decode = new Http2HpackStringDecode(&events, &inf);
+ Http2HpackStringDecode* const decode = new Http2HpackStringDecode();
void teardown() override
{
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[10];
- bool success = decode->translate(buf, 11, bytes_processed, res, 10, bytes_written);
+ bool success = decode->translate(buf, 11, bytes_processed, res, 10, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(memcmp(res, "custom-key", 10) == 0);
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[12];
- bool success = decode->translate(buf, 12, bytes_processed, res, 12, bytes_written);
+ bool success = decode->translate(buf, 12, bytes_processed, res, 12, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(memcmp(res, "custom-key", 10) == 0);
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[130];
- bool success = decode->translate(buf, 130, bytes_processed, res, 130, bytes_written);
+ bool success = decode->translate(buf, 130, bytes_processed, res, 130, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == 130);
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res = 10; // random value, just to check it wasn't overwritten
- bool success = decode->translate(&buf, 1, bytes_processed, &res, 1, bytes_written);
+ bool success = decode->translate(&buf, 1, bytes_processed, &res, 1, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == 1);
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res = 0;
- bool success = decode->translate(buf, 2, bytes_processed, &res, 1, bytes_written);
+ bool success = decode->translate(buf, 2, bytes_processed, &res, 1, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == 2);
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[MAX_OCTETS];
bool success = decode->translate(buf, MAX_OCTETS, bytes_processed, res,
- MAX_OCTETS, bytes_written);
+ MAX_OCTETS, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == MAX_OCTETS);
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[1];
- bool success = decode->translate(buf, 2, bytes_processed, res, 1, bytes_written);
+ bool success = decode->translate(buf, 2, bytes_processed, res, 1, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == 2);
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[1];
- bool success = decode->translate(buf, 2, bytes_processed, res, 1, bytes_written);
+ bool success = decode->translate(buf, 2, bytes_processed, res, 1, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == 2);
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[3];
- bool success = decode->translate(buf, 3, bytes_processed, res, 3, bytes_written);
+ bool success = decode->translate(buf, 3, bytes_processed, res, 3, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == 3);
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[3];
- bool success = decode->translate(buf, 3, bytes_processed, res, 3, bytes_written);
+ bool success = decode->translate(buf, 3, bytes_processed, res, 3, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == 3);
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[19];
- bool success = decode->translate(buf, 13, bytes_processed, res, 19, bytes_written);
+ bool success = decode->translate(buf, 13, bytes_processed, res, 19, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == 13);
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[9];
- bool success = decode->translate(buf, 7, bytes_processed, res, 9, bytes_written);
+ bool success = decode->translate(buf, 7, bytes_processed, res, 9, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == 7);
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[12];
- bool success = decode->translate(buf, 9, bytes_processed, res, 12, bytes_written);
+ bool success = decode->translate(buf, 9, bytes_processed, res, 12, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == 9);
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[14];
- bool success = decode->translate(buf, 10, bytes_processed, res, 14, bytes_written);
+ bool success = decode->translate(buf, 10, bytes_processed, res, 14, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == 10);
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[35];
- bool success = decode->translate(buf, 23, bytes_processed, res, 35, bytes_written);
+ bool success = decode->translate(buf, 23, bytes_processed, res, 35, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == 23);
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[73];
- bool success = decode->translate(buf, 46, bytes_processed, res, 73, bytes_written);
+ bool success = decode->translate(buf, 46, bytes_processed, res, 73, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == 46);
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[6];
- bool success = decode->translate(buf, 5, bytes_processed, res, 6, bytes_written);
+ bool success = decode->translate(buf, 5, bytes_processed, res, 6, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == 5);
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[32];
- bool success = decode->translate(buf, 21, bytes_processed, res, 32, bytes_written);
+ bool success = decode->translate(buf, 21, bytes_processed, res, 32, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == 21);
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[36];
- bool success = decode->translate(buf, 24, bytes_processed, res, 36, bytes_written);
+ bool success = decode->translate(buf, 24, bytes_processed, res, 36, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == 24);
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[12];
- bool success = decode->translate(buf, 9, bytes_processed, res, 12, bytes_written);
+ bool success = decode->translate(buf, 9, bytes_processed, res, 12, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == 9);
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[70];
- bool success = decode->translate(buf, 45, bytes_processed, res, 70, bytes_written);
+ bool success = decode->translate(buf, 45, bytes_processed, res, 70, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == 45);
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[88];
uint8_t expected[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
- bool success = decode->translate(buf, 55, bytes_processed, res, 88, bytes_written);
+ bool success = decode->translate(buf, 55, bytes_processed, res, 88, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == 55);
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[93];
uint8_t expected[16] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F};
- bool success = decode->translate(buf, 58, bytes_processed, res, 93, bytes_written);
+ bool success = decode->translate(buf, 58, bytes_processed, res, 93, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == 58);
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[79];
uint8_t expected[17] = {0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F};
- bool success = decode->translate(buf, 49, bytes_processed, res, 79, bytes_written);
+ bool success = decode->translate(buf, 49, bytes_processed, res, 79, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == 49);
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[76];
uint8_t expected[16] = {0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F};
- bool success = decode->translate(buf, 47, bytes_processed, res, 76, bytes_written);
+ bool success = decode->translate(buf, 47, bytes_processed, res, 76, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == 47);
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[74];
uint8_t expected[16] = {0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF};
- bool success = decode->translate(buf, 46, bytes_processed, res, 74, bytes_written);
+ bool success = decode->translate(buf, 46, bytes_processed, res, 74, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == 46);
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[73];
uint8_t expected[16] = {0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF};
- bool success = decode->translate(buf, 45, bytes_processed, res, 73, bytes_written);
+ bool success = decode->translate(buf, 45, bytes_processed, res, 73, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == 45);
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[80];
uint8_t expected[16] = {0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF};
- bool success = decode->translate(buf, 50, bytes_processed, res, 80, bytes_written);
+ bool success = decode->translate(buf, 50, bytes_processed, res, 80, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == 50);
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[82];
uint8_t expected[16] = {0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF};
- bool success = decode->translate(buf, 51, bytes_processed, res, 82, bytes_written);
+ bool success = decode->translate(buf, 51, bytes_processed, res, 82, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == 51);
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[76];
uint8_t expected[16] = {0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF};
- bool success = decode->translate(buf, 47, bytes_processed, res, 76, bytes_written);
+ bool success = decode->translate(buf, 47, bytes_processed, res, 76, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == 47);
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[88];
uint8_t expected[16] = {0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF};
- bool success = decode->translate(buf, 55, bytes_processed, res, 88, bytes_written);
+ bool success = decode->translate(buf, 55, bytes_processed, res, 88, bytes_written, &events, &inf);
// check results
CHECK(success == true);
CHECK(bytes_processed == 55);
// prepare decode object
Http2EventGen local_events;
Http2Infractions local_inf;
- Http2HpackStringDecode local_decode(&local_events, &local_inf);
+ Http2HpackStringDecode local_decode;
// prepare buf to decode - use field length 0
uint8_t buf = 0;
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res;
- bool success = local_decode.translate(&buf, 0, bytes_processed, &res, 1, bytes_written);
+ bool success = local_decode.translate(&buf, 0, bytes_processed, &res, 1, bytes_written, &local_events, &local_inf);
// check results
CHECK(success == false);
CHECK(bytes_processed == 0);
// prepare decode object
Http2EventGen local_events;
Http2Infractions local_inf;
- Http2HpackStringDecode local_decode(&local_events, &local_inf);
+ Http2HpackStringDecode local_decode;
// prepare buf to decode - length is 1, no string
uint8_t buf = 1;
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[2];
- bool success = local_decode.translate(&buf, 1, bytes_processed, res, 2, bytes_written);
+ bool success = local_decode.translate(&buf, 1, bytes_processed, res, 2, bytes_written, &local_events, &local_inf);
// check results
CHECK(success == false);
CHECK(bytes_written == 0);
// prepare decode object
Http2EventGen local_events;
Http2Infractions local_inf;
- Http2HpackStringDecode local_decode(&local_events, &local_inf);
+ Http2HpackStringDecode local_decode;
// prepare buf to decode - bad int
uint8_t buf[2] = { 0x7f, 0x80 };
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[2];
- bool success = local_decode.translate(buf, 2, bytes_processed, res, 2, bytes_written);
+ bool success = local_decode.translate(buf, 2, bytes_processed, res, 2, bytes_written, &local_events, &local_inf);
// check results
CHECK(success == false);
CHECK(bytes_processed == 2);
// prepare decode object
Http2EventGen local_events;
Http2Infractions local_inf;
- Http2HpackStringDecode local_decode(&local_events, &local_inf);
+ Http2HpackStringDecode local_decode;
// prepare buf to decode - int + string == MAX_OCTETS+1 (Field limitation + 1)
uint8_t buf[MAX_OCTETS];
buf[0] = 0x7F;
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[MAX_OCTETS];
bool success = local_decode.translate(buf, MAX_OCTETS, bytes_processed, res,
- MAX_OCTETS, bytes_written);
+ MAX_OCTETS, bytes_written, &local_events, &local_inf);
// check results
CHECK(success == false);
CHECK(bytes_processed == 4);
// prepare decode object
Http2EventGen local_events;
Http2Infractions local_inf;
- Http2HpackStringDecode local_decode(&local_events, &local_inf);
+ Http2HpackStringDecode local_decode;
// prepare buf to decode
uint8_t buf[MAX_OCTETS];
buf[0] = 0x7F;
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[MAX_OCTETS-5];
bool success = local_decode.translate(buf, MAX_OCTETS, bytes_processed, res,
- MAX_OCTETS-5, bytes_written);
+ MAX_OCTETS-5, bytes_written, &local_events, &local_inf);
// check results
CHECK(success == false);
CHECK(bytes_processed == 4);
CHECK(bytes_written == 0);
- CHECK(local_inf.get_raw() == (1<<INF_OUT_BUFF_OUT_OF_SPACE));
- CHECK(local_events.get_raw() == (1<<(EVENT_STRING_DECODE_FAILURE-1)));
+ CHECK(local_inf.get_raw() == (1<<INF_DECODED_HEADER_BUFF_OUT_OF_SPACE));
+ CHECK(local_events.get_raw() == (1<<(EVENT_MISFORMATTED_HTTP2-1)));
}
TEST(http2_hpack_string_decode_infractions, huffman_1_byte_bad_padding)
// prepare decode object
Http2EventGen local_events;
Http2Infractions local_inf;
- Http2HpackStringDecode local_decode(&local_events, &local_inf);
+ Http2HpackStringDecode local_decode;
// prepare buf to decode - bad padding
uint8_t buf[2] = { 0x81, 0x54 };
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[2];
- bool success = local_decode.translate(buf, 2, bytes_processed, res, 2, bytes_written);
+ bool success = local_decode.translate(buf, 2, bytes_processed, res, 2, bytes_written, &local_events, &local_inf);
// check results
CHECK(success == false);
CHECK(bytes_processed == 2);
// prepare decode object
Http2EventGen local_events;
Http2Infractions local_inf;
- Http2HpackStringDecode local_decode(&local_events, &local_inf);
+ Http2HpackStringDecode local_decode;
// prepare buf to decode - Incomplete code 0xFF
uint8_t buf[2] = { 0x81, 0xFF };
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[2];
- bool success = local_decode.translate(buf, 2, bytes_processed, res, 2, bytes_written);
+ bool success = local_decode.translate(buf, 2, bytes_processed, res, 2, bytes_written, &local_events, &local_inf);
// check results
CHECK(success == false);
CHECK(bytes_processed == 2);
// prepare decode object
Http2EventGen local_events;
Http2Infractions local_inf;
- Http2HpackStringDecode local_decode(&local_events, &local_inf);
+ Http2HpackStringDecode local_decode;
// prepare buf to decode - Incomplete code 0xFE
uint8_t buf[2] = { 0x81, 0xFE };
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[2];
- bool success = local_decode.translate(buf, 2, bytes_processed, res, 2, bytes_written);
+ bool success = local_decode.translate(buf, 2, bytes_processed, res, 2, bytes_written, &local_events, &local_inf);
// check results
CHECK(success == false);
CHECK(bytes_processed == 2);
// prepare decode object
Http2EventGen local_events;
Http2Infractions local_inf;
- Http2HpackStringDecode local_decode(&local_events, &local_inf);
+ Http2HpackStringDecode local_decode;
// prepare buf to decode - Incomplete code 0xFFFE
uint8_t buf[3] = { 0x82, 0xFF, 0xFE };
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[5];
- bool success = local_decode.translate(buf, 3, bytes_processed, res, 5, bytes_written);
+ bool success = local_decode.translate(buf, 3, bytes_processed, res, 5, bytes_written, &local_events, &local_inf);
// check results
CHECK(success == false);
CHECK(bytes_processed == 3);
// prepare decode object
Http2EventGen local_events;
Http2Infractions local_inf;
- Http2HpackStringDecode local_decode(&local_events, &local_inf);
+ Http2HpackStringDecode local_decode;
// prepare buf to decode - Incomplete code 0xFFFFFE
uint8_t buf[4] = { 0x83, 0xFF, 0xFF, 0xFE };
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[7];
- bool success = local_decode.translate(buf, 4, bytes_processed, res, 7, bytes_written);
+ bool success = local_decode.translate(buf, 4, bytes_processed, res, 7, bytes_written, &local_events, &local_inf);
// check results
CHECK(success == false);
CHECK(bytes_processed == 4);
// prepare decode object
Http2EventGen local_events;
Http2Infractions local_inf;
- Http2HpackStringDecode local_decode(&local_events, &local_inf);
+ Http2HpackStringDecode local_decode;
// prepare buf to decode - ';' (8 bits) and incomplete code 0xFF
uint8_t buf[3] = { 0x82, 0xFB, 0xFF };
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[5];
- bool success = local_decode.translate(buf, 3, bytes_processed, res, 5, bytes_written);
+ bool success = local_decode.translate(buf, 3, bytes_processed, res, 5, bytes_written, &local_events, &local_inf);
// check results
CHECK(success == false);
CHECK(bytes_processed == 3);
// prepare decode object
Http2EventGen local_events;
Http2Infractions local_inf;
- Http2HpackStringDecode local_decode(&local_events, &local_inf);
+ Http2HpackStringDecode local_decode;
// prepare buf to decode - '0' (5 bits) and incomplete code 0xFF with padding
uint8_t buf[3] = { 0x82, 0x07, 0xFF };
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[5];
- bool success = local_decode.translate(buf, 3, bytes_processed, res, 5, bytes_written);
+ bool success = local_decode.translate(buf, 3, bytes_processed, res, 5, bytes_written, &local_events, &local_inf);
// check results
CHECK(success == false);
CHECK(bytes_processed == 3);
// prepare decode object
Http2EventGen local_events;
Http2Infractions local_inf;
- Http2HpackStringDecode local_decode(&local_events, &local_inf);
+ Http2HpackStringDecode local_decode;
// prepare buf to decode - bad padding
uint8_t buf[5] = { 0x84, 0xFF, 0xFF, 0xFF, 0xFF };
// decode
uint32_t bytes_processed = 0, bytes_written = 0;
uint8_t res[10];
- bool success = local_decode.translate(buf, 5, bytes_processed, res, 10, bytes_written);
+ bool success = local_decode.translate(buf, 5, bytes_processed, res, 10, bytes_written, &local_events, &local_inf);
// check results
CHECK(success == false);
CHECK(bytes_processed == 4);
#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 <CppUTest/TestHarness.h>
#include <CppUTestExt/MockSupport.h>
+#include <cstdio>
+
using namespace snort;
using namespace HttpCommon;
using namespace Http2Enums;
unsigned HttpTestManager::test_input = IN_NONE;
unsigned HttpTestManager::test_output = IN_NONE;
int DetectionEngine::queue_event(unsigned int, unsigned int, Actions::Type) { return 0; }
+void Field::print(FILE*, char const*) const {}
TEST_GROUP(http2_get_buf_test)
{
#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_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_string_decode.h"
+#include "service_inspectors/http2_inspect/http2_stream_splitter.h"
#include "http2_flow_data_test.h"
// Stubs whose sole purpose is to make the test code link
unsigned HttpTestManager::test_input = IN_NONE;
unsigned HttpTestManager::test_output = IN_NONE;
+FILE* HttpTestManager::test_out = nullptr ;
int DetectionEngine::queue_event(unsigned int, unsigned int, Actions::Type) { return 0; }
+void Field::print(_IO_FILE*, char const*) const {}
TEST_GROUP(http2_scan_test)
{