By moving the call to Load() up in SearchProvider::Start(), we are giving a chance...
[chromium-blink-merge.git] / net / tools / spdyshark / packet-spdy.c
blobbecc585c0cb7a3e6115ae6feae9a146d8888f0dc
1 /* packet-spdy.c
2 * Routines for SPDY packet disassembly
3 * For now, the protocol spec can be found at
4 * http://dev.chromium.org/spdy/spdy-protocol
6 * Copyright 2010, Google Inc.
7 * Eric Shienbrood <ers@google.com>
9 * $Id$
11 * Wireshark - Network traffic analyzer
12 * By Gerald Combs <gerald@wireshark.org>
13 * Copyright 1998 Gerald Combs
15 * Originally based on packet-http.c
17 * This program is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU General Public License
19 * as published by the Free Software Foundation; either version 2
20 * of the License, or (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
36 #include <string.h>
37 #include <ctype.h>
39 #include <glib.h>
40 #include <epan/conversation.h>
41 #include <epan/packet.h>
42 #include <epan/strutil.h>
43 #include <epan/base64.h>
44 #include <epan/emem.h>
45 #include <epan/stats_tree.h>
47 #include <epan/req_resp_hdrs.h>
48 #include "packet-spdy.h"
49 #include <epan/dissectors/packet-tcp.h>
50 #include <epan/dissectors/packet-ssl.h>
51 #include <epan/prefs.h>
52 #include <epan/expert.h>
53 #include <epan/uat.h>
55 #define SPDY_FIN 0x01
57 /* The types of SPDY control frames */
58 typedef enum _spdy_type {
59 SPDY_DATA,
60 SPDY_SYN_STREAM,
61 SPDY_SYN_REPLY,
62 SPDY_FIN_STREAM,
63 SPDY_HELLO,
64 SPDY_NOOP,
65 SPDY_PING,
66 SPDY_INVALID
67 } spdy_frame_type_t;
69 static const char *frame_type_names[] = {
70 "DATA", "SYN_STREAM", "SYN_REPLY", "FIN_STREAM", "HELLO", "NOOP",
71 "PING", "INVALID"
75 * This structure will be tied to each SPDY frame.
76 * Note that there may be multiple SPDY frames
77 * in one packet.
79 typedef struct _spdy_frame_info_t {
80 guint32 stream_id;
81 guint8 *header_block;
82 guint header_block_len;
83 guint16 frame_type;
84 } spdy_frame_info_t;
87 * This structures keeps track of all the data frames
88 * associated with a stream, so that they can be
89 * reassembled into a single chunk.
91 typedef struct _spdy_data_frame_t {
92 guint8 *data;
93 guint32 length;
94 guint32 framenum;
95 } spdy_data_frame_t;
97 typedef struct _spdy_stream_info_t {
98 gchar *content_type;
99 gchar *content_type_parameters;
100 gchar *content_encoding;
101 GSList *data_frames;
102 tvbuff_t *assembled_data;
103 guint num_data_frames;
104 } spdy_stream_info_t;
106 #include <epan/tap.h>
109 static int spdy_tap = -1;
110 static int spdy_eo_tap = -1;
112 static int proto_spdy = -1;
113 static int hf_spdy_syn_stream = -1;
114 static int hf_spdy_syn_reply = -1;
115 static int hf_spdy_control_bit = -1;
116 static int hf_spdy_version = -1;
117 static int hf_spdy_type = -1;
118 static int hf_spdy_flags = -1;
119 static int hf_spdy_flags_fin = -1;
120 static int hf_spdy_length = -1;
121 static int hf_spdy_header = -1;
122 static int hf_spdy_header_name = -1;
123 static int hf_spdy_header_name_text = -1;
124 static int hf_spdy_header_value = -1;
125 static int hf_spdy_header_value_text = -1;
126 static int hf_spdy_streamid = -1;
127 static int hf_spdy_associated_streamid = -1;
128 static int hf_spdy_priority = -1;
129 static int hf_spdy_num_headers = -1;
130 static int hf_spdy_num_headers_string = -1;
132 static gint ett_spdy = -1;
133 static gint ett_spdy_syn_stream = -1;
134 static gint ett_spdy_syn_reply = -1;
135 static gint ett_spdy_fin_stream = -1;
136 static gint ett_spdy_flags = -1;
137 static gint ett_spdy_header = -1;
138 static gint ett_spdy_header_name = -1;
139 static gint ett_spdy_header_value = -1;
141 static gint ett_spdy_encoded_entity = -1;
143 static dissector_handle_t data_handle;
144 static dissector_handle_t media_handle;
145 static dissector_handle_t spdy_handle;
147 /* Stuff for generation/handling of fields for custom HTTP headers */
148 typedef struct _header_field_t {
149 gchar* header_name;
150 gchar* header_desc;
151 } header_field_t;
154 * desegmentation of SPDY control frames
155 * (when we are over TCP or another protocol providing the desegmentation API)
157 static gboolean spdy_desegment_control_frames = TRUE;
160 * desegmentation of SPDY data frames bodies
161 * (when we are over TCP or another protocol providing the desegmentation API)
162 * TODO let the user filter on content-type the bodies he wants desegmented
164 static gboolean spdy_desegment_data_frames = TRUE;
166 static gboolean spdy_assemble_entity_bodies = TRUE;
169 * Decompression of zlib encoded entities.
171 #ifdef HAVE_LIBZ
172 static gboolean spdy_decompress_body = TRUE;
173 static gboolean spdy_decompress_headers = TRUE;
174 #else
175 static gboolean spdy_decompress_body = FALSE;
176 static gboolean spdy_decompress_headers = FALSE;
177 #endif
178 static gboolean spdy_debug = FALSE;
180 #define TCP_PORT_DAAP 3689
183 * SSDP is implemented atop HTTP (yes, it really *does* run over UDP).
185 #define TCP_PORT_SSDP 1900
186 #define UDP_PORT_SSDP 1900
189 * tcp and ssl ports
192 #define TCP_DEFAULT_RANGE "80,8080"
193 #define SSL_DEFAULT_RANGE "443"
195 static range_t *global_spdy_tcp_range = NULL;
196 static range_t *global_spdy_ssl_range = NULL;
198 static range_t *spdy_tcp_range = NULL;
199 static range_t *spdy_ssl_range = NULL;
201 static const value_string vals_status_code[] = {
202 { 100, "Continue" },
203 { 101, "Switching Protocols" },
204 { 102, "Processing" },
205 { 199, "Informational - Others" },
207 { 200, "OK"},
208 { 201, "Created"},
209 { 202, "Accepted"},
210 { 203, "Non-authoritative Information"},
211 { 204, "No Content"},
212 { 205, "Reset Content"},
213 { 206, "Partial Content"},
214 { 207, "Multi-Status"},
215 { 299, "Success - Others"},
217 { 300, "Multiple Choices"},
218 { 301, "Moved Permanently"},
219 { 302, "Found"},
220 { 303, "See Other"},
221 { 304, "Not Modified"},
222 { 305, "Use Proxy"},
223 { 307, "Temporary Redirect"},
224 { 399, "Redirection - Others"},
226 { 400, "Bad Request"},
227 { 401, "Unauthorized"},
228 { 402, "Payment Required"},
229 { 403, "Forbidden"},
230 { 404, "Not Found"},
231 { 405, "Method Not Allowed"},
232 { 406, "Not Acceptable"},
233 { 407, "Proxy Authentication Required"},
234 { 408, "Request Time-out"},
235 { 409, "Conflict"},
236 { 410, "Gone"},
237 { 411, "Length Required"},
238 { 412, "Precondition Failed"},
239 { 413, "Request Entity Too Large"},
240 { 414, "Request-URI Too Long"},
241 { 415, "Unsupported Media Type"},
242 { 416, "Requested Range Not Satisfiable"},
243 { 417, "Expectation Failed"},
244 { 418, "I'm a teapot"}, /* RFC 2324 */
245 { 422, "Unprocessable Entity"},
246 { 423, "Locked"},
247 { 424, "Failed Dependency"},
248 { 499, "Client Error - Others"},
250 { 500, "Internal Server Error"},
251 { 501, "Not Implemented"},
252 { 502, "Bad Gateway"},
253 { 503, "Service Unavailable"},
254 { 504, "Gateway Time-out"},
255 { 505, "HTTP Version not supported"},
256 { 507, "Insufficient Storage"},
257 { 599, "Server Error - Others"},
259 { 0, NULL}
262 static const char spdy_dictionary[] =
263 "optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-"
264 "languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi"
265 "f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser"
266 "-agent10010120020120220320420520630030130230330430530630740040140240340440"
267 "5406407408409410411412413414415416417500501502503504505accept-rangesageeta"
268 "glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic"
269 "ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran"
270 "sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati"
271 "oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo"
272 "ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe"
273 "pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic"
274 "ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1"
275 ".1statusversionurl";
277 static void reset_decompressors(void)
279 if (spdy_debug) printf("Should reset SPDY decompressors\n");
282 static spdy_conv_t *
283 get_spdy_conversation_data(packet_info *pinfo)
285 conversation_t *conversation;
286 spdy_conv_t *conv_data;
287 int retcode;
289 conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
290 if (spdy_debug) {
291 printf("\n===========================================\n\n");
292 printf("Conversation for frame #%d is %p\n", pinfo->fd->num, conversation);
293 if (conversation)
294 printf(" conv_data=%p\n", conversation_get_proto_data(conversation, proto_spdy));
297 if(!conversation) /* Conversation does not exist yet - create it */
298 conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
300 /* Retrieve information from conversation
302 conv_data = conversation_get_proto_data(conversation, proto_spdy);
303 if(!conv_data) {
304 /* Setup the conversation structure itself */
305 conv_data = se_alloc0(sizeof(spdy_conv_t));
307 conv_data->streams = NULL;
308 if (spdy_decompress_headers) {
309 conv_data->rqst_decompressor = se_alloc0(sizeof(z_stream));
310 conv_data->rply_decompressor = se_alloc0(sizeof(z_stream));
311 retcode = inflateInit(conv_data->rqst_decompressor);
312 if (retcode == Z_OK)
313 retcode = inflateInit(conv_data->rply_decompressor);
314 if (retcode != Z_OK)
315 printf("frame #%d: inflateInit() failed: %d\n", pinfo->fd->num, retcode);
316 else if (spdy_debug)
317 printf("created decompressor\n");
318 conv_data->dictionary_id = adler32(0L, Z_NULL, 0);
319 conv_data->dictionary_id = adler32(conv_data->dictionary_id,
320 spdy_dictionary,
321 sizeof(spdy_dictionary));
324 conversation_add_proto_data(conversation, proto_spdy, conv_data);
325 register_postseq_cleanup_routine(reset_decompressors);
327 return conv_data;
330 static void
331 spdy_save_stream_info(spdy_conv_t *conv_data,
332 guint32 stream_id,
333 gchar *content_type,
334 gchar *content_type_params,
335 gchar *content_encoding)
337 spdy_stream_info_t *si;
339 if (conv_data->streams == NULL)
340 conv_data->streams = g_array_new(FALSE, TRUE, sizeof(spdy_stream_info_t *));
341 if (stream_id < conv_data->streams->len)
342 DISSECTOR_ASSERT(g_array_index(conv_data->streams, spdy_stream_info_t*, stream_id) == NULL);
343 else
344 g_array_set_size(conv_data->streams, stream_id+1);
345 si = se_alloc(sizeof(spdy_stream_info_t));
346 si->content_type = content_type;
347 si->content_type_parameters = content_type_params;
348 si->content_encoding = content_encoding;
349 si->data_frames = NULL;
350 si->num_data_frames = 0;
351 si->assembled_data = NULL;
352 g_array_index(conv_data->streams, spdy_stream_info_t*, stream_id) = si;
353 if (spdy_debug)
354 printf("Saved stream info for ID %u, content type %s\n", stream_id, content_type);
357 static spdy_stream_info_t *
358 spdy_get_stream_info(spdy_conv_t *conv_data, guint32 stream_id)
360 if (conv_data->streams == NULL || stream_id >= conv_data->streams->len)
361 return NULL;
362 else
363 return g_array_index(conv_data->streams, spdy_stream_info_t*, stream_id);
366 static void
367 spdy_add_data_chunk(spdy_conv_t *conv_data, guint32 stream_id, guint32 frame,
368 guint8 *data, guint32 length)
370 spdy_stream_info_t *si = spdy_get_stream_info(conv_data, stream_id);
372 if (si == NULL) {
373 if (spdy_debug) printf("No stream_info found for stream %d\n", stream_id);
374 } else {
375 spdy_data_frame_t *df = g_malloc(sizeof(spdy_data_frame_t));
376 df->data = data;
377 df->length = length;
378 df->framenum = frame;
379 si->data_frames = g_slist_append(si->data_frames, df);
380 ++si->num_data_frames;
381 if (spdy_debug)
382 printf("Saved %u bytes of data for stream %u frame %u\n",
383 length, stream_id, df->framenum);
387 static void
388 spdy_increment_data_chunk_count(spdy_conv_t *conv_data, guint32 stream_id)
390 spdy_stream_info_t *si = spdy_get_stream_info(conv_data, stream_id);
391 if (si != NULL)
392 ++si->num_data_frames;
396 * Return the number of data frames saved so far for the specified stream.
398 static guint
399 spdy_get_num_data_frames(spdy_conv_t *conv_data, guint32 stream_id)
401 spdy_stream_info_t *si = spdy_get_stream_info(conv_data, stream_id);
403 return si == NULL ? 0 : si->num_data_frames;
406 static spdy_stream_info_t *
407 spdy_assemble_data_frames(spdy_conv_t *conv_data, guint32 stream_id)
409 spdy_stream_info_t *si = spdy_get_stream_info(conv_data, stream_id);
410 tvbuff_t *tvb;
412 if (si == NULL)
413 return NULL;
416 * Compute the total amount of data and concatenate the
417 * data chunks, if it hasn't already been done.
419 if (si->assembled_data == NULL) {
420 spdy_data_frame_t *df;
421 guint8 *data;
422 guint32 datalen;
423 guint32 offset;
424 guint32 framenum;
425 GSList *dflist = si->data_frames;
426 if (dflist == NULL)
427 return si;
428 dflist = si->data_frames;
429 datalen = 0;
431 * I'd like to use a composite tvbuff here, but since
432 * only a real-data tvbuff can be the child of another
433 * tvb, I can't. It would be nice if this limitation
434 * could be fixed.
436 while (dflist != NULL) {
437 df = dflist->data;
438 datalen += df->length;
439 dflist = g_slist_next(dflist);
441 if (datalen != 0) {
442 data = se_alloc(datalen);
443 dflist = si->data_frames;
444 offset = 0;
445 framenum = 0;
446 while (dflist != NULL) {
447 df = dflist->data;
448 memcpy(data+offset, df->data, df->length);
449 offset += df->length;
450 dflist = g_slist_next(dflist);
452 tvb = tvb_new_real_data(data, datalen, datalen);
453 si->assembled_data = tvb;
456 return si;
459 static void
460 spdy_discard_data_frames(spdy_stream_info_t *si)
462 GSList *dflist = si->data_frames;
463 spdy_data_frame_t *df;
465 if (dflist == NULL)
466 return;
467 while (dflist != NULL) {
468 df = dflist->data;
469 if (df->data != NULL) {
470 g_free(df->data);
471 df->data = NULL;
473 dflist = g_slist_next(dflist);
475 /*g_slist_free(si->data_frames);
476 si->data_frames = NULL; */
479 // TODO(cbentzel): tvb_child_uncompress should be exported by wireshark.
480 static tvbuff_t* spdy_tvb_child_uncompress(tvbuff_t *parent _U_, tvbuff_t *tvb,
481 int offset, int comprlen)
483 tvbuff_t *new_tvb = tvb_uncompress(tvb, offset, comprlen);
484 if (new_tvb)
485 tvb_set_child_real_data_tvbuff (parent, new_tvb);
486 return new_tvb;
489 static int
490 dissect_spdy_data_frame(tvbuff_t *tvb, int offset,
491 packet_info *pinfo,
492 proto_tree *top_level_tree,
493 proto_tree *spdy_tree,
494 proto_item *spdy_proto,
495 spdy_conv_t *conv_data)
497 guint32 stream_id;
498 guint8 flags;
499 guint32 frame_length;
500 proto_item *ti;
501 proto_tree *flags_tree;
502 guint32 reported_datalen;
503 guint32 datalen;
504 dissector_table_t media_type_subdissector_table;
505 dissector_table_t port_subdissector_table;
506 dissector_handle_t handle;
507 guint num_data_frames;
508 gboolean dissected;
510 stream_id = tvb_get_bits32(tvb, (offset << 3) + 1, 31, FALSE);
511 flags = tvb_get_guint8(tvb, offset+4);
512 frame_length = tvb_get_ntoh24(tvb, offset+5);
514 if (spdy_debug)
515 printf("Data frame [stream_id=%u flags=0x%x length=%d]\n",
516 stream_id, flags, frame_length);
517 if (spdy_tree) proto_item_append_text(spdy_tree, ", data frame");
518 col_add_fstr(pinfo->cinfo, COL_INFO, "DATA[%u] length=%d",
519 stream_id, frame_length);
521 proto_item_append_text(spdy_proto, ":%s stream=%d length=%d",
522 flags & SPDY_FIN ? " [FIN]" : "",
523 stream_id, frame_length);
525 proto_tree_add_boolean(spdy_tree, hf_spdy_control_bit, tvb, offset, 1, 0);
526 proto_tree_add_uint(spdy_tree, hf_spdy_streamid, tvb, offset, 4, stream_id);
527 ti = proto_tree_add_uint_format(spdy_tree, hf_spdy_flags, tvb, offset+4, 1, flags,
528 "Flags: 0x%02x%s", flags, flags&SPDY_FIN ? " (FIN)" : "");
530 flags_tree = proto_item_add_subtree(ti, ett_spdy_flags);
531 proto_tree_add_boolean(flags_tree, hf_spdy_flags_fin, tvb, offset+4, 1, flags);
532 proto_tree_add_uint(spdy_tree, hf_spdy_length, tvb, offset+5, 3, frame_length);
534 datalen = tvb_length_remaining(tvb, offset);
535 if (datalen > frame_length)
536 datalen = frame_length;
538 reported_datalen = tvb_reported_length_remaining(tvb, offset);
539 if (reported_datalen > frame_length)
540 reported_datalen = frame_length;
542 num_data_frames = spdy_get_num_data_frames(conv_data, stream_id);
543 if (datalen != 0 || num_data_frames != 0) {
545 * There's stuff left over; process it.
547 tvbuff_t *next_tvb = NULL;
548 tvbuff_t *data_tvb = NULL;
549 spdy_stream_info_t *si = NULL;
550 void *save_private_data = NULL;
551 guint8 *copied_data;
552 gboolean private_data_changed = FALSE;
553 gboolean is_single_chunk = FALSE;
554 gboolean have_entire_body;
557 * Create a tvbuff for the payload.
559 if (datalen != 0) {
560 next_tvb = tvb_new_subset(tvb, offset+8, datalen,
561 reported_datalen);
562 is_single_chunk = num_data_frames == 0 && (flags & SPDY_FIN) != 0;
563 if (!pinfo->fd->flags.visited) {
564 if (!is_single_chunk) {
565 if (spdy_assemble_entity_bodies) {
566 copied_data = tvb_memdup(next_tvb, 0, datalen);
567 spdy_add_data_chunk(conv_data, stream_id, pinfo->fd->num,
568 copied_data, datalen);
569 } else
570 spdy_increment_data_chunk_count(conv_data, stream_id);
573 } else
574 is_single_chunk = (num_data_frames == 1);
576 if (!(flags & SPDY_FIN)) {
577 col_set_fence(pinfo->cinfo, COL_INFO);
578 col_add_fstr(pinfo->cinfo, COL_INFO, " (partial entity)");
579 proto_item_append_text(spdy_proto, " (partial entity body)");
580 /* would like the proto item to say */
581 /* " (entity body fragment N of M)" */
582 goto body_dissected;
584 have_entire_body = is_single_chunk;
586 * On seeing the last data frame in a stream, we can
587 * reassemble the frames into one data block.
589 si = spdy_assemble_data_frames(conv_data, stream_id);
590 if (si == NULL)
591 goto body_dissected;
592 data_tvb = si->assembled_data;
593 if (spdy_assemble_entity_bodies)
594 have_entire_body = TRUE;
596 if (!have_entire_body)
597 goto body_dissected;
599 if (data_tvb == NULL)
600 data_tvb = next_tvb;
601 else
602 add_new_data_source(pinfo, data_tvb, "Assembled entity body");
604 if (have_entire_body && si->content_encoding != NULL &&
605 g_ascii_strcasecmp(si->content_encoding, "identity") != 0) {
607 * We currently can't handle, for example, "compress";
608 * just handle them as data for now.
610 * After July 7, 2004 the LZW patent expires, so support
611 * might be added then. However, I don't think that
612 * anybody ever really implemented "compress", due to
613 * the aforementioned patent.
615 tvbuff_t *uncomp_tvb = NULL;
616 proto_item *e_ti = NULL;
617 proto_item *ce_ti = NULL;
618 proto_tree *e_tree = NULL;
620 if (spdy_decompress_body &&
621 (g_ascii_strcasecmp(si->content_encoding, "gzip") == 0 ||
622 g_ascii_strcasecmp(si->content_encoding, "deflate")
623 == 0)) {
624 uncomp_tvb = spdy_tvb_child_uncompress(tvb, data_tvb, 0,
625 tvb_length(data_tvb));
628 * Add the encoded entity to the protocol tree
630 e_ti = proto_tree_add_text(top_level_tree, data_tvb,
631 0, tvb_length(data_tvb),
632 "Content-encoded entity body (%s): %u bytes",
633 si->content_encoding,
634 tvb_length(data_tvb));
635 e_tree = proto_item_add_subtree(e_ti, ett_spdy_encoded_entity);
636 if (si->num_data_frames > 1) {
637 GSList *dflist;
638 spdy_data_frame_t *df;
639 guint32 framenum;
640 ce_ti = proto_tree_add_text(e_tree, data_tvb, 0,
641 tvb_length(data_tvb),
642 "Assembled from %d frames in packet(s)", si->num_data_frames);
643 dflist = si->data_frames;
644 framenum = 0;
645 while (dflist != NULL) {
646 df = dflist->data;
647 if (framenum != df->framenum) {
648 proto_item_append_text(ce_ti, " #%u", df->framenum);
649 framenum = df->framenum;
651 dflist = g_slist_next(dflist);
655 if (uncomp_tvb != NULL) {
657 * Decompression worked
660 /* XXX - Don't free this, since it's possible
661 * that the data was only partially
662 * decompressed, such as when desegmentation
663 * isn't enabled.
665 tvb_free(next_tvb);
667 proto_item_append_text(e_ti, " -> %u bytes", tvb_length(uncomp_tvb));
668 data_tvb = uncomp_tvb;
669 add_new_data_source(pinfo, data_tvb, "Uncompressed entity body");
670 } else {
671 if (spdy_decompress_body)
672 proto_item_append_text(e_ti, " [Error: Decompression failed]");
673 call_dissector(data_handle, data_tvb, pinfo, e_tree);
675 goto body_dissected;
678 if (si != NULL)
679 spdy_discard_data_frames(si);
681 * Do subdissector checks.
683 * First, check whether some subdissector asked that they
684 * be called if something was on some particular port.
687 port_subdissector_table = find_dissector_table("http.port");
688 media_type_subdissector_table = find_dissector_table("media_type");
689 if (have_entire_body && port_subdissector_table != NULL)
690 handle = dissector_get_port_handle(port_subdissector_table,
691 pinfo->match_port);
692 else
693 handle = NULL;
694 if (handle == NULL && have_entire_body && si->content_type != NULL &&
695 media_type_subdissector_table != NULL) {
697 * We didn't find any subdissector that
698 * registered for the port, and we have a
699 * Content-Type value. Is there any subdissector
700 * for that content type?
702 save_private_data = pinfo->private_data;
703 private_data_changed = TRUE;
705 if (si->content_type_parameters)
706 pinfo->private_data = ep_strdup(si->content_type_parameters);
707 else
708 pinfo->private_data = NULL;
710 * Calling the string handle for the media type
711 * dissector table will set pinfo->match_string
712 * to si->content_type for us.
714 pinfo->match_string = si->content_type;
715 handle = dissector_get_string_handle(
716 media_type_subdissector_table,
717 si->content_type);
719 if (handle != NULL) {
721 * We have a subdissector - call it.
723 dissected = call_dissector(handle, data_tvb, pinfo, top_level_tree);
724 } else
725 dissected = FALSE;
727 if (dissected) {
729 * The subdissector dissected the body.
730 * Fix up the top-level item so that it doesn't
731 * include the stuff for that protocol.
733 if (ti != NULL)
734 proto_item_set_len(ti, offset);
735 } else if (have_entire_body && si->content_type != NULL) {
737 * Calling the default media handle if there is a content-type that
738 * wasn't handled above.
740 call_dissector(media_handle, next_tvb, pinfo, top_level_tree);
741 } else {
742 /* Call the default data dissector */
743 call_dissector(data_handle, next_tvb, pinfo, top_level_tree);
746 body_dissected:
748 * Do *not* attempt at freeing the private data;
749 * it may be in use by subdissectors.
751 if (private_data_changed) /*restore even NULL value*/
752 pinfo->private_data = save_private_data;
754 * We've processed "datalen" bytes worth of data
755 * (which may be no data at all); advance the
756 * offset past whatever data we've processed.
759 return frame_length + 8;
762 static guint8 *
763 spdy_decompress_header_block(tvbuff_t *tvb, z_streamp decomp,
764 guint32 dictionary_id, int offset,
765 guint32 length, guint *uncomp_length)
767 int retcode;
768 size_t bufsize = 16384;
769 const guint8 *hptr = tvb_get_ptr(tvb, offset, length);
770 guint8 *uncomp_block = ep_alloc(bufsize);
771 decomp->next_in = (Bytef *)hptr;
772 decomp->avail_in = length;
773 decomp->next_out = uncomp_block;
774 decomp->avail_out = bufsize;
775 retcode = inflate(decomp, Z_SYNC_FLUSH);
776 if (retcode == Z_NEED_DICT) {
777 if (decomp->adler != dictionary_id) {
778 printf("decompressor wants dictionary %#x, but we have %#x\n",
779 (guint)decomp->adler, dictionary_id);
780 } else {
781 retcode = inflateSetDictionary(decomp,
782 spdy_dictionary,
783 sizeof(spdy_dictionary));
784 if (retcode == Z_OK)
785 retcode = inflate(decomp, Z_SYNC_FLUSH);
789 if (retcode != Z_OK) {
790 return NULL;
791 } else {
792 *uncomp_length = bufsize - decomp->avail_out;
793 if (spdy_debug)
794 printf("Inflation SUCCEEDED. uncompressed size=%d\n", *uncomp_length);
795 if (decomp->avail_in != 0)
796 if (spdy_debug)
797 printf(" but there were %d input bytes left over\n", decomp->avail_in);
799 return se_memdup(uncomp_block, *uncomp_length);
803 * Try to determine heuristically whether the header block is
804 * compressed. For an uncompressed block, the first two bytes
805 * gives the number of headers. Each header name and value is
806 * a two-byte length followed by ASCII characters.
808 static gboolean
809 spdy_check_header_compression(tvbuff_t *tvb,
810 int offset,
811 guint32 frame_length)
813 guint16 length;
814 if (!tvb_bytes_exist(tvb, offset, 6))
815 return 1;
816 length = tvb_get_ntohs(tvb, offset);
817 if (length > frame_length)
818 return 1;
819 length = tvb_get_ntohs(tvb, offset+2);
820 if (length > frame_length)
821 return 1;
822 if (spdy_debug) printf("Looks like the header block is not compressed\n");
823 return 0;
826 // TODO(cbentzel): Change wireshark to export p_remove_proto_data, rather
827 // than duplicating code here.
828 typedef struct _spdy_frame_proto_data {
829 int proto;
830 void *proto_data;
831 } spdy_frame_proto_data;
833 static gint spdy_p_compare(gconstpointer a, gconstpointer b)
835 const spdy_frame_proto_data *ap = (const spdy_frame_proto_data *)a;
836 const spdy_frame_proto_data *bp = (const spdy_frame_proto_data *)b;
838 if (ap -> proto > bp -> proto)
839 return 1;
840 else if (ap -> proto == bp -> proto)
841 return 0;
842 else
843 return -1;
847 static void spdy_p_remove_proto_data(frame_data *fd, int proto)
849 spdy_frame_proto_data temp;
850 GSList *item;
852 temp.proto = proto;
853 temp.proto_data = NULL;
855 item = g_slist_find_custom(fd->pfd, (gpointer *)&temp, spdy_p_compare);
857 if (item) {
858 fd->pfd = g_slist_remove(fd->pfd, item->data);
862 static spdy_frame_info_t *
863 spdy_save_header_block(frame_data *fd,
864 guint32 stream_id,
865 guint frame_type,
866 guint8 *header,
867 guint length)
869 GSList *filist = p_get_proto_data(fd, proto_spdy);
870 spdy_frame_info_t *frame_info = se_alloc(sizeof(spdy_frame_info_t));
871 if (filist != NULL)
872 spdy_p_remove_proto_data(fd, proto_spdy);
873 frame_info->stream_id = stream_id;
874 frame_info->header_block = header;
875 frame_info->header_block_len = length;
876 frame_info->frame_type = frame_type;
877 filist = g_slist_append(filist, frame_info);
878 p_add_proto_data(fd, proto_spdy, filist);
879 return frame_info;
880 /* TODO(ers) these need to get deleted when no longer needed */
883 static spdy_frame_info_t *
884 spdy_find_saved_header_block(frame_data *fd,
885 guint32 stream_id,
886 guint16 frame_type)
888 GSList *filist = p_get_proto_data(fd, proto_spdy);
889 while (filist != NULL) {
890 spdy_frame_info_t *fi = filist->data;
891 if (fi->stream_id == stream_id && fi->frame_type == frame_type)
892 return fi;
893 filist = g_slist_next(filist);
895 return NULL;
899 * Given a content type string that may contain optional parameters,
900 * return the parameter string, if any, otherwise return NULL. This
901 * also has the side effect of null terminating the content type
902 * part of the original string.
904 static gchar *
905 spdy_parse_content_type(gchar *content_type)
907 gchar *cp = content_type;
909 while (*cp != '\0' && *cp != ';' && !isspace(*cp)) {
910 *cp = tolower(*cp);
911 ++cp;
913 if (*cp == '\0')
914 cp = NULL;
916 if (cp != NULL) {
917 *cp++ = '\0';
918 while (*cp == ';' || isspace(*cp))
919 ++cp;
920 if (*cp != '\0')
921 return cp;
923 return NULL;
926 static int
927 dissect_spdy_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
928 proto_tree *tree, spdy_conv_t *conv_data)
930 guint8 control_bit;
931 guint16 version;
932 guint16 frame_type;
933 guint8 flags;
934 guint32 frame_length;
935 guint32 stream_id;
936 guint32 associated_stream_id;
937 gint priority;
938 guint16 num_headers;
939 guint32 fin_status;
940 guint8 *frame_header;
941 const char *proto_tag;
942 const char *frame_type_name;
943 proto_tree *spdy_tree = NULL;
944 proto_item *ti = NULL;
945 proto_item *spdy_proto = NULL;
946 int orig_offset;
947 int hoffset;
948 int hdr_offset = 0;
949 spdy_frame_type_t spdy_type;
950 proto_tree *sub_tree;
951 proto_tree *flags_tree;
952 tvbuff_t *header_tvb = NULL;
953 gboolean headers_compressed;
954 gchar *hdr_verb = NULL;
955 gchar *hdr_url = NULL;
956 gchar *hdr_version = NULL;
957 gchar *content_type = NULL;
958 gchar *content_encoding = NULL;
961 * Minimum size for a SPDY frame is 8 bytes.
963 if (tvb_reported_length_remaining(tvb, offset) < 8)
964 return -1;
966 proto_tag = "SPDY";
968 if (check_col(pinfo->cinfo, COL_PROTOCOL))
969 col_set_str(pinfo->cinfo, COL_PROTOCOL, proto_tag);
972 * Is this a control frame or a data frame?
974 orig_offset = offset;
975 control_bit = tvb_get_bits8(tvb, offset << 3, 1);
976 if (control_bit) {
977 version = tvb_get_bits16(tvb, (offset << 3) + 1, 15, FALSE);
978 frame_type = tvb_get_ntohs(tvb, offset+2);
979 if (frame_type >= SPDY_INVALID) {
980 return -1;
982 frame_header = ep_tvb_memdup(tvb, offset, 16);
983 } else {
984 version = 1; /* avoid gcc warning */
985 frame_type = SPDY_DATA;
986 frame_header = NULL; /* avoid gcc warning */
988 frame_type_name = frame_type_names[frame_type];
989 offset += 4;
990 flags = tvb_get_guint8(tvb, offset);
991 frame_length = tvb_get_ntoh24(tvb, offset+1);
992 offset += 4;
994 * Make sure there's as much data as the frame header says there is.
996 if ((guint)tvb_reported_length_remaining(tvb, offset) < frame_length) {
997 if (spdy_debug)
998 printf("Not enough header data: %d vs. %d\n",
999 frame_length, tvb_reported_length_remaining(tvb, offset));
1000 return -1;
1002 if (tree) {
1003 spdy_proto = proto_tree_add_item(tree, proto_spdy, tvb, orig_offset, frame_length+8, FALSE);
1004 spdy_tree = proto_item_add_subtree(spdy_proto, ett_spdy);
1007 if (control_bit) {
1008 if (spdy_debug)
1009 printf("Control frame [version=%d type=%d flags=0x%x length=%d]\n",
1010 version, frame_type, flags, frame_length);
1011 if (tree) proto_item_append_text(spdy_tree, ", control frame");
1012 } else {
1013 return dissect_spdy_data_frame(tvb, orig_offset, pinfo, tree,
1014 spdy_tree, spdy_proto, conv_data);
1016 num_headers = 0;
1017 sub_tree = NULL; /* avoid gcc warning */
1018 switch (frame_type) {
1019 case SPDY_SYN_STREAM:
1020 case SPDY_SYN_REPLY:
1021 if (tree) {
1022 int hf;
1023 hf = frame_type == SPDY_SYN_STREAM ? hf_spdy_syn_stream : hf_spdy_syn_reply;
1024 ti = proto_tree_add_bytes(spdy_tree, hf, tvb,
1025 orig_offset, 16, frame_header);
1026 sub_tree = proto_item_add_subtree(ti, ett_spdy_syn_stream);
1028 stream_id = tvb_get_bits32(tvb, (offset << 3) + 1, 31, FALSE);
1029 offset += 4;
1030 if (frame_type == SPDY_SYN_STREAM) {
1031 associated_stream_id = tvb_get_bits32(tvb, (offset << 3) + 1, 31, FALSE);
1032 offset += 4;
1033 priority = tvb_get_bits8(tvb, offset << 3, 2);
1034 offset += 2;
1035 } else {
1036 // The next two bytes have no meaning in SYN_REPLY
1037 offset += 2;
1039 if (tree) {
1040 proto_tree_add_boolean(sub_tree, hf_spdy_control_bit, tvb, orig_offset, 1, control_bit);
1041 proto_tree_add_uint(sub_tree, hf_spdy_version, tvb, orig_offset, 2, version);
1042 proto_tree_add_uint(sub_tree, hf_spdy_type, tvb, orig_offset+2, 2, frame_type);
1043 ti = proto_tree_add_uint_format(sub_tree, hf_spdy_flags, tvb, orig_offset+4, 1, flags,
1044 "Flags: 0x%02x%s", flags, flags&SPDY_FIN ? " (FIN)" : "");
1045 flags_tree = proto_item_add_subtree(ti, ett_spdy_flags);
1046 proto_tree_add_boolean(flags_tree, hf_spdy_flags_fin, tvb, orig_offset+4, 1, flags);
1047 proto_tree_add_uint(sub_tree, hf_spdy_length, tvb, orig_offset+5, 3, frame_length);
1048 proto_tree_add_uint(sub_tree, hf_spdy_streamid, tvb, orig_offset+8, 4, stream_id);
1049 if (frame_type == SPDY_SYN_STREAM) {
1050 proto_tree_add_uint(sub_tree, hf_spdy_associated_streamid, tvb, orig_offset+12, 4, associated_stream_id);
1051 proto_tree_add_uint(sub_tree, hf_spdy_priority, tvb, orig_offset+16, 1, priority);
1053 proto_item_append_text(spdy_proto, ": %s%s stream=%d length=%d",
1054 frame_type_name,
1055 flags & SPDY_FIN ? " [FIN]" : "",
1056 stream_id, frame_length);
1057 if (spdy_debug)
1058 printf(" stream ID=%u priority=%d\n", stream_id, priority);
1060 break;
1062 case SPDY_FIN_STREAM:
1063 stream_id = tvb_get_bits32(tvb, (offset << 3) + 1, 31, FALSE);
1064 fin_status = tvb_get_ntohl(tvb, offset);
1065 // TODO(ers) fill in tree and summary
1066 offset += 8;
1067 break;
1069 case SPDY_HELLO:
1070 // TODO(ers) fill in tree and summary
1071 stream_id = 0; /* avoid gcc warning */
1072 break;
1074 default:
1075 stream_id = 0; /* avoid gcc warning */
1076 return -1;
1077 break;
1081 * Process the name-value pairs one at a time, after possibly
1082 * decompressing the header block.
1084 if (frame_type == SPDY_SYN_STREAM || frame_type == SPDY_SYN_REPLY) {
1085 headers_compressed = spdy_check_header_compression(tvb, offset, frame_length);
1086 if (!spdy_decompress_headers || !headers_compressed) {
1087 header_tvb = tvb;
1088 hdr_offset = offset;
1089 } else {
1090 spdy_frame_info_t *per_frame_info =
1091 spdy_find_saved_header_block(pinfo->fd,
1092 stream_id,
1093 frame_type == SPDY_SYN_REPLY);
1094 if (per_frame_info == NULL) {
1095 guint uncomp_length;
1096 z_streamp decomp = frame_type == SPDY_SYN_STREAM ?
1097 conv_data->rqst_decompressor : conv_data->rply_decompressor;
1098 guint8 *uncomp_ptr =
1099 spdy_decompress_header_block(tvb, decomp,
1100 conv_data->dictionary_id,
1101 offset,
1102 frame_length + 8 - (offset - orig_offset),
1103 &uncomp_length);
1104 if (uncomp_ptr == NULL) { /* decompression failed */
1105 if (spdy_debug)
1106 printf("Frame #%d: Inflation failed\n", pinfo->fd->num);
1107 proto_item_append_text(spdy_proto, " [Error: Header decompression failed]");
1108 // Should we just bail here?
1109 } else {
1110 if (spdy_debug)
1111 printf("Saving %u bytes of uncomp hdr\n", uncomp_length);
1112 per_frame_info =
1113 spdy_save_header_block(pinfo->fd, stream_id, frame_type == SPDY_SYN_REPLY,
1114 uncomp_ptr, uncomp_length);
1116 } else if (spdy_debug) {
1117 printf("Found uncompressed header block len %u for stream %u frame_type=%d\n",
1118 per_frame_info->header_block_len,
1119 per_frame_info->stream_id,
1120 per_frame_info->frame_type);
1122 if (per_frame_info != NULL) {
1123 header_tvb = tvb_new_child_real_data(tvb,
1124 per_frame_info->header_block,
1125 per_frame_info->header_block_len,
1126 per_frame_info->header_block_len);
1127 add_new_data_source(pinfo, header_tvb, "Uncompressed headers");
1128 hdr_offset = 0;
1131 offset = orig_offset + 8 + frame_length;
1132 num_headers = tvb_get_ntohs(header_tvb, hdr_offset);
1133 hdr_offset += 2;
1134 if (header_tvb == NULL ||
1135 (headers_compressed && !spdy_decompress_headers)) {
1136 num_headers = 0;
1137 ti = proto_tree_add_string(sub_tree, hf_spdy_num_headers_string,
1138 tvb,
1139 frame_type == SPDY_SYN_STREAM ? orig_offset+18 : orig_offset + 14,
1141 "Unknown (header block is compressed)");
1142 } else
1143 ti = proto_tree_add_uint(sub_tree, hf_spdy_num_headers,
1144 tvb,
1145 frame_type == SPDY_SYN_STREAM ? orig_offset+18 : orig_offset +14,
1146 2, num_headers);
1148 spdy_type = SPDY_INVALID; /* type not known yet */
1149 if (spdy_debug)
1150 printf(" %d Headers:\n", num_headers);
1151 if (num_headers > frame_length) {
1152 printf("Number of headers is greater than frame length!\n");
1153 proto_item_append_text(ti, " [Error: Number of headers is larger than frame length]");
1154 col_add_fstr(pinfo->cinfo, COL_INFO, "%s[%d]", frame_type_name, stream_id);
1155 return frame_length+8;
1157 hdr_verb = hdr_url = hdr_version = content_type = content_encoding = NULL;
1158 while (num_headers-- && tvb_reported_length_remaining(header_tvb, hdr_offset) != 0) {
1159 gchar *header_name;
1160 gchar *header_value;
1161 proto_tree *header_tree;
1162 proto_tree *name_tree;
1163 proto_tree *value_tree;
1164 proto_item *header;
1165 gint16 length;
1166 gint header_length = 0;
1168 hoffset = hdr_offset;
1170 header = proto_tree_add_item(spdy_tree, hf_spdy_header, header_tvb,
1171 hdr_offset, frame_length, FALSE);
1172 header_tree = proto_item_add_subtree(header, ett_spdy_header);
1174 length = tvb_get_ntohs(header_tvb, hdr_offset);
1175 hdr_offset += 2;
1176 header_name = (gchar *)tvb_get_ephemeral_string(header_tvb, hdr_offset, length);
1177 hdr_offset += length;
1178 header_length += hdr_offset - hoffset;
1179 if (tree) {
1180 ti = proto_tree_add_text(header_tree, header_tvb, hoffset, length+2, "Name: %s",
1181 header_name);
1182 name_tree = proto_item_add_subtree(ti, ett_spdy_header_name);
1183 proto_tree_add_uint(name_tree, hf_spdy_length, header_tvb, hoffset, 2, length);
1184 proto_tree_add_string_format(name_tree, hf_spdy_header_name_text, header_tvb, hoffset+2, length,
1185 header_name, "Text: %s", format_text(header_name, length));
1188 hoffset = hdr_offset;
1189 length = tvb_get_ntohs(header_tvb, hdr_offset);
1190 hdr_offset += 2;
1191 header_value = (gchar *)tvb_get_ephemeral_string(header_tvb, hdr_offset, length);
1192 hdr_offset += length;
1193 header_length += hdr_offset - hoffset;
1194 if (tree) {
1195 ti = proto_tree_add_text(header_tree, header_tvb, hoffset, length+2, "Value: %s",
1196 header_value);
1197 value_tree = proto_item_add_subtree(ti, ett_spdy_header_value);
1198 proto_tree_add_uint(value_tree, hf_spdy_length, header_tvb, hoffset, 2, length);
1199 proto_tree_add_string_format(value_tree, hf_spdy_header_value_text, header_tvb, hoffset+2, length,
1200 header_value, "Text: %s", format_text(header_value, length));
1201 proto_item_append_text(header, ": %s: %s", header_name, header_value);
1202 proto_item_set_len(header, header_length);
1204 if (spdy_debug) printf(" %s: %s\n", header_name, header_value);
1206 * TODO(ers) check that the header name contains only legal characters.
1208 if (g_ascii_strcasecmp(header_name, "method") == 0 ||
1209 g_ascii_strcasecmp(header_name, "status") == 0) {
1210 hdr_verb = header_value;
1211 } else if (g_ascii_strcasecmp(header_name, "url") == 0) {
1212 hdr_url = header_value;
1213 } else if (g_ascii_strcasecmp(header_name, "version") == 0) {
1214 hdr_version = header_value;
1215 } else if (g_ascii_strcasecmp(header_name, "content-type") == 0) {
1216 content_type = se_strdup(header_value);
1217 } else if (g_ascii_strcasecmp(header_name, "content-encoding") == 0) {
1218 content_encoding = se_strdup(header_value);
1221 if (hdr_version != NULL) {
1222 if (hdr_url != NULL) {
1223 col_add_fstr(pinfo->cinfo, COL_INFO, "%s[%d]: %s %s %s",
1224 frame_type_name, stream_id, hdr_verb, hdr_url, hdr_version);
1225 } else {
1226 col_add_fstr(pinfo->cinfo, COL_INFO, "%s[%d]: %s %s",
1227 frame_type_name, stream_id, hdr_verb, hdr_version);
1229 } else {
1230 col_add_fstr(pinfo->cinfo, COL_INFO, "%s[%d]", frame_type_name, stream_id);
1233 * If we expect data on this stream, we need to remember the content
1234 * type and content encoding.
1236 if (content_type != NULL && !pinfo->fd->flags.visited) {
1237 gchar *content_type_params = spdy_parse_content_type(content_type);
1238 spdy_save_stream_info(conv_data, stream_id, content_type,
1239 content_type_params, content_encoding);
1242 return offset - orig_offset;
1245 static int
1246 dissect_spdy(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1248 spdy_conv_t *conv_data;
1249 int offset = 0;
1250 int len;
1251 int firstpkt = 1;
1254 * The first byte of a SPDY packet must be either 0 or
1255 * 0x80. If it's not, assume that this is not SPDY.
1256 * (In theory, a data frame could have a stream ID
1257 * >= 2^24, in which case it won't have 0 for a first
1258 * byte, but this is a pretty reliable heuristic for
1259 * now.)
1261 guint8 first_byte = tvb_get_guint8(tvb, 0);
1262 if (first_byte != 0x80 && first_byte != 0x0)
1263 return 0;
1265 conv_data = get_spdy_conversation_data(pinfo);
1267 while (tvb_reported_length_remaining(tvb, offset) != 0) {
1268 if (!firstpkt) {
1269 col_add_fstr(pinfo->cinfo, COL_INFO, " >> ");
1270 col_set_fence(pinfo->cinfo, COL_INFO);
1272 len = dissect_spdy_message(tvb, offset, pinfo, tree, conv_data);
1273 if (len <= 0)
1274 return 0;
1275 offset += len;
1277 * OK, we've set the Protocol and Info columns for the
1278 * first SPDY message; set a fence so that subsequent
1279 * SPDY messages don't overwrite the Info column.
1281 col_set_fence(pinfo->cinfo, COL_INFO);
1282 firstpkt = 0;
1284 return 1;
1287 static gboolean
1288 dissect_spdy_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1290 if (!value_is_in_range(global_spdy_tcp_range, pinfo->destport) &&
1291 !value_is_in_range(global_spdy_tcp_range, pinfo->srcport))
1292 return FALSE;
1293 return dissect_spdy(tvb, pinfo, tree) != 0;
1296 static void reinit_spdy(void)
1300 // NMAKE complains about flags_set_truth not being constant. Duplicate
1301 // the values inside of it.
1302 static const true_false_string tfs_spdy_set_notset = { "Set", "Not set" };
1304 void
1305 proto_register_spdy(void)
1307 static hf_register_info hf[] = {
1308 { &hf_spdy_syn_stream,
1309 { "Syn Stream", "spdy.syn_stream",
1310 FT_BYTES, BASE_NONE, NULL, 0x0,
1311 "", HFILL }},
1312 { &hf_spdy_syn_reply,
1313 { "Syn Reply", "spdy.syn_reply",
1314 FT_BYTES, BASE_NONE, NULL, 0x0,
1315 "", HFILL }},
1316 { &hf_spdy_control_bit,
1317 { "Control bit", "spdy.control_bit",
1318 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1319 "TRUE if SPDY control frame", HFILL }},
1320 { &hf_spdy_version,
1321 { "Version", "spdy.version",
1322 FT_UINT16, BASE_DEC, NULL, 0x0,
1323 "", HFILL }},
1324 { &hf_spdy_type,
1325 { "Type", "spdy.type",
1326 FT_UINT16, BASE_DEC, NULL, 0x0,
1327 "", HFILL }},
1328 { &hf_spdy_flags,
1329 { "Flags", "spdy.flags",
1330 FT_UINT8, BASE_HEX, NULL, 0x0,
1331 "", HFILL }},
1332 { &hf_spdy_flags_fin,
1333 { "Fin", "spdy.flags.fin",
1334 FT_BOOLEAN, 8, TFS(&tfs_spdy_set_notset),
1335 SPDY_FIN, "", HFILL }},
1336 { &hf_spdy_length,
1337 { "Length", "spdy.length",
1338 FT_UINT24, BASE_DEC, NULL, 0x0,
1339 "", HFILL }},
1340 { &hf_spdy_header,
1341 { "Header", "spdy.header",
1342 FT_NONE, BASE_NONE, NULL, 0x0,
1343 "", HFILL }},
1344 { &hf_spdy_header_name,
1345 { "Name", "spdy.header.name",
1346 FT_NONE, BASE_NONE, NULL, 0x0,
1347 "", HFILL }},
1348 { &hf_spdy_header_name_text,
1349 { "Text", "spdy.header.name.text",
1350 FT_STRING, BASE_NONE, NULL, 0x0,
1351 "", HFILL }},
1352 { &hf_spdy_header_value,
1353 { "Value", "spdy.header.value",
1354 FT_NONE, BASE_NONE, NULL, 0x0,
1355 "", HFILL }},
1356 { &hf_spdy_header_value_text,
1357 { "Text", "spdy.header.value.text",
1358 FT_STRING, BASE_NONE, NULL, 0x0,
1359 "", HFILL }},
1360 { &hf_spdy_streamid,
1361 { "Stream ID", "spdy.streamid",
1362 FT_UINT32, BASE_DEC, NULL, 0x0,
1363 "", HFILL }},
1364 { &hf_spdy_associated_streamid,
1365 { "Associated Stream ID", "spdy.associated.streamid",
1366 FT_UINT32, BASE_DEC, NULL, 0x0,
1367 "", HFILL }},
1368 { &hf_spdy_priority,
1369 { "Priority", "spdy.priority",
1370 FT_UINT8, BASE_DEC, NULL, 0x0,
1371 "", HFILL }},
1372 { &hf_spdy_num_headers,
1373 { "Number of headers", "spdy.numheaders",
1374 FT_UINT16, BASE_DEC, NULL, 0x0,
1375 "", HFILL }},
1376 { &hf_spdy_num_headers_string,
1377 { "Number of headers", "spdy.numheaders",
1378 FT_STRING, BASE_NONE, NULL, 0x0,
1379 "", HFILL }},
1381 static gint *ett[] = {
1382 &ett_spdy,
1383 &ett_spdy_syn_stream,
1384 &ett_spdy_syn_reply,
1385 &ett_spdy_fin_stream,
1386 &ett_spdy_flags,
1387 &ett_spdy_header,
1388 &ett_spdy_header_name,
1389 &ett_spdy_header_value,
1390 &ett_spdy_encoded_entity,
1393 module_t *spdy_module;
1395 proto_spdy = proto_register_protocol("SPDY", "SPDY", "spdy");
1396 proto_register_field_array(proto_spdy, hf, array_length(hf));
1397 proto_register_subtree_array(ett, array_length(ett));
1398 new_register_dissector("spdy", dissect_spdy, proto_spdy);
1399 spdy_module = prefs_register_protocol(proto_spdy, reinit_spdy);
1400 prefs_register_bool_preference(spdy_module, "desegment_headers",
1401 "Reassemble SPDY control frames spanning multiple TCP segments",
1402 "Whether the SPDY dissector should reassemble control frames "
1403 "spanning multiple TCP segments. "
1404 "To use this option, you must also enable "
1405 "\"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
1406 &spdy_desegment_control_frames);
1407 prefs_register_bool_preference(spdy_module, "desegment_body",
1408 "Reassemble SPDY bodies spanning multiple TCP segments",
1409 "Whether the SPDY dissector should reassemble "
1410 "data frames spanning multiple TCP segments. "
1411 "To use this option, you must also enable "
1412 "\"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
1413 &spdy_desegment_data_frames);
1414 prefs_register_bool_preference(spdy_module, "assemble_data_frames",
1415 "Assemble SPDY bodies that consist of multiple DATA frames",
1416 "Whether the SPDY dissector should reassemble multiple "
1417 "data frames into an entity body.",
1418 &spdy_assemble_entity_bodies);
1419 #ifdef HAVE_LIBZ
1420 prefs_register_bool_preference(spdy_module, "decompress_headers",
1421 "Uncompress SPDY headers",
1422 "Whether to uncompress SPDY headers.",
1423 &spdy_decompress_headers);
1424 prefs_register_bool_preference(spdy_module, "decompress_body",
1425 "Uncompress entity bodies",
1426 "Whether to uncompress entity bodies that are compressed "
1427 "using \"Content-Encoding: \"",
1428 &spdy_decompress_body);
1429 #endif
1430 prefs_register_bool_preference(spdy_module, "debug_output",
1431 "Print debug info on stdout",
1432 "Print debug info on stdout",
1433 &spdy_debug);
1434 #if 0
1435 prefs_register_string_preference(ssl_module, "debug_file", "SPDY debug file",
1436 "Redirect SPDY debug to file name; "
1437 "leave empty to disable debugging, "
1438 "or use \"" SPDY_DEBUG_USE_STDOUT "\""
1439 " to redirect output to stdout\n",
1440 (const gchar **)&sdpy_debug_file_name);
1441 #endif
1442 prefs_register_obsolete_preference(spdy_module, "tcp_alternate_port");
1444 range_convert_str(&global_spdy_tcp_range, TCP_DEFAULT_RANGE, 65535);
1445 spdy_tcp_range = range_empty();
1446 prefs_register_range_preference(spdy_module, "tcp.port", "TCP Ports",
1447 "TCP Ports range",
1448 &global_spdy_tcp_range, 65535);
1450 range_convert_str(&global_spdy_ssl_range, SSL_DEFAULT_RANGE, 65535);
1451 spdy_ssl_range = range_empty();
1452 prefs_register_range_preference(spdy_module, "ssl.port", "SSL/TLS Ports",
1453 "SSL/TLS Ports range",
1454 &global_spdy_ssl_range, 65535);
1456 spdy_handle = new_create_dissector_handle(dissect_spdy, proto_spdy);
1458 * Register for tapping
1460 spdy_tap = register_tap("spdy"); /* SPDY statistics tap */
1461 spdy_eo_tap = register_tap("spdy_eo"); /* SPDY Export Object tap */
1464 void
1465 proto_reg_handoff_spdy(void)
1467 data_handle = find_dissector("data");
1468 media_handle = find_dissector("media");
1469 heur_dissector_add("tcp", dissect_spdy_heur, proto_spdy);
1473 * Content-Type: message/http
1476 static gint proto_message_spdy = -1;
1477 static gint ett_message_spdy = -1;
1479 static void
1480 dissect_message_spdy(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1482 proto_tree *subtree;
1483 proto_item *ti;
1484 gint offset = 0, next_offset;
1485 gint len;
1487 if (check_col(pinfo->cinfo, COL_INFO))
1488 col_append_str(pinfo->cinfo, COL_INFO, " (message/spdy)");
1489 if (tree) {
1490 ti = proto_tree_add_item(tree, proto_message_spdy,
1491 tvb, 0, -1, FALSE);
1492 subtree = proto_item_add_subtree(ti, ett_message_spdy);
1493 while (tvb_reported_length_remaining(tvb, offset) != 0) {
1494 len = tvb_find_line_end(tvb, offset,
1495 tvb_ensure_length_remaining(tvb, offset),
1496 &next_offset, FALSE);
1497 if (len == -1)
1498 break;
1499 proto_tree_add_text(subtree, tvb, offset, next_offset - offset,
1500 "%s", tvb_format_text(tvb, offset, len));
1501 offset = next_offset;
1506 void
1507 proto_register_message_spdy(void)
1509 static gint *ett[] = {
1510 &ett_message_spdy,
1513 proto_message_spdy = proto_register_protocol(
1514 "Media Type: message/spdy",
1515 "message/spdy",
1516 "message-spdy"
1518 proto_register_subtree_array(ett, array_length(ett));
1521 void
1522 proto_reg_handoff_message_spdy(void)
1524 dissector_handle_t message_spdy_handle;
1526 message_spdy_handle = create_dissector_handle(dissect_message_spdy,
1527 proto_message_spdy);
1529 dissector_add_string("media_type", "message/spdy", message_spdy_handle);
1531 reinit_spdy();