Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(842)

Unified Diff: net/tools/spdyshark/packet-spdy.c

Issue 650167: Wireshark dissector for SPDY protocol. (Closed)
Patch Set: Created 10 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: net/tools/spdyshark/packet-spdy.c
diff --git a/net/tools/spdyshark/packet-spdy.c b/net/tools/spdyshark/packet-spdy.c
new file mode 100644
index 0000000000000000000000000000000000000000..4b48596fd5eb770fd04c97258f7132b22b00d115
--- /dev/null
+++ b/net/tools/spdyshark/packet-spdy.c
@@ -0,0 +1,1464 @@
+/* packet-spdy.c
+ * Routines for SPDY packet disassembly
+ * For now, the protocol spec can be found at
+ * http://dev.chromium.org/spdy/spdy-protocol
+ *
+ * Copyright 2010, Google Inc.
+ * Eric Shienbrood <ers@google.com>
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * Originally based on packet-http.c
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include <ctype.h>
+
+#include <glib.h>
+#include <epan/conversation.h>
+#include <epan/packet.h>
+#include <epan/strutil.h>
+#include <epan/base64.h>
+#include <epan/emem.h>
+#include <epan/stats_tree.h>
+
+#include <epan/req_resp_hdrs.h>
+#include <plugins/spdy/packet-spdy.h>
+#include <epan/dissectors/packet-tcp.h>
+#include <epan/dissectors/packet-ssl.h>
+#include <epan/prefs.h>
+#include <epan/expert.h>
+#include <epan/uat.h>
+
+#define SPDY_FIN 0x01
+
+/* The types of SPDY control frames */
+typedef enum _spdy_type {
+ SPDY_DATA,
+ SPDY_SYN_STREAM,
+ SPDY_SYN_REPLY,
+ SPDY_FIN_STREAM,
+ SPDY_HELLO,
+ SPDY_NOOP,
+ SPDY_PING,
+ SPDY_INVALID
+} spdy_frame_type_t;
+
+static const char *frame_type_names[] = {
+ "DATA", "SYN_STREAM", "SYN_REPLY", "FIN_STREAM", "HELLO", "NOOP",
+ "PING", "INVALID"
+};
+
+/*
+ * This structure will be tied to each SPDY frame.
+ * Note that there may be multiple SPDY frames
+ * in one packet.
+ */
+typedef struct _spdy_frame_info_t {
+ guint32 stream_id;
+ guint8 *header_block;
+ guint header_block_len;
+ guint16 frame_type;
+} spdy_frame_info_t;
+
+/*
+ * This structures keeps track of all the data frames
+ * associated with a stream, so that they can be
+ * reassembled into a single chunk.
+ */
+typedef struct _spdy_data_frame_t {
+ guint8 *data;
+ guint32 length;
+ guint32 framenum;
+} spdy_data_frame_t;
+
+typedef struct _spdy_stream_info_t {
+ gchar *content_type;
+ gchar *content_type_parameters;
+ gchar *content_encoding;
+ GSList *data_frames;
+ tvbuff_t *assembled_data;
+ guint num_data_frames;
+} spdy_stream_info_t;
+
+#include <epan/tap.h>
+
+
+static int spdy_tap = -1;
+static int spdy_eo_tap = -1;
+
+static int proto_spdy = -1;
+static int hf_spdy_syn_stream = -1;
+static int hf_spdy_syn_reply = -1;
+static int hf_spdy_control_bit = -1;
+static int hf_spdy_version = -1;
+static int hf_spdy_type = -1;
+static int hf_spdy_flags = -1;
+static int hf_spdy_flags_fin = -1;
+static int hf_spdy_length = -1;
+static int hf_spdy_header = -1;
+static int hf_spdy_header_name = -1;
+static int hf_spdy_header_name_text = -1;
+static int hf_spdy_header_value = -1;
+static int hf_spdy_header_value_text = -1;
+static int hf_spdy_streamid = -1;
+static int hf_spdy_priority = -1;
+static int hf_spdy_num_headers = -1;
+static int hf_spdy_num_headers_string = -1;
+
+static gint ett_spdy = -1;
+static gint ett_spdy_syn_stream = -1;
+static gint ett_spdy_syn_reply = -1;
+static gint ett_spdy_fin_stream = -1;
+static gint ett_spdy_flags = -1;
+static gint ett_spdy_header = -1;
+static gint ett_spdy_header_name = -1;
+static gint ett_spdy_header_value = -1;
+
+static gint ett_spdy_encoded_entity = -1;
+
+static dissector_handle_t data_handle;
+static dissector_handle_t media_handle;
+static dissector_handle_t spdy_handle;
+
+/* Stuff for generation/handling of fields for custom HTTP headers */
+typedef struct _header_field_t {
+ gchar* header_name;
+ gchar* header_desc;
+} header_field_t;
+
+/*
+ * desegmentation of SPDY control frames
+ * (when we are over TCP or another protocol providing the desegmentation API)
+ */
+static gboolean spdy_desegment_control_frames = TRUE;
+
+/*
+ * desegmentation of SPDY data frames bodies
+ * (when we are over TCP or another protocol providing the desegmentation API)
+ * TODO let the user filter on content-type the bodies he wants desegmented
+ */
+static gboolean spdy_desegment_data_frames = TRUE;
+
+static gboolean spdy_assemble_entity_bodies = TRUE;
+
+/*
+ * Decompression of zlib encoded entities.
+ */
+#ifdef HAVE_LIBZ
+static gboolean spdy_decompress_body = TRUE;
+static gboolean spdy_decompress_headers = TRUE;
+#else
+static gboolean spdy_decompress_body = FALSE;
+static gboolean spdy_decompress_headers = FALSE;
+#endif
+static gboolean spdy_debug = FALSE;
+
+#define TCP_PORT_DAAP 3689
Mike Belshe 2010/02/22 21:55:08 Remove?
+
+/*
+ * SSDP is implemented atop HTTP (yes, it really *does* run over UDP).
+ */
+#define TCP_PORT_SSDP 1900
Mike Belshe 2010/02/22 21:55:08 Remove?
+#define UDP_PORT_SSDP 1900
+
+/*
+ * tcp and ssl ports
+ */
+
+#define TCP_DEFAULT_RANGE "80,8080"
+#define SSL_DEFAULT_RANGE "443"
+
+static range_t *global_spdy_tcp_range = NULL;
+static range_t *global_spdy_ssl_range = NULL;
+
+static range_t *spdy_tcp_range = NULL;
+static range_t *spdy_ssl_range = NULL;
+
+static const value_string vals_status_code[] = {
+ { 100, "Continue" },
+ { 101, "Switching Protocols" },
+ { 102, "Processing" },
+ { 199, "Informational - Others" },
+
+ { 200, "OK"},
+ { 201, "Created"},
+ { 202, "Accepted"},
+ { 203, "Non-authoritative Information"},
+ { 204, "No Content"},
+ { 205, "Reset Content"},
+ { 206, "Partial Content"},
+ { 207, "Multi-Status"},
+ { 299, "Success - Others"},
+
+ { 300, "Multiple Choices"},
+ { 301, "Moved Permanently"},
+ { 302, "Found"},
+ { 303, "See Other"},
+ { 304, "Not Modified"},
+ { 305, "Use Proxy"},
+ { 307, "Temporary Redirect"},
+ { 399, "Redirection - Others"},
+
+ { 400, "Bad Request"},
+ { 401, "Unauthorized"},
+ { 402, "Payment Required"},
+ { 403, "Forbidden"},
+ { 404, "Not Found"},
+ { 405, "Method Not Allowed"},
+ { 406, "Not Acceptable"},
+ { 407, "Proxy Authentication Required"},
+ { 408, "Request Time-out"},
+ { 409, "Conflict"},
+ { 410, "Gone"},
+ { 411, "Length Required"},
+ { 412, "Precondition Failed"},
+ { 413, "Request Entity Too Large"},
+ { 414, "Request-URI Too Long"},
+ { 415, "Unsupported Media Type"},
+ { 416, "Requested Range Not Satisfiable"},
+ { 417, "Expectation Failed"},
+ { 418, "I'm a teapot"}, /* RFC 2324 */
+ { 422, "Unprocessable Entity"},
+ { 423, "Locked"},
+ { 424, "Failed Dependency"},
+ { 499, "Client Error - Others"},
+
+ { 500, "Internal Server Error"},
+ { 501, "Not Implemented"},
+ { 502, "Bad Gateway"},
+ { 503, "Service Unavailable"},
+ { 504, "Gateway Time-out"},
+ { 505, "HTTP Version not supported"},
+ { 507, "Insufficient Storage"},
+ { 599, "Server Error - Others"},
+
+ { 0, NULL}
+};
+
+static const char spdy_dictionary[] =
+ "optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-"
+ "languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi"
+ "f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser"
+ "-agent10010120020120220320420520630030130230330430530630740040140240340440"
+ "5406407408409410411412413414415416417500501502503504505accept-rangesageeta"
+ "glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic"
+ "ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran"
+ "sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati"
+ "oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo"
+ "ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe"
+ "pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic"
+ "ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1"
+ ".1statusversionurl";
+
+static void reset_decompressors(void)
+{
+ if (spdy_debug) printf("Should reset SPDY decompressors\n");
Mike Belshe 2010/02/22 21:55:08 nit: 2 lines
Eric Shienbrood 2010/02/22 23:30:32 C++ style guide says: "Short conditional statement
+}
+
+static spdy_conv_t *
+get_spdy_conversation_data(packet_info *pinfo)
+{
+ conversation_t *conversation;
Mike Belshe 2010/02/22 21:55:08 nit: conversation_t* conversation. there are mor
Eric Shienbrood 2010/02/22 23:30:32 I'm not sure we should be following the Google sty
cbentzel 2010/02/23 02:21:58 Agree. I think the ultimate goal is to get this in
+ spdy_conv_t *conv_data;
+ int retcode;
+
+ conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
Mike Belshe 2010/02/22 21:55:08 nit: 80 cols.
+ if (spdy_debug) {
+ printf("\n===========================================\n\n");
+ printf("Conversation for frame #%d is %p\n", pinfo->fd->num, conversation);
+ if (conversation)
+ printf(" conv_data=%p\n", conversation_get_proto_data(conversation, proto_spdy));
+ }
+
+ if(!conversation) /* Conversation does not exist yet - create it */
+ conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
Mike Belshe 2010/02/22 21:55:08 nit: 80cols
+
+ /* Retrieve information from conversation
+ */
+ conv_data = conversation_get_proto_data(conversation, proto_spdy);
+ if(!conv_data) {
+ /* Setup the conversation structure itself */
+ conv_data = se_alloc0(sizeof(spdy_conv_t));
+
+ conv_data->streams = NULL;
+ if (spdy_decompress_headers) {
+ conv_data->rqst_decompressor = se_alloc0(sizeof(z_stream));
+ conv_data->rply_decompressor = se_alloc0(sizeof(z_stream));
+ retcode = inflateInit(conv_data->rqst_decompressor);
+ if (retcode == Z_OK)
+ retcode = inflateInit(conv_data->rply_decompressor);
+ if (retcode != Z_OK)
+ printf("frame #%d: inflateInit() failed: %d\n", pinfo->fd->num, retcode);
+ else if (spdy_debug)
+ printf("created decompressor\n");
+ conv_data->dictionary_id = adler32(0L, Z_NULL, 0);
+ conv_data->dictionary_id = adler32(conv_data->dictionary_id,
+ spdy_dictionary,
+ sizeof(spdy_dictionary));
+ }
+
+ conversation_add_proto_data(conversation, proto_spdy, conv_data);
+ register_postseq_cleanup_routine(reset_decompressors);
+ }
+ return conv_data;
+}
+
+static void
+spdy_save_stream_info(spdy_conv_t *conv_data,
+ guint32 stream_id,
+ gchar *content_type,
+ gchar *content_type_params,
+ gchar *content_encoding)
+{
+ spdy_stream_info_t *si;
+
+ if (conv_data->streams == NULL)
+ conv_data->streams = g_array_new(FALSE, TRUE, sizeof(spdy_stream_info_t *));
+ if (stream_id < conv_data->streams->len)
+ DISSECTOR_ASSERT(g_array_index(conv_data->streams, spdy_stream_info_t*, stream_id) == NULL);
+ else
+ g_array_set_size(conv_data->streams, stream_id+1);
+ si = se_alloc(sizeof(spdy_stream_info_t));
+ si->content_type = content_type;
+ si->content_type_parameters = content_type_params;
+ si->content_encoding = content_encoding;
+ si->data_frames = NULL;
+ si->num_data_frames = 0;
+ si->assembled_data = NULL;
+ g_array_index(conv_data->streams, spdy_stream_info_t*, stream_id) = si;
+ if (spdy_debug)
+ printf("Saved stream info for ID %u, content type %s\n", stream_id, content_type);
+}
+
+static spdy_stream_info_t *
+spdy_get_stream_info(spdy_conv_t *conv_data, guint32 stream_id)
+{
+ if (conv_data->streams == NULL || stream_id >= conv_data->streams->len)
+ return NULL;
+ else
+ return g_array_index(conv_data->streams, spdy_stream_info_t*, stream_id);
+}
+
+static void
+spdy_add_data_chunk(spdy_conv_t *conv_data, guint32 stream_id, guint32 frame,
+ guint8 *data, guint32 length)
+{
+ spdy_stream_info_t *si = spdy_get_stream_info(conv_data, stream_id);
+
+ if (si == NULL) {
+ if (spdy_debug) printf("No stream_info found for stream %d\n", stream_id);
+ } else {
+ spdy_data_frame_t *df = g_malloc(sizeof(spdy_data_frame_t));
+ df->data = data;
+ df->length = length;
+ df->framenum = frame;
+ si->data_frames = g_slist_append(si->data_frames, df);
+ ++si->num_data_frames;
+ if (spdy_debug)
+ printf("Saved %u bytes of data for stream %u frame %u\n",
+ length, stream_id, df->framenum);
+ }
+}
+
+static void
+spdy_increment_data_chunk_count(spdy_conv_t *conv_data, guint32 stream_id)
+{
+ spdy_stream_info_t *si = spdy_get_stream_info(conv_data, stream_id);
+ if (si != NULL)
+ ++si->num_data_frames;
+}
+
+/*
+ * Return the number of data frames saved so far for the specified stream.
+ */
+static guint
+spdy_get_num_data_frames(spdy_conv_t *conv_data, guint32 stream_id)
+{
+ spdy_stream_info_t *si = spdy_get_stream_info(conv_data, stream_id);
+
+ return si == NULL ? 0 : si->num_data_frames;
+}
+
+static spdy_stream_info_t *
+spdy_assemble_data_frames(spdy_conv_t *conv_data, guint32 stream_id)
+{
+ spdy_stream_info_t *si = spdy_get_stream_info(conv_data, stream_id);
+ tvbuff_t *tvb;
+
+ if (si == NULL)
+ return NULL;
+
+ /*
+ * Compute the total amount of data and concatenate the
+ * data chunks, if it hasn't already been done.
+ */
+ if (si->assembled_data == NULL) {
+ spdy_data_frame_t *df;
+ guint8 *data;
+ guint32 datalen;
+ guint32 offset;
+ guint32 framenum;
+ GSList *dflist = si->data_frames;
+ if (dflist == NULL)
+ return si;
+ dflist = si->data_frames;
+ datalen = 0;
+ /*
+ * I'd like to use a composite tvbuff here, but since
+ * only a real-data tvbuff can be the child of another
+ * tvb, I can't. It would be nice if this limitation
+ * could be fixed.
+ */
+ while (dflist != NULL) {
+ df = dflist->data;
+ datalen += df->length;
+ dflist = g_slist_next(dflist);
+ }
+ if (datalen != 0) {
+ data = se_alloc(datalen);
+ dflist = si->data_frames;
+ offset = 0;
+ framenum = 0;
+ while (dflist != NULL) {
+ df = dflist->data;
+ memcpy(data+offset, df->data, df->length);
+ offset += df->length;
+ dflist = g_slist_next(dflist);
+ }
+ tvb = tvb_new_real_data(data, datalen, datalen);
+ si->assembled_data = tvb;
+ }
+ }
+ return si;
+}
+
+static void
+spdy_discard_data_frames(spdy_stream_info_t *si)
+{
+ GSList *dflist = si->data_frames;
+ spdy_data_frame_t *df;
+
+ if (dflist == NULL)
+ return;
+ while (dflist != NULL) {
+ df = dflist->data;
+ if (df->data != NULL) {
+ g_free(df->data);
+ df->data = NULL;
+ }
+ dflist = g_slist_next(dflist);
+ }
+ /*g_slist_free(si->data_frames);
+ si->data_frames = NULL; */
+}
+
+static int
+dissect_spdy_data_frame(tvbuff_t *tvb, int offset,
+ packet_info *pinfo,
+ proto_tree *top_level_tree,
+ proto_tree *spdy_tree,
+ proto_item *spdy_proto,
+ spdy_conv_t *conv_data)
+{
+ guint32 stream_id;
+ guint8 flags;
+ guint32 frame_length;
+ proto_item *ti;
+ proto_tree *flags_tree;
+ guint32 reported_datalen;
+ guint32 datalen;
+ dissector_table_t media_type_subdissector_table;
+ dissector_table_t port_subdissector_table;
+ dissector_handle_t handle;
+ guint num_data_frames;
+ gboolean dissected;
+
+ stream_id = tvb_get_bits32(tvb, (offset << 3) + 1, 31, FALSE);
+ flags = tvb_get_guint8(tvb, offset+4);
+ frame_length = tvb_get_ntoh24(tvb, offset+5);
+
+ if (spdy_debug)
+ printf("Data frame [stream_id=%u flags=0x%x length=%d]\n",
+ stream_id, flags, frame_length);
+ if (spdy_tree) proto_item_append_text(spdy_tree, ", data frame");
+ col_add_fstr(pinfo->cinfo, COL_INFO, "DATA[%u] length=%d",
+ stream_id, frame_length);
+
+ proto_item_append_text(spdy_proto, ":%s stream=%d length=%d",
+ flags & SPDY_FIN ? " [FIN]" : "",
+ stream_id, frame_length);
+
+ proto_tree_add_boolean(spdy_tree, hf_spdy_control_bit, tvb, offset, 1, 0);
+ proto_tree_add_uint(spdy_tree, hf_spdy_streamid, tvb, offset, 4, stream_id);
+ ti = proto_tree_add_uint_format(spdy_tree, hf_spdy_flags, tvb, offset+4, 1, flags,
+ "Flags: 0x%02x%s", flags, flags&SPDY_FIN ? " (FIN)" : "");
+
+ flags_tree = proto_item_add_subtree(ti, ett_spdy_flags);
+ proto_tree_add_boolean(flags_tree, hf_spdy_flags_fin, tvb, offset+4, 1, flags);
+ proto_tree_add_uint(spdy_tree, hf_spdy_length, tvb, offset+5, 3, frame_length);
+
+ datalen = tvb_length_remaining(tvb, offset);
+ if (datalen > frame_length)
+ datalen = frame_length;
+
+ reported_datalen = tvb_reported_length_remaining(tvb, offset);
+ if (reported_datalen > frame_length)
+ reported_datalen = frame_length;
+
+ num_data_frames = spdy_get_num_data_frames(conv_data, stream_id);
+ if (datalen != 0 || num_data_frames != 0) {
+ /*
+ * There's stuff left over; process it.
+ */
+ tvbuff_t *next_tvb = NULL;
+ tvbuff_t *data_tvb = NULL;
+ spdy_stream_info_t *si = NULL;
+ void *save_private_data = NULL;
+ guint8 *copied_data;
+ gboolean private_data_changed = FALSE;
+ gboolean is_single_chunk = FALSE;
+ gboolean have_entire_body;
+
+ /*
+ * Create a tvbuff for the payload.
+ */
+ if (datalen != 0) {
+ next_tvb = tvb_new_subset(tvb, offset+8, datalen,
+ reported_datalen);
+ is_single_chunk = num_data_frames == 0 && (flags & SPDY_FIN) != 0;
+ if (!pinfo->fd->flags.visited) {
+ if (!is_single_chunk) {
+ if (spdy_assemble_entity_bodies) {
+ copied_data = tvb_memdup(next_tvb, 0, datalen);
+ spdy_add_data_chunk(conv_data, stream_id, pinfo->fd->num,
+ copied_data, datalen);
+ } else
+ spdy_increment_data_chunk_count(conv_data, stream_id);
+ }
+ }
+ } else
+ is_single_chunk = (num_data_frames == 1);
+
+ if (!(flags & SPDY_FIN)) {
+ col_set_fence(pinfo->cinfo, COL_INFO);
+ col_add_fstr(pinfo->cinfo, COL_INFO, " (partial entity)");
+ proto_item_append_text(spdy_proto, " (partial entity body)");
+ /* would like the proto item to say */
+ /* " (entity body fragment N of M)" */
+ goto body_dissected;
+ }
+ have_entire_body = is_single_chunk;
+ /*
+ * On seeing the last data frame in a stream, we can
+ * reassemble the frames into one data block.
+ */
+ si = spdy_assemble_data_frames(conv_data, stream_id);
+ if (si == NULL)
+ goto body_dissected;
+ data_tvb = si->assembled_data;
+ if (spdy_assemble_entity_bodies)
+ have_entire_body = TRUE;
+
+ if (!have_entire_body)
+ goto body_dissected;
+
+ if (data_tvb == NULL)
+ data_tvb = next_tvb;
+ else
+ add_new_data_source(pinfo, data_tvb, "Assembled entity body");
+
+ if (have_entire_body && si->content_encoding != NULL &&
+ g_ascii_strcasecmp(si->content_encoding, "identity") != 0) {
+ /*
+ * We currently can't handle, for example, "compress";
+ * just handle them as data for now.
+ *
+ * After July 7, 2004 the LZW patent expires, so support
+ * might be added then. However, I don't think that
+ * anybody ever really implemented "compress", due to
+ * the aforementioned patent.
+ */
+ tvbuff_t *uncomp_tvb = NULL;
+ proto_item *e_ti = NULL;
+ proto_item *ce_ti = NULL;
+ proto_tree *e_tree = NULL;
+
+ if (spdy_decompress_body &&
+ (g_ascii_strcasecmp(si->content_encoding, "gzip") == 0 ||
+ g_ascii_strcasecmp(si->content_encoding, "deflate")
+ == 0)) {
+
+ uncomp_tvb = tvb_child_uncompress(tvb, data_tvb, 0,
+ tvb_length(data_tvb));
+ }
+ /*
+ * Add the encoded entity to the protocol tree
+ */
+ e_ti = proto_tree_add_text(top_level_tree, data_tvb,
+ 0, tvb_length(data_tvb),
+ "Content-encoded entity body (%s): %u bytes",
+ si->content_encoding,
+ tvb_length(data_tvb));
+ e_tree = proto_item_add_subtree(e_ti, ett_spdy_encoded_entity);
+ if (si->num_data_frames > 1) {
+ GSList *dflist;
+ spdy_data_frame_t *df;
+ guint32 framenum;
+ ce_ti = proto_tree_add_text(e_tree, data_tvb, 0,
+ tvb_length(data_tvb),
+ "Assembled from %d frames in packet(s)", si->num_data_frames);
+ dflist = si->data_frames;
+ framenum = 0;
+ while (dflist != NULL) {
+ df = dflist->data;
+ if (framenum != df->framenum) {
+ proto_item_append_text(ce_ti, " #%u", df->framenum);
+ framenum = df->framenum;
+ }
+ dflist = g_slist_next(dflist);
+ }
+ }
+
+ if (uncomp_tvb != NULL) {
+ /*
+ * Decompression worked
+ */
+
+ /* XXX - Don't free this, since it's possible
+ * that the data was only partially
+ * decompressed, such as when desegmentation
+ * isn't enabled.
+ *
+ tvb_free(next_tvb);
+ */
+ proto_item_append_text(e_ti, " -> %u bytes", tvb_length(uncomp_tvb));
+ data_tvb = uncomp_tvb;
+ add_new_data_source(pinfo, data_tvb, "Uncompressed entity body");
+ } else {
+ if (spdy_decompress_body)
+ proto_item_append_text(e_ti, " [Error: Decompression failed]");
+ call_dissector(data_handle, data_tvb, pinfo, e_tree);
+
+ goto body_dissected;
+ }
+ }
+ if (si != NULL)
+ spdy_discard_data_frames(si);
+ /*
+ * Do subdissector checks.
+ *
+ * First, check whether some subdissector asked that they
+ * be called if something was on some particular port.
+ */
+
+ port_subdissector_table = find_dissector_table("http.port");
+ media_type_subdissector_table = find_dissector_table("media_type");
+ if (have_entire_body && port_subdissector_table != NULL)
+ handle = dissector_get_port_handle(port_subdissector_table,
+ pinfo->match_port);
+ else
+ handle = NULL;
+ if (handle == NULL && have_entire_body && si->content_type != NULL &&
+ media_type_subdissector_table != NULL) {
+ /*
+ * We didn't find any subdissector that
+ * registered for the port, and we have a
+ * Content-Type value. Is there any subdissector
+ * for that content type?
+ */
+ save_private_data = pinfo->private_data;
+ private_data_changed = TRUE;
+
+ if (si->content_type_parameters)
+ pinfo->private_data = ep_strdup(si->content_type_parameters);
+ else
+ pinfo->private_data = NULL;
+ /*
+ * Calling the string handle for the media type
+ * dissector table will set pinfo->match_string
+ * to si->content_type for us.
+ */
+ pinfo->match_string = si->content_type;
+ handle = dissector_get_string_handle(
+ media_type_subdissector_table,
+ si->content_type);
+ }
+ if (handle != NULL) {
+ /*
+ * We have a subdissector - call it.
+ */
+ dissected = call_dissector(handle, data_tvb, pinfo, top_level_tree);
+ } else
+ dissected = FALSE;
+
+ if (dissected) {
+ /*
+ * The subdissector dissected the body.
+ * Fix up the top-level item so that it doesn't
+ * include the stuff for that protocol.
+ */
+ if (ti != NULL)
+ proto_item_set_len(ti, offset);
+ } else if (have_entire_body && si->content_type != NULL) {
+ /*
+ * Calling the default media handle if there is a content-type that
+ * wasn't handled above.
+ */
+ call_dissector(media_handle, next_tvb, pinfo, top_level_tree);
+ } else {
+ /* Call the default data dissector */
+ call_dissector(data_handle, next_tvb, pinfo, top_level_tree);
+ }
+
+body_dissected:
+ /*
+ * Do *not* attempt at freeing the private data;
+ * it may be in use by subdissectors.
+ */
+ if (private_data_changed) /*restore even NULL value*/
+ pinfo->private_data = save_private_data;
+ /*
+ * We've processed "datalen" bytes worth of data
+ * (which may be no data at all); advance the
+ * offset past whatever data we've processed.
+ */
+ }
+ return frame_length + 8;
+}
+
+static guint8 *
+spdy_decompress_header_block(tvbuff_t *tvb, z_streamp decomp,
+ guint32 dictionary_id, int offset,
+ guint32 length, guint *uncomp_length)
+{
+ int retcode;
+ size_t bufsize = 16384;
+ const guint8 *hptr = tvb_get_ptr(tvb, offset, length);
+ guint8 *uncomp_block = ep_alloc(bufsize);
+ decomp->next_in = (Bytef *)hptr;
+ decomp->avail_in = length;
+ decomp->next_out = uncomp_block;
+ decomp->avail_out = bufsize;
+ retcode = inflate(decomp, Z_SYNC_FLUSH);
+ if (retcode == Z_NEED_DICT) {
+ if (decomp->adler != dictionary_id) {
+ printf("decompressor wants dictionary %#x, but we have %#x\n",
+ (guint)decomp->adler, dictionary_id);
+ } else {
+ retcode = inflateSetDictionary(decomp,
+ spdy_dictionary,
+ sizeof(spdy_dictionary));
Mike Belshe 2010/02/22 21:55:08 This should be sizeof(spdy_dictionary)-1. Probabl
Eric Shienbrood 2010/02/22 23:30:32 Has this changed? It should have been sizeof(...)-
+ if (retcode == Z_OK)
+ retcode = inflate(decomp, Z_SYNC_FLUSH);
+ }
+ }
+
+ if (retcode != Z_OK) {
+ return NULL;
+ } else {
+ *uncomp_length = bufsize - decomp->avail_out;
+ if (spdy_debug)
+ printf("Inflation SUCCEEDED. uncompressed size=%d\n", *uncomp_length);
+ if (decomp->avail_in != 0)
+ if (spdy_debug)
+ printf(" but there were %d input bytes left over\n", decomp->avail_in);
+ }
+ return se_memdup(uncomp_block, *uncomp_length);
+}
+
+/*
+ * Try to determine heuristically whether the header block is
+ * compressed. For an uncompressed block, the first two bytes
+ * gives the number of headers. Each header name and value is
+ * a two-byte length followed by ASCII characters.
+ */
+static gboolean
+spdy_check_header_compression(tvbuff_t *tvb,
+ int offset,
+ guint32 frame_length)
+{
+ guint16 length;
+ if (!tvb_bytes_exist(tvb, offset, 6))
+ return 1;
+ length = tvb_get_ntohs(tvb, offset);
+ if (length > frame_length)
+ return 1;
+ length = tvb_get_ntohs(tvb, offset+2);
+ if (length > frame_length)
+ return 1;
+ if (spdy_debug) printf("Looks like the header block is not compressed\n");
+ return 0;
+}
+
+static spdy_frame_info_t *
+spdy_save_header_block(frame_data *fd,
+ guint32 stream_id,
+ guint frame_type,
+ guint8 *header,
+ guint length)
+{
+ GSList *filist = p_get_proto_data(fd, proto_spdy);
+ spdy_frame_info_t *frame_info = se_alloc(sizeof(spdy_frame_info_t));
+ if (filist != NULL)
+ p_remove_proto_data(fd, proto_spdy);
+ frame_info->stream_id = stream_id;
+ frame_info->header_block = header;
+ frame_info->header_block_len = length;
+ frame_info->frame_type = frame_type;
+ filist = g_slist_append(filist, frame_info);
+ p_add_proto_data(fd, proto_spdy, filist);
+ return frame_info;
+ /* TODO(ers) these need to get deleted when no longer needed */
+}
+
+static spdy_frame_info_t *
+spdy_find_saved_header_block(frame_data *fd,
+ guint32 stream_id,
+ guint16 frame_type)
+{
+ GSList *filist = p_get_proto_data(fd, proto_spdy);
+ while (filist != NULL) {
+ spdy_frame_info_t *fi = filist->data;
+ if (fi->stream_id == stream_id && fi->frame_type == frame_type)
+ return fi;
+ filist = g_slist_next(filist);
+ }
+ return NULL;
+}
+
+/*
+ * Given a content type string that may contain optional parameters,
+ * return the parameter string, if any, otherwise return NULL. This
+ * also has the side effect of null terminating the content type
+ * part of the original string.
+ */
+static gchar *
+spdy_parse_content_type(gchar *content_type)
+{
+ gchar *cp = content_type;
+
+ while (*cp != '\0' && *cp != ';' && !isspace(*cp)) {
+ *cp = tolower(*cp);
+ ++cp;
+ }
+ if (*cp == '\0')
+ cp = NULL;
+
+ if (cp != NULL) {
+ *cp++ = '\0';
+ while (*cp == ';' || isspace(*cp))
+ ++cp;
+ if (*cp != '\0')
+ return cp;
+ }
+ return NULL;
+}
+
+static int
+dissect_spdy_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
+ proto_tree *tree, spdy_conv_t *conv_data)
+{
+ guint8 control_bit;
+ guint16 version;
+ guint16 frame_type;
+ guint8 flags;
+ guint32 frame_length;
+ guint32 stream_id;
+ gint priority;
+ guint16 num_headers;
+ guint32 fin_status;
+ guint8 *frame_header;
+ const char *proto_tag;
+ const char *frame_type_name;
+ proto_tree *spdy_tree = NULL;
+ proto_item *ti = NULL;
+ proto_item *spdy_proto = NULL;
+ int orig_offset;
+ int hoffset;
+ int hdr_offset = 0;
+ spdy_frame_type_t spdy_type;
+ proto_tree *sub_tree;
+ proto_tree *flags_tree;
+ tvbuff_t *header_tvb = NULL;
+ gboolean headers_compressed;
+ gchar *hdr_verb = NULL;
+ gchar *hdr_url = NULL;
+ gchar *hdr_version = NULL;
+ gchar *content_type = NULL;
+ gchar *content_encoding = NULL;
+
+ /*
+ * Minimum size for a SPDY frame is 8 bytes.
+ */
+ if (tvb_reported_length_remaining(tvb, offset) < 8)
+ return -1;
+
+ proto_tag = "SPDY";
+
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, proto_tag);
+
+ /*
+ * Is this a control frame or a data frame?
+ */
+ orig_offset = offset;
+ control_bit = tvb_get_bits8(tvb, offset << 3, 1);
+ if (control_bit) {
+ version = tvb_get_bits16(tvb, (offset << 3) + 1, 15, FALSE);
+ frame_type = tvb_get_ntohs(tvb, offset+2);
+ if (frame_type >= SPDY_INVALID) {
+ return -1;
+ }
+ frame_header = ep_tvb_memdup(tvb, offset, 16);
+ } else {
+ version = 1; /* avoid gcc warning */
+ frame_type = SPDY_DATA;
+ frame_header = NULL; /* avoid gcc warning */
+ }
+ frame_type_name = frame_type_names[frame_type];
+ offset += 4;
+ flags = tvb_get_guint8(tvb, offset);
+ frame_length = tvb_get_ntoh24(tvb, offset+1);
+ offset += 4;
+ /*
+ * Make sure there's as much data as the frame header says there is.
+ */
+ if ((guint)tvb_reported_length_remaining(tvb, offset) < frame_length) {
+ if (spdy_debug)
+ printf("Not enough header data: %d vs. %d\n",
+ frame_length, tvb_reported_length_remaining(tvb, offset));
+ return -1;
+ }
+ if (tree) {
+ spdy_proto = proto_tree_add_item(tree, proto_spdy, tvb, orig_offset, frame_length+8, FALSE);
+ spdy_tree = proto_item_add_subtree(spdy_proto, ett_spdy);
+ }
+
+ if (control_bit) {
+ if (spdy_debug)
+ printf("Control frame [version=%d type=%d flags=0x%x length=%d]\n",
+ version, frame_type, flags, frame_length);
+ if (tree) proto_item_append_text(spdy_tree, ", control frame");
+ } else {
+ return dissect_spdy_data_frame(tvb, orig_offset, pinfo, tree,
+ spdy_tree, spdy_proto, conv_data);
+ }
+ num_headers = 0;
+ sub_tree = NULL; /* avoid gcc warning */
+ switch (frame_type) {
+ case SPDY_SYN_STREAM:
+ case SPDY_SYN_REPLY:
+ if (tree) {
+ int hf;
+ hf = frame_type == SPDY_SYN_STREAM ? hf_spdy_syn_stream : hf_spdy_syn_reply;
+ ti = proto_tree_add_bytes(spdy_tree, hf, tvb,
+ orig_offset, 16, frame_header);
+ sub_tree = proto_item_add_subtree(ti, ett_spdy_syn_stream);
+ }
+ stream_id = tvb_get_bits32(tvb, (offset << 3) + 1, 31, FALSE);
+ offset += 4;
+ priority = tvb_get_bits8(tvb, offset << 3, 2);
+ offset += 2;
+ if (tree) {
+ proto_tree_add_boolean(sub_tree, hf_spdy_control_bit, tvb, orig_offset, 1, control_bit);
+ proto_tree_add_uint(sub_tree, hf_spdy_version, tvb, orig_offset, 2, version);
+ proto_tree_add_uint(sub_tree, hf_spdy_type, tvb, orig_offset+2, 2, frame_type);
+ ti = proto_tree_add_uint_format(sub_tree, hf_spdy_flags, tvb, orig_offset+4, 1, flags,
+ "Flags: 0x%02x%s", flags, flags&SPDY_FIN ? " (FIN)" : "");
+ flags_tree = proto_item_add_subtree(ti, ett_spdy_flags);
+ proto_tree_add_boolean(flags_tree, hf_spdy_flags_fin, tvb, orig_offset+4, 1, flags);
+ proto_tree_add_uint(sub_tree, hf_spdy_length, tvb, orig_offset+5, 3, frame_length);
+ proto_tree_add_uint(sub_tree, hf_spdy_streamid, tvb, orig_offset+8, 4, stream_id);
+ proto_tree_add_uint(sub_tree, hf_spdy_priority, tvb, orig_offset+12, 1, priority);
+ proto_item_append_text(spdy_proto, ": %s%s stream=%d length=%d",
+ frame_type_name,
+ flags & SPDY_FIN ? " [FIN]" : "",
+ stream_id, frame_length);
+ if (spdy_debug)
+ printf(" stream ID=%u priority=%d\n", stream_id, priority);
+ }
+ break;
+
+ case SPDY_FIN_STREAM:
+ stream_id = tvb_get_bits32(tvb, (offset << 3) + 1, 31, FALSE);
+ fin_status = tvb_get_ntohl(tvb, offset);
+ // TODO(ers) fill in tree and summary
+ offset += 8;
+ break;
+
+ case SPDY_HELLO:
+ // TODO(ers) fill in tree and summary
+ stream_id = 0; /* avoid gcc warning */
+ break;
+
+ default:
+ stream_id = 0; /* avoid gcc warning */
+ return -1;
+ break;
+ }
+
+ /*
+ * Process the name-value pairs one at a time, after possibly
+ * decompressing the header block.
+ */
+ if (frame_type == SPDY_SYN_STREAM || frame_type == SPDY_SYN_REPLY) {
+ headers_compressed = spdy_check_header_compression(tvb, offset, frame_length);
+ if (!spdy_decompress_headers || !headers_compressed) {
+ header_tvb = tvb;
+ hdr_offset = offset;
+ } else {
+ spdy_frame_info_t *per_frame_info =
+ spdy_find_saved_header_block(pinfo->fd,
+ stream_id,
+ frame_type == SPDY_SYN_REPLY);
+ if (per_frame_info == NULL) {
+ guint uncomp_length;
+ z_streamp decomp = frame_type == SPDY_SYN_STREAM ?
+ conv_data->rqst_decompressor : conv_data->rply_decompressor;
+ guint8 *uncomp_ptr =
+ spdy_decompress_header_block(tvb, decomp,
+ conv_data->dictionary_id,
+ offset, frame_length-6, &uncomp_length);
+ if (uncomp_ptr == NULL) { /* decompression failed */
+ if (spdy_debug)
+ printf("Frame #%d: Inflation failed\n", pinfo->fd->num);
+ proto_item_append_text(spdy_proto, " [Error: Header decompression failed]");
+ } else {
+ if (spdy_debug)
+ printf("Saving %u bytes of uncomp hdr\n", uncomp_length);
+ per_frame_info =
+ spdy_save_header_block(pinfo->fd, stream_id, frame_type == SPDY_SYN_REPLY,
+ uncomp_ptr, uncomp_length);
+ }
+ } else if (spdy_debug) {
+ printf("Found uncompressed header block len %u for stream %u frame_type=%d\n",
+ per_frame_info->header_block_len,
+ per_frame_info->stream_id,
+ per_frame_info->frame_type);
+ }
+ if (per_frame_info != NULL) {
+ header_tvb = tvb_new_child_real_data(tvb,
+ per_frame_info->header_block,
+ per_frame_info->header_block_len,
+ per_frame_info->header_block_len);
+ add_new_data_source(pinfo, header_tvb, "Uncompressed headers");
+ hdr_offset = 0;
+ }
+ }
+ offset += frame_length-6;
+ num_headers = tvb_get_ntohs(header_tvb, hdr_offset);
+ hdr_offset += 2;
+ if (header_tvb == NULL ||
+ (headers_compressed && !spdy_decompress_headers)) {
+ num_headers = 0;
+ ti = proto_tree_add_string(sub_tree, hf_spdy_num_headers_string,
+ tvb, orig_offset+14, 2,
+ "Unknown (header block is compressed)");
+ } else
+ ti = proto_tree_add_uint(sub_tree, hf_spdy_num_headers,
+ tvb, orig_offset+14, 2, num_headers);
+ }
+ spdy_type = SPDY_INVALID; /* type not known yet */
+ if (spdy_debug)
+ printf(" %d Headers:\n", num_headers);
+ if (num_headers > frame_length) {
+ printf("Number of headers is greater than frame length!\n");
+ proto_item_append_text(ti, " [Error: Number of headers is larger than frame length]");
+ col_add_fstr(pinfo->cinfo, COL_INFO, "%s[%d]", frame_type_name, stream_id);
+ return frame_length+8;
+ }
+ hdr_verb = hdr_url = hdr_version = content_type = content_encoding = NULL;
+ while (num_headers-- && tvb_reported_length_remaining(header_tvb, hdr_offset) != 0) {
+ gchar *header_name;
+ gchar *header_value;
+ proto_tree *header_tree;
+ proto_tree *name_tree;
+ proto_tree *value_tree;
+ proto_item *header;
+ gint16 length;
+ gint header_length = 0;
+
+ hoffset = hdr_offset;
+
+ header = proto_tree_add_item(spdy_tree, hf_spdy_header, header_tvb,
+ hdr_offset, frame_length, FALSE);
+ header_tree = proto_item_add_subtree(header, ett_spdy_header);
+
+ length = tvb_get_ntohs(header_tvb, hdr_offset);
+ hdr_offset += 2;
+ header_name = (gchar *)tvb_get_ephemeral_string(header_tvb, hdr_offset, length);
+ hdr_offset += length;
+ header_length += hdr_offset - hoffset;
+ if (tree) {
+ ti = proto_tree_add_text(header_tree, header_tvb, hoffset, length+2, "Name: %s",
+ header_name);
+ name_tree = proto_item_add_subtree(ti, ett_spdy_header_name);
+ proto_tree_add_uint(name_tree, hf_spdy_length, header_tvb, hoffset, 2, length);
+ proto_tree_add_string_format(name_tree, hf_spdy_header_name_text, header_tvb, hoffset+2, length,
+ header_name, "Text: %s", format_text(header_name, length));
+ }
+
+ hoffset = hdr_offset;
+ length = tvb_get_ntohs(header_tvb, hdr_offset);
+ hdr_offset += 2;
+ header_value = (gchar *)tvb_get_ephemeral_string(header_tvb, hdr_offset, length);
+ hdr_offset += length;
+ header_length += hdr_offset - hoffset;
+ if (tree) {
+ ti = proto_tree_add_text(header_tree, header_tvb, hoffset, length+2, "Value: %s",
+ header_value);
+ value_tree = proto_item_add_subtree(ti, ett_spdy_header_value);
+ proto_tree_add_uint(value_tree, hf_spdy_length, header_tvb, hoffset, 2, length);
+ proto_tree_add_string_format(value_tree, hf_spdy_header_value_text, header_tvb, hoffset+2, length,
+ header_value, "Text: %s", format_text(header_value, length));
+ proto_item_append_text(header, ": %s: %s", header_name, header_value);
+ proto_item_set_len(header, header_length);
+ }
+ if (spdy_debug) printf(" %s: %s\n", header_name, header_value);
+ /*
+ * TODO(ers) check that the header name contains only legal characters.
+ */
+ if (g_ascii_strcasecmp(header_name, "method") == 0 ||
+ g_ascii_strcasecmp(header_name, "status") == 0) {
+ hdr_verb = header_value;
+ } else if (g_ascii_strcasecmp(header_name, "url") == 0) {
+ hdr_url = header_value;
+ } else if (g_ascii_strcasecmp(header_name, "version") == 0) {
+ hdr_version = header_value;
+ } else if (g_ascii_strcasecmp(header_name, "content-type") == 0) {
+ content_type = se_strdup(header_value);
+ } else if (g_ascii_strcasecmp(header_name, "content-encoding") == 0) {
+ content_encoding = se_strdup(header_value);
+ }
+ }
+ if (hdr_version != NULL) {
+ if (hdr_url != NULL) {
+ col_add_fstr(pinfo->cinfo, COL_INFO, "%s[%d]: %s %s %s",
+ frame_type_name, stream_id, hdr_verb, hdr_url, hdr_version);
+ } else {
+ col_add_fstr(pinfo->cinfo, COL_INFO, "%s[%d]: %s %s",
+ frame_type_name, stream_id, hdr_verb, hdr_version);
+ }
+ } else {
+ col_add_fstr(pinfo->cinfo, COL_INFO, "%s[%d]", frame_type_name, stream_id);
+ }
+ /*
+ * If we expect data on this stream, we need to remember the content
+ * type and content encoding.
+ */
+ if (content_type != NULL && !pinfo->fd->flags.visited) {
+ gchar *content_type_params = spdy_parse_content_type(content_type);
+ spdy_save_stream_info(conv_data, stream_id, content_type,
+ content_type_params, content_encoding);
+ }
+
+ return offset - orig_offset;
+}
+
+static int
+dissect_spdy(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ spdy_conv_t *conv_data;
+ int offset = 0;
+ int len;
+ int firstpkt = 1;
+
+ /*
+ * The first byte of a SPDY packet must be either 0 or
+ * 0x80. If it's not, assume that this is not SPDY.
+ * (In theory, a data frame could have a stream ID
+ * >= 2^24, in which case it won't have 0 for a first
+ * byte, but this is a pretty reliable heuristic for
+ * now.)
+ */
+ guint8 first_byte = tvb_get_guint8(tvb, 0);
+ if (first_byte != 0x80 && first_byte != 0x0)
+ return 0;
+
+ conv_data = get_spdy_conversation_data(pinfo);
+
+ while (tvb_reported_length_remaining(tvb, offset) != 0) {
+ if (!firstpkt) {
+ col_add_fstr(pinfo->cinfo, COL_INFO, " >> ");
+ col_set_fence(pinfo->cinfo, COL_INFO);
+ }
+ len = dissect_spdy_message(tvb, offset, pinfo, tree, conv_data);
+ if (len <= 0)
+ return 0;
+ offset += len;
+ /*
+ * OK, we've set the Protocol and Info columns for the
+ * first SPDY message; set a fence so that subsequent
+ * SPDY messages don't overwrite the Info column.
+ */
+ col_set_fence(pinfo->cinfo, COL_INFO);
+ firstpkt = 0;
+ }
+ return 1;
+}
+
+static gboolean
+dissect_spdy_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ if (!value_is_in_range(global_spdy_tcp_range, pinfo->destport) &&
+ !value_is_in_range(global_spdy_tcp_range, pinfo->srcport))
+ return FALSE;
+ return dissect_spdy(tvb, pinfo, tree) != 0;
+}
+
+static void reinit_spdy(void)
+{
+}
+
+// NMAKE complains about flags_set_truth not being constant. Duplicate
+// the values inside of it.
+static const true_false_string tfs_spdy_set_notset = { "Set", "Not set" };
+
+void
+proto_register_spdy(void)
+{
+ static hf_register_info hf[] = {
+ { &hf_spdy_syn_stream,
+ { "Syn Stream", "spdy.syn_stream",
+ FT_BYTES, BASE_HEX, NULL, 0x0,
+ "", HFILL }},
+ { &hf_spdy_syn_reply,
+ { "Syn Reply", "spdy.syn_reply",
+ FT_BYTES, BASE_HEX, NULL, 0x0,
+ "", HFILL }},
+ { &hf_spdy_control_bit,
+ { "Control bit", "spdy.control_bit",
+ FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "TRUE if SPDY control frame", HFILL }},
+ { &hf_spdy_version,
+ { "Version", "spdy.version",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ "", HFILL }},
+ { &hf_spdy_type,
+ { "Type", "spdy.type",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ "", HFILL }},
+ { &hf_spdy_flags,
+ { "Flags", "spdy.flags",
+ FT_UINT8, BASE_HEX, NULL, 0x0,
+ "", HFILL }},
+ { &hf_spdy_flags_fin,
+ { "Fin", "spdy.flags.fin",
+ FT_BOOLEAN, 8, TFS(&tfs_spdy_set_notset),
+ SPDY_FIN, "", HFILL }},
+ { &hf_spdy_length,
+ { "Length", "spdy.length",
+ FT_UINT24, BASE_DEC, NULL, 0x0,
+ "", HFILL }},
+ { &hf_spdy_header,
+ { "Header", "spdy.header",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ "", HFILL }},
+ { &hf_spdy_header_name,
+ { "Name", "spdy.header.name",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ "", HFILL }},
+ { &hf_spdy_header_name_text,
+ { "Text", "spdy.header.name.text",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ "", HFILL }},
+ { &hf_spdy_header_value,
+ { "Value", "spdy.header.value",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ "", HFILL }},
+ { &hf_spdy_header_value_text,
+ { "Text", "spdy.header.value.text",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ "", HFILL }},
+ { &hf_spdy_streamid,
+ { "Stream ID", "spdy.streamid",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "", HFILL }},
+ { &hf_spdy_priority,
+ { "Priority", "spdy.priority",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ "", HFILL }},
+ { &hf_spdy_num_headers,
+ { "Number of headers", "spdy.numheaders",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ "", HFILL }},
+ { &hf_spdy_num_headers_string,
+ { "Number of headers", "spdy.numheaders",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ "", HFILL }},
+ };
+ static gint *ett[] = {
+ &ett_spdy,
+ &ett_spdy_syn_stream,
+ &ett_spdy_syn_reply,
+ &ett_spdy_fin_stream,
+ &ett_spdy_flags,
+ &ett_spdy_header,
+ &ett_spdy_header_name,
+ &ett_spdy_header_value,
+ &ett_spdy_encoded_entity,
+ };
+
+ module_t *spdy_module;
+
+ proto_spdy = proto_register_protocol("SPDY", "SPDY", "spdy");
+ proto_register_field_array(proto_spdy, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+ new_register_dissector("spdy", dissect_spdy, proto_spdy);
+ spdy_module = prefs_register_protocol(proto_spdy, reinit_spdy);
+ prefs_register_bool_preference(spdy_module, "desegment_headers",
+ "Reassemble SPDY control frames spanning multiple TCP segments",
+ "Whether the SPDY dissector should reassemble control frames "
+ "spanning multiple TCP segments. "
+ "To use this option, you must also enable "
+ "\"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
+ &spdy_desegment_control_frames);
+ prefs_register_bool_preference(spdy_module, "desegment_body",
+ "Reassemble SPDY bodies spanning multiple TCP segments",
+ "Whether the SPDY dissector should reassemble "
+ "data frames spanning multiple TCP segments. "
+ "To use this option, you must also enable "
+ "\"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
+ &spdy_desegment_data_frames);
+ prefs_register_bool_preference(spdy_module, "assemble_data_frames",
+ "Assemble SPDY bodies that consist of multiple DATA frames",
+ "Whether the SPDY dissector should reassemble multiple "
+ "data frames into an entity body.",
+ &spdy_assemble_entity_bodies);
+#ifdef HAVE_LIBZ
+ prefs_register_bool_preference(spdy_module, "decompress_headers",
+ "Uncompress SPDY headers",
+ "Whether to uncompress SPDY headers.",
+ &spdy_decompress_headers);
+ prefs_register_bool_preference(spdy_module, "decompress_body",
+ "Uncompress entity bodies",
+ "Whether to uncompress entity bodies that are compressed "
+ "using \"Content-Encoding: \"",
+ &spdy_decompress_body);
+#endif
+ prefs_register_bool_preference(spdy_module, "debug_output",
+ "Print debug info on stdout",
+ "Print debug info on stdout",
+ &spdy_debug);
+#if 0
+ prefs_register_string_preference(ssl_module, "debug_file", "SPDY debug file",
+ "Redirect SPDY debug to file name; "
+ "leave empty to disable debugging, "
+ "or use \"" SPDY_DEBUG_USE_STDOUT "\""
+ " to redirect output to stdout\n",
+ (const gchar **)&sdpy_debug_file_name);
+#endif
+ prefs_register_obsolete_preference(spdy_module, "tcp_alternate_port");
+
+ range_convert_str(&global_spdy_tcp_range, TCP_DEFAULT_RANGE, 65535);
+ spdy_tcp_range = range_empty();
+ prefs_register_range_preference(spdy_module, "tcp.port", "TCP Ports",
+ "TCP Ports range",
+ &global_spdy_tcp_range, 65535);
+
+ range_convert_str(&global_spdy_ssl_range, SSL_DEFAULT_RANGE, 65535);
+ spdy_ssl_range = range_empty();
+ prefs_register_range_preference(spdy_module, "ssl.port", "SSL/TLS Ports",
+ "SSL/TLS Ports range",
+ &global_spdy_ssl_range, 65535);
+
+ spdy_handle = new_create_dissector_handle(dissect_spdy, proto_spdy);
+ /*
+ * Register for tapping
+ */
+ spdy_tap = register_tap("spdy"); /* SPDY statistics tap */
+ spdy_eo_tap = register_tap("spdy_eo"); /* SPDY Export Object tap */
+}
+
+void
+proto_reg_handoff_spdy(void)
+{
+ data_handle = find_dissector("data");
+ media_handle = find_dissector("media");
+ heur_dissector_add("tcp", dissect_spdy_heur, proto_spdy);
+}
+
+/*
+ * Content-Type: message/http
+ */
+
+static gint proto_message_spdy = -1;
+static gint ett_message_spdy = -1;
+
+static void
+dissect_message_spdy(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ proto_tree *subtree;
+ proto_item *ti;
+ gint offset = 0, next_offset;
+ gint len;
+
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_append_str(pinfo->cinfo, COL_INFO, " (message/spdy)");
+ if (tree) {
+ ti = proto_tree_add_item(tree, proto_message_spdy,
+ tvb, 0, -1, FALSE);
+ subtree = proto_item_add_subtree(ti, ett_message_spdy);
+ while (tvb_reported_length_remaining(tvb, offset) != 0) {
+ len = tvb_find_line_end(tvb, offset,
+ tvb_ensure_length_remaining(tvb, offset),
+ &next_offset, FALSE);
+ if (len == -1)
+ break;
+ proto_tree_add_text(subtree, tvb, offset, next_offset - offset,
+ "%s", tvb_format_text(tvb, offset, len));
+ offset = next_offset;
+ }
+ }
+}
+
+void
+proto_register_message_spdy(void)
+{
+ static gint *ett[] = {
+ &ett_message_spdy,
+ };
+
+ proto_message_spdy = proto_register_protocol(
+ "Media Type: message/spdy",
+ "message/spdy",
+ "message-spdy"
+ );
+ proto_register_subtree_array(ett, array_length(ett));
+}
+
+void
+proto_reg_handoff_message_spdy(void)
+{
+ dissector_handle_t message_spdy_handle;
+
+ message_spdy_handle = create_dissector_handle(dissect_message_spdy,
+ proto_message_spdy);
+
+ dissector_add_string("media_type", "message/spdy", message_spdy_handle);
+
+ reinit_spdy();
+}

Powered by Google App Engine
This is Rietveld 408576698