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>
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.
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>
57 /* The types of SPDY control frames */
58 typedef enum _spdy_type
{
69 static const char *frame_type_names
[] = {
70 "DATA", "SYN_STREAM", "SYN_REPLY", "FIN_STREAM", "HELLO", "NOOP",
75 * This structure will be tied to each SPDY frame.
76 * Note that there may be multiple SPDY frames
79 typedef struct _spdy_frame_info_t
{
82 guint header_block_len
;
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
{
97 typedef struct _spdy_stream_info_t
{
99 gchar
*content_type_parameters
;
100 gchar
*content_encoding
;
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
{
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.
172 static gboolean spdy_decompress_body
= TRUE
;
173 static gboolean spdy_decompress_headers
= TRUE
;
175 static gboolean spdy_decompress_body
= FALSE
;
176 static gboolean spdy_decompress_headers
= FALSE
;
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
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
[] = {
203 { 101, "Switching Protocols" },
204 { 102, "Processing" },
205 { 199, "Informational - Others" },
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"},
221 { 304, "Not Modified"},
223 { 307, "Temporary Redirect"},
224 { 399, "Redirection - Others"},
226 { 400, "Bad Request"},
227 { 401, "Unauthorized"},
228 { 402, "Payment Required"},
231 { 405, "Method Not Allowed"},
232 { 406, "Not Acceptable"},
233 { 407, "Proxy Authentication Required"},
234 { 408, "Request Time-out"},
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"},
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"},
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");
283 get_spdy_conversation_data(packet_info
*pinfo
)
285 conversation_t
*conversation
;
286 spdy_conv_t
*conv_data
;
289 conversation
= find_conversation(pinfo
->fd
->num
, &pinfo
->src
, &pinfo
->dst
, pinfo
->ptype
, pinfo
->srcport
, pinfo
->destport
, 0);
291 printf("\n===========================================\n\n");
292 printf("Conversation for frame #%d is %p\n", pinfo
->fd
->num
, 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
);
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
);
313 retcode
= inflateInit(conv_data
->rply_decompressor
);
315 printf("frame #%d: inflateInit() failed: %d\n", pinfo
->fd
->num
, retcode
);
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
,
321 sizeof(spdy_dictionary
));
324 conversation_add_proto_data(conversation
, proto_spdy
, conv_data
);
325 register_postseq_cleanup_routine(reset_decompressors
);
331 spdy_save_stream_info(spdy_conv_t
*conv_data
,
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
);
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
;
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
)
363 return g_array_index(conv_data
->streams
, spdy_stream_info_t
*, stream_id
);
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
);
373 if (spdy_debug
) printf("No stream_info found for stream %d\n", stream_id
);
375 spdy_data_frame_t
*df
= g_malloc(sizeof(spdy_data_frame_t
));
378 df
->framenum
= frame
;
379 si
->data_frames
= g_slist_append(si
->data_frames
, df
);
380 ++si
->num_data_frames
;
382 printf("Saved %u bytes of data for stream %u frame %u\n",
383 length
, stream_id
, df
->framenum
);
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
);
392 ++si
->num_data_frames
;
396 * Return the number of data frames saved so far for the specified stream.
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
);
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
;
425 GSList
*dflist
= si
->data_frames
;
428 dflist
= si
->data_frames
;
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
436 while (dflist
!= NULL
) {
438 datalen
+= df
->length
;
439 dflist
= g_slist_next(dflist
);
442 data
= se_alloc(datalen
);
443 dflist
= si
->data_frames
;
446 while (dflist
!= NULL
) {
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
;
460 spdy_discard_data_frames(spdy_stream_info_t
*si
)
462 GSList
*dflist
= si
->data_frames
;
463 spdy_data_frame_t
*df
;
467 while (dflist
!= NULL
) {
469 if (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
);
485 tvb_set_child_real_data_tvbuff (parent
, new_tvb
);
490 dissect_spdy_data_frame(tvbuff_t
*tvb
, int offset
,
492 proto_tree
*top_level_tree
,
493 proto_tree
*spdy_tree
,
494 proto_item
*spdy_proto
,
495 spdy_conv_t
*conv_data
)
499 guint32 frame_length
;
501 proto_tree
*flags_tree
;
502 guint32 reported_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
;
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);
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
;
552 gboolean private_data_changed
= FALSE
;
553 gboolean is_single_chunk
= FALSE
;
554 gboolean have_entire_body
;
557 * Create a tvbuff for the payload.
560 next_tvb
= tvb_new_subset(tvb
, offset
+8, 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
);
570 spdy_increment_data_chunk_count(conv_data
, stream_id
);
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)" */
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
);
592 data_tvb
= si
->assembled_data
;
593 if (spdy_assemble_entity_bodies
)
594 have_entire_body
= TRUE
;
596 if (!have_entire_body
)
599 if (data_tvb
== NULL
)
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")
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) {
638 spdy_data_frame_t
*df
;
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
;
645 while (dflist
!= NULL
) {
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
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");
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
);
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
,
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
);
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
,
719 if (handle
!= NULL
) {
721 * We have a subdissector - call it.
723 dissected
= call_dissector(handle
, data_tvb
, pinfo
, top_level_tree
);
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.
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
);
742 /* Call the default data dissector */
743 call_dissector(data_handle
, next_tvb
, pinfo
, top_level_tree
);
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;
763 spdy_decompress_header_block(tvbuff_t
*tvb
, z_streamp decomp
,
764 guint32 dictionary_id
, int offset
,
765 guint32 length
, guint
*uncomp_length
)
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
);
781 retcode
= inflateSetDictionary(decomp
,
783 sizeof(spdy_dictionary
));
785 retcode
= inflate(decomp
, Z_SYNC_FLUSH
);
789 if (retcode
!= Z_OK
) {
792 *uncomp_length
= bufsize
- decomp
->avail_out
;
794 printf("Inflation SUCCEEDED. uncompressed size=%d\n", *uncomp_length
);
795 if (decomp
->avail_in
!= 0)
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.
809 spdy_check_header_compression(tvbuff_t
*tvb
,
811 guint32 frame_length
)
814 if (!tvb_bytes_exist(tvb
, offset
, 6))
816 length
= tvb_get_ntohs(tvb
, offset
);
817 if (length
> frame_length
)
819 length
= tvb_get_ntohs(tvb
, offset
+2);
820 if (length
> frame_length
)
822 if (spdy_debug
) printf("Looks like the header block is not compressed\n");
826 // TODO(cbentzel): Change wireshark to export p_remove_proto_data, rather
827 // than duplicating code here.
828 typedef struct _spdy_frame_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
)
840 else if (ap
-> proto
== bp
-> proto
)
847 static void spdy_p_remove_proto_data(frame_data
*fd
, int proto
)
849 spdy_frame_proto_data temp
;
853 temp
.proto_data
= NULL
;
855 item
= g_slist_find_custom(fd
->pfd
, (gpointer
*)&temp
, spdy_p_compare
);
858 fd
->pfd
= g_slist_remove(fd
->pfd
, item
->data
);
862 static spdy_frame_info_t
*
863 spdy_save_header_block(frame_data
*fd
,
869 GSList
*filist
= p_get_proto_data(fd
, proto_spdy
);
870 spdy_frame_info_t
*frame_info
= se_alloc(sizeof(spdy_frame_info_t
));
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
);
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
,
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
)
893 filist
= g_slist_next(filist
);
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.
905 spdy_parse_content_type(gchar
*content_type
)
907 gchar
*cp
= content_type
;
909 while (*cp
!= '\0' && *cp
!= ';' && !isspace(*cp
)) {
918 while (*cp
== ';' || isspace(*cp
))
927 dissect_spdy_message(tvbuff_t
*tvb
, int offset
, packet_info
*pinfo
,
928 proto_tree
*tree
, spdy_conv_t
*conv_data
)
934 guint32 frame_length
;
936 guint32 associated_stream_id
;
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
;
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)
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);
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
) {
982 frame_header
= ep_tvb_memdup(tvb
, offset
, 16);
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
];
990 flags
= tvb_get_guint8(tvb
, offset
);
991 frame_length
= tvb_get_ntoh24(tvb
, offset
+1);
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
) {
998 printf("Not enough header data: %d vs. %d\n",
999 frame_length
, tvb_reported_length_remaining(tvb
, offset
));
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
);
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");
1013 return dissect_spdy_data_frame(tvb
, orig_offset
, pinfo
, tree
,
1014 spdy_tree
, spdy_proto
, conv_data
);
1017 sub_tree
= NULL
; /* avoid gcc warning */
1018 switch (frame_type
) {
1019 case SPDY_SYN_STREAM
:
1020 case SPDY_SYN_REPLY
:
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
);
1030 if (frame_type
== SPDY_SYN_STREAM
) {
1031 associated_stream_id
= tvb_get_bits32(tvb
, (offset
<< 3) + 1, 31, FALSE
);
1033 priority
= tvb_get_bits8(tvb
, offset
<< 3, 2);
1036 // The next two bytes have no meaning in SYN_REPLY
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",
1055 flags
& SPDY_FIN
? " [FIN]" : "",
1056 stream_id
, frame_length
);
1058 printf(" stream ID=%u priority=%d\n", stream_id
, priority
);
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
1070 // TODO(ers) fill in tree and summary
1071 stream_id
= 0; /* avoid gcc warning */
1075 stream_id
= 0; /* avoid gcc warning */
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
) {
1088 hdr_offset
= offset
;
1090 spdy_frame_info_t
*per_frame_info
=
1091 spdy_find_saved_header_block(pinfo
->fd
,
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
,
1102 frame_length
+ 8 - (offset
- orig_offset
),
1104 if (uncomp_ptr
== NULL
) { /* decompression failed */
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?
1111 printf("Saving %u bytes of uncomp hdr\n", uncomp_length
);
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");
1131 offset
= orig_offset
+ 8 + frame_length
;
1132 num_headers
= tvb_get_ntohs(header_tvb
, hdr_offset
);
1134 if (header_tvb
== NULL
||
1135 (headers_compressed
&& !spdy_decompress_headers
)) {
1137 ti
= proto_tree_add_string(sub_tree
, hf_spdy_num_headers_string
,
1139 frame_type
== SPDY_SYN_STREAM
? orig_offset
+18 : orig_offset
+ 14,
1141 "Unknown (header block is compressed)");
1143 ti
= proto_tree_add_uint(sub_tree
, hf_spdy_num_headers
,
1145 frame_type
== SPDY_SYN_STREAM
? orig_offset
+18 : orig_offset
+14,
1148 spdy_type
= SPDY_INVALID
; /* type not known yet */
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) {
1160 gchar
*header_value
;
1161 proto_tree
*header_tree
;
1162 proto_tree
*name_tree
;
1163 proto_tree
*value_tree
;
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
);
1176 header_name
= (gchar
*)tvb_get_ephemeral_string(header_tvb
, hdr_offset
, length
);
1177 hdr_offset
+= length
;
1178 header_length
+= hdr_offset
- hoffset
;
1180 ti
= proto_tree_add_text(header_tree
, header_tvb
, hoffset
, length
+2, "Name: %s",
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
);
1191 header_value
= (gchar
*)tvb_get_ephemeral_string(header_tvb
, hdr_offset
, length
);
1192 hdr_offset
+= length
;
1193 header_length
+= hdr_offset
- hoffset
;
1195 ti
= proto_tree_add_text(header_tree
, header_tvb
, hoffset
, length
+2, "Value: %s",
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
);
1226 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "%s[%d]: %s %s",
1227 frame_type_name
, stream_id
, hdr_verb
, hdr_version
);
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
;
1246 dissect_spdy(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
)
1248 spdy_conv_t
*conv_data
;
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
1261 guint8 first_byte
= tvb_get_guint8(tvb
, 0);
1262 if (first_byte
!= 0x80 && first_byte
!= 0x0)
1265 conv_data
= get_spdy_conversation_data(pinfo
);
1267 while (tvb_reported_length_remaining(tvb
, offset
) != 0) {
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
);
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
);
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
))
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" };
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,
1312 { &hf_spdy_syn_reply
,
1313 { "Syn Reply", "spdy.syn_reply",
1314 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
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
}},
1321 { "Version", "spdy.version",
1322 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
1325 { "Type", "spdy.type",
1326 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
1329 { "Flags", "spdy.flags",
1330 FT_UINT8
, BASE_HEX
, NULL
, 0x0,
1332 { &hf_spdy_flags_fin
,
1333 { "Fin", "spdy.flags.fin",
1334 FT_BOOLEAN
, 8, TFS(&tfs_spdy_set_notset
),
1335 SPDY_FIN
, "", HFILL
}},
1337 { "Length", "spdy.length",
1338 FT_UINT24
, BASE_DEC
, NULL
, 0x0,
1341 { "Header", "spdy.header",
1342 FT_NONE
, BASE_NONE
, NULL
, 0x0,
1344 { &hf_spdy_header_name
,
1345 { "Name", "spdy.header.name",
1346 FT_NONE
, BASE_NONE
, NULL
, 0x0,
1348 { &hf_spdy_header_name_text
,
1349 { "Text", "spdy.header.name.text",
1350 FT_STRING
, BASE_NONE
, NULL
, 0x0,
1352 { &hf_spdy_header_value
,
1353 { "Value", "spdy.header.value",
1354 FT_NONE
, BASE_NONE
, NULL
, 0x0,
1356 { &hf_spdy_header_value_text
,
1357 { "Text", "spdy.header.value.text",
1358 FT_STRING
, BASE_NONE
, NULL
, 0x0,
1360 { &hf_spdy_streamid
,
1361 { "Stream ID", "spdy.streamid",
1362 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
1364 { &hf_spdy_associated_streamid
,
1365 { "Associated Stream ID", "spdy.associated.streamid",
1366 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
1368 { &hf_spdy_priority
,
1369 { "Priority", "spdy.priority",
1370 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
1372 { &hf_spdy_num_headers
,
1373 { "Number of headers", "spdy.numheaders",
1374 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
1376 { &hf_spdy_num_headers_string
,
1377 { "Number of headers", "spdy.numheaders",
1378 FT_STRING
, BASE_NONE
, NULL
, 0x0,
1381 static gint
*ett
[] = {
1383 &ett_spdy_syn_stream
,
1384 &ett_spdy_syn_reply
,
1385 &ett_spdy_fin_stream
,
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
);
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
);
1430 prefs_register_bool_preference(spdy_module
, "debug_output",
1431 "Print debug info on stdout",
1432 "Print debug info on stdout",
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
);
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",
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 */
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;
1480 dissect_message_spdy(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
)
1482 proto_tree
*subtree
;
1484 gint offset
= 0, next_offset
;
1487 if (check_col(pinfo
->cinfo
, COL_INFO
))
1488 col_append_str(pinfo
->cinfo
, COL_INFO
, " (message/spdy)");
1490 ti
= proto_tree_add_item(tree
, proto_message_spdy
,
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
);
1499 proto_tree_add_text(subtree
, tvb
, offset
, next_offset
- offset
,
1500 "%s", tvb_format_text(tvb
, offset
, len
));
1501 offset
= next_offset
;
1507 proto_register_message_spdy(void)
1509 static gint
*ett
[] = {
1513 proto_message_spdy
= proto_register_protocol(
1514 "Media Type: message/spdy",
1518 proto_register_subtree_array(ett
, array_length(ett
));
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
);