Index: net/tools/spdyshark/packet-spdy.c |
diff --git a/net/tools/spdyshark/packet-spdy.c b/net/tools/spdyshark/packet-spdy.c |
deleted file mode 100644 |
index becc585c0cb7a3e6115ae6feae9a146d8888f0dc..0000000000000000000000000000000000000000 |
--- a/net/tools/spdyshark/packet-spdy.c |
+++ /dev/null |
@@ -1,1532 +0,0 @@ |
-/* 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 "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_associated_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 |
- |
-/* |
- * SSDP is implemented atop HTTP (yes, it really *does* run over UDP). |
- */ |
-#define TCP_PORT_SSDP 1900 |
-#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"); |
-} |
- |
-static spdy_conv_t * |
-get_spdy_conversation_data(packet_info *pinfo) |
-{ |
- conversation_t *conversation; |
- spdy_conv_t *conv_data; |
- int retcode; |
- |
- conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0); |
- 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); |
- |
- /* 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; */ |
-} |
- |
-// TODO(cbentzel): tvb_child_uncompress should be exported by wireshark. |
-static tvbuff_t* spdy_tvb_child_uncompress(tvbuff_t *parent _U_, tvbuff_t *tvb, |
- int offset, int comprlen) |
-{ |
- tvbuff_t *new_tvb = tvb_uncompress(tvb, offset, comprlen); |
- if (new_tvb) |
- tvb_set_child_real_data_tvbuff (parent, new_tvb); |
- return new_tvb; |
-} |
- |
-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 = spdy_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)); |
- 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; |
-} |
- |
-// TODO(cbentzel): Change wireshark to export p_remove_proto_data, rather |
-// than duplicating code here. |
-typedef struct _spdy_frame_proto_data { |
- int proto; |
- void *proto_data; |
-} spdy_frame_proto_data; |
- |
-static gint spdy_p_compare(gconstpointer a, gconstpointer b) |
-{ |
- const spdy_frame_proto_data *ap = (const spdy_frame_proto_data *)a; |
- const spdy_frame_proto_data *bp = (const spdy_frame_proto_data *)b; |
- |
- if (ap -> proto > bp -> proto) |
- return 1; |
- else if (ap -> proto == bp -> proto) |
- return 0; |
- else |
- return -1; |
- |
-} |
- |
-static void spdy_p_remove_proto_data(frame_data *fd, int proto) |
-{ |
- spdy_frame_proto_data temp; |
- GSList *item; |
- |
- temp.proto = proto; |
- temp.proto_data = NULL; |
- |
- item = g_slist_find_custom(fd->pfd, (gpointer *)&temp, spdy_p_compare); |
- |
- if (item) { |
- fd->pfd = g_slist_remove(fd->pfd, item->data); |
- } |
-} |
- |
-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) |
- spdy_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; |
- guint32 associated_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; |
- if (frame_type == SPDY_SYN_STREAM) { |
- associated_stream_id = tvb_get_bits32(tvb, (offset << 3) + 1, 31, FALSE); |
- offset += 4; |
- priority = tvb_get_bits8(tvb, offset << 3, 2); |
- offset += 2; |
- } else { |
- // The next two bytes have no meaning in SYN_REPLY |
- 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); |
- if (frame_type == SPDY_SYN_STREAM) { |
- proto_tree_add_uint(sub_tree, hf_spdy_associated_streamid, tvb, orig_offset+12, 4, associated_stream_id); |
- proto_tree_add_uint(sub_tree, hf_spdy_priority, tvb, orig_offset+16, 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 + 8 - (offset - orig_offset), |
- &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]"); |
- // Should we just bail here? |
- } 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 = orig_offset + 8 + frame_length; |
- 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, |
- frame_type == SPDY_SYN_STREAM ? orig_offset+18 : orig_offset + 14, |
- 2, |
- "Unknown (header block is compressed)"); |
- } else |
- ti = proto_tree_add_uint(sub_tree, hf_spdy_num_headers, |
- tvb, |
- frame_type == SPDY_SYN_STREAM ? orig_offset+18 : 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_NONE, NULL, 0x0, |
- "", HFILL }}, |
- { &hf_spdy_syn_reply, |
- { "Syn Reply", "spdy.syn_reply", |
- FT_BYTES, BASE_NONE, 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_associated_streamid, |
- { "Associated Stream ID", "spdy.associated.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(); |
-} |