diff options
author | Alexis La Goutte <alexis.lagoutte@gmail.com> | 2013-08-30 11:14:09 +0000 |
---|---|---|
committer | Alexis La Goutte <alexis.lagoutte@gmail.com> | 2013-08-30 11:14:09 +0000 |
commit | c42a4f77061dcf648544ec16d49e52e9d769daf4 (patch) | |
tree | 9b84d263d622f18a710c1d3701daa678e1c1bd0c /epan/dissectors/packet-http2.c | |
parent | d89a3bab7755b30c0d73237a208d0ccd8c896b26 (diff) |
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
Diffstat (limited to 'epan/dissectors/packet-http2.c')
-rw-r--r-- | epan/dissectors/packet-http2.c | 867 |
1 files changed, 867 insertions, 0 deletions
diff --git a/epan/dissectors/packet-http2.c b/epan/dissectors/packet-http2.c new file mode 100644 index 0000000000..951b578672 --- /dev/null +++ b/epan/dissectors/packet-http2.c @@ -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: + */ |