memcpy(http2_payload_cur, empty_settings_frame, sizeof(empty_settings_frame));
http2_payload_cur += sizeof(empty_settings_frame);
}
- write_frame_hdr(http2_payload_cur, hdr_len, FT_HEADERS, END_HEADERS, control.stream_id);
+ write_frame_hdr(http2_payload_cur, hdr_len, FT_HEADERS, FLAG_END_HEADERS, control.stream_id);
memcpy(http2_payload_cur, http2_hdr, hdr_len);
http2_payload_cur += hdr_len;
const uint8_t* http_body_cur = control.http_page + body_offset;
{
const uint32_t cur_len = (body_len > 1<<14) ? 1<<14 : body_len;
body_len -= cur_len;
- const uint8_t flags = (body_len == 0) ? END_STREAM : 0;
+ const uint8_t flags = (body_len == 0) ? FLAG_END_STREAM : 0;
write_frame_hdr(http2_payload_cur, cur_len, FT_DATA, flags, control.stream_id);
memcpy(http2_payload_cur, http_body_cur, cur_len);
http2_payload_cur += cur_len;
session_data->padding_length[source_id];
data_bytes_read = 0;
- if (frame_flags & PADDED)
+ if (frame_flags & FLAG_PADDED)
{
data_len -= 1;
frame_bytes_seen += 1;
Http2DummyPacket dummy_pkt;
dummy_pkt.flow = session_data->flow;
uint32_t unused = 0;
- if ((data_bytes_read == data_len) && (frame_flags & END_STREAM))
+ if ((data_bytes_read == data_len) && (frame_flags & FLAG_END_STREAM))
{
Http2Stream* const stream =
session_data->find_stream(session_data->current_stream[source_id]);
session_data->scan_state[source_id] = SCAN_FRAME_HEADER;
frame_bytes_seen = 0;
- if (frame_flags & END_STREAM)
+ if (frame_flags & FLAG_END_STREAM)
{
Http2Stream* const stream = session_data->find_stream(
session_data->current_stream[source_id]);
const uint8_t frame_flags =
get_frame_flags(session_data->lead_frame_header[source_id]);
cur_data_offset = cur_pos;
- if (frame_flags & PADDED)
+ if (frame_flags & FLAG_PADDED)
reassemble_state = GET_PADDING_LEN;
else if (reassemble_data_len > 0)
reassemble_state = SEND_DATA;
else
{
- assert(frame_flags & END_STREAM);
+ assert(frame_flags & FLAG_END_STREAM);
reassemble_state = SEND_EMPTY_DATA;
}
}
reassemble_state = SEND_DATA;
else
{
- assert(get_frame_flags(session_data->lead_frame_header[source_id]) & END_STREAM);
+ assert(get_frame_flags(session_data->lead_frame_header[source_id]) & FLAG_END_STREAM);
reassemble_state = SEND_EMPTY_DATA;
}
break;
}
}
-uint8_t Http2DataFrame::get_flags_mask() const { return (END_STREAM|PADDED); }
+uint8_t Http2DataFrame::get_flags_mask() const { return (FLAG_END_STREAM|FLAG_PADDED); }
#ifdef REG_TEST
void Http2DataFrame::print_frame(FILE* output)
static const int DATA_SECTION_SIZE = 16384;
static const int FRAME_HEADER_LENGTH = 9;
static const uint32_t NO_STREAM_ID = 0xFFFFFFFF;
-static const uint32_t CONCURRENT_STREAMS_LIMIT = 100;
// Perform memory allocation and deallocation tracking for Http2Stream objects in increments of 25
static const uint32_t STREAM_MEMORY_TRACKING_INCREMENT = 25;
enum HeaderFrameFlags
{
- ACK = 0x1,
- END_STREAM = 0x1,
- END_HEADERS = 0x4,
- PADDED = 0x8,
- PRIORITY = 0x20,
- NO_HEADER = 0x80, //No valid flags use this bit
+ FLAG_ACK = 0x1,
+ FLAG_END_STREAM = 0x1,
+ FLAG_END_HEADERS = 0x4,
+ FLAG_PADDED = 0x8,
+ FLAG_PRIORITY = 0x20,
+ FLAG_NO_HEADER = 0x80, //No valid flags use this bit
};
enum SettingsFrameIds
{
- HEADER_TABLE_SIZE = 1,
- ENABLE_PUSH,
- MAX_CONCURRENT_STREAMS,
- INITIAL_WINDOW_SIZE,
- MAX_FRAME_SIZE,
- MAX_HEADER_LIST_SIZE,
+ SFID_HEADER_TABLE_SIZE = 1,
+ SFID_ENABLE_PUSH,
+ SFID_MAX_CONCURRENT_STREAMS,
+ SFID_INITIAL_WINDOW_SIZE,
+ SFID_MAX_FRAME_SIZE,
+ SFID_MAX_HEADER_LIST_SIZE,
};
enum ScanState { SCAN_FRAME_HEADER, SCAN_PADDING_LENGTH, SCAN_DATA, SCAN_EMPTY_DATA };
return nullptr;
}
-Http2Stream* Http2FlowData::get_stream(const uint32_t key, const SourceId source_id)
+Http2Stream* Http2FlowData::get_processing_stream(const SourceId source_id, uint32_t concurrent_streams_limit)
{
+ const uint32_t key = processing_stream_id;
class Http2Stream* stream = find_stream(key);
if (!stream)
{
- if (concurrent_streams >= CONCURRENT_STREAMS_LIMIT)
+ if (concurrent_streams >= concurrent_streams_limit)
{
*infractions[source_id] += INF_TOO_MANY_STREAMS;
events[source_id]->create_event(EVENT_TOO_MANY_STREAMS);
return find_stream(get_processing_stream_id());
}
-Http2Stream* Http2FlowData::get_processing_stream(const SourceId source_id)
-{
- return get_stream(get_processing_stream_id(), source_id);
-}
-
uint32_t Http2FlowData::get_processing_stream_id() const
{
return processing_stream_id;
Http2Stream* find_current_stream(const HttpCommon::SourceId source_id) const;
uint32_t get_current_stream_id(const HttpCommon::SourceId source_id) const;
- Http2Stream* get_processing_stream(const HttpCommon::SourceId source_id);
+ Http2Stream* get_processing_stream(const HttpCommon::SourceId source_id, uint32_t concurrent_streams_limit);
Http2Stream* find_processing_stream() const;
uint32_t get_processing_stream_id() const;
void set_processing_stream_id(const HttpCommon::SourceId source_id);
#endif
private:
- Http2Stream* get_stream(const uint32_t key, const HttpCommon::SourceId source_id);
Http2Stream* get_hi_stream() const;
Http2Stream* find_stream(const uint32_t key) const;
void delete_processing_stream();
Http2Frame(header_buffer, header_len, data_buffer, data_len, session_data_, source_id_, stream_)
{
// Remove stream dependency if present
- if (get_flags() & PRIORITY)
+ if (get_flags() & FLAG_PRIORITY)
hpack_headers_offset = 5;
// Set up HPACK decoding
}
uint8_t Http2HeadersFrame::get_flags_mask() const
-{ return (END_STREAM|END_HEADERS|PADDED|PRIORITY); }
+{ return (FLAG_END_STREAM|FLAG_END_HEADERS|FLAG_PADDED|FLAG_PRIORITY); }
#ifdef REG_TEST
void Http2HeadersFrame::print_frame(FILE* output)
return;
// if END_STREAM flag set on headers, tell http_inspect not to expect a message body
- if (get_flags() & END_STREAM)
+ if (get_flags() & FLAG_END_STREAM)
stream->get_hi_flow_data()->finish_h2_body(source_id, HttpEnums::H2_BODY_NO_BODY, false);
process_decoded_headers(http_flow, source_id);
{
if (stream->get_state(source_id) == STREAM_ERROR)
return;
- if (get_flags() & END_STREAM)
+ if (get_flags() & FLAG_END_STREAM)
stream->set_state(source_id, STREAM_COMPLETE);
else
stream->set_state(source_id, STREAM_EXPECT_BODY);
if (!process_frame)
return;
- if (!(get_flags() & END_STREAM))
+ if (!(get_flags() & FLAG_END_STREAM))
{
// Trailers without END_STREAM flag set.
*session_data->infractions[source_id] += INF_TRAILERS_NOT_END;
{
encoder_set_max_size = true;
if (new_size <= session_data->get_recipient_connection_settings(source_id)->
- get_param(HEADER_TABLE_SIZE))
+ get_param(SFID_HEADER_TABLE_SIZE))
{
dynamic_table.update_size(new_size);
return true;
}
session_data->set_processing_stream_id(source_id);
- Http2Stream* stream = session_data->get_processing_stream(source_id);
+ Http2Stream* stream = session_data->get_processing_stream(source_id, params->concurrent_streams_limit);
if (!stream)
{
delete[] session_data->frame_data[source_id];
session_data->processing_partial_header = false;
}
+void Http2Inspect::show(const SnortConfig*) const
+{
+ assert(params);
+ ConfigLogger::log_value("concurrent_streams_limit", params->concurrent_streams_limit);
+}
+
#ifdef REG_TEST
static void print_flow_issues(FILE* output, Http2Infractions* const infractions,
Http2EventGen* const events)
bool get_fp_buf(snort::InspectionBuffer::Type ibt, snort::Packet* p,
snort::InspectionBuffer& b) override;
bool configure(snort::SnortConfig*) override;
+ void show(const snort::SnortConfig*) const override;
void eval(snort::Packet* p) override;
void clear(snort::Packet* p) override;
const Parameter Http2Module::http2_params[] =
{
+ { "concurrent_streams_limit", Parameter::PT_INT, "100:1000", "100",
+ "Maximum number of concurrent streams allowed in a single HTTP/2 flow" },
#ifdef REG_TEST
{ "test_input", Parameter::PT_BOOL, nullptr, "false",
"read HTTP/2 messages from text file" },
bool Http2Module::set(const char*, Value& val, SnortConfig*)
{
+ if (val.is("concurrent_streams_limit"))
+ {
+ params->concurrent_streams_limit = val.get_uint32();
+ }
#ifdef REG_TEST
- if (val.is("test_input"))
+ else if (val.is("test_input"))
{
params->test_input = val.get_bool();
}
{
params->show_scan = val.get_bool();
}
+#endif
else
{
return false;
}
return true;
-#else
- UNUSED(val);
- return false;
-#endif
}
bool Http2Module::end(const char*, int, SnortConfig*)
struct Http2ParaList
{
public:
+ uint32_t concurrent_streams_limit;
#ifdef REG_TEST
int64_t print_amount;
Http2Frame(header_buffer, header_len, data_buffer, data_len, ssn_data, src_id, _stream) { }
uint8_t get_flags_mask() const override
- { return Http2Enums::ACK; }
+ { return Http2Enums::FLAG_ACK; }
};
#endif
session_data->events[source_id]->create_event(EVENT_INVALID_STREAM_ID);
}
- if (session_data->get_recipient_connection_settings(source_id)->get_param(ENABLE_PUSH) == 0)
+ if (session_data->get_recipient_connection_settings(source_id)->get_param(SFID_ENABLE_PUSH) == 0)
{
session_data->events[source_id]->create_event(EVENT_PUSH_WHEN_PROHIBITED);
*session_data->infractions[source_id] += INF_PUSH_WHEN_PROHIBITED;
}
uint8_t Http2PushPromiseFrame::get_flags_mask() const
-{ return (END_HEADERS|PADDED); }
+{ return (FLAG_END_HEADERS|FLAG_PADDED); }
#ifdef REG_TEST
void Http2PushPromiseFrame::print_frame(FILE* output)
return;
}
- if (ACK & get_flags())
+ if (FLAG_ACK & get_flags())
return;
if (src_id == HttpCommon::SRC_SERVER && !ssn_data->was_server_settings_received())
data_pos += SfSize;
- if (parameter_id < HEADER_TABLE_SIZE or parameter_id > MAX_HEADER_LIST_SIZE)
+ if (parameter_id < SFID_HEADER_TABLE_SIZE or parameter_id > SFID_MAX_HEADER_LIST_SIZE)
{
session_data->events[source_id]->create_event(EVENT_SETTINGS_FRAME_UNKN_PARAM);
*session_data->infractions[source_id] += INF_SETTINGS_FRAME_UNKN_PARAM;
bool Http2SettingsFrame::sanity_check()
{
- const bool ack = ACK & get_flags();
+ const bool ack = FLAG_ACK & get_flags();
// FIXIT-E this next check should possibly be moved to valid_sequence()
if (get_stream_id() != 0)
{
switch (id)
{
- case HEADER_TABLE_SIZE:
+ case SFID_HEADER_TABLE_SIZE:
// Sending a table size parameter informs the receiver the maximum hpack dynamic
// table size they may use.
session_data->get_hpack_decoder((HttpCommon::SourceId) (1 - source_id))->
get_decode_table()->settings_table_size_update(value);
break;
- case ENABLE_PUSH:
+ case SFID_ENABLE_PUSH:
// Only values of 0 or 1 are allowed
if (!(value == 0 or value == 1))
{
if (bad_frame)
fprintf(output, " Error in settings frame.");
- else if (ACK & get_flags())
+ else if (FLAG_ACK & get_flags())
fprintf(output, " ACK");
else
fprintf(output, " Parameters in current frame - %d.", (data.length()/6)) ;
uint32_t Http2ConnectionSettings::get_param(uint16_t id)
{
- assert(id >= HEADER_TABLE_SIZE);
- assert(id <= MAX_HEADER_LIST_SIZE);
+ assert(id >= SFID_HEADER_TABLE_SIZE);
+ assert(id <= SFID_MAX_HEADER_LIST_SIZE);
return parameters[id - 1];
}
void Http2ConnectionSettings::set_param(uint16_t id, uint32_t value)
{
- assert(id >= HEADER_TABLE_SIZE);
- assert(id <= MAX_HEADER_LIST_SIZE);
+ assert(id >= SFID_HEADER_TABLE_SIZE);
+ assert(id <= SFID_MAX_HEADER_LIST_SIZE);
parameters[id - 1] = value;
}
bool handle_update(uint16_t id, uint32_t value);
uint8_t get_flags_mask() const override
- { return Http2Enums::ACK; }
+ { return Http2Enums::FLAG_ACK; }
bool bad_frame = false;
};
StreamSplitter::Status status = StreamSplitter::FLUSH;
session_data->continuation_expected[source_id] = false;
if (((type == FT_HEADERS) || (type == FT_PUSH_PROMISE) || (type == FT_CONTINUATION)) &&
- !(frame_flags & END_HEADERS))
+ !(frame_flags & FLAG_END_HEADERS))
{
session_data->continuation_expected[source_id] = true;
status = StreamSplitter::SEARCH;
}
// Do flags check for continuation frame, since it is not saved
// as lead frame for later.
- if ((frame_flags & END_HEADERS) != frame_flags)
+ if ((frame_flags & FLAG_END_HEADERS) != frame_flags)
{
*session_data->infractions[source_id] += INF_INVALID_FLAG;
session_data->events[source_id]->create_event(EVENT_INVALID_FLAG);
assert(session_data->scan_remaining_frame_octets[source_id] == 0);
session_data->scan_remaining_frame_octets[source_id] = frame_length;
- if ((frame_flags & PADDED) &&
+ if ((frame_flags & FLAG_PADDED) &&
(type == FT_DATA || type == FT_HEADERS || type == FT_PUSH_PROMISE))
{
if (frame_length == 0)
const uint8_t frame_flags =
get_frame_flags(session_data->lead_frame_header[source_id]);
const uint8_t type = session_data->frame_type[source_id];
- if ((frame_flags & PADDED) && !session_data->continuation_frame[source_id] &&
+ if ((frame_flags & FLAG_PADDED) && !session_data->continuation_frame[source_id] &&
(type == FT_HEADERS || type == FT_PUSH_PROMISE))
session_data->read_padding_len[source_id] = true;
if (frame_header_buffer)
return frame_header_buffer[frame_flags_index];
else
- return NO_HEADER;
+ return FLAG_NO_HEADER;
}
uint32_t get_stream_id_from_header(const uint8_t* frame_header_buffer)