From me and Stephen Ludin via https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=9042 Add HTTP2 Dissector Based on draft-ietf-httpbis-http2-06

TODO :
* Support HTTP Header Compression (draft-ietf-httpbis-header-compression)
* Enhance display of Data
* Reassembling of continuation frame (and other frame)
* Add same tap and ping/pong time response

svn path=/trunk/; revision=51591
This commit is contained in:
Alexis La Goutte 2013-08-30 11:14:09 +00:00
parent d89a3bab77
commit c42a4f7706
4 changed files with 871 additions and 0 deletions

View File

@ -2979,6 +2979,7 @@ Alexis La Goutte <alexis.lagoutte[AT]gmail.com> {
Aruba ERM dissector
ATMTCP dissector
CAPWAP dissector
HTTP2 dissector
PAPI dissector
MONGO dissector
WebSocket dissector
@ -3761,6 +3762,7 @@ Bart Van Assche <bvanassche[AT]acm.org>
Peter Lemenkov <lemenkov[AT]gmail.com>
Karl Beldan <karl.beldan[AT]gmail.com>
Jiri Engelthaler <engycz[AT]gmail.com>
Stephen Ludin <sludin[AT]ludin.org>
Dan Lasley <dlasley[AT]promus.com> gave permission for his
dumpit() hex-dump routine to be used.

View File

@ -725,6 +725,7 @@ set(DISSECTOR_SRC
dissectors/packet-hsr.c
dissectors/packet-hsrp.c
dissectors/packet-http.c
dissectors/packet-http2.c
dissectors/packet-http-urlencoded.c
dissectors/packet-hyperscsi.c
dissectors/packet-i2c.c

View File

@ -659,6 +659,7 @@ DISSECTOR_SRC = \
packet-hsr.c \
packet-hsrp.c \
packet-http.c \
packet-http2.c \
packet-http-urlencoded.c \
packet-hyperscsi.c \
packet-i2c.c \

View File

@ -0,0 +1,867 @@
/* packet-http2.c
* Routines for HTTP2 dissection
* Copyright 2013, Alexis La Goutte <alexis.lagoutte@gmail.com>
* Copyright 2013, Stephen Ludin <sludin@ludin.org>
*
* $Id$
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*/
/*
* The information used comes from:
* Hypertext Transfer Protocol version 2.0 draft-ietf-httpbis-http2-06
* HTTP Header Compression draft-ietf-httpbis-header-compression-02
*
* TODO
* Support HTTP Header Compression (draft-ietf-httpbis-header-compression)
* Enhance display of Data
* Reassembling of continuation frame (and other frame)
* Add same tap and ping/pong time response
*/
#include "config.h"
#include <glib.h>
#include <epan/packet.h>
#include <epan/prefs.h>
#include <epan/expert.h>
#include "packet-tcp.h"
/* Packet Header */
static int proto_http2 = -1;
static int hf_http2 = -1;
static int hf_http2_length = -1;
static int hf_http2_type = -1;
static int hf_http2_r = -1;
static int hf_http2_streamid = -1;
static int hf_http2_magic = -1;
static int hf_http2_unknown = -1;
/* Flags */
static int hf_http2_flags = -1;
static int hf_http2_flags_end_stream = -1;
static int hf_http2_flags_end_headers = -1;
static int hf_http2_flags_priority = -1;
static int hf_http2_flags_end_push_promise = -1;
static int hf_http2_flags_pong = -1;
static int hf_http2_flags_reserved = -1;
static int hf_http2_flags_reserved1 = -1;
static int hf_http2_flags_reserved2 = -1;
static int hf_http2_flags_reserved3 = -1;
static int hf_http2_flags_reserved4 = -1;
/* Headers */
static int hf_http2_headers_r = -1;
static int hf_http2_headers_priority = -1;
static int hf_http2_headers = -1;
/* Priority */
static int hf_http2_priority_r = -1;
static int hf_http2_priority = -1;
/* RST Stream */
static int hf_http2_rst_stream_error = -1;
/* Settings */
static int hf_http2_settings = -1;
static int hf_http2_settings_r = -1;
static int hf_http2_settings_identifier = -1;
static int hf_http2_settings_max_concurrent_streams = -1;
static int hf_http2_settings_initial_window_size = -1;
static int hf_http2_settings_flow_control_options = -1;
static int hf_http2_settings_unknown = -1;
/* Push Promise */
static int hf_http2_push_promise_r = -1;
static int hf_http2_push_promise_promised_stream_id = -1;
static int hf_http2_push_promise_header = -1;
/* Ping */
static int hf_http2_ping = -1;
static int hf_http2_pong = -1;
/* Priority */
static int hf_http2_goaway_r = -1;
static int hf_http2_goaway_last_stream_id = -1;
static int hf_http2_goaway_error = -1;
static int hf_http2_goaway_addata = -1;
/* Window Update */
static int hf_http2_window_update_r = -1;
static int hf_http2_window_update_window_size_increment = -1;
/* Continuation */
static int hf_http2_continuation_header = -1;
static gint ett_http2 = -1;
static gint ett_http2_header = -1;
static gint ett_http2_flags = -1;
static gint ett_http2_settings = -1;
static expert_field ei_http2_flags_epp_old = EI_INIT;
static dissector_handle_t data_handle;
#define FRAME_HEADER_LENGTH 8
#define MAGIC_FRAME_LENGTH 24
#define MASK_HTTP2_RESERVED 0x80000000
#define MASK_HTTP2_STREAMID 0X7FFFFFFF
#define MASK_HTTP2_PRIORITY 0X7FFFFFFF
/* Header Type Code */
#define HTTP2_DATA 0
#define HTTP2_HEADERS 1
#define HTTP2_PRIORITY 2
#define HTTP2_RST_STREAM 3
#define HTTP2_SETTINGS 4
#define HTTP2_PUSH_PROMISE 5
#define HTTP2_PING 6
#define HTTP2_GOAWAY 7
#define HTTP2_WINDOW_UPDATE 9
#define HTTP2_CONTINUATION 10
static const value_string http2_type_vals[] = {
{ HTTP2_DATA, "DATA" },
{ HTTP2_HEADERS, "HEADERS" },
{ HTTP2_PRIORITY, "PRIORITY" },
{ HTTP2_RST_STREAM, "RST_STREAM" },
{ HTTP2_SETTINGS, "SETTINGS" },
{ HTTP2_PUSH_PROMISE, "PUSH_PROMISE" },
{ HTTP2_PING, "PING" },
{ HTTP2_GOAWAY, "GOAWAY" },
{ HTTP2_WINDOW_UPDATE, "WINDOW_UPDATE" },
{ HTTP2_CONTINUATION, "CONTINUATION" },
{ 0, NULL }
};
/* Flags */
#define HTTP2_FLAGS_ES 0x01
#define HTTP2_FLAGS_EH 0x04
#define HTTP2_FLAGS_PR 0x08
#define HTTP2_FLAGS_EPP_OLD 0x01 /* from draft-04 */
#define HTTP2_FLAGS_EPP 0x04
#define HTTP2_FLAGS_PO 0x01
#define HTTP2_FLAGS_R 0xFF
#define HTTP2_FLAGS_R1 0xFE
#define HTTP2_FLAGS_R2 0xFA
#define HTTP2_FLAGS_R3 0xF2
#define HTTP2_FLAGS_R4 0xFB
/* Magic Header : PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n */
static guint8 kMagicHello[] = {
0x50, 0x52, 0x49, 0x20, 0x2a, 0x20, 0x48, 0x54,
0x54, 0x50, 0x2f, 0x32, 0x2e, 0x30, 0x0d, 0x0a,
0x0d, 0x0a, 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a
};
/* Error Codes */
#define EC_NO_ERROR 0
#define EC_PROTOCOL_ERROR 1
#define EC_INTERNAL_ERROR 2
#define EC_FLOW_CONTROL_ERROR 4
#define EC_STREAM_CLOSED 5
#define EC_FRAME_TOO_LARGE 6
#define EC_REFUSED_STREAM 7
#define EC_CANCEL 8
#define EC_COMPRESSION_ERROR 9
static const value_string http2_error_codes_vals[] = {
{ EC_NO_ERROR, "NO ERROR" },
{ EC_PROTOCOL_ERROR, "PROTOCOL_ERROR" },
{ EC_INTERNAL_ERROR, "INTERNAL_ERROR" },
{ EC_FLOW_CONTROL_ERROR, "FLOW_CONTROL_ERROR" },
{ EC_STREAM_CLOSED, "STREAM_CLOSED" },
{ EC_FRAME_TOO_LARGE, "FRAME_TOO_LARGE" },
{ EC_REFUSED_STREAM, "REFUSED_STREAM" },
{ EC_CANCEL, "CANCEL" },
{ EC_COMPRESSION_ERROR, "COMPRESSION_ERROR" },
{ 0, NULL }
};
/* Settings */
#define HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS 4
#define HTTP2_SETTINGS_INITIAL_WINDOW_SIZE 7
#define HTTP2_SETTINGS_FLOW_CONTROL_OPTIONS 10
static const value_string http2_settings_vals[] = {
{ HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, "Max concurrent streams" },
{ HTTP2_SETTINGS_INITIAL_WINDOW_SIZE, "Initial Windows size" },
{ HTTP2_SETTINGS_FLOW_CONTROL_OPTIONS, "Flow Control Options" },
{ 0, NULL }
};
/* Flags
+--------+---------------+---------------------------+--------------+
| Frame | Name | Flags | Section |
| Type | | | |
+--------+---------------+---------------------------+--------------+
| 0 | DATA | END_STREAM(1) | Section 6.1 |
| 1 | HEADERS | END_STREAM(1), | Section 6.2 |
| | | END_HEADERS(4), | |
| | | PRIORITY(8) | |
| 2 | PRIORITY | - | Section 6.3 |
| 3 | RST_STREAM | - | Section 6.4 |
| 4 | SETTINGS | - | Section 6.5 |
| 5 | PUSH_PROMISE | END_PUSH_PROMISE(4) | Section 6.6 |
| 6 | PING | PONG(1) | Section 6.7 |
| 7 | GOAWAY | - | Section 6.8 |
| 9 | WINDOW_UPDATE | - | Section 6.9 |
| 10 | CONTINUATION | END_STREAM(1), | Section 6.10 |
| | | END_HEADERS(4) | |
+--------+---------------+---------------------------+--------------+
*/
static guint8
dissect_http2_header_flags(tvbuff_t *tvb, packet_info *pinfo, proto_tree *http2_tree, guint offset, guint8 type)
{
proto_item *ti_flags;
proto_tree *flags_tree;
guint8 flags;
ti_flags = proto_tree_add_item(http2_tree, hf_http2_flags, tvb, offset, 1, ENC_NA);
flags_tree = proto_item_add_subtree(ti_flags, ett_http2_flags);
flags = tvb_get_guint8(tvb, offset);
switch(type){
case HTTP2_DATA:
proto_tree_add_item(flags_tree, hf_http2_flags_end_stream, tvb, offset, 1, ENC_NA);
proto_tree_add_item(flags_tree, hf_http2_flags_reserved1, tvb, offset, 1, ENC_NA);
break;
case HTTP2_HEADERS:
proto_tree_add_item(flags_tree, hf_http2_flags_end_stream, tvb, offset, 1, ENC_NA);
proto_tree_add_item(flags_tree, hf_http2_flags_end_headers, tvb, offset, 1, ENC_NA);
proto_tree_add_item(flags_tree, hf_http2_flags_priority, tvb, offset, 1, ENC_NA);
proto_tree_add_item(flags_tree, hf_http2_flags_reserved3, tvb, offset, 1, ENC_NA);
break;
case HTTP2_PUSH_PROMISE:
if(flags & HTTP2_FLAGS_EPP_OLD)
{
expert_add_info(pinfo, ti_flags, &ei_http2_flags_epp_old );
}
proto_tree_add_item(flags_tree, hf_http2_flags_end_push_promise, tvb, offset, 1, ENC_NA);
proto_tree_add_item(flags_tree, hf_http2_flags_reserved4, tvb, offset, 1, ENC_NA);
break;
case HTTP2_PING:
proto_tree_add_item(flags_tree, hf_http2_flags_pong, tvb, offset, 1, ENC_NA);
proto_tree_add_item(flags_tree, hf_http2_flags_reserved1, tvb, offset, 1, ENC_NA);
break;
case HTTP2_CONTINUATION:
proto_tree_add_item(flags_tree, hf_http2_flags_end_stream, tvb, offset, 1, ENC_NA);
proto_tree_add_item(flags_tree, hf_http2_flags_end_headers, tvb, offset, 1, ENC_NA);
proto_tree_add_item(flags_tree, hf_http2_flags_reserved2, tvb, offset, 1, ENC_NA);
break;
case HTTP2_PRIORITY:
case HTTP2_RST_STREAM:
case HTTP2_SETTINGS:
case HTTP2_GOAWAY:
case HTTP2_WINDOW_UPDATE:
default:
/* No flags !*/
proto_tree_add_item(flags_tree, hf_http2_flags_reserved, tvb, offset, 1, ENC_NA);
break;
}
return flags;
}
/* Headers */
static int
dissect_http2_headers(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *http2_tree, guint offset, guint8 flags)
{
if(flags & HTTP2_FLAGS_PR)
{
proto_tree_add_item(http2_tree, hf_http2_headers_r, tvb, offset, 4, ENC_NA);
proto_tree_add_item(http2_tree, hf_http2_headers_priority, tvb, offset, 4, ENC_NA);
offset += 4;
}
/* TODO : Support header decompression */
proto_tree_add_item(http2_tree, hf_http2_headers, tvb, offset, -1, ENC_ASCII|ENC_NA);
offset += tvb_reported_length_remaining(tvb, offset);
return offset;
}
/* Priority */
static int
dissect_http2_priority(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *http2_tree, guint offset, guint8 flags _U_)
{
proto_tree_add_item(http2_tree, hf_http2_priority_r, tvb, offset, 4, ENC_NA);
proto_tree_add_item(http2_tree, hf_http2_priority, tvb, offset, 4, ENC_NA);
offset += 4;
return offset;
}
/* RST Stream */
static int
dissect_http2_rst_stream(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *http2_tree, guint offset, guint8 flags _U_)
{
proto_tree_add_item(http2_tree, hf_http2_rst_stream_error, tvb, offset, 4, ENC_NA);
offset += 4;
return offset;
}
/* Settings */
static int
dissect_http2_settings(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *http2_tree, guint offset, guint8 flags _U_)
{
guint32 settingsid;
proto_item *ti_settings;
proto_tree *settings_tree;
while(tvb_reported_length_remaining(tvb, offset) > 0){
ti_settings = proto_tree_add_item(http2_tree, hf_http2_settings, tvb, offset, 8, ENC_NA);
settings_tree = proto_item_add_subtree(ti_settings, ett_http2_settings);
proto_tree_add_item(settings_tree, hf_http2_settings_r, tvb, offset, 1, ENC_NA);
offset +=1;
proto_tree_add_item(settings_tree, hf_http2_settings_identifier, tvb, offset, 3, ENC_NA);
settingsid = tvb_get_ntoh24(tvb, offset);
proto_item_append_text(ti_settings, " - %s", val_to_str( settingsid, http2_settings_vals, "Unknown (%u)") );
offset +=3;
switch(settingsid){
case HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
proto_tree_add_item(settings_tree, hf_http2_settings_max_concurrent_streams, tvb, offset, 4, ENC_NA);
break;
case HTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
proto_tree_add_item(settings_tree, hf_http2_settings_initial_window_size, tvb, offset, 4, ENC_NA);
break;
case HTTP2_SETTINGS_FLOW_CONTROL_OPTIONS:
proto_tree_add_item(settings_tree, hf_http2_settings_flow_control_options, tvb, offset, 4, ENC_NA);
break;
default:
proto_tree_add_item(settings_tree, hf_http2_settings_unknown, tvb, offset, 4, ENC_NA);
break;
}
proto_item_append_text(ti_settings, " : %u", tvb_get_ntohl(tvb, offset));
offset += 4;
}
return offset;
}
/* Push Promise */
static int
dissect_http2_push_promise(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *http2_tree, guint offset, guint8 flags _U_)
{
proto_tree_add_item(http2_tree, hf_http2_push_promise_r, tvb, offset, 4, ENC_NA);
proto_tree_add_item(http2_tree, hf_http2_push_promise_promised_stream_id, tvb, offset, 4, ENC_NA);
offset += 4;
/* TODO : Support header decompression */
proto_tree_add_item(http2_tree, hf_http2_push_promise_header, tvb, offset, -1, ENC_ASCII|ENC_NA);
offset += tvb_reported_length_remaining(tvb, offset);
return offset;
}
/* Ping */
static int
dissect_http2_ping(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *http2_tree, guint offset, guint8 flags)
{
/* TODO : Add Response time */
if(flags & HTTP2_FLAGS_PO)
{
proto_tree_add_item(http2_tree, hf_http2_pong, tvb, offset, 8, ENC_NA);
}else{
proto_tree_add_item(http2_tree, hf_http2_ping, tvb, offset, 8, ENC_NA);
}
offset += 8;
return offset;
}
/* Goaway */
static int
dissect_http2_goaway(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *http2_tree, guint offset, guint8 flags _U_)
{
proto_tree_add_item(http2_tree, hf_http2_goaway_r, tvb, offset, 4, ENC_NA);
proto_tree_add_item(http2_tree, hf_http2_goaway_last_stream_id, tvb, offset, 4, ENC_NA);
offset += 4;
proto_tree_add_item(http2_tree, hf_http2_goaway_error, tvb, offset, 4, ENC_NA);
offset += 4;
if(tvb_reported_length_remaining(tvb, offset) > 0)
{
proto_tree_add_item(http2_tree, hf_http2_goaway_addata , tvb, offset, -1, ENC_NA);
offset += tvb_reported_length_remaining(tvb, offset);
}
return offset;
}
/* Window Update */
static int
dissect_http2_window_update(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *http2_tree, guint offset, guint8 flags _U_)
{
proto_tree_add_item(http2_tree, hf_http2_window_update_r, tvb, offset, 4, ENC_NA);
proto_tree_add_item(http2_tree, hf_http2_window_update_window_size_increment, tvb, offset, 4, ENC_NA);
offset += 4;
return offset;
}
static int
dissect_http2_continuation(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *http2_tree, guint offset, guint8 flags _U_)
{
/* TODO : Support "Reassemble Header" and header decompression */
proto_tree_add_item(http2_tree, hf_http2_continuation_header, tvb, offset, -1, ENC_ASCII|ENC_NA);
offset += tvb_reported_length_remaining(tvb, offset);
return offset;
}
static void
dissect_http2_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree )
{
proto_item *ti;
proto_tree *http2_tree;
tvbuff_t *next_tvb;
guint offset = 0;
guint8 type, flags;
guint16 length;
guint32 streamid;
/* 4.1 Frame Format
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Length (16) | Type (8) | Flags (8) |
+-+-------------+---------------+-------------------------------+
|R| Stream Identifier (31) |
+-+-------------------------------------------------------------+
| Frame Payload (0...) ...
+---------------------------------------------------------------+
*/
ti = proto_tree_add_item(tree, hf_http2, tvb, 0, -1, ENC_NA);
http2_tree = proto_item_add_subtree(ti, ett_http2_header);
/* 3.5 Connection Header
Upon establishment of a TCP connection and determination that
HTTP/2.0 will be used by both peers, each endpoint MUST send a
connection header as a final confirmation and to establish the
initial settings for the HTTP/2.0 connection.
*/
/* tvb_memeql makes certain there are enough bytes in the buffer.
* returns -1 if there are not enough bytes or if there is not a
* match. Returns 0 on a match
*/
if (tvb_memeql(tvb, offset, kMagicHello, MAGIC_FRAME_LENGTH) == 0 )
{
col_append_sep_str( pinfo->cinfo, COL_INFO, ", ", "Magic" );
proto_item_set_len(ti, MAGIC_FRAME_LENGTH);
proto_item_append_text(ti, ": Magic");
proto_tree_add_item(http2_tree, hf_http2_magic, tvb, offset, MAGIC_FRAME_LENGTH, ENC_ASCII|ENC_NA);
return;
}
proto_tree_add_item(http2_tree, hf_http2_length, tvb, offset, 2, ENC_NA);
length = tvb_get_ntohs(tvb, offset);
offset += 2;
proto_tree_add_item(http2_tree, hf_http2_type, tvb, offset, 1, ENC_NA);
type = tvb_get_guint8(tvb, offset);
col_append_sep_fstr( pinfo->cinfo, COL_INFO, ", ", "%s", val_to_str(type, http2_type_vals, "Unknown type (%d)"));
offset += 1;
flags = dissect_http2_header_flags(tvb, pinfo, http2_tree, offset, type);
offset += 1;
proto_tree_add_item(http2_tree, hf_http2_r, tvb, offset, 4, ENC_NA);
proto_tree_add_item(http2_tree, hf_http2_streamid, tvb, offset, 4, ENC_NA);
streamid = tvb_get_ntohl(tvb, offset) & MASK_HTTP2_STREAMID;
proto_item_append_text(ti, ": %s, Stream ID: %u, Length %u", val_to_str(type, http2_type_vals, "Unknown type (%d)"), streamid, length);
offset += 4;
switch(type){
case HTTP2_DATA: /* Data (0) */
/* TODO: Enhance dissect (Display in text ?) */
next_tvb = tvb_new_subset_remaining(tvb, offset);
call_dissector(data_handle, next_tvb, pinfo, http2_tree);
break;
case HTTP2_HEADERS: /* Headers (1) */
dissect_http2_headers(tvb, pinfo, http2_tree, offset, flags);
break;
case HTTP2_PRIORITY: /* Priority (2) */
dissect_http2_priority(tvb, pinfo, http2_tree, offset, flags);
break;
case HTTP2_RST_STREAM: /* RST Stream (3) */
dissect_http2_rst_stream(tvb, pinfo, http2_tree, offset, flags);
break;
case HTTP2_SETTINGS: /* Settings (4) */
dissect_http2_settings(tvb, pinfo, http2_tree, offset, flags);
break;
case HTTP2_PUSH_PROMISE: /* PUSH Promise (5) */
dissect_http2_push_promise(tvb, pinfo, http2_tree, offset, flags);
break;
case HTTP2_PING: /* Ping (6) */
dissect_http2_ping(tvb, pinfo, http2_tree, offset, flags);
break;
case HTTP2_GOAWAY: /* Goaway (7) */
dissect_http2_goaway(tvb, pinfo, http2_tree, offset, flags);
break;
case HTTP2_WINDOW_UPDATE: /* Window Update (9) */
dissect_http2_window_update(tvb, pinfo, http2_tree, offset, flags);
break;
case HTTP2_CONTINUATION: /* Continuation (10) */
dissect_http2_continuation(tvb, pinfo, http2_tree, offset, flags);
break;
default:
proto_tree_add_item(http2_tree, hf_http2_unknown, tvb, offset, -1, ENC_NA);
break;
}
return;
}
static guint get_http2_message_len( packet_info *pinfo _U_, tvbuff_t *tvb, int offset )
{
if ( tvb_memeql( tvb, offset, kMagicHello, MAGIC_FRAME_LENGTH ) == 0 ) {
return MAGIC_FRAME_LENGTH;
}
return (guint)tvb_get_ntohs(tvb, offset) + FRAME_HEADER_LENGTH;
}
static int
dissect_http2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
void *data _U_)
{
proto_item *ti;
proto_tree *http2_tree;
/* Check that there's enough data */
if (tvb_length(tvb) < FRAME_HEADER_LENGTH)
return 0;
col_set_str(pinfo->cinfo, COL_PROTOCOL, "HTTP2");
col_clear(pinfo->cinfo, COL_INFO);
ti = proto_tree_add_item(tree, proto_http2, tvb, 0, -1, ENC_NA);
proto_item_append_text(ti, " (draft-06)");
http2_tree = proto_item_add_subtree(ti, ett_http2);
tcp_dissect_pdus(tvb, pinfo, http2_tree, TRUE, FRAME_HEADER_LENGTH,
get_http2_message_len, dissect_http2_pdu);
return tvb_length(tvb);
}
void
proto_register_http2(void)
{
static hf_register_info hf[] = {
/* Packet Header */
{ &hf_http2,
{ "Stream", "http2",
FT_NONE, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_http2_length,
{ "Length", "http2.length",
FT_UINT16, BASE_DEC, NULL, 0x0,
"The length of the frame payload (The 8 octets of the frame header are not included)", HFILL }
},
{ &hf_http2_type,
{ "Type", "http2.type",
FT_UINT8, BASE_DEC, VALS(http2_type_vals), 0x0,
"The frame type determines how the remainder of the frame header and payload are interpreted", HFILL }
},
{ &hf_http2_r,
{ "Reserved", "http2.r",
FT_UINT32, BASE_HEX, NULL, MASK_HTTP2_RESERVED,
"The semantics of this bit are undefined and the bit MUST remain unset (0) when sending and MUST be ignored when receiving", HFILL }
},
{ &hf_http2_streamid,
{ "Stream Identifier", "http2.streamid",
FT_UINT32, BASE_DEC, NULL, MASK_HTTP2_STREAMID,
"A 31-bit stream identifier", HFILL }
},
{ &hf_http2_magic,
{ "Magic", "http2.magic",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_http2_unknown,
{ "Unknown", "http2.unknown",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
/* Flags */
{ &hf_http2_flags,
{ "Flags", "http2.flags",
FT_UINT8, BASE_HEX, NULL, 0x0,
"Flags are assigned semantics specific to the indicated frame type", HFILL }
},
{ &hf_http2_flags_end_stream,
{ "End Stream", "http2.flags.es",
FT_BOOLEAN, 8, NULL, HTTP2_FLAGS_ES,
"Set indicates that this frame is the last that the endpoint will send for the identified stream", HFILL }
},
{ &hf_http2_flags_end_headers,
{ "End Headers", "http2.flags.eh",
FT_BOOLEAN, 8, NULL, HTTP2_FLAGS_EH,
"Set indicates that this frame ends the sequence of header block fragments necessary to provide a complete set of headers", HFILL }
},
{ &hf_http2_flags_priority,
{ "Priority", "http2.flags.pr",
FT_BOOLEAN, 8, NULL, HTTP2_FLAGS_PR,
"Set indicates that the first four octets of this frame contain a single reserved bit and a 31-bit priority", HFILL }
},
{ &hf_http2_flags_end_push_promise,
{ "End Push Promise", "http2.flags.epp",
FT_BOOLEAN, 8, NULL, HTTP2_FLAGS_EPP,
"Set indicates that this frame ends the sequence of header block fragments necessary to provide a complete set of headers", HFILL }
},
{ &hf_http2_flags_pong,
{ "Pong", "http2.flags.po",
FT_BOOLEAN, 8, NULL, HTTP2_FLAGS_PO,
"Set indicates that this PING frame is a PING response", HFILL }
},
{ &hf_http2_flags_reserved,
{ "Reserved", "http2.flags.r",
FT_UINT8, BASE_HEX, NULL, HTTP2_FLAGS_R,
"(Must be zero)", HFILL }
},
{ &hf_http2_flags_reserved1,
{ "Reserved", "http2.flags.r1",
FT_UINT8, BASE_HEX, NULL, HTTP2_FLAGS_R1,
"(Must be zero)", HFILL }
},
{ &hf_http2_flags_reserved2,
{ "Reserved", "http2.flags.r2",
FT_UINT8, BASE_HEX, NULL, HTTP2_FLAGS_R2,
"(Must be zero)", HFILL }
},
{ &hf_http2_flags_reserved3,
{ "Reserved", "http2.flags.r3",
FT_UINT8, BASE_HEX, NULL, HTTP2_FLAGS_R3,
"(Must be zero)", HFILL }
},
{ &hf_http2_flags_reserved4,
{ "Reserved", "http2.flags.r4",
FT_UINT8, BASE_HEX, NULL, HTTP2_FLAGS_R4,
"(Must be zero)", HFILL }
},
/* Headers */
{ &hf_http2_headers_r,
{ "Reserved", "http2.headers.r",
FT_UINT32, BASE_HEX, NULL, MASK_HTTP2_RESERVED,
"Must be zero", HFILL }
},
{ &hf_http2_headers_priority,
{ "Priority", "http2.headers.priority",
FT_UINT32, BASE_DEC, NULL, MASK_HTTP2_PRIORITY,
"Priority for the stream (0 is the highest priority)", HFILL }
},
{ &hf_http2_headers,
{ "Headers", "http2.headers",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
/* Priority */
{ &hf_http2_priority_r,
{ "Reserved", "http2.priority.r",
FT_UINT32, BASE_HEX, NULL, MASK_HTTP2_RESERVED,
"Must be zero", HFILL }
},
{ &hf_http2_priority,
{ "Priority", "http2.priority",
FT_UINT32, BASE_DEC, NULL, MASK_HTTP2_PRIORITY,
"Priority for the stream (0 is the highest priority)", HFILL }
},
/* RST Stream */
{ &hf_http2_rst_stream_error,
{ "Error", "http2.rst_stream.error",
FT_UINT32, BASE_DEC, VALS(http2_error_codes_vals), 0x0,
"The error code indicates why the stream is being terminated", HFILL }
},
/* Settings */
{ &hf_http2_settings,
{ "Settings", "http2.settings",
FT_NONE, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_http2_settings_r,
{ "Reserved", "http2.settings.r",
FT_UINT8, BASE_HEX, NULL, 0x0,
"Must be zero", HFILL }
},
{ &hf_http2_settings_identifier,
{ "Settings Identifier", "http2.settings.id",
FT_UINT24, BASE_DEC, VALS(http2_settings_vals), 0x0,
NULL, HFILL }
},
{ &hf_http2_settings_max_concurrent_streams,
{ "Max concurrent streams", "http2.settings.max_concurrent_streams",
FT_UINT32, BASE_DEC, NULL, 0x0,
"Indicates the maximum number of concurrent streams that the sender will allow", HFILL }
},
{ &hf_http2_settings_initial_window_size,
{ "Initial Windows Size", "http2.settings.initial_window_size",
FT_UINT32, BASE_DEC, NULL, 0x0,
"Indicates the sender's initial window size (in bytes) for stream level flow control", HFILL }
},
{ &hf_http2_settings_flow_control_options,
{ "Flow Control Options", "http2.settings.flow_control_options",
FT_UINT32, BASE_DEC, NULL, 0x0,
"Indicates flow control options", HFILL }
},
{ &hf_http2_settings_unknown,
{ "Unknown Settings", "http2.settings.unknown",
FT_UINT32, BASE_DEC, NULL, 0x0,
NULL, HFILL }
},
/* Push Promise */
{ &hf_http2_push_promise_r,
{ "Reserved", "http2.push_promise.r",
FT_UINT32, BASE_HEX, NULL, MASK_HTTP2_RESERVED,
"Must be zero", HFILL }
},
{ &hf_http2_push_promise_promised_stream_id,
{ "Promised-Stream-ID", "http2.push_promise.promised_stream_id",
FT_UINT32, BASE_DEC, NULL, MASK_HTTP2_PRIORITY,
"Identifies the stream the endpoint intends to start sending frames for", HFILL }
},
{ &hf_http2_push_promise_header,
{ "Header", "http2.push_promise.header",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
/* Ping / Pong */
{ &hf_http2_ping,
{ "Ping", "http2.ping",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_http2_pong,
{ "Pong", "http2.pong",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
/* Goaway */
{ &hf_http2_goaway_r,
{ "Reserved", "http2.goway.r",
FT_UINT32, BASE_HEX, NULL, MASK_HTTP2_RESERVED,
"Must be zero", HFILL }
},
{ &hf_http2_goaway_last_stream_id,
{ "Promised-Stream-ID", "http2.goaway.last_stream_id",
FT_UINT32, BASE_DEC, NULL, MASK_HTTP2_PRIORITY,
"Contains the highest numbered stream identifier for which the sender of the GOAWAY frame has received frames on and might have taken some action on", HFILL }
},
{ &hf_http2_goaway_error,
{ "Error", "http2.goaway.error",
FT_UINT32, BASE_DEC, VALS(http2_error_codes_vals), 0x0,
"The error code indicates the reason for closing the connection", HFILL }
},
{ &hf_http2_goaway_addata,
{ "Additional Debug Data", "http2.goaway.addata",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
/* Window Update */
{ &hf_http2_window_update_r,
{ "Reserved", "http2.window_update.r",
FT_UINT32, BASE_HEX, NULL, MASK_HTTP2_RESERVED,
"Must be zero", HFILL }
},
{ &hf_http2_window_update_window_size_increment,
{ "Window Size Increment", "http2.window_update.window_size_increment",
FT_UINT32, BASE_DEC, NULL, MASK_HTTP2_PRIORITY,
"Indicating the number of bytes that the sender can transmit in addition to the existing flow control window", HFILL }
},
/* Continuation */
{ &hf_http2_continuation_header,
{ "Continuation Header", "http2.continuation.header",
FT_STRING, BASE_NONE, NULL, 0x0,
"Contains a header block fragment", HFILL }
},
};
static ei_register_info ei[] = {
{ &ei_http2_flags_epp_old, { "http2.flags.epp_old", PI_PROTOCOL, PI_WARN, "Deprecated End Push Promise flags (Now need to use 0x04)", EXPFILL }},
};
static gint *ett[] = {
&ett_http2,
&ett_http2_header,
&ett_http2_flags,
&ett_http2_settings
};
expert_module_t* expert_http2;
proto_http2 = proto_register_protocol("HyperText Transfer Protocol 2", "HTTP2", "http2");
proto_register_field_array(proto_http2, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
expert_http2 = expert_register_protocol(proto_http2);
expert_register_field_array(expert_http2, ei, array_length(ei));
new_register_dissector("http2", dissect_http2, proto_http2);
}
void
proto_reg_handoff_http2(void)
{
data_handle = find_dissector("data");
}
/*
* Editor modelines - http://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/