aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--epan/dissectors/packet-http2.c261
1 files changed, 250 insertions, 11 deletions
diff --git a/epan/dissectors/packet-http2.c b/epan/dissectors/packet-http2.c
index e2d04e9600..48675afa9f 100644
--- a/epan/dissectors/packet-http2.c
+++ b/epan/dissectors/packet-http2.c
@@ -51,14 +51,53 @@
#include "packet-tcp.h"
+#define http2_header_repr_type_VALUE_STRING_LIST(XXX) \
+ XXX(HTTP2_HD_NONE, 0x00, "") \
+ XXX(HTTP2_HD_INDEXED, 0x01, "Indexed Header Field") \
+ XXX(HTTP2_HD_LITERAL_INDEXING_INDEXED_NAME, 0x02, "Literal Header Field with Incremental Indexing - Indexed Name") \
+ XXX(HTTP2_HD_LITERAL_INDEXING_NEW_NAME, 0x03, "Literal Header Field with Incremental Indexing - New Name") \
+ XXX(HTTP2_HD_LITERAL_INDEXED_NAME, 0x04, "Literal Header Field without Indexing - Indexed Name") \
+ XXX(HTTP2_HD_LITERAL_NEW_NAME, 0x05, "Literal Header Field without Indexing - New Name") \
+ XXX(HTTP2_HD_LITERAL_NEVER_INDEXING_INDEXED_NAME, 0x06, "Literal Header Field never Indexed - Indexed Name") \
+ XXX(HTTP2_HD_LITERAL_NEVER_INDEXING_NEW_NAME, 0x07, "Literal Header Field never Indexed - New Name") \
+ XXX(HTTP2_HD_HEADER_TABLE_SIZE_UPDATE, 0x08, "Maximum Header Table Size Change")
+
+VALUE_STRING_ENUM(http2_header_repr_type);
+VALUE_STRING_ARRAY(http2_header_repr_type);
+
/* Decompressed header field */
typedef struct {
- /* header data */
- char *data;
- /* length of data */
- guint datalen;
+ /* one of http2_header_repr_type */
+ gint type;
+ /* encoded (compressed) length */
+ gint length;
+ union {
+ struct {
+ /* header data */
+ char *data;
+ /* length of data */
+ guint datalen;
+ /* name index or name/value index if type is one of
+ HTTP2_HD_INDEXED and HTTP2_HD_*_INDEXED_NAMEs */
+ guint index;
+ };
+ /* header table size if type == HTTP2_HD_HEADER_TABLE_SIZE_UPDATE */
+ guint header_table_size;
+ };
} http2_header_t;
+/* Context to decode header representation */
+typedef struct {
+ /* one of http2_header_repr_type */
+ gint type;
+ /* final or temporal result of decoding integer */
+ guint integer;
+ /* next bit shift to made when decoding integer */
+ guint next_shift;
+ /* TRUE if integer decoding was completed */
+ gboolean complete;
+} http2_header_repr_info_t;
+
/* Cached decompressed header data in one packet_info */
typedef struct {
/* list of pointer to wmem_array_t, which is array of
@@ -93,6 +132,7 @@ typedef struct {
hd_inflater[1]. */
wmem_queue_t *settings_queue[2];
nghttp2_hd_inflater *hd_inflater[2];
+ http2_header_repr_info_t header_repr_info[2];
tcp_flow_t *fwd_flow;
} http2_session_t;
@@ -149,6 +189,10 @@ static int hf_http2_header_name_length = -1;
static int hf_http2_header_name = -1;
static int hf_http2_header_value_length = -1;
static int hf_http2_header_value = -1;
+static int hf_http2_header_repr = -1;
+static int hf_http2_header_index = -1;
+static int hf_http2_header_table_size_update = -1;
+static int hf_http2_header_table_size = -1;
/* RST Stream */
static int hf_http2_rst_stream_error = -1;
/* Settings */
@@ -424,6 +468,141 @@ apply_and_pop_settings(packet_info *pinfo, http2_session_t *h2session)
}
}
+/* Decode integer from buf at position p, using prefix bits. This
+ function can be called several times if buf does not contain whole
+ integer. header_repr_info remembers the result of previous call.
+ Returns the number bytes processed. */
+static guint read_integer(http2_header_repr_info_t *header_repr_info,
+ const guint8 *buf, guint len, guint p, guint prefix)
+{
+ guint k = (1 << prefix) - 1;
+ guint n = header_repr_info->integer;
+ guint shift = header_repr_info->next_shift;
+
+ if(n == 0) {
+ DISSECTOR_ASSERT(p < len);
+
+ if((buf[p] & k) != k) {
+ header_repr_info->integer = buf[p] & k;
+ header_repr_info->complete = TRUE;
+ return p + 1;
+ }
+
+ n = k;
+
+ ++p;
+ }
+
+ for(; p < len; ++p, shift += 7) {
+ DISSECTOR_ASSERT(p < len);
+
+ n += (buf[p] & 0x7F) << shift;
+
+ if((buf[p] & 0x80) == 0) {
+ header_repr_info->complete = TRUE;
+ ++p;
+ break;
+ }
+ }
+
+ header_repr_info->integer = n;
+ header_repr_info->next_shift = shift;
+ return p;
+}
+
+static void
+reset_http2_header_repr_info(http2_header_repr_info_t *header_repr_info)
+{
+ header_repr_info->type = HTTP2_HD_NONE;
+ header_repr_info->integer = 0;
+ header_repr_info->next_shift = 0;
+ header_repr_info->complete = FALSE;
+}
+
+/* Reads zero or more header table size update and optionally header
+ representation information. This function returns when first
+ header representation is decoded or buf is processed completely.
+ This function returns the number bytes processed for header table
+ size update. */
+static guint
+process_http2_header_repr_info(wmem_array_t *headers,
+ http2_header_repr_info_t *header_repr_info,
+ const guint8 *buf, guint len)
+{
+ guint i;
+ guint start;
+
+ if(header_repr_info->type != HTTP2_HD_NONE &&
+ header_repr_info->type != HTTP2_HD_HEADER_TABLE_SIZE_UPDATE &&
+ header_repr_info->complete) {
+ return 0;
+ }
+
+ start = 0;
+
+ for(i = 0; i < len;) {
+ if(header_repr_info->type == HTTP2_HD_NONE) {
+ guchar c = buf[i];
+ if((c & 0xE0) == 0x20) {
+ header_repr_info->type = HTTP2_HD_HEADER_TABLE_SIZE_UPDATE;
+
+ i = read_integer(header_repr_info, buf, len, i, 5);
+ } else if(c & 0x80) {
+ header_repr_info->type = HTTP2_HD_INDEXED;
+ i = read_integer(header_repr_info, buf, len, i, 7);
+ } else if(c == 0x40 || c == 0 || c == 0x10) {
+ /* New name */
+ header_repr_info->complete = TRUE;
+ if(c & 0x40) {
+ header_repr_info->type = HTTP2_HD_LITERAL_INDEXING_NEW_NAME;
+ } else if((c & 0xF0) == 0x10) {
+ header_repr_info->type = HTTP2_HD_LITERAL_NEVER_INDEXING_NEW_NAME;
+ } else {
+ header_repr_info->type = HTTP2_HD_LITERAL_NEW_NAME;
+ }
+ } else {
+ /* indexed name */
+ if(c & 0x40) {
+ header_repr_info->type = HTTP2_HD_LITERAL_INDEXING_INDEXED_NAME;
+ i = read_integer(header_repr_info, buf, len, i, 6);
+ } else if((c & 0xF0) == 0x10) {
+ header_repr_info->type = HTTP2_HD_LITERAL_NEVER_INDEXING_INDEXED_NAME;
+ i = read_integer(header_repr_info, buf, len, i, 4);
+ } else {
+ header_repr_info->type = HTTP2_HD_LITERAL_INDEXED_NAME;
+ i = read_integer(header_repr_info, buf, len, i, 4);
+ }
+ }
+ } else {
+ i = read_integer(header_repr_info, buf, len, i, 8);
+ }
+
+ if(header_repr_info->complete) {
+ if(header_repr_info->type == HTTP2_HD_HEADER_TABLE_SIZE_UPDATE) {
+ http2_header_t *out;
+
+ out = wmem_new(wmem_file_scope(), http2_header_t);
+
+ out->type = header_repr_info->type;
+ out->length = i - start;
+ out->header_table_size = header_repr_info->integer;
+
+ wmem_array_append(headers, out, 1);
+
+ reset_http2_header_repr_info(header_repr_info);
+ /* continue to decode header table size update or
+ first header encoding is encountered. */
+ start = i;
+ } else {
+ /* Break on first header encoding */
+ break;
+ }
+ }
+ }
+
+ return start;
+}
+
static void
inflate_http2_header_block(tvbuff_t *tvb, packet_info *pinfo, guint offset,
proto_tree *tree, size_t headlen,
@@ -444,6 +623,7 @@ inflate_http2_header_block(tvbuff_t *tvb, packet_info *pinfo, guint offset,
int final;
int flow_index;
http2_header_data_t *header_data;
+ http2_header_repr_info_t *header_repr_info;
wmem_list_t *header_list;
wmem_array_t *headers;
guint i;
@@ -464,6 +644,7 @@ inflate_http2_header_block(tvbuff_t *tvb, packet_info *pinfo, guint offset,
flow_index = select_http2_flow_index(pinfo, h2session);
hd_inflater = h2session->hd_inflater[flow_index];
+ header_repr_info = &h2session->header_repr_info[flow_index];
final = flags & HTTP2_FLAGS_END_HEADERS;
@@ -483,6 +664,8 @@ inflate_http2_header_block(tvbuff_t *tvb, packet_info *pinfo, guint offset,
headbuf += rv;
headlen -= rv;
+ rv -= process_http2_header_repr_info(headers, header_repr_info, headbuf - rv, rv);
+
if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {
char *str;
guint32 len;
@@ -490,6 +673,10 @@ inflate_http2_header_block(tvbuff_t *tvb, packet_info *pinfo, guint offset,
out = wmem_new(wmem_file_scope(), http2_header_t);
+ out->type = header_repr_info->type;
+ out->length = rv;
+ out->index = header_repr_info->integer;
+
out->datalen = (guint)(4 + nv.namelen + 4 + nv.valuelen);
/* Prepare buffer... with the following format
@@ -514,6 +701,8 @@ inflate_http2_header_block(tvbuff_t *tvb, packet_info *pinfo, guint offset,
out->data = str;
wmem_array_append(headers, out, 1);
+
+ reset_http2_header_repr_info(header_repr_info);
}
if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
nghttp2_hd_inflate_end_headers(hd_inflater);
@@ -552,6 +741,10 @@ inflate_http2_header_block(tvbuff_t *tvb, packet_info *pinfo, guint offset,
in = (http2_header_t*)wmem_array_index(headers, i);
+ if(in->type == HTTP2_HD_HEADER_TABLE_SIZE_UPDATE) {
+ continue;
+ }
+
str = (char *)g_malloc(in->datalen);
memcpy(str, in->data, in->datalen);
@@ -569,34 +762,60 @@ inflate_http2_header_block(tvbuff_t *tvb, packet_info *pinfo, guint offset,
ti = proto_tree_add_uint(tree, hf_http2_header_length, header_tvb, hoffset, 1, header_len);
PROTO_ITEM_SET_GENERATED(ti);
- while (tvb_reported_length_remaining(header_tvb, hoffset) > 0) {
+ for(i = 0; i < wmem_array_get_count(headers); ++i) {
+ http2_header_t *in = (http2_header_t*)wmem_array_index(headers, i);
+
+ if(in->type == HTTP2_HD_HEADER_TABLE_SIZE_UPDATE) {
+ header = proto_tree_add_item(tree, hf_http2_header_table_size_update, tvb, offset, in->length, ENC_NA);
+
+ header_tree = proto_item_add_subtree(header, ett_http2_headers);
+
+ proto_tree_add_uint(header_tree, hf_http2_header_table_size, tvb, offset, in->length, in->header_table_size);
+
+ offset += in->length;
+ continue;
+ }
+
/* Populate tree with header name/value details. */
/* Add 'Header' subtree with description. */
- header = proto_tree_add_item(tree, hf_http2_header, header_tvb, hoffset, -1, ENC_NA);
+
+ header = proto_tree_add_item(tree, hf_http2_header, tvb, offset, in->length, ENC_NA);
+
header_tree = proto_item_add_subtree(header, ett_http2_headers);
/* header value length */
- proto_tree_add_item(header_tree, hf_http2_header_name_length, header_tvb, hoffset, 4, ENC_LITTLE_ENDIAN);
header_name_length = tvb_get_letohl(header_tvb, hoffset);
+ proto_tree_add_uint(header_tree, hf_http2_header_name_length, tvb, offset, in->length, header_name_length);
hoffset += 4;
/* Add header name. */
- proto_tree_add_item(header_tree, hf_http2_header_name, header_tvb, hoffset, header_name_length, ENC_ASCII|ENC_NA);
header_name = (gchar *)tvb_get_string_enc(wmem_packet_scope(), header_tvb, hoffset, header_name_length, ENC_ASCII|ENC_NA);
+ proto_tree_add_string(header_tree, hf_http2_header_name, tvb, offset, in->length, header_name);
hoffset += header_name_length;
/* header value length */
- proto_tree_add_item(header_tree, hf_http2_header_value_length, header_tvb, hoffset, 4, ENC_LITTLE_ENDIAN);
header_value_length = tvb_get_letohl(header_tvb, hoffset);
+ proto_tree_add_uint(header_tree, hf_http2_header_value_length, tvb, offset, in->length, header_value_length);
hoffset += 4;
/* Add header value. */
- proto_tree_add_item(header_tree, hf_http2_header_value, header_tvb, hoffset, header_value_length, ENC_ASCII|ENC_NA);
header_value = (gchar *)tvb_get_string_enc(wmem_packet_scope(),header_tvb, hoffset, header_value_length, ENC_ASCII|ENC_NA);
+ proto_tree_add_string(header_tree, hf_http2_header_value, tvb, offset, in->length, header_value);
hoffset += header_value_length;
+ /* Add encoding representation */
+ proto_tree_add_string(header_tree, hf_http2_header_repr, tvb, offset, in->length, http2_header_repr_type[in->type].strptr);
+
+ if(in->type == HTTP2_HD_INDEXED ||
+ in->type == HTTP2_HD_LITERAL_INDEXING_INDEXED_NAME ||
+ in->type == HTTP2_HD_LITERAL_INDEXED_NAME ||
+ in->type == HTTP2_HD_LITERAL_NEVER_INDEXING_INDEXED_NAME) {
+ proto_tree_add_uint(header_tree, hf_http2_header_index, tvb, offset, in->length, in->index);
+ }
+
proto_item_append_text(header, ": %s: %s", header_name, header_value);
- proto_item_set_len(header, 4 + header_name_length + 4 + header_value_length);
+
+ offset += in->length;
}
}
@@ -1399,6 +1618,26 @@ proto_register_http2(void)
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
+ { &hf_http2_header_repr,
+ { "Representation", "http2.header.repr",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_http2_header_index,
+ { "Index", "http2.header.index",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_http2_header_table_size_update,
+ { "Header table size update", "http2.header_table_size_update",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }
+ },
+ { &hf_http2_header_table_size,
+ { "Header table size", "http2.header_table_size_update.header_table_size",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }
+ },
/* RST Stream */
{ &hf_http2_rst_stream_error,
{ "Error", "http2.rst_stream.error",