HTTP2: Store all packets' header indexes in header_stream_info struct
authorHuang Qiangxiong <qiangxiong.huang@qq.com>
Sun, 30 Jul 2017 13:39:11 +0000 (13:39 +0000)
committerMichael Mann <mmann78@netscape.net>
Tue, 26 Sep 2017 01:50:34 +0000 (01:50 +0000)
Store all packets' headers indexes of oneway stream in its header_stream_info struct.
(Just store pointers refer to each HEADERS or CONTINUATION packets' header arrays)

Add http2_get_header_value() function to allow other dissectors to get HTTP2
headers of this stream later in DATA frames.

Ping-Bug: 13932
Change-Id: I9f623f66045845c338cd6233d4c6da3f6875fc69
Reviewed-on: https://code.wireshark.org/review/22859
Petri-Dish: Michael Mann <mmann78@netscape.net>
Reviewed-by: Michael Mann <mmann78@netscape.net>
epan/dissectors/packet-http2.c
epan/dissectors/packet-http2.h

index 637bb2920d5af0756e64946ff72e5d8b110f6b73..5aef83c9b74d133204abcff429c35ab634e753f2 100644 (file)
@@ -165,6 +165,9 @@ typedef struct {
 typedef struct {
     http2_frame_num_t header_start_in;
     http2_frame_num_t header_end_in;
+    /* list of pointer to wmem_array_t, which is array of http2_header_t
+    * that come from all HEADERS and CONTINUATION frames. */
+    wmem_list_t *stream_header_list;
 } http2_header_stream_info_t;
 
 /* struct to reference uni-directional per-stream info */
@@ -539,6 +542,8 @@ get_stream_info(http2_session_t *http2_session)
     http2_stream_info_t *stream_info = (http2_stream_info_t *)wmem_map_lookup(stream_map, GINT_TO_POINTER(stream_id));
     if (stream_info == NULL) {
         stream_info = wmem_new0(wmem_file_scope(), http2_stream_info_t);
+        stream_info->oneway_stream_info[0].header_stream_info.stream_header_list = wmem_list_new(wmem_file_scope());
+        stream_info->oneway_stream_info[1].header_stream_info.stream_header_list = wmem_list_new(wmem_file_scope());
         stream_info->stream_id = stream_id;
         wmem_map_insert(stream_map, GINT_TO_POINTER(stream_id), stream_info);
     }
@@ -627,11 +632,16 @@ get_http2_frame_num(tvbuff_t *tvb, packet_info *pinfo)
 }
 
 static http2_oneway_stream_info_t*
-get_oneway_stream_info(packet_info *pinfo)
+get_oneway_stream_info(packet_info *pinfo, gboolean the_other_direction)
 {
     http2_session_t *http2_session = get_http2_session(pinfo);
     http2_stream_info_t *http2_stream_info = get_stream_info(http2_session);
     int flow_index = select_http2_flow_index(pinfo, http2_session);
+    if (the_other_direction) {
+        /* need stream info of the other direction,
+        so set index from 0 to 1, or from 1 to 0 */
+        flow_index ^= 1;
+    }
 
     return &http2_stream_info->oneway_stream_info[flow_index];
 }
@@ -639,20 +649,20 @@ get_oneway_stream_info(packet_info *pinfo)
 static http2_data_stream_body_info_t*
 get_data_stream_body_info(packet_info *pinfo)
 {
-    return &(get_oneway_stream_info(pinfo)->data_stream_body_info);
+    return &(get_oneway_stream_info(pinfo, FALSE)->data_stream_body_info);
 }
 
 
 static http2_data_stream_reassembly_info_t*
 get_data_reassembly_info(packet_info *pinfo)
 {
-    return &(get_oneway_stream_info(pinfo)->data_stream_reassembly_info);
+    return &(get_oneway_stream_info(pinfo, FALSE)->data_stream_reassembly_info);
 }
 
 static http2_header_stream_info_t*
-get_header_stream_info(packet_info *pinfo)
+get_header_stream_info(packet_info *pinfo, gboolean the_other_direction)
 {
-    return &(get_oneway_stream_info(pinfo)->header_stream_info);
+    return &(get_oneway_stream_info(pinfo, the_other_direction)->header_stream_info);
 }
 
 static void
@@ -867,7 +877,7 @@ static gboolean http2_hdrcache_equal(gconstpointer lhs, gconstpointer rhs)
 static int
 is_in_header_context(tvbuff_t *tvb, packet_info *pinfo)
 {
-    http2_header_stream_info_t *stream_info = get_header_stream_info(pinfo);
+    http2_header_stream_info_t *stream_info = get_header_stream_info(pinfo, FALSE);
     if (get_http2_frame_num(tvb, pinfo) >= stream_info->header_start_in) {
         /* We either haven't established the frame that the headers end in so we are currently in the HEADERS context,
          * or if we have, it should be equal or less that the current frame number */
@@ -1022,6 +1032,7 @@ inflate_http2_header_block(tvbuff_t *tvb, packet_info *pinfo, guint offset,
     guint i;
     const gchar *method_header_value = NULL;
     const gchar *path_header_value = NULL;
+    http2_header_stream_info_t* header_stream_info;
 
     if (!http2_hdrcache_map) {
         http2_hdrcache_map = wmem_map_new(wmem_file_scope(), http2_hdrcache_hash, http2_hdrcache_equal);
@@ -1140,6 +1151,12 @@ inflate_http2_header_block(tvbuff_t *tvb, packet_info *pinfo, guint offset,
             header_data->current = wmem_list_head(header_list);
         }
 
+        /* add this packet headers to stream header list */
+        header_stream_info = get_header_stream_info(pinfo, FALSE);
+        if (header_stream_info) {
+            wmem_list_append(header_stream_info->stream_header_list, headers);
+        }
+
     } else {
         headers = (wmem_array_t*)wmem_list_frame_data(header_data->current);
 
@@ -1565,12 +1582,74 @@ dissect_http2_data_body(tvbuff_t *tvb, packet_info *pinfo, proto_tree *http2_tre
     }
 }
 
+/* Get header value from current or the other direction stream_header_list */
+const gchar*
+http2_get_header_value(packet_info *pinfo, const gchar* name, gboolean the_other_direction)
+{
+    http2_header_stream_info_t* header_stream_info;
+    wmem_list_frame_t* frame;
+    wmem_array_t* headers;
+    guint i;
+    guint32 name_len;
+    guint32 value_len;
+    http2_header_t *hdr;
+    gchar* data;
+
+    header_stream_info = get_header_stream_info(pinfo, the_other_direction);
+    if (!header_stream_info) {
+        return NULL;
+    }
+
+    for (frame = wmem_list_head(header_stream_info->stream_header_list);
+        frame;
+        frame = wmem_list_frame_next(frame))
+    {   /* each frame contains one HEADERS or CONTINUATION frame's headers */
+        headers = (wmem_array_t*)wmem_list_frame_data(frame);
+        if (!headers) {
+            continue;
+        }
+
+        for (i = 0; i < wmem_array_get_count(headers); ++i) {
+            hdr = (http2_header_t*)wmem_array_index(headers, i);
+            if (hdr->type == HTTP2_HD_HEADER_TABLE_SIZE_UPDATE) {
+                continue;
+            }
+
+            /* parsing data as format:
+                   name length (uint32)
+                   name (string)
+                   value length (uint32)
+                   value (string)
+            */
+            data = (gchar*) hdr->table.data.data;
+            name_len = pntoh32(data);
+            if (strlen(name) == name_len && strncmp(data + 4, name, name_len) == 0) {
+                value_len = pntoh32(data + 4 + name_len);
+                if (4 + name_len + 4 + value_len == hdr->table.data.datalen) {
+                    /* return value */
+                    return wmem_strndup(wmem_packet_scope(), data + 4 + name_len + 4, value_len);
+                }
+                else {
+                    return NULL; /* unexpected error */
+                }
+            }
+        }
+    }
+
+    return NULL;
+}
 #else
 static void
 dissect_http2_data_body(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *http2_tree, guint offset, guint8 flags _U_, gint datalen)
 {
     proto_tree_add_item(http2_tree, hf_http2_data_data, tvb, offset, datalen, ENC_NA);
 }
+
+const gchar*
+http2_get_header_value(packet_info *pinfo _U_, const gchar* name _U_, gboolean the_other_direction _U_)
+{
+    return NULL;
+}
 #endif
 
 /* Data (0) */
@@ -1627,7 +1706,7 @@ dissect_http2_headers(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *http2_t
     /* Mark this frame as the first header frame seen and last if the END_HEADERS flag
      * is set. We use this to ensure when we read header values, we are not reading ones
      * that have come from a PUSH_PROMISE header (and associated CONTINUATION frames) */
-    http2_header_stream_info_t *stream_info = get_header_stream_info(pinfo);
+    http2_header_stream_info_t *stream_info = get_header_stream_info(pinfo, FALSE);
     if (stream_info->header_start_in == 0) {
         stream_info->header_start_in = get_http2_frame_num(tvb, pinfo);
     }
@@ -1873,7 +1952,7 @@ dissect_http2_continuation(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *ht
     /* Mark this as the last CONTINUATION frame for a HEADERS frame. This is used to know the context when we read
      * header (is the source a HEADER frame or a PUSH_PROMISE frame?) */
     if (flags & HTTP2_FLAGS_END_HEADERS) {
-        http2_header_stream_info_t *stream_info = get_header_stream_info(pinfo);
+        http2_header_stream_info_t *stream_info = get_header_stream_info(pinfo, FALSE);
         if (stream_info->header_start_in != 0 && stream_info->header_end_in == 0) {
             stream_info->header_end_in = get_http2_frame_num(tvb, pinfo);
         }
index 5704fe8126f894e9a3f1018beb1a0358c1be1cf6..4b07c39692adc8995ec0674742e3785759cc4312 100644 (file)
 
 int dissect_http2_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_ );
 
+/** Get header value from current or the other direction stream.
+ * Return the value of a header if it appear in previous HEADERS or PROMISE frames in
+ * current or the other direction stream. Subdissector may invoke this function to get http2
+ * header value.
+ * @param pinfo  packet info pointer. Http2 dissector passes it to subdissector
+ *               when dissecting http2.data.data.
+ * @param name   the name of header.
+ * @param the_other_direction  FALSE means from current direction stream, TRUE from the other.
+ * @return  NULL if header was not found. Or header value. Note: the value is allocated
+ *          by wmem_packet_scope().
+ */
+const gchar* http2_get_header_value(packet_info *pinfo, const gchar* name, gboolean the_other_direction);
+
 #endif
 
 /*