http2_module.h
http2_request_line.cc
http2_request_line.h
+ http2_settings_frame.cc
+ http2_settings_frame.h
http2_start_line.cc
http2_start_line.h
http2_status_line.cc
EVENT_UNEXPECTED_CONTINUATION = 5,
EVENT_MISFORMATTED_HTTP2 = 6,
EVENT_PREFACE_MATCH_FAILURE = 7,
- EVENT_REQUEST_WITHOUT_REQUIRED_FIELD = 9,
- EVENT_RESPONSE_WITHOUT_STATUS = 10,
- EVENT_INVALID_HEADER = 11,
+ EVENT_REQUEST_WITHOUT_REQUIRED_FIELD = 8,
+ EVENT_RESPONSE_WITHOUT_STATUS = 9,
+ EVENT_INVALID_HEADER = 10,
+ EVENT_SETTINGS_FRAME_ERROR = 11,
+ EVENT_SETTINGS_FRAME_UNKN_PARAM = 12,
EVENT__MAX_VALUE
};
INF_PSEUDO_HEADER_URI_FORM_MISMATCH = 15,
INF_RESPONSE_WITHOUT_STATUS = 16,
INF_HPACK_INDEX_OUT_OF_BOUNDS = 17,
+ INF_INVALID_SETTINGS_FRAME = 18,
+ INF_SETTINGS_FRAME_UNKN_PARAM = 19,
INF__MAX_VALUE
};
STATUS = 14,
};
+enum SettingsFrameIds
+{
+ HEADER_TABLE_SIZE = 1,
+ ENABLE_PUSH,
+ MAX_CONCURRENT_STREAMS,
+ INITIAL_WINDOW_SIZE,
+ MAX_FRAME_SIZE,
+ MAX_HEADER_LIST_SIZE,
+};
+
} // end namespace Http2Enums
#endif
#include "http2_hpack.h"
#include "http2_hpack_int_decode.h"
#include "http2_hpack_string_decode.h"
+#include "http2_settings_frame.h"
using Http2Infractions = Infractions<Http2Enums::INF__MAX_VALUE, Http2Enums::INF__NONE>;
friend class Http2Hpack;
friend class Http2Inspect;
friend class Http2RequestLine;
+ friend class Http2SettingsFrame;
friend class Http2StartLine;
friend class Http2StatusLine;
friend class Http2StreamSplitter;
// Used in eval()
bool frame_in_detection = false;
class Http2Frame* current_frame[2] = { nullptr, nullptr };
+ Http2ConnectionSettings connection_settings[2];
Http2HpackDecoder hpack_decoder[2];
// Internal to scan()
#include "http2_enum.h"
#include "http2_flow_data.h"
#include "http2_headers_frame.h"
+#include "http2_settings_frame.h"
#include "service_inspectors/http_inspect/http_field.h"
using namespace HttpCommon;
return new Http2HeadersFrame(header, header_len, data, data_len, session_data,
source_id);
break;
+ case FT_SETTINGS:
+ return new Http2SettingsFrame(header, header_len, data, data_len, session_data,
+ source_id);
+ break;
default:
return new Http2Frame(header, header_len, data, data_len, session_data, source_id);
break;
return 0;
}
+uint32_t Http2Frame::get_stream_id()
+{
+ if (header.length() <= 0)
+ return INVALID_STREAM_ID;
+
+ const uint8_t* header_start = header.start();
+ return ((header_start[stream_id_index] & 0x7f) << 24) +
+ (header_start[stream_id_index + 1] << 16) +
+ (header_start[stream_id_index + 2] << 8) +
+ header_start[stream_id_index + 3];
+}
+
#ifdef REG_TEST
void Http2Frame::print_frame(FILE* output)
{
const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* session_data,
HttpCommon::SourceId source_id);
uint8_t get_flags();
+ uint32_t get_stream_id();
Field header;
Field data;
HttpCommon::SourceId source_id;
const static uint8_t flags_index = 4;
+ const static uint8_t stream_id_index = 5;
+ const static uint32_t INVALID_STREAM_ID = 0xFFFFFFFF;
};
#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2019-2019 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+// http2_settings_frame.cc author Deepak Ramadass <deramada@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "http2_settings_frame.h"
+
+#include "service_inspectors/http_inspect/http_test_manager.h"
+
+#include "http2_enum.h"
+#include "http2_flow_data.h"
+
+using namespace Http2Enums;
+
+static const uint8_t SfSize = 6;
+
+static uint16_t get_parameter_id(const uint8_t* data_buffer)
+{
+ return (data_buffer[0] << 8) + data_buffer[1];
+}
+
+static uint32_t get_parameter_value(const uint8_t* data_buffer)
+{
+ static const uint8_t frame_value_index = 2;
+ return (data_buffer[frame_value_index] << 24) +
+ (data_buffer[frame_value_index + 1] << 16) +
+ (data_buffer[frame_value_index + 2] << 8) +
+ data_buffer[frame_value_index + 3];
+}
+
+Http2SettingsFrame::Http2SettingsFrame(const uint8_t* header_buffer, const int32_t header_len,
+ const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* session_data,
+ HttpCommon::SourceId source_id) : Http2Frame(header_buffer, header_len, data_buffer, data_len,
+ session_data, source_id)
+{
+ if (!sanity_check())
+ {
+ session_data->events[source_id]->create_event(EVENT_SETTINGS_FRAME_ERROR);
+ *session_data->infractions[source_id] += INF_INVALID_SETTINGS_FRAME;
+ return;
+ }
+
+ if (SfAck & get_flags())
+ return;
+
+ parse_settings_frame();
+}
+
+void Http2SettingsFrame::parse_settings_frame()
+{
+ int32_t data_pos = 0;
+
+ while (data_pos < data.length())
+ {
+ uint16_t parameter_id = get_parameter_id(data.start() + data_pos);
+ uint32_t parameter_value = get_parameter_value(data.start() + data_pos);
+
+ data_pos += SfSize;
+
+ if (parameter_id < HEADER_TABLE_SIZE or parameter_id > 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;
+ continue;
+ }
+
+ session_data->connection_settings[source_id].set_param(parameter_id, parameter_value);
+ }
+}
+
+bool Http2SettingsFrame::sanity_check()
+{
+ const bool ack = SfAck & get_flags();
+
+ if (get_stream_id() != 0)
+ bad_frame = true;
+ else if (!ack and ((data.length() % 6) != 0))
+ bad_frame = true;
+ else if (ack and data.length() > 0)
+ bad_frame = true;
+
+ return !(bad_frame);
+}
+
+#ifdef REG_TEST
+void Http2SettingsFrame::print_frame(FILE* output)
+{
+ fprintf(output, "SETTINGS frame:");
+
+ if (bad_frame)
+ fprintf(output, " Error in settings frame.");
+ else if (SfAck & get_flags())
+ fprintf(output, " ACK");
+ else
+ fprintf(output, " Parameters in current frame - %d.", (data.length()/6)) ;
+
+ fprintf(output, "\n");
+ Http2Frame::print_frame(output);
+ fprintf(output, "\n");
+}
+#endif
+
+uint32_t Http2ConnectionSettings::get_param(uint16_t id)
+{
+ assert(id >= HEADER_TABLE_SIZE);
+ assert(id <= 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);
+
+ parameters[id - 1] = value;
+}
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2019-2019 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+// http2_settings_frame.cc author Deepak Ramadass <deramada@cisco.com>
+
+#ifndef HTTP2_SETTINGS_FRAME_H
+#define HTTP2_SETTINGS_FRAME_H
+
+#include "http2_frame.h"
+
+class Field;
+class Http2ConnectionSettings;
+class Http2Frame;
+
+class Http2SettingsFrame : public Http2Frame
+{
+public:
+ friend Http2Frame* Http2Frame::new_frame(const uint8_t* header_buffer, const int32_t header_len,
+ const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* session_data,
+ HttpCommon::SourceId source_id);
+
+#ifdef REG_TEST
+ void print_frame(FILE* output) override;
+#endif
+
+private:
+ Http2SettingsFrame(const uint8_t* header_buffer, const int32_t header_len,
+ const uint8_t* data_buffer, const int32_t data_len, Http2FlowData* session_data,
+ HttpCommon::SourceId source_id);
+
+ void parse_settings_frame();
+ bool sanity_check();
+
+ bool bad_frame = false;
+ static const uint8_t SfAck = 0x01;
+};
+
+class Http2ConnectionSettings
+{
+public:
+ uint32_t get_param(uint16_t id);
+ void set_param(uint16_t id, uint32_t value);
+
+private:
+ void validate_param_id(uint16_t id);
+
+ static const uint16_t PARAMETER_COUNT = 6;
+ uint32_t parameters[PARAMETER_COUNT] = {
+ 4096, // Header table size
+ 1, // Push promise
+ 100, // Max concurrent Streams
+ 65535, // Window size
+ 16384, // Max frame size
+ 4294967295 // Max header list size
+ };
+};
+#endif
{ EVENT_REQUEST_WITHOUT_REQUIRED_FIELD, "HTTP/2 request missing required header field" },
{ EVENT_RESPONSE_WITHOUT_STATUS, "HTTP/2 response has no status code" },
{ EVENT_INVALID_HEADER, "invalid HTTP/2 header field" },
+ { EVENT_SETTINGS_FRAME_ERROR, "error in HTTP/2 settings frame" },
+ { EVENT_SETTINGS_FRAME_UNKN_PARAM, "unknown parameter in HTTP/2 settings frame" },
{ 0, nullptr }
};