*flush_offset = FRAME_HEADER_LENGTH;
}
- uint32_t cur_pos = 0;
+ uint32_t cur_pos = leftover_bytes;
while ((cur_pos < length) && (data_state != FULL_FRAME))
{
}
}
- frame_bytes_seen += cur_pos;
+ frame_bytes_seen += (cur_pos - leftover_bytes);
session_data->scan_remaining_frame_octets[source_id] = frame_length - frame_bytes_seen;
*flush_offset += cur_pos;
if (scan_result != StreamSplitter::SEARCH)
return StreamSplitter::ABORT;
}
- } // fallthrough
+ } // fallthrough
case HEADER_SENT:
{
- if (cur_data)
+ if (cur_data || leftover_bytes)
{
scan_result = session_data->hi_ss[source_id]->scan(&dummy_pkt, data + cur_data_offset,
- cur_data, unused, &http_flush_offset);
- bytes_sent_http += cur_data;
+ cur_data + leftover_bytes, unused, &http_flush_offset);
if (scan_result != StreamSplitter::SEARCH)
- return StreamSplitter::ABORT;
+ {
+ if (scan_result == StreamSplitter::FLUSH)
+ {
+ bytes_sent_http += http_flush_offset;
+ leftover_bytes = cur_data + leftover_bytes - http_flush_offset;
+ *flush_offset -= leftover_bytes;
+ session_data->mid_packet[source_id] = true;
+ return scan_result;
+ }
+ else
+ return StreamSplitter::ABORT;
+ }
+
+ bytes_sent_http += (cur_data + leftover_bytes);
+ leftover_bytes = 0;
}
if (data_state == FULL_FRAME)
{
5, unused, &http_flush_offset);
bytes_sent_http +=5;
assert(scan_result == StreamSplitter::FLUSH);
-
+ session_data->mid_packet[source_id] = false;
session_data->scan_octets_seen[source_id] = 0;
session_data->scan_remaining_frame_octets[source_id] = 0;
}
if (!http2_scan(data, length, flush_offset))
return StreamSplitter::ABORT;
- return Http2DataCutter::http_scan(data, flush_offset);
+ return http_scan(data, flush_offset);
}
-const StreamBuffer Http2DataCutter::reassemble(unsigned total, unsigned offset, const
- uint8_t* data, unsigned len)
+const StreamBuffer Http2DataCutter::reassemble(unsigned, const uint8_t* data,
+ unsigned len)
{
StreamBuffer frame_buf { nullptr, 0 };
- if (offset == 0)
- {
- padding_read = data_bytes_read = hdr_bytes_read = 0;
- }
cur_data = cur_padding = cur_data_offset = 0;
unsigned cur_pos = 0;
{
case SKIP_FRAME_HDR:
{
- if (hdr_bytes_read == 0)
+ if (reassemble_hdr_bytes_read == 0)
{
session_data->frame_header[source_id] = new uint8_t[FRAME_HEADER_LENGTH];
session_data->frame_header_size[source_id] = FRAME_HEADER_LENGTH;
}
- const uint32_t missing = FRAME_HEADER_LENGTH - hdr_bytes_read;
+ const uint32_t missing = FRAME_HEADER_LENGTH - reassemble_hdr_bytes_read;
const uint32_t cur_frame = ((len - cur_pos) < missing) ? (len - cur_pos) : missing;
- memcpy(session_data->frame_header[source_id] + hdr_bytes_read, data + cur_pos,
+ memcpy(session_data->frame_header[source_id] + reassemble_hdr_bytes_read, data +
+ cur_pos,
cur_frame);
- hdr_bytes_read += cur_frame;
+ reassemble_hdr_bytes_read += cur_frame;
cur_pos += cur_frame;
- if (hdr_bytes_read == FRAME_HEADER_LENGTH)
+ if (reassemble_hdr_bytes_read == FRAME_HEADER_LENGTH)
{
cur_data_offset = cur_pos;
reassemble_state = (padding_len) ? SKIP_PADDING_LEN : SEND_CHUNK_HDR;
bytes_sent_http, 0, (const uint8_t*)chunk_hdr.c_str(), chunk_hdr.length(), 0,
copied);
assert(copied == (unsigned)chunk_hdr.length());
+ reassemble_bytes_sent += copied;
reassemble_state = SEND_DATA;
- } // fallthrough
+ } // fallthrough
case SEND_DATA:
{
- const uint32_t missing = data_len - data_bytes_read;
+ const uint32_t missing = data_len - reassemble_data_bytes_read;
cur_data = ((len - cur_pos) >= missing) ? missing : (len - cur_pos);
- data_bytes_read += cur_data;
+ reassemble_data_bytes_read += cur_data;
cur_pos += cur_data;
unsigned copied;
+ uint32_t flags = (bytes_sent_http == (cur_data + reassemble_bytes_sent)) ?
+ PKT_PDU_TAIL : 0;
frame_buf = session_data->hi_ss[source_id]->reassemble(session_data->flow,
bytes_sent_http, 0, data + cur_data_offset, cur_data,
- 0, copied);
+ flags, copied);
assert(copied == (unsigned)cur_data);
+ reassemble_bytes_sent += copied;
- if (data_bytes_read == data_len)
+ if (reassemble_data_bytes_read == data_len)
reassemble_state = (padding_len) ? SKIP_PADDING : SEND_CRLF;
break;
}
case SKIP_PADDING:
{
- const uint32_t missing = padding_len - padding_read;
+ const uint32_t missing = padding_len - reassemble_padding_read;
cur_padding = ((len - cur_pos) >= missing) ?
missing : (len - cur_pos);
cur_pos += cur_padding;
- padding_read += cur_padding;
- if (padding_read == padding_len)
+ reassemble_padding_read += cur_padding;
+ if (reassemble_padding_read == padding_len)
reassemble_state = SEND_CRLF;
break;
}
}
}
- if (len + offset == total)
- assert(reassemble_state == SEND_CRLF);
-
if (reassemble_state == SEND_CRLF)
{
unsigned copied;
frame_buf = session_data->hi_ss[source_id]->reassemble(session_data->flow,
bytes_sent_http, 0,(const unsigned char*)"\r\n0\r\n", 5, PKT_PDU_TAIL, copied);
assert(copied == 5);
+ }
- assert(frame_buf.data != nullptr);
+ if (frame_buf.data != nullptr)
+ {
session_data->frame_data[source_id] = const_cast <uint8_t*>(frame_buf.data);
session_data->frame_data_size[source_id] = frame_buf.length;
+ bytes_sent_http = reassemble_bytes_sent = 0;
}
return frame_buf;
friend class Http2Stream;
friend class Http2StreamSplitter;
friend snort::StreamSplitter::Status data_scan(Http2FlowData* session_data, const uint8_t* data,
- uint32_t length, uint32_t* flush_offset, HttpCommon::SourceId source_id, uint32_t frame_length, bool is_padded);
+ uint32_t length, uint32_t* flush_offset, HttpCommon::SourceId source_id,
+ uint32_t frame_length, bool is_padded);
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 snort::StreamSplitter::Status non_data_scan(Http2FlowData* session_data,
+ uint32_t length, uint32_t* flush_offset, HttpCommon::SourceId source_id,
+ uint32_t frame_length, uint8_t type, uint8_t frame_flags, uint32_t& data_offset);
size_t size_of() override
{ return sizeof(*this); }
// Stream access
class StreamInfo
{
- public:
+public:
const uint32_t id;
class Http2Stream* stream;
uint32_t get_current_stream_id(const HttpCommon::SourceId source_id);
Http2HpackDecoder* get_hpack_decoder(const HttpCommon::SourceId source_id)
- { return &hpack_decoder[source_id]; }
+ { return &hpack_decoder[source_id]; }
Http2ConnectionSettings* get_connection_settings(const HttpCommon::SourceId source_id)
- { return &connection_settings[source_id]; }
+ { return &connection_settings[source_id]; }
+
protected:
snort::Flow* flow;
HttpInspect* const hi;
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 };
+ bool mid_packet[2] = { false, false };
// Scan signals to reassemble()
bool payload_discard[2] = { false, false };
return data_cutter->scan(data, length, flush_offset);
}
+StreamSplitter::Status non_data_scan(Http2FlowData* session_data,
+ uint32_t length, uint32_t* flush_offset, HttpCommon::SourceId source_id,
+ uint32_t frame_length, uint8_t type, uint8_t frame_flags, uint32_t& data_offset)
+{
+ // Compute frame section length once per frame
+ if (session_data->scan_remaining_frame_octets[source_id] == 0)
+ {
+ 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);
+ return StreamSplitter::ABORT;
+ }
+
+ if (frame_length + FRAME_HEADER_LENGTH > MAX_OCTETS)
+ {
+ // FIXIT-M long non-data frame needs to be supported
+ return StreamSplitter::ABORT;
+ }
+
+ session_data->scan_remaining_frame_octets[source_id] = frame_length;
+ session_data->total_bytes_in_split[source_id] += FRAME_HEADER_LENGTH +
+ frame_length;
+ }
+
+ // If we don't have the full frame, keep scanning
+ if (length - data_offset < session_data->scan_remaining_frame_octets[source_id])
+ {
+ session_data->scan_remaining_frame_octets[source_id] -= (length - data_offset);
+ data_offset = length;
+ return StreamSplitter::SEARCH;
+ }
+
+ // Have the full frame
+ StreamSplitter::Status status = StreamSplitter::FLUSH;
+ switch (type)
+ {
+ 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
+ // are 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;
+ default:
+ break;
+ }
+
+ 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;
+ return status;
+}
+
StreamSplitter::Status implement_scan(Http2FlowData* session_data, const uint8_t* data,
uint32_t length, uint32_t* flush_offset, HttpCommon::SourceId source_id)
{
switch (validate_preface(data, length, session_data->scan_octets_seen[source_id]))
{
case V_GOOD:
- break;
+ *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;
+ return StreamSplitter::FLUSH;
case V_BAD:
session_data->events[source_id]->create_event(EVENT_PREFACE_MATCH_FAILURE);
return StreamSplitter::ABORT;
session_data->scan_octets_seen[source_id] += length;
return StreamSplitter::SEARCH;
}
-
- *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)
+ else if (session_data->mid_packet[source_id])
{
// Continuation of ongoing data frame
- session_data->num_frame_headers[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->scan_remaining_frame_octets[source_id] = DATA_SECTION_SIZE;
- else
- 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 frame section, keep scanning
- if (session_data->scan_remaining_frame_octets[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 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->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;
+ Http2Stream* const stream = session_data->find_stream(
+ session_data->current_stream[source_id]);
+ Http2DataCutter* data_cutter = stream->get_data_cutter(source_id);
+ return data_cutter->scan(data, length, flush_offset);
}
else
{
// need to process multiple frames in a single scan
do
{
- // Scanning a new frame
if (session_data->scan_octets_seen[source_id] == 0)
+ {
+ // Scanning a new frame
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.
data_offset += remaining_header_in_data;
if (session_data->scan_octets_seen[source_id] < FRAME_HEADER_LENGTH)
- {
- status = StreamSplitter::SEARCH;
- break;
- }
+ return StreamSplitter::SEARCH;
// We have the full frame header, compute some variables
const uint32_t frame_length = get_frame_length(session_data->
scan_frame_header[source_id]);
const uint8_t type = session_data->frame_type[source_id] = get_frame_type(
session_data->scan_frame_header[source_id]);
- uint8_t frame_flags = get_frame_flags(session_data->
+ const uint8_t frame_flags = get_frame_flags(session_data->
scan_frame_header[source_id]);
session_data->current_stream[source_id] =
get_stream_id(session_data->scan_frame_header[source_id]);
if (type == FT_DATA)
return data_scan(session_data, data, length, flush_offset, source_id,
frame_length, ((frame_flags & PADDED) !=0));
-
- // Compute frame section length once per frame
- if (session_data->scan_remaining_frame_octets[source_id] == 0)
- {
- 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 (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 we don't have the full frame, keep scanning
- if (length - data_offset < session_data->scan_remaining_frame_octets[source_id])
- {
- session_data->scan_remaining_frame_octets[source_id] -= (length - data_offset);
- status = StreamSplitter::SEARCH;
- break;
- }
-
- // Have the full frame
- switch (type)
- {
- 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
- // are 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;
- default:
- break;
- }
-
- 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;
+ else
+ status = non_data_scan(session_data, length, flush_offset, source_id,
+ frame_length, type, frame_flags, data_offset);
}
while (status == StreamSplitter::SEARCH && data_offset < length);
}
Http2Stream* const stream = session_data->find_stream(
session_data->current_stream[source_id]);
Http2DataCutter* data_cutter = stream->get_data_cutter(source_id);
- StreamBuffer http_frame_buf = data_cutter->reassemble(total, offset, data, len);
+ StreamBuffer http_frame_buf = data_cutter->reassemble(total, data, len);
if (http_frame_buf.data)
{
- delete data_cutter;
- stream->set_data_cutter(nullptr, source_id);
stream->set_abort_data_processing(source_id);
session_data->frame_data[source_id] = const_cast<uint8_t*>(http_frame_buf.data);
session_data->frame_data_size[source_id] = http_frame_buf.length;