protected:
// 0 element refers to client frame, 1 element refers to server frame
bool preface[2] = { true, false };
- bool header_coming[2] = { false, false };
uint8_t* frame_header[2] = { nullptr, nullptr };
- uint8_t* frame[2] = { nullptr, nullptr };
- uint32_t frame_size[2] = { 0, 0 };
+ 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* http2_decoded_header[2] = { nullptr, nullptr };
uint32_t http2_decoded_header_size[2] = { 0, 0 };
+ bool frame_in_detection = false;
+
+ // Internal to scan
+ bool continuation_expected[2] = { false, false };
+ uint8_t* currently_processing_frame_header[2] = { nullptr, nullptr };
+ uint32_t inspection_section_length[2] = { 0, 0 };
uint32_t leftover_data[2] = { 0, 0 };
+
+ // Used internally by scan and reassemble
uint32_t octets_seen[2] = { 0, 0 };
- uint32_t header_octets_seen[2] = { 0, 0 };
- uint32_t inspection_section_length[2] = { 0, 0 };
- bool frame_in_detection = false;
+ uint8_t header_octets_seen[2] = { 0, 0 };
+
+ // Scan signals to reassemble
+ bool header_coming[2] = { false, false };
+ uint32_t frames_aggregated[2] = { 0, 0 };
- int32_t get_frame_type(HttpCommon::SourceId source_id);
- int32_t get_frame_flags(HttpCommon::SourceId source_id);
-
- bool continuation_expected = false;
- //FIXIT-M Most of this will need to change when we handle multiple streams, so this vector is
- //not intended to be the best long-term solution
- std::vector<uint32_t> continuation_frame_lengths;
- uint8_t* header_frame_header[2] = { nullptr, nullptr };
-
+ // Internal to reassemble
+ 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 };
+
// 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.
Http2Infractions* infractions[2] = { new Http2Infractions, new Http2Infractions };
return (frame_buffer[0] << 16) + (frame_buffer[1] << 8) + frame_buffer[2];
}
+static uint8_t get_frame_type(const uint8_t *frame_buffer)
+{
+ const uint8_t frame_type_index = 3;
+ if (frame_buffer)
+ return frame_buffer[frame_type_index];
+ else
+ return FT__NONE;
+}
+
+static uint8_t get_frame_flags(const uint8_t *frame_buffer)
+{
+ const uint8_t frame_flags_index = 4;
+ if (frame_buffer)
+ return frame_buffer[frame_flags_index];
+ else
+ return NO_HEADER;
+}
+
StreamSplitter::Status implement_scan(Http2FlowData* session_data, const uint8_t* data,
uint32_t length, uint32_t* flush_offset, HttpCommon::SourceId source_id)
{
*flush_offset = 0;
do
{
- if (session_data->frame_header[source_id] == nullptr)
+ if (session_data->currently_processing_frame_header[source_id] == nullptr)
{
session_data->header_coming[source_id] = true;
- session_data->frame_header[source_id] = new uint8_t[FRAME_HEADER_LENGTH];
+ session_data->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
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]++)
{
- session_data->frame_header[source_id][session_data->header_octets_seen[source_id]] = data[k];
+ session_data->currently_processing_frame_header[source_id]
+ [session_data->header_octets_seen[source_id]] = data[k];
}
if (session_data->header_octets_seen[source_id] < FRAME_HEADER_LENGTH)
{
status = StreamSplitter::SEARCH;
break;
}
- int type = session_data->get_frame_type(source_id);
+ 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->frame_header[source_id]);
+ 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;
// 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
- if (type == FT_HEADERS && !(session_data->get_frame_flags(source_id) & END_HEADERS))
+ uint8_t frame_flags = get_frame_flags(session_data->
+ currently_processing_frame_header[source_id]);
+ if (type == FT_HEADERS && !(frame_flags & END_HEADERS))
{
- session_data->continuation_expected = true;
- // We need to save the header frame header for reassembly
- session_data->header_frame_header[source_id] = new uint8_t[FRAME_HEADER_LENGTH];
- memcpy(session_data->header_frame_header[source_id],
- session_data->frame_header[source_id], FRAME_HEADER_LENGTH);
+ session_data->continuation_expected[source_id] = true;
- delete[] session_data->frame_header[source_id];
- session_data->frame_header[source_id] = nullptr;
+ session_data->header_octets_seen[source_id] = 0;
+ session_data->frames_aggregated[source_id] += 1;
status = StreamSplitter::SEARCH;
data += frame_length + FRAME_HEADER_LENGTH;
}
- else if ( type == FT_CONTINUATION && session_data->continuation_expected)
+ else if ( type == FT_CONTINUATION && session_data->continuation_expected[source_id])
{
- if (!(session_data->get_frame_flags(source_id) & END_HEADERS))
+ if (!(frame_flags & END_HEADERS))
{
- // For continuation frames we only need the frame length
- // FIXIT-M Need to verify that continuation frame has correct stream id
- session_data->continuation_frame_lengths.push_back(frame_length);
-
- delete[] session_data->frame_header[source_id];
- session_data->frame_header[source_id] = nullptr;
+ session_data->header_octets_seen[source_id] = 0;
+ session_data->frames_aggregated[source_id] += 1;
status = StreamSplitter::SEARCH;
data += frame_length + FRAME_HEADER_LENGTH;
}
{
// continuation frame ending headers
status = StreamSplitter::FLUSH;
- session_data->continuation_expected = false;
+ session_data->continuation_expected[source_id] = false;
}
}
//FIXIT-M CONTINUATION frames can also follow PUSH_PROMISE frames, which is not
session_data->events[source_id]->create_event(EVENT_UNEXPECTED_CONTINUATION);
status = StreamSplitter::ABORT;
}
- else if (session_data->continuation_expected)
+ 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);
if (offset == 0)
{
- session_data->frame[source_id] = new uint8_t[total];
- session_data->frame_size[source_id] = total;
- }
- assert(session_data->frame_size[source_id] == total);
-
- memcpy(session_data->frame[source_id]+offset, data, len);
-
- if (flags & PKT_PDU_TAIL)
- {
- assert(offset+len == total);
if (!session_data->header_coming[source_id])
+ session_data->frame_data[source_id] = new uint8_t[total];
+ else
{
- session_data->frame_data[source_id] = session_data->frame[source_id];
- session_data->frame_data_size[source_id] = session_data->frame_size[source_id];
- }
- else if (session_data->frame_size[source_id] == FRAME_HEADER_LENGTH)
- {
- session_data->frame_data[source_id] = nullptr;
- session_data->frame_data_size[source_id] = 0;
+ 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];
}
- else
+ session_data->header_octets_seen[source_id] = 0;
+ session_data->frame_data_size[source_id] = 0;
+ session_data->frame_header_offset[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
+ {
+ uint32_t data_pos = 0;
+ do
{
- // Adjust for frame header
- session_data->frame_data[source_id] =
- session_data->frame[source_id] + FRAME_HEADER_LENGTH;
- session_data->frame_data_size[source_id] =
- session_data->frame_size[source_id] - FRAME_HEADER_LENGTH;
-
- const int type = session_data->get_frame_type(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];
- uint8_t header_payload_offset = 0;
- uint8_t pad_len = 0;
+ uint32_t remaining_len = len - data_pos;
- uint8_t frame_flags;
- uint32_t header_frame_length;
- if (type == FT_HEADERS)
- {
- frame_flags = session_data->get_frame_flags(source_id);
- header_frame_length = get_frame_length(session_data->frame_header[source_id]);
- }
- else
+ if (session_data->header_octets_seen[source_id] < FRAME_HEADER_LENGTH)
+ {
+ uint8_t remaining_header = FRAME_HEADER_LENGTH -
+ session_data->header_octets_seen[source_id];
+ if (remaining_header > remaining_len)
{
- assert(session_data->header_frame_header[source_id] != nullptr);
- frame_flags = session_data->header_frame_header[source_id][4];
- header_frame_length = get_frame_length(session_data->header_frame_header[source_id]);
+ memcpy(session_data->frame_header[source_id] +
+ session_data->frame_header_offset[source_id] +
+ session_data->header_octets_seen[source_id], data + data_pos,
+ remaining_len);
+ session_data->header_octets_seen[source_id] += remaining_len;
+ break;
}
+ memcpy(session_data->frame_header[source_id] +
+ session_data->frame_header_offset[source_id] +
+ session_data->header_octets_seen[source_id], data + data_pos,
+ remaining_header);
+ session_data->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]);
+ frame_flags = get_frame_flags(session_data->frame_header[source_id] +
+ session_data->frame_header_offset[source_id]);
if (frame_flags & PADDED)
{
- header_payload_offset += 1;
+ frame_data_offset += 1;
pad_len = session_data->frame_data[source_id][0];
}
//FIXIT-M handle stream dependency and weight. For now just skip over
if (frame_flags & PRIORITY)
{
- header_payload_offset += 5;
+ 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;
+ }
- //FIXIT-H This will eventually be the decoded header buffer. For now just copy directly
- uint32_t header_payload_len = header_frame_length - header_payload_offset - pad_len;
- memcpy(session_data->http2_decoded_header[source_id], session_data->frame_data[source_id] +
- header_payload_offset, header_payload_len);
- session_data->http2_decoded_header_size[source_id] = header_payload_len;
+ 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->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);
+ }
- // check for continuation frames, skipping over frame headers
- if (type == FT_CONTINUATION)
- {
- header_payload_offset += header_payload_len + pad_len + FRAME_HEADER_LENGTH;
- for (uint32_t continuation_length : session_data->continuation_frame_lengths)
- {
- assert(header_payload_offset + continuation_length < total);
- assert(session_data->http2_decoded_header_size[source_id] + continuation_length <
- MAX_OCTETS);
- memcpy(session_data->http2_decoded_header[source_id] +
- session_data->http2_decoded_header_size[source_id],
- session_data->frame_data[source_id] + header_payload_offset,
- continuation_length);
- session_data->http2_decoded_header_size[source_id] += continuation_length;
- header_payload_offset += continuation_length + FRAME_HEADER_LENGTH;
- }
-
- // The last continuation frame header is stored in the frame_header buffer
- uint32_t final_continuation_length =
- get_frame_length(session_data->frame_header[source_id]);
- assert(header_payload_offset + final_continuation_length < total);
- assert(session_data->http2_decoded_header_size[source_id] +
- final_continuation_length < MAX_OCTETS);
- memcpy(session_data->http2_decoded_header[source_id] +
- session_data->http2_decoded_header_size[source_id],
- session_data->frame_data[source_id] + header_payload_offset,
- final_continuation_length);
- session_data->http2_decoded_header_size[source_id] += final_continuation_length;
- }
+ if (flags & PKT_PDU_TAIL)
+ {
+ if (session_data->header_coming[source_id])
+ {
+ const uint8_t type = get_frame_type(session_data->frame_header[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];
}
}
// Return 0-length non-null buffer to stream which signals detection required, but don't
// create pkt_data buffer
frame_buf.length = 0;
- frame_buf.data = session_data->frame[source_id];
+ if (session_data->frame_data[source_id])
+ frame_buf.data = session_data->frame_data[source_id];
+ else
+ frame_buf.data = session_data->frame_header[source_id];
}
return frame_buf;
}
CHECK(result == StreamSplitter::FLUSH);
CHECK(flush_offset == 19);
CHECK(session_data->get_header_coming(SRC_SERVER));
- CHECK(memcmp(session_data->get_frame_header(SRC_SERVER),
+ CHECK(memcmp(session_data->get_currently_processing_frame_header(SRC_SERVER),
"\x00\x00\x10\x04\x05\x06\x07\x08\x09", 9) == 0);
}
TEST(http2_reassemble_test, basic_with_header)
{
session_data->set_header_coming(true, SRC_CLIENT);
+ session_data->set_aggregated_frames (1, 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);
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_three_pieces)
{
session_data->set_header_coming(true, SRC_CLIENT);
+ session_data->set_aggregated_frames (1, SRC_CLIENT);
StreamBuffer buffer = implement_reassemble(session_data, 19, 0,
(const uint8_t*)"\x00\x00\x0A\x02\x00\x00",
6, 0, SRC_CLIENT);
CHECK(buffer.length == 0);
CHECK(session_data->get_frame_data_size(SRC_CLIENT) == 0);
- CHECK(session_data->get_frame_data(SRC_CLIENT) == nullptr);
implement_reassemble(session_data, 19, 6,
(const uint8_t*)"\x00\x00\x00" "01234",
8, 0, SRC_CLIENT);
- CHECK(session_data->get_frame_data(SRC_CLIENT) == nullptr);
buffer = implement_reassemble(session_data, 19, 14,
(const uint8_t*)"56789",
5, PKT_PDU_TAIL, SRC_CLIENT);
implement_reassemble(session_data, 24, 0,
(const uint8_t*)"P",
1, 0, SRC_CLIENT);
- CHECK(session_data->get_frame_data_size(SRC_CLIENT) == 0);
- CHECK(session_data->get_frame_data(SRC_CLIENT) == nullptr);
+ CHECK(session_data->get_frame_data_size(SRC_CLIENT) == 1);
+ CHECK(memcmp(session_data->get_frame_data(SRC_CLIENT), "P", 1) == 0);
implement_reassemble(session_data, 24, 1,
(const uint8_t*)"RI * HTTP/2.0\r\n\r\nSM\r\n\r\n",
23, PKT_PDU_TAIL, SRC_CLIENT);