/src/wireshark/epan/dissectors/packet-spdy.c
Line | Count | Source (jump to first uncovered line) |
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 |
5 | | * |
6 | | * Copyright 2010, Google Inc. |
7 | | * Hasan Khalil <hkhalil@google.com> |
8 | | * Chris Bentzel <cbentzel@google.com> |
9 | | * Eric Shienbrood <ers@google.com> |
10 | | * |
11 | | * Copyright 2013-2014 |
12 | | * Alexis La Goutte <alexis.lagoutte@gmail.com> |
13 | | * |
14 | | * Wireshark - Network traffic analyzer |
15 | | * By Gerald Combs <gerald@wireshark.org> |
16 | | * Copyright 1998 Gerald Combs |
17 | | * |
18 | | * Originally based on packet-http.c |
19 | | * |
20 | | * SPDX-License-Identifier: GPL-2.0-or-later |
21 | | */ |
22 | | |
23 | | #include "config.h" |
24 | | |
25 | | #include <epan/packet.h> |
26 | | #include <epan/prefs.h> |
27 | | #include <epan/expert.h> |
28 | | #include <epan/tap.h> |
29 | | #include <epan/tfs.h> |
30 | | #include <wsutil/array.h> |
31 | | #include "packet-tcp.h" |
32 | | #include "packet-tls.h" |
33 | | #include "packet-media-type.h" |
34 | | |
35 | | #if defined(HAVE_ZLIB) && !defined(HAVE_ZLIBNG) |
36 | | #define ZLIB_CONST |
37 | 6 | #define ZLIB_PREFIX(x) x |
38 | | #include <zlib.h> |
39 | | typedef z_stream zlib_stream; |
40 | | #endif /* HAVE_ZLIB */ |
41 | | |
42 | | #ifdef HAVE_ZLIBNG |
43 | | #define ZLIB_PREFIX(x) zng_ ## x |
44 | | #include <zlib-ng.h> |
45 | | typedef zng_stream zlib_stream; |
46 | | #endif /* HAVE_ZLIBNG */ |
47 | | |
48 | | void proto_register_spdy(void); |
49 | | void proto_reg_handoff_spdy(void); |
50 | | |
51 | 0 | #define MIN_SPDY_VERSION 3 |
52 | | |
53 | 57 | #define SPDY_STREAM_ID_MASK 0x7FFFFFFF |
54 | | |
55 | | /* |
56 | | * Conversation data - used for assembling multi-data-frame |
57 | | * entities and for decompressing request & reply header blocks. |
58 | | */ |
59 | | typedef struct _spdy_conv_t { |
60 | | #if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG) |
61 | | #ifdef HAVE_ZLIBNG |
62 | | zng_streamp rqst_decompressor; |
63 | | zng_streamp rply_decompressor; |
64 | | uint32_t dictionary_id; |
65 | | #else |
66 | | z_streamp rqst_decompressor; |
67 | | z_streamp rply_decompressor; |
68 | | uLong dictionary_id; |
69 | | #endif |
70 | | #endif |
71 | | wmem_tree_t *streams; |
72 | | } spdy_conv_t; |
73 | | |
74 | | |
75 | | /* The types of SPDY frames */ |
76 | 45 | #define SPDY_DATA 0 |
77 | 30 | #define SPDY_SYN_STREAM 1 |
78 | 15 | #define SPDY_SYN_REPLY 2 |
79 | 0 | #define SPDY_RST_STREAM 3 |
80 | 15 | #define SPDY_SETTINGS 4 |
81 | 0 | #define SPDY_PING 6 |
82 | 0 | #define SPDY_GOAWAY 7 |
83 | 0 | #define SPDY_HEADERS 8 |
84 | 0 | #define SPDY_WINDOW_UPDATE 9 |
85 | 0 | #define SPDY_CREDENTIAL 10 |
86 | 1 | #define SPDY_INVALID 11 |
87 | | |
88 | 29 | #define SPDY_FLAG_FIN 0x01 |
89 | 14 | #define SPDY_FLAG_UNIDIRECTIONAL 0x02 |
90 | 14 | #define SPDY_FLAG_SETTINGS_CLEAR_SETTINGS 0x01 |
91 | | |
92 | | /* Flags for each setting in a SETTINGS frame. */ |
93 | 14 | #define SPDY_FLAG_SETTINGS_PERSIST_VALUE 0x01 |
94 | 14 | #define SPDY_FLAG_SETTINGS_PERSISTED 0x02 |
95 | | |
96 | 14 | #define TCP_PORT_SPDY 6121 |
97 | | |
98 | | static const value_string frame_type_names[] = { |
99 | | { SPDY_DATA, "DATA" }, |
100 | | { SPDY_SYN_STREAM, "SYN_STREAM" }, |
101 | | { SPDY_SYN_REPLY, "SYN_REPLY" }, |
102 | | { SPDY_RST_STREAM, "RST_STREAM" }, |
103 | | { SPDY_SETTINGS, "SETTINGS" }, |
104 | | { SPDY_PING, "PING" }, |
105 | | { SPDY_GOAWAY, "GOAWAY" }, |
106 | | { SPDY_HEADERS, "HEADERS" }, |
107 | | { SPDY_WINDOW_UPDATE, "WINDOW_UPDATE" }, |
108 | | { SPDY_CREDENTIAL, "CREDENTIAL" }, |
109 | | { SPDY_INVALID, "INVALID" }, |
110 | | { 0, NULL } |
111 | | }; |
112 | | |
113 | | static const value_string rst_stream_status_names[] = { |
114 | | { 1, "PROTOCOL_ERROR" }, |
115 | | { 2, "INVALID_STREAM" }, |
116 | | { 3, "REFUSED_STREAM" }, |
117 | | { 4, "UNSUPPORTED_VERSION" }, |
118 | | { 5, "CANCEL" }, |
119 | | { 6, "INTERNAL_ERROR" }, |
120 | | { 7, "FLOW_CONTROL_ERROR" }, |
121 | | { 8, "STREAM_IN_USE" }, |
122 | | { 9, "STREAM_ALREADY_CLOSED" }, |
123 | | { 10, "INVALID_CREDENTIALS" }, |
124 | | { 11, "FRAME_TOO_LARGE" }, |
125 | | { 12, "INVALID" }, |
126 | | { 0, NULL } |
127 | | }; |
128 | | |
129 | | static const value_string setting_id_names[] = { |
130 | | { 1, "UPLOAD_BANDWIDTH" }, |
131 | | { 2, "DOWNLOAD_BANDWIDTH" }, |
132 | | { 3, "ROUND_TRIP_TIME" }, |
133 | | { 4, "MAX_CONCURRENT_STREAMS" }, |
134 | | { 5, "CURRENT_CWND" }, |
135 | | { 6, "DOWNLOAD_RETRANS_RATE" }, |
136 | | { 7, "INITIAL_WINDOW_SIZE" }, |
137 | | { 0, NULL } |
138 | | }; |
139 | | |
140 | | static const value_string goaway_status_names[] = { |
141 | | { 0, "OK" }, |
142 | | { 1, "PROTOCOL_ERROR" }, |
143 | | { 11, "INTERNAL_ERROR" }, |
144 | | { 0, NULL } |
145 | | }; |
146 | | |
147 | | /* |
148 | | * This structure will be tied to each SPDY frame and is used as an argument for |
149 | | * dissect_spdy_*_payload() functions. |
150 | | */ |
151 | | typedef struct _spdy_control_frame_info_t { |
152 | | bool control_bit; |
153 | | uint16_t version; |
154 | | uint16_t type; |
155 | | uint8_t flags; |
156 | | uint32_t length; /* Actually only 24 bits. */ |
157 | | } spdy_control_frame_info_t; |
158 | | |
159 | | /* |
160 | | * This structure will be tied to each SPDY header frame. |
161 | | * Only applies to frames containing headers: SYN_STREAM, SYN_REPLY, HEADERS |
162 | | * Note that there may be multiple SPDY frames in one packet. |
163 | | */ |
164 | | typedef struct _spdy_header_info_t { |
165 | | uint32_t stream_id; |
166 | | uint8_t *header_block; |
167 | | unsigned header_block_len; |
168 | | uint16_t frame_type; |
169 | | } spdy_header_info_t; |
170 | | |
171 | | static wmem_list_t *header_info_list; |
172 | | |
173 | | /* |
174 | | * This structures keeps track of all the data frames |
175 | | * associated with a stream, so that they can be |
176 | | * reassembled into a single chunk. |
177 | | */ |
178 | | typedef struct _spdy_data_frame_t { |
179 | | uint8_t *data; |
180 | | uint32_t length; |
181 | | uint32_t framenum; |
182 | | } spdy_data_frame_t; |
183 | | |
184 | | typedef struct _spdy_stream_info_t { |
185 | | media_container_type_t container_type; |
186 | | char *content_type; |
187 | | char *content_type_parameters; |
188 | | char *content_encoding; |
189 | | wmem_list_t *data_frames; |
190 | | tvbuff_t *assembled_data; |
191 | | unsigned num_data_frames; |
192 | | } spdy_stream_info_t; |
193 | | |
194 | | /* Handles for metadata population. */ |
195 | | |
196 | | static int spdy_tap; |
197 | | static int spdy_eo_tap; |
198 | | |
199 | | static int proto_spdy; |
200 | | static int hf_spdy_data; |
201 | | static int hf_spdy_control_bit; |
202 | | static int hf_spdy_version; |
203 | | static int hf_spdy_type; |
204 | | static int hf_spdy_flags; |
205 | | static int hf_spdy_flags_fin; |
206 | | static int hf_spdy_flags_unidirectional; |
207 | | static int hf_spdy_flags_clear_settings; |
208 | | static int hf_spdy_flags_persist_value; |
209 | | static int hf_spdy_flags_persisted; |
210 | | static int hf_spdy_length; |
211 | | static int hf_spdy_header_block; |
212 | | static int hf_spdy_header; |
213 | | static int hf_spdy_header_name; |
214 | | static int hf_spdy_header_value; |
215 | | static int hf_spdy_streamid; |
216 | | static int hf_spdy_associated_streamid; |
217 | | static int hf_spdy_priority; |
218 | | static int hf_spdy_unused; |
219 | | static int hf_spdy_slot; |
220 | | static int hf_spdy_num_headers; |
221 | | static int hf_spdy_rst_stream_status; |
222 | | static int hf_spdy_num_settings; |
223 | | static int hf_spdy_setting; |
224 | | static int hf_spdy_setting_id; |
225 | | static int hf_spdy_setting_value; |
226 | | static int hf_spdy_ping_id; |
227 | | static int hf_spdy_goaway_last_good_stream_id; |
228 | | static int hf_spdy_goaway_status; |
229 | | static int hf_spdy_window_update_delta; |
230 | | |
231 | | static int ett_spdy; |
232 | | static int ett_spdy_flags; |
233 | | static int ett_spdy_header_block; |
234 | | static int ett_spdy_header; |
235 | | static int ett_spdy_setting; |
236 | | |
237 | | static int ett_spdy_encoded_entity; |
238 | | |
239 | | static expert_field ei_spdy_inflation_failed; |
240 | | static expert_field ei_spdy_mal_frame_data; |
241 | | static expert_field ei_spdy_mal_setting_frame; |
242 | | static expert_field ei_spdy_invalid_rst_stream; |
243 | | static expert_field ei_spdy_invalid_go_away; |
244 | | static expert_field ei_spdy_invalid_frame_type; |
245 | | static expert_field ei_spdy_reassembly_info; |
246 | | |
247 | | static dissector_handle_t media_handle; |
248 | | static dissector_handle_t spdy_handle; |
249 | | static dissector_table_t media_type_subdissector_table; |
250 | | static dissector_table_t port_subdissector_table; |
251 | | |
252 | | static bool spdy_assemble_entity_bodies = true; |
253 | | |
254 | | /* |
255 | | * Decompression of zlib encoded entities. |
256 | | */ |
257 | | #if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG) |
258 | | static bool spdy_decompress_body = true; |
259 | | static bool spdy_decompress_headers = true; |
260 | | #else |
261 | | static bool spdy_decompress_body; |
262 | | static bool spdy_decompress_headers; |
263 | | #endif |
264 | | |
265 | | #if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG) |
266 | | static const char spdy_dictionary[] = { |
267 | | 0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69, /* - - - - o p t i */ |
268 | | 0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68, /* o n s - - - - h */ |
269 | | 0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70, /* e a d - - - - p */ |
270 | | 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70, /* o s t - - - - p */ |
271 | | 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65, /* u t - - - - d e */ |
272 | | 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, /* l e t e - - - - */ |
273 | | 0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00, /* t r a c e - - - */ |
274 | | 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00, /* - a c c e p t - */ |
275 | | 0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, /* - - - a c c e p */ |
276 | | 0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, /* t - c h a r s e */ |
277 | | 0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, /* t - - - - a c c */ |
278 | | 0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, /* e p t - e n c o */ |
279 | | 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f, /* d i n g - - - - */ |
280 | | 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, /* a c c e p t - l */ |
281 | | 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, /* a n g u a g e - */ |
282 | | 0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, /* - - - a c c e p */ |
283 | | 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, /* t - r a n g e s */ |
284 | | 0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00, /* - - - - a g e - */ |
285 | | 0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, /* - - - a l l o w */ |
286 | | 0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68, /* - - - - a u t h */ |
287 | | 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, /* o r i z a t i o */ |
288 | | 0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63, /* n - - - - c a c */ |
289 | | 0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, /* h e - c o n t r */ |
290 | | 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, /* o l - - - - c o */ |
291 | | 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, /* n n e c t i o n */ |
292 | | 0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, /* - - - - c o n t */ |
293 | | 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, /* e n t - b a s e */ |
294 | | 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, /* - - - - c o n t */ |
295 | | 0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, /* e n t - e n c o */ |
296 | | 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, /* d i n g - - - - */ |
297 | | 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, /* c o n t e n t - */ |
298 | | 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, /* l a n g u a g e */ |
299 | | 0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74, /* - - - - c o n t */ |
300 | | 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, /* e n t - l e n g */ |
301 | | 0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, /* t h - - - - c o */ |
302 | | 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, /* n t e n t - l o */ |
303 | | 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, /* c a t i o n - - */ |
304 | | 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, /* - - c o n t e n */ |
305 | | 0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00, /* t - m d 5 - - - */ |
306 | | 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, /* - c o n t e n t */ |
307 | | 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, /* - r a n g e - - */ |
308 | | 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, /* - - c o n t e n */ |
309 | | 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, /* t - t y p e - - */ |
310 | | 0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, /* - - d a t e - - */ |
311 | | 0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00, /* - - e t a g - - */ |
312 | | 0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, /* - - e x p e c t */ |
313 | | 0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69, /* - - - - e x p i */ |
314 | | 0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66, /* r e s - - - - f */ |
315 | | 0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68, /* r o m - - - - h */ |
316 | | 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69, /* o s t - - - - i */ |
317 | | 0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, /* f - m a t c h - */ |
318 | | 0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f, /* - - - i f - m o */ |
319 | | 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, /* d i f i e d - s */ |
320 | | 0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, /* i n c e - - - - */ |
321 | | 0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d, /* i f - n o n e - */ |
322 | | 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, /* m a t c h - - - */ |
323 | | 0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67, /* - i f - r a n g */ |
324 | | 0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d, /* e - - - - i f - */ |
325 | | 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, /* u n m o d i f i */ |
326 | | 0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, /* e d - s i n c e */ |
327 | | 0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74, /* - - - - l a s t */ |
328 | | 0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, /* - m o d i f i e */ |
329 | | 0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63, /* d - - - - l o c */ |
330 | | 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, /* a t i o n - - - */ |
331 | | 0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72, /* - m a x - f o r */ |
332 | | 0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00, /* w a r d s - - - */ |
333 | | 0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00, /* - p r a g m a - */ |
334 | | 0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79, /* - - - p r o x y */ |
335 | | 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, /* - a u t h e n t */ |
336 | | 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, /* i c a t e - - - */ |
337 | | 0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, /* - p r o x y - a */ |
338 | | 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, /* u t h o r i z a */ |
339 | | 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05, /* t i o n - - - - */ |
340 | | 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, /* r a n g e - - - */ |
341 | | 0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, /* - r e f e r e r */ |
342 | | 0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72, /* - - - - r e t r */ |
343 | | 0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00, /* y - a f t e r - */ |
344 | | 0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, /* - - - s e r v e */ |
345 | | 0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00, /* r - - - - t e - */ |
346 | | 0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, /* - - - t r a i l */ |
347 | | 0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72, /* e r - - - - t r */ |
348 | | 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, /* a n s f e r - e */ |
349 | | 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, /* n c o d i n g - */ |
350 | | 0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, /* - - - u p g r a */ |
351 | | 0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73, /* d e - - - - u s */ |
352 | | 0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, /* e r - a g e n t */ |
353 | | 0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79, /* - - - - v a r y */ |
354 | | 0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00, /* - - - - v i a - */ |
355 | | 0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, /* - - - w a r n i */ |
356 | | 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77, /* n g - - - - w w */ |
357 | | 0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, /* w - a u t h e n */ |
358 | | 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, /* t i c a t e - - */ |
359 | | 0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, /* - - m e t h o d */ |
360 | | 0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00, /* - - - - g e t - */ |
361 | | 0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, /* - - - s t a t u */ |
362 | | 0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30, /* s - - - - 2 0 0 */ |
363 | | 0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76, /* - O K - - - - v */ |
364 | | 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, /* e r s i o n - - */ |
365 | | 0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, /* - - H T T P - 1 */ |
366 | | 0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, /* - 1 - - - - u r */ |
367 | | 0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62, /* l - - - - p u b */ |
368 | | 0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73, /* l i c - - - - s */ |
369 | | 0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69, /* e t - c o o k i */ |
370 | | 0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65, /* e - - - - k e e */ |
371 | | 0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00, /* p - a l i v e - */ |
372 | | 0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, /* - - - o r i g i */ |
373 | | 0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32, /* n 1 0 0 1 0 1 2 */ |
374 | | 0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35, /* 0 1 2 0 2 2 0 5 */ |
375 | | 0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30, /* 2 0 6 3 0 0 3 0 */ |
376 | | 0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33, /* 2 3 0 3 3 0 4 3 */ |
377 | | 0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37, /* 0 5 3 0 6 3 0 7 */ |
378 | | 0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30, /* 4 0 2 4 0 5 4 0 */ |
379 | | 0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34, /* 6 4 0 7 4 0 8 4 */ |
380 | | 0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31, /* 0 9 4 1 0 4 1 1 */ |
381 | | 0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31, /* 4 1 2 4 1 3 4 1 */ |
382 | | 0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34, /* 4 4 1 5 4 1 6 4 */ |
383 | | 0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34, /* 1 7 5 0 2 5 0 4 */ |
384 | | 0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e, /* 5 0 5 2 0 3 - N */ |
385 | | 0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f, /* o n - A u t h o */ |
386 | | 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, /* r i t a t i v e */ |
387 | | 0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, /* - I n f o r m a */ |
388 | | 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20, /* t i o n 2 0 4 - */ |
389 | | 0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, /* N o - C o n t e */ |
390 | | 0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f, /* n t 3 0 1 - M o */ |
391 | | 0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d, /* v e d - P e r m */ |
392 | | 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34, /* a n e n t l y 4 */ |
393 | | 0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52, /* 0 0 - B a d - R */ |
394 | | 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30, /* e q u e s t 4 0 */ |
395 | | 0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, /* 1 - U n a u t h */ |
396 | | 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30, /* o r i z e d 4 0 */ |
397 | | 0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64, /* 3 - F o r b i d */ |
398 | | 0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e, /* d e n 4 0 4 - N */ |
399 | | 0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, /* o t - F o u n d */ |
400 | | 0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65, /* 5 0 0 - I n t e */ |
401 | | 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, /* r n a l - S e r */ |
402 | | 0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f, /* v e r - E r r o */ |
403 | | 0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74, /* r 5 0 1 - N o t */ |
404 | | 0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, /* - I m p l e m e */ |
405 | | 0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20, /* n t e d 5 0 3 - */ |
406 | | 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, /* S e r v i c e - */ |
407 | | 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, /* U n a v a i l a */ |
408 | | 0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46, /* b l e J a n - F */ |
409 | | 0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41, /* e b - M a r - A */ |
410 | | 0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a, /* p r - M a y - J */ |
411 | | 0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41, /* u n - J u l - A */ |
412 | | 0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20, /* u g - S e p t - */ |
413 | | 0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20, /* O c t - N o v - */ |
414 | | 0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30, /* D e c - 0 0 - 0 */ |
415 | | 0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e, /* 0 - 0 0 - M o n */ |
416 | | 0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57, /* - - T u e - - W */ |
417 | | 0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c, /* e d - - T h u - */ |
418 | | 0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61, /* - F r i - - S a */ |
419 | | 0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20, /* t - - S u n - - */ |
420 | | 0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b, /* G M T c h u n k */ |
421 | | 0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, /* e d - t e x t - */ |
422 | | 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61, /* h t m l - i m a */ |
423 | | 0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69, /* g e - p n g - i */ |
424 | | 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67, /* m a g e - j p g */ |
425 | | 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, /* - i m a g e - g */ |
426 | | 0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, /* i f - a p p l i */ |
427 | | 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, /* c a t i o n - x */ |
428 | | 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, /* m l - a p p l i */ |
429 | | 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, /* c a t i o n - x */ |
430 | | 0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, /* h t m l - x m l */ |
431 | | 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, /* - t e x t - p l */ |
432 | | 0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74, /* a i n - t e x t */ |
433 | | 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, /* - j a v a s c r */ |
434 | | 0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c, /* i p t - p u b l */ |
435 | | 0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, /* i c p r i v a t */ |
436 | | 0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, /* e m a x - a g e */ |
437 | | 0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65, /* - g z i p - d e */ |
438 | | 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, /* f l a t e - s d */ |
439 | | 0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, /* c h c h a r s e */ |
440 | | 0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63, /* t - u t f - 8 c */ |
441 | | 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69, /* h a r s e t - i */ |
442 | | 0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, /* s o - 8 8 5 9 - */ |
443 | | 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a, /* 1 - u t f - - - */ |
444 | | 0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e /* - e n q - 0 - */ |
445 | | }; |
446 | | |
447 | | /* callback function used at the end of file-scope to cleanup zlib's inflate |
448 | | * streams to avoid memory leaks. |
449 | | * XXX: can we be more aggressive and call this sooner for finished streams? |
450 | | */ |
451 | | static bool inflate_end_cb (wmem_allocator_t *allocator _U_, |
452 | 0 | wmem_cb_event_t event _U_, void *user_data) { |
453 | | #ifdef HAVE_ZLIBNG |
454 | | ZLIB_PREFIX(inflateEnd)((zng_streamp)user_data); |
455 | | #else |
456 | 0 | ZLIB_PREFIX(inflateEnd)((z_streamp)user_data); |
457 | 0 | #endif |
458 | 0 | return false; |
459 | 0 | } |
460 | | #endif |
461 | | |
462 | | /* |
463 | | * Protocol initialization |
464 | | */ |
465 | | static void |
466 | | spdy_init_protocol(void) |
467 | 14 | { |
468 | 14 | header_info_list = NULL; |
469 | 14 | } |
470 | | |
471 | | /* |
472 | | * Returns conversation data for a given packet. If conversation data can't be |
473 | | * found, creates and returns new conversation data. |
474 | | */ |
475 | 16 | static spdy_conv_t * get_or_create_spdy_conversation_data(packet_info *pinfo) { |
476 | 16 | conversation_t *conversation; |
477 | 16 | spdy_conv_t *conv_data; |
478 | 16 | #if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG) |
479 | 16 | int retcode; |
480 | 16 | #endif |
481 | | |
482 | 16 | conversation = find_or_create_conversation(pinfo); |
483 | | |
484 | | /* Retrieve information from conversation */ |
485 | 16 | conv_data = (spdy_conv_t *)conversation_get_proto_data(conversation, proto_spdy); |
486 | 16 | if (!conv_data) { |
487 | | /* Set up the conversation structure itself */ |
488 | 3 | conv_data = wmem_new0(wmem_file_scope(), spdy_conv_t); |
489 | | |
490 | 3 | conv_data->streams = NULL; |
491 | 3 | if (spdy_decompress_headers) { |
492 | 3 | #if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG) |
493 | 3 | conv_data->rqst_decompressor = wmem_new0(wmem_file_scope(), zlib_stream); |
494 | 3 | conv_data->rply_decompressor = wmem_new0(wmem_file_scope(), zlib_stream); |
495 | 3 | retcode = ZLIB_PREFIX(inflateInit)(conv_data->rqst_decompressor); |
496 | 3 | if (retcode == Z_OK) { |
497 | 3 | wmem_register_callback(wmem_file_scope(), inflate_end_cb, |
498 | 3 | conv_data->rqst_decompressor); |
499 | 3 | retcode = ZLIB_PREFIX(inflateInit)(conv_data->rply_decompressor); |
500 | 3 | if (retcode == Z_OK) { |
501 | 3 | wmem_register_callback(wmem_file_scope(), inflate_end_cb, |
502 | 3 | conv_data->rply_decompressor); |
503 | 3 | } |
504 | 3 | } |
505 | | |
506 | | /* XXX - use wsutil/adler32.h? */ |
507 | 3 | conv_data->dictionary_id = ZLIB_PREFIX(adler32)(0L, Z_NULL, 0); |
508 | 3 | conv_data->dictionary_id = ZLIB_PREFIX(adler32)(conv_data->dictionary_id, |
509 | 3 | spdy_dictionary, |
510 | 3 | (uInt)sizeof(spdy_dictionary)); |
511 | 3 | #endif |
512 | 3 | } |
513 | | |
514 | 3 | conversation_add_proto_data(conversation, proto_spdy, conv_data); |
515 | 3 | } |
516 | | |
517 | 16 | return conv_data; |
518 | 16 | } |
519 | | |
520 | | /* |
521 | | * Retains state on a given stream. |
522 | | */ |
523 | | static void spdy_save_stream_info(spdy_conv_t *conv_data, |
524 | | uint32_t stream_id, |
525 | | media_container_type_t container_type, |
526 | | char *content_type, |
527 | | char *content_type_params, |
528 | 0 | char *content_encoding) { |
529 | 0 | spdy_stream_info_t *si; |
530 | |
|
531 | 0 | if (conv_data->streams == NULL) { |
532 | 0 | conv_data->streams = wmem_tree_new(wmem_file_scope()); |
533 | 0 | } |
534 | |
|
535 | 0 | si = wmem_new(wmem_file_scope(), spdy_stream_info_t); |
536 | 0 | si->container_type = container_type; |
537 | 0 | si->content_type = content_type; |
538 | 0 | si->content_type_parameters = content_type_params; |
539 | 0 | si->content_encoding = content_encoding; |
540 | 0 | si->data_frames = wmem_list_new(wmem_file_scope()); |
541 | 0 | si->num_data_frames = 0; |
542 | 0 | si->assembled_data = NULL; |
543 | 0 | wmem_tree_insert32(conv_data->streams, stream_id, si); |
544 | 0 | } |
545 | | |
546 | | /* |
547 | | * Retrieves previously saved state on a given stream. |
548 | | */ |
549 | | static spdy_stream_info_t* spdy_get_stream_info(spdy_conv_t *conv_data, |
550 | | uint32_t stream_id) |
551 | 11 | { |
552 | 11 | if (conv_data->streams == NULL) |
553 | 11 | return NULL; |
554 | | |
555 | 0 | return (spdy_stream_info_t*)wmem_tree_lookup32(conv_data->streams, stream_id); |
556 | 11 | } |
557 | | |
558 | | /* |
559 | | * Adds a data chunk to a given SPDY conversation/stream. |
560 | | */ |
561 | | static void spdy_add_data_chunk(spdy_conv_t *conv_data, |
562 | | uint32_t stream_id, |
563 | | uint32_t frame, |
564 | | uint8_t *data, |
565 | | uint32_t length) |
566 | 0 | { |
567 | 0 | spdy_stream_info_t *si = spdy_get_stream_info(conv_data, stream_id); |
568 | |
|
569 | 0 | if (si != NULL) { |
570 | 0 | spdy_data_frame_t *df = (spdy_data_frame_t *)wmem_new(wmem_file_scope(), spdy_data_frame_t); |
571 | 0 | df->data = data; |
572 | 0 | df->length = length; |
573 | 0 | df->framenum = frame; |
574 | 0 | wmem_list_append(si->data_frames, df); |
575 | 0 | ++si->num_data_frames; |
576 | 0 | } |
577 | 0 | } |
578 | | |
579 | | /* |
580 | | * Increment the count of DATA frames found on a given stream. |
581 | | */ |
582 | | static void spdy_increment_data_chunk_count(spdy_conv_t *conv_data, |
583 | 0 | uint32_t stream_id) { |
584 | 0 | spdy_stream_info_t *si = spdy_get_stream_info(conv_data, stream_id); |
585 | 0 | if (si != NULL) { |
586 | 0 | ++si->num_data_frames; |
587 | 0 | } |
588 | 0 | } |
589 | | |
590 | | /* |
591 | | * Return the number of data frames saved so far for the specified stream. |
592 | | */ |
593 | | static unsigned spdy_get_num_data_frames(spdy_conv_t *conv_data, |
594 | 11 | uint32_t stream_id) { |
595 | 11 | spdy_stream_info_t *si = spdy_get_stream_info(conv_data, stream_id); |
596 | | |
597 | 11 | return si == NULL ? 0 : si->num_data_frames; |
598 | 11 | } |
599 | | |
600 | | /* |
601 | | * Reassembles DATA frames for a given stream into one tvb. |
602 | | */ |
603 | | static spdy_stream_info_t* spdy_assemble_data_frames(spdy_conv_t *conv_data, |
604 | 0 | uint32_t stream_id) { |
605 | 0 | spdy_stream_info_t *si = spdy_get_stream_info(conv_data, stream_id); |
606 | 0 | tvbuff_t *tvb; |
607 | |
|
608 | 0 | if (si == NULL) { |
609 | 0 | return NULL; |
610 | 0 | } |
611 | | |
612 | | /* |
613 | | * Compute the total amount of data and concatenate the |
614 | | * data chunks, if it hasn't already been done. |
615 | | */ |
616 | 0 | if (si->assembled_data == NULL) { |
617 | 0 | spdy_data_frame_t *df; |
618 | 0 | uint8_t *data; |
619 | 0 | uint32_t datalen; |
620 | 0 | uint32_t offset; |
621 | 0 | wmem_list_t *dflist = si->data_frames; |
622 | 0 | wmem_list_frame_t *frame; |
623 | 0 | if (wmem_list_count(dflist) == 0) { |
624 | 0 | return si; |
625 | 0 | } |
626 | 0 | datalen = 0; |
627 | | /* |
628 | | * It'd be nice to use a composite tvbuff here, but since |
629 | | * only a real-data tvbuff can be the child of another |
630 | | * tvb, we can't. It would be nice if this limitation |
631 | | * could be fixed. |
632 | | */ |
633 | 0 | frame = wmem_list_frame_next(wmem_list_head(dflist)); |
634 | 0 | while (frame != NULL) { |
635 | 0 | df = (spdy_data_frame_t *)wmem_list_frame_data(frame); |
636 | 0 | datalen += df->length; |
637 | 0 | frame = wmem_list_frame_next(frame); |
638 | 0 | } |
639 | 0 | if (datalen != 0) { |
640 | 0 | data = (uint8_t *)wmem_alloc(wmem_file_scope(), datalen); |
641 | 0 | dflist = si->data_frames; |
642 | 0 | offset = 0; |
643 | 0 | frame = wmem_list_frame_next(wmem_list_head(dflist)); |
644 | 0 | while (frame != NULL) { |
645 | 0 | df = (spdy_data_frame_t *)wmem_list_frame_data(frame); |
646 | 0 | memcpy(data+offset, df->data, df->length); |
647 | 0 | offset += df->length; |
648 | 0 | frame = wmem_list_frame_next(frame); |
649 | 0 | } |
650 | 0 | tvb = tvb_new_real_data(data, datalen, datalen); |
651 | 0 | si->assembled_data = tvb; |
652 | 0 | } |
653 | 0 | } |
654 | 0 | return si; |
655 | 0 | } |
656 | | |
657 | | /* |
658 | | * Same as dissect_spdy_stream_id below, except with explicit field index. |
659 | | */ |
660 | | static void dissect_spdy_stream_id_field(tvbuff_t *tvb, |
661 | | int offset, |
662 | | packet_info *pinfo _U_, |
663 | | proto_tree *frame_tree, |
664 | | const int hfindex) |
665 | 0 | { |
666 | 0 | uint32_t stream_id = tvb_get_ntohl(tvb, offset) & SPDY_STREAM_ID_MASK; |
667 | | |
668 | | /* Add stream id to tree. */ |
669 | 0 | proto_tree_add_item(frame_tree, hfindex, tvb, offset, 4, ENC_BIG_ENDIAN); |
670 | |
|
671 | 0 | if (hfindex == hf_spdy_streamid) { |
672 | 0 | proto_item_append_text(frame_tree, ", Stream: %u", stream_id); |
673 | 0 | } |
674 | 0 | } |
675 | | |
676 | | /* |
677 | | * Adds flag details to proto tree. |
678 | | */ |
679 | | static void dissect_spdy_flags(tvbuff_t *tvb, |
680 | | int offset, |
681 | | proto_tree *frame_tree, |
682 | 15 | const spdy_control_frame_info_t *frame) { |
683 | 15 | proto_item *flags_ti; |
684 | 15 | proto_tree *flags_tree; |
685 | | |
686 | | /* Create flags substree. */ |
687 | 15 | flags_ti = proto_tree_add_item(frame_tree, hf_spdy_flags, tvb, offset, 1, ENC_BIG_ENDIAN); |
688 | 15 | flags_tree = proto_item_add_subtree(flags_ti, ett_spdy_flags); |
689 | | |
690 | | /* Add FIN flag for appropriate frames. */ |
691 | 15 | if (frame->type == SPDY_DATA || |
692 | 15 | frame->type == SPDY_SYN_STREAM || |
693 | 15 | frame->type == SPDY_SYN_REPLY || |
694 | 15 | frame->type == SPDY_HEADERS) { |
695 | | /* Add FIN flag. */ |
696 | 15 | proto_tree_add_item(flags_tree, hf_spdy_flags_fin, tvb, offset, 1, ENC_BIG_ENDIAN); |
697 | 15 | if (frame->flags & SPDY_FLAG_FIN) { |
698 | 1 | proto_item_append_text(frame_tree, " (FIN)"); |
699 | 1 | proto_item_append_text(flags_ti, " (FIN)"); |
700 | 1 | } |
701 | 15 | } |
702 | | |
703 | | /* Add UNIDIRECTIONAL flag, only applicable for SYN_STREAM. */ |
704 | 15 | if (frame->type == SPDY_SYN_STREAM) { |
705 | 0 | proto_tree_add_item(flags_tree, hf_spdy_flags_unidirectional, tvb, |
706 | 0 | offset, 1, ENC_BIG_ENDIAN); |
707 | 0 | if (frame->flags & SPDY_FLAG_UNIDIRECTIONAL) { |
708 | 0 | proto_item_append_text(flags_ti, " (UNIDIRECTIONAL)"); |
709 | 0 | } |
710 | 0 | } |
711 | | |
712 | | /* Add CLEAR_SETTINGS flag, only applicable for SETTINGS. */ |
713 | 15 | if (frame->type == SPDY_SETTINGS) { |
714 | 0 | proto_tree_add_item(flags_tree, hf_spdy_flags_clear_settings, tvb, |
715 | 0 | offset, 1, ENC_BIG_ENDIAN); |
716 | 0 | if (frame->flags & SPDY_FLAG_SETTINGS_CLEAR_SETTINGS) { |
717 | 0 | proto_item_append_text(flags_ti, " (CLEAR)"); |
718 | 0 | } |
719 | 0 | } |
720 | 15 | } |
721 | | |
722 | | /* |
723 | | * Performs DATA frame payload dissection. |
724 | | */ |
725 | | static int dissect_spdy_data_payload(tvbuff_t *tvb, |
726 | | int offset, |
727 | | packet_info *pinfo, |
728 | | proto_tree *top_level_tree _U_, |
729 | | proto_tree *spdy_tree, |
730 | | proto_item *spdy_proto, |
731 | | spdy_conv_t *conv_data, |
732 | | uint32_t stream_id, |
733 | | const spdy_control_frame_info_t *frame) |
734 | 15 | { |
735 | 15 | dissector_handle_t handle; |
736 | 15 | unsigned num_data_frames; |
737 | 15 | bool dissected; |
738 | 15 | media_content_info_t content_info; |
739 | | |
740 | | /* Add frame description. */ |
741 | 15 | proto_item_append_text(spdy_proto, ", Stream: %d, Length: %d", |
742 | 15 | stream_id, |
743 | 15 | frame->length); |
744 | | |
745 | | /* Add data. */ |
746 | 15 | proto_tree_add_item(spdy_tree, hf_spdy_data, tvb, offset, frame->length, ENC_NA); |
747 | | |
748 | 15 | num_data_frames = spdy_get_num_data_frames(conv_data, stream_id); |
749 | 15 | if (frame->length != 0 || num_data_frames != 0) { |
750 | | /* |
751 | | * There's stuff left over; process it. |
752 | | */ |
753 | 0 | tvbuff_t *next_tvb = NULL; |
754 | 0 | tvbuff_t *data_tvb = NULL; |
755 | 0 | spdy_stream_info_t *si = NULL; |
756 | 0 | uint8_t *copied_data; |
757 | 0 | bool is_single_chunk = false; |
758 | 0 | bool have_entire_body; |
759 | 0 | char *media_str = NULL; |
760 | | |
761 | | /* |
762 | | * Create a tvbuff for the payload. |
763 | | */ |
764 | 0 | if (frame->length != 0) { |
765 | 0 | next_tvb = tvb_new_subset_length(tvb, offset, frame->length); |
766 | 0 | is_single_chunk = num_data_frames == 0 && |
767 | 0 | (frame->flags & SPDY_FLAG_FIN) != 0; |
768 | 0 | if (!pinfo->fd->visited) { |
769 | 0 | if (!is_single_chunk) { |
770 | 0 | if (spdy_assemble_entity_bodies) { |
771 | 0 | copied_data = (uint8_t *)tvb_memdup(wmem_file_scope(),next_tvb, 0, frame->length); |
772 | 0 | spdy_add_data_chunk(conv_data, stream_id, pinfo->num, copied_data, frame->length); |
773 | 0 | } else { |
774 | 0 | spdy_increment_data_chunk_count(conv_data, stream_id); |
775 | 0 | } |
776 | 0 | } |
777 | 0 | } |
778 | 0 | } else { |
779 | 0 | is_single_chunk = (num_data_frames == 1); |
780 | 0 | } |
781 | |
|
782 | 0 | if (!(frame->flags & SPDY_FLAG_FIN)) { |
783 | 0 | col_set_fence(pinfo->cinfo, COL_INFO); |
784 | 0 | proto_item_append_text(spdy_proto, " (partial entity body)"); |
785 | | /* would like the proto item to say */ |
786 | | /* " (entity body fragment N of M)" */ |
787 | 0 | goto body_dissected; |
788 | 0 | } |
789 | 0 | have_entire_body = is_single_chunk; |
790 | | /* |
791 | | * On seeing the last data frame in a stream, we can |
792 | | * reassemble the frames into one data block. |
793 | | */ |
794 | 0 | si = spdy_assemble_data_frames(conv_data, stream_id); |
795 | 0 | if (si == NULL) { |
796 | 0 | goto body_dissected; |
797 | 0 | } |
798 | 0 | data_tvb = si->assembled_data; |
799 | 0 | if (spdy_assemble_entity_bodies) { |
800 | 0 | have_entire_body = true; |
801 | 0 | } |
802 | |
|
803 | 0 | if (!have_entire_body) { |
804 | 0 | goto body_dissected; |
805 | 0 | } |
806 | | |
807 | 0 | if (data_tvb == NULL) { |
808 | 0 | if (next_tvb == NULL) |
809 | 0 | goto body_dissected; |
810 | 0 | data_tvb = next_tvb; |
811 | 0 | } else { |
812 | 0 | add_new_data_source(pinfo, data_tvb, "Assembled entity body"); |
813 | 0 | } |
814 | | |
815 | 0 | if (have_entire_body && si->content_encoding != NULL && |
816 | 0 | g_ascii_strcasecmp(si->content_encoding, "identity") != 0) { |
817 | | /* |
818 | | * We currently can't handle, for example, "compress"; |
819 | | * just handle them as data for now. |
820 | | * |
821 | | * After July 7, 2004 the LZW patent expires, so support |
822 | | * might be added then. However, I don't think that |
823 | | * anybody ever really implemented "compress", due to |
824 | | * the aforementioned patent. |
825 | | */ |
826 | 0 | tvbuff_t *uncomp_tvb = NULL; |
827 | 0 | proto_item *e_ti = NULL; |
828 | 0 | proto_tree *e_tree = NULL; |
829 | |
|
830 | 0 | if (spdy_decompress_body && |
831 | 0 | (g_ascii_strcasecmp(si->content_encoding, "gzip") == 0 || |
832 | 0 | g_ascii_strcasecmp(si->content_encoding, "deflate") == 0)) { |
833 | 0 | uncomp_tvb = tvb_child_uncompress_zlib(tvb, data_tvb, 0, |
834 | 0 | tvb_reported_length(data_tvb)); |
835 | 0 | } |
836 | | /* |
837 | | * Add the encoded entity to the protocol tree |
838 | | */ |
839 | 0 | e_tree = proto_tree_add_subtree_format(spdy_tree, data_tvb, |
840 | 0 | 0, tvb_reported_length(data_tvb), ett_spdy_encoded_entity, &e_ti, |
841 | 0 | "Content-encoded entity body (%s): %u bytes", |
842 | 0 | si->content_encoding, |
843 | 0 | tvb_reported_length(data_tvb)); |
844 | 0 | if (si->num_data_frames > 1) { |
845 | 0 | wmem_list_t *dflist = si->data_frames; |
846 | 0 | wmem_list_frame_t *frame_item; |
847 | 0 | spdy_data_frame_t *df; |
848 | 0 | uint32_t framenum = 0; |
849 | 0 | wmem_strbuf_t *str_frames = wmem_strbuf_new(pinfo->pool, ""); |
850 | |
|
851 | 0 | frame_item = wmem_list_frame_next(wmem_list_head(dflist)); |
852 | 0 | while (frame_item != NULL) { |
853 | 0 | df = (spdy_data_frame_t *)wmem_list_frame_data(frame_item); |
854 | 0 | if (framenum != df->framenum) { |
855 | 0 | wmem_strbuf_append_printf(str_frames, " #%u", df->framenum); |
856 | 0 | framenum = df->framenum; |
857 | 0 | } |
858 | 0 | frame_item = wmem_list_frame_next(frame_item); |
859 | 0 | } |
860 | |
|
861 | 0 | proto_tree_add_expert_format(e_tree, pinfo, &ei_spdy_reassembly_info, data_tvb, 0, |
862 | 0 | tvb_reported_length(data_tvb), |
863 | 0 | "Assembled from %d frames in packet(s)%s", |
864 | 0 | si->num_data_frames, wmem_strbuf_get_str(str_frames)); |
865 | 0 | } |
866 | |
|
867 | 0 | if (uncomp_tvb != NULL) { |
868 | | /* |
869 | | * Decompression worked |
870 | | */ |
871 | | |
872 | | /* XXX - Don't free this, since it's possible |
873 | | * that the data was only partially |
874 | | * decompressed, such as when desegmentation |
875 | | * isn't enabled. |
876 | | * |
877 | | tvb_free(next_tvb); |
878 | | */ |
879 | 0 | proto_item_append_text(e_ti, " -> %u bytes", tvb_reported_length(uncomp_tvb)); |
880 | 0 | data_tvb = uncomp_tvb; |
881 | 0 | add_new_data_source(pinfo, data_tvb, "Uncompressed entity body"); |
882 | 0 | } else { |
883 | 0 | if (spdy_decompress_body) { |
884 | 0 | proto_item_append_text(e_ti, " [Error: Decompression failed]"); |
885 | 0 | } |
886 | 0 | call_data_dissector(data_tvb, pinfo, e_tree); |
887 | |
|
888 | 0 | goto body_dissected; |
889 | 0 | } |
890 | 0 | } |
891 | | |
892 | | /* |
893 | | * Do subdissector checks. |
894 | | * |
895 | | * First, check whether some subdissector asked that they |
896 | | * be called if something was on some particular port. |
897 | | */ |
898 | | |
899 | 0 | if (have_entire_body && port_subdissector_table != NULL) { |
900 | 0 | handle = dissector_get_uint_handle(port_subdissector_table, |
901 | 0 | pinfo->match_uint); |
902 | 0 | } else { |
903 | 0 | handle = NULL; |
904 | 0 | } |
905 | 0 | if (handle == NULL && have_entire_body && si->content_type != NULL && |
906 | 0 | media_type_subdissector_table != NULL) { |
907 | | /* |
908 | | * We didn't find any subdissector that |
909 | | * registered for the port, and we have a |
910 | | * Content-Type value. Is there any subdissector |
911 | | * for that content type? |
912 | | */ |
913 | 0 | if (si->content_type_parameters) { |
914 | 0 | media_str = wmem_strdup(pinfo->pool, si->content_type_parameters); |
915 | 0 | } |
916 | | /* |
917 | | * Calling the string handle for the media type |
918 | | * dissector table will set pinfo->match_string |
919 | | * to si->content_type for us. |
920 | | */ |
921 | 0 | pinfo->match_string = si->content_type; |
922 | 0 | handle = dissector_get_string_handle(media_type_subdissector_table, |
923 | 0 | si->content_type); |
924 | 0 | } |
925 | 0 | content_info.type = si->container_type; |
926 | 0 | content_info.media_str = media_str; |
927 | 0 | content_info.data = NULL; |
928 | 0 | if (handle != NULL) { |
929 | | /* |
930 | | * We have a subdissector - call it. |
931 | | */ |
932 | 0 | dissected = call_dissector_with_data(handle, data_tvb, pinfo, spdy_tree, &content_info); |
933 | 0 | } else { |
934 | 0 | dissected = false; |
935 | 0 | } |
936 | |
|
937 | 0 | if (!dissected && have_entire_body && si->content_type != NULL) { |
938 | | /* |
939 | | * Calling the default media handle if there is a content-type that |
940 | | * wasn't handled above. |
941 | | */ |
942 | 0 | call_dissector_with_data(media_handle, next_tvb, pinfo, spdy_tree, &content_info); |
943 | 0 | } else { |
944 | | /* Call the default data dissector */ |
945 | 0 | call_data_dissector(next_tvb, pinfo, spdy_tree); |
946 | 0 | } |
947 | |
|
948 | 0 | body_dissected: |
949 | | /* |
950 | | * We've processed frame->length bytes worth of data |
951 | | * (which may be no data at all); advance the |
952 | | * offset past whatever data we've processed. |
953 | | */ |
954 | 0 | ; |
955 | 0 | } |
956 | 15 | return frame->length; |
957 | 15 | } |
958 | | |
959 | | #if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG) |
960 | | /* |
961 | | * Performs header decompression. |
962 | | * |
963 | | * The returned buffer is automatically scoped to the lifetime of the capture |
964 | | * (via wmem_memdup() with file scope). |
965 | | */ |
966 | 0 | #define DECOMPRESS_BUFSIZE 16384 |
967 | | |
968 | | static uint8_t* spdy_decompress_header_block(tvbuff_t *tvb, |
969 | | packet_info *pinfo, |
970 | | #ifdef HAVE_ZLIBNG |
971 | | zng_streamp decomp, |
972 | | #else |
973 | | z_streamp decomp, |
974 | | #endif |
975 | | uLong dictionary_id, |
976 | | int offset, |
977 | | uint32_t length, |
978 | 0 | unsigned *uncomp_length) { |
979 | 0 | int retcode; |
980 | 0 | const uint8_t *hptr = tvb_get_ptr(tvb, offset, length); |
981 | 0 | uint8_t *uncomp_block = (uint8_t *)wmem_alloc(pinfo->pool, DECOMPRESS_BUFSIZE); |
982 | |
|
983 | 0 | #ifdef z_const |
984 | 0 | decomp->next_in = (z_const Bytef *)hptr; |
985 | | #else |
986 | | DIAG_OFF(cast-qual) |
987 | | decomp->next_in = (Bytef *)hptr; |
988 | | DIAG_ON(cast-qual) |
989 | | #endif |
990 | 0 | decomp->avail_in = length; |
991 | 0 | decomp->next_out = uncomp_block; |
992 | 0 | decomp->avail_out = DECOMPRESS_BUFSIZE; |
993 | 0 | retcode = ZLIB_PREFIX(inflate)(decomp, Z_SYNC_FLUSH); |
994 | 0 | if (retcode == Z_NEED_DICT) { |
995 | 0 | if (decomp->adler == dictionary_id) { |
996 | 0 | retcode = ZLIB_PREFIX(inflateSetDictionary)(decomp, |
997 | 0 | spdy_dictionary, |
998 | 0 | sizeof(spdy_dictionary)); |
999 | 0 | if (retcode == Z_OK) { |
1000 | 0 | retcode = ZLIB_PREFIX(inflate)(decomp, Z_SYNC_FLUSH); |
1001 | 0 | } |
1002 | 0 | } |
1003 | 0 | } |
1004 | | |
1005 | | /* Handle errors. */ |
1006 | 0 | if (retcode != Z_OK) { |
1007 | 0 | return NULL; |
1008 | 0 | } |
1009 | | |
1010 | | /* Handle successful inflation. */ |
1011 | 0 | *uncomp_length = DECOMPRESS_BUFSIZE - decomp->avail_out; |
1012 | |
|
1013 | 0 | return (uint8_t *)wmem_memdup(wmem_file_scope(), uncomp_block, *uncomp_length); |
1014 | 0 | } |
1015 | | #endif |
1016 | | |
1017 | | |
1018 | | /* |
1019 | | * Saves state on header data for a given stream. |
1020 | | */ |
1021 | | static spdy_header_info_t* spdy_save_header_block(packet_info *pinfo _U_, |
1022 | | uint32_t stream_id, |
1023 | | uint16_t frame_type, |
1024 | | uint8_t *header, |
1025 | 0 | unsigned length) { |
1026 | 0 | spdy_header_info_t *header_info; |
1027 | |
|
1028 | 0 | if (header_info_list == NULL) |
1029 | 0 | header_info_list = wmem_list_new(wmem_file_scope()); |
1030 | |
|
1031 | 0 | header_info = wmem_new(wmem_file_scope(), spdy_header_info_t); |
1032 | 0 | header_info->stream_id = stream_id; |
1033 | 0 | header_info->header_block = header; |
1034 | 0 | header_info->header_block_len = length; |
1035 | 0 | header_info->frame_type = frame_type; |
1036 | 0 | wmem_list_append(header_info_list, header_info); |
1037 | 0 | return header_info; |
1038 | 0 | } |
1039 | | |
1040 | | /* |
1041 | | * Retrieves saved state for a given stream. |
1042 | | */ |
1043 | | static spdy_header_info_t* spdy_find_saved_header_block(packet_info *pinfo _U_, |
1044 | | uint32_t stream_id, |
1045 | 0 | uint16_t frame_type) { |
1046 | 0 | wmem_list_frame_t *frame; |
1047 | |
|
1048 | 0 | if ((header_info_list == NULL) || (wmem_list_head(header_info_list) == NULL)) |
1049 | 0 | return NULL; |
1050 | | |
1051 | 0 | frame = wmem_list_frame_next(wmem_list_head(header_info_list)); |
1052 | 0 | while (frame != NULL) { |
1053 | 0 | spdy_header_info_t *hi = (spdy_header_info_t *)wmem_list_frame_data(frame); |
1054 | 0 | if (hi->stream_id == stream_id && hi->frame_type == frame_type) |
1055 | 0 | return hi; |
1056 | 0 | frame = wmem_list_frame_next(frame); |
1057 | 0 | } |
1058 | 0 | return NULL; |
1059 | 0 | } |
1060 | | |
1061 | | /* |
1062 | | * Given a content type string that may contain optional parameters, |
1063 | | * return the parameter string, if any, otherwise return NULL. This |
1064 | | * also has the side effect of null terminating the content type |
1065 | | * part of the original string. |
1066 | | */ |
1067 | 0 | static char* spdy_parse_content_type(char *content_type) { |
1068 | 0 | char *cp = content_type; |
1069 | |
|
1070 | 0 | while (*cp != '\0' && *cp != ';' && !g_ascii_isspace(*cp)) { |
1071 | 0 | *cp = g_ascii_tolower(*cp); |
1072 | 0 | ++cp; |
1073 | 0 | } |
1074 | 0 | if (*cp == '\0') { |
1075 | 0 | cp = NULL; |
1076 | 0 | } |
1077 | |
|
1078 | 0 | if (cp != NULL) { |
1079 | 0 | *cp++ = '\0'; |
1080 | 0 | while (*cp == ';' || g_ascii_isspace(*cp)) { |
1081 | 0 | ++cp; |
1082 | 0 | } |
1083 | 0 | if (*cp != '\0') { |
1084 | 0 | return cp; |
1085 | 0 | } |
1086 | 0 | } |
1087 | 0 | return NULL; |
1088 | 0 | } |
1089 | | |
1090 | | static int dissect_spdy_header_payload( |
1091 | | tvbuff_t *tvb, |
1092 | | int offset, |
1093 | | packet_info *pinfo, |
1094 | | proto_tree *frame_tree, |
1095 | | const spdy_control_frame_info_t *frame, |
1096 | 0 | spdy_conv_t *conv_data) { |
1097 | 0 | uint32_t stream_id; |
1098 | 0 | int header_block_length = frame->length; |
1099 | 0 | int hdr_offset = 0; |
1100 | 0 | tvbuff_t *header_tvb = NULL; |
1101 | 0 | const char *hdr_method = NULL; |
1102 | 0 | const char *hdr_path = NULL; |
1103 | 0 | const char *hdr_version = NULL; |
1104 | 0 | const char *hdr_host = NULL; |
1105 | 0 | const char *hdr_scheme = NULL; |
1106 | 0 | const char *hdr_status = NULL; |
1107 | 0 | char *content_type = NULL; |
1108 | 0 | char *content_encoding = NULL; |
1109 | 0 | uint32_t num_headers = 0; |
1110 | 0 | proto_item *header_block_item; |
1111 | 0 | proto_tree *header_block_tree; |
1112 | | |
1113 | | /* Get stream id, which is present in all types of header frames. */ |
1114 | 0 | stream_id = tvb_get_ntohl(tvb, offset) & SPDY_STREAM_ID_MASK; |
1115 | 0 | dissect_spdy_stream_id_field(tvb, offset, pinfo, frame_tree, hf_spdy_streamid); |
1116 | 0 | offset += 4; |
1117 | | |
1118 | | /* Get SYN_STREAM-only fields. */ |
1119 | 0 | if (frame->type == SPDY_SYN_STREAM) { |
1120 | | /* Get associated stream ID. */ |
1121 | 0 | dissect_spdy_stream_id_field(tvb, offset, pinfo, frame_tree, hf_spdy_associated_streamid); |
1122 | 0 | offset += 4; |
1123 | | |
1124 | | /* Get priority */ |
1125 | 0 | proto_tree_add_item(frame_tree, hf_spdy_priority, tvb, offset, 2, ENC_BIG_ENDIAN); |
1126 | 0 | proto_tree_add_item(frame_tree, hf_spdy_unused, tvb, offset, 2, ENC_BIG_ENDIAN); |
1127 | 0 | proto_tree_add_item(frame_tree, hf_spdy_slot, tvb, offset, 2, ENC_BIG_ENDIAN); |
1128 | 0 | offset += 2; |
1129 | 0 | } |
1130 | | |
1131 | | |
1132 | | /* Get our header block length. */ |
1133 | 0 | switch (frame->type) { |
1134 | 0 | case SPDY_SYN_STREAM: |
1135 | 0 | header_block_length -= 10; |
1136 | 0 | break; |
1137 | 0 | case SPDY_SYN_REPLY: |
1138 | 0 | case SPDY_HEADERS: |
1139 | 0 | header_block_length -= 4; |
1140 | 0 | break; |
1141 | 0 | default: |
1142 | | /* Unhandled case. This should never happen. */ |
1143 | 0 | DISSECTOR_ASSERT_NOT_REACHED(); |
1144 | 0 | } |
1145 | | |
1146 | | /* Add the header block. */ |
1147 | 0 | header_block_item = proto_tree_add_item(frame_tree, |
1148 | 0 | hf_spdy_header_block, |
1149 | 0 | tvb, |
1150 | 0 | offset, |
1151 | 0 | header_block_length, |
1152 | 0 | ENC_NA); |
1153 | 0 | header_block_tree = proto_item_add_subtree(header_block_item, |
1154 | 0 | ett_spdy_header_block); |
1155 | | |
1156 | | /* Decompress header block as necessary. */ |
1157 | 0 | if (!spdy_decompress_headers) { |
1158 | 0 | header_tvb = tvb; |
1159 | 0 | hdr_offset = offset; |
1160 | 0 | } else { |
1161 | 0 | spdy_header_info_t *header_info; |
1162 | | |
1163 | | /* First attempt to find previously decompressed data. |
1164 | | * This will not work correctly for lower-level frames that contain more |
1165 | | * than one SPDY frame of the same type. We assume this to never be the |
1166 | | * case, though. */ |
1167 | 0 | header_info = spdy_find_saved_header_block(pinfo, |
1168 | 0 | stream_id, |
1169 | 0 | frame->type); |
1170 | | |
1171 | | /* Generate decompressed data and store it, since none was found. */ |
1172 | 0 | if (header_info == NULL) { |
1173 | 0 | uint8_t *uncomp_ptr = NULL; |
1174 | 0 | unsigned uncomp_length = 0; |
1175 | 0 | #if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG) |
1176 | | #ifdef HAVE_ZLIBNG |
1177 | | zng_streamp decomp; |
1178 | | #else |
1179 | 0 | z_streamp decomp; |
1180 | 0 | #endif |
1181 | | |
1182 | | /* Get our decompressor. */ |
1183 | 0 | if (stream_id % 2 == 0) { |
1184 | | /* Even streams are server-initiated and should never get a |
1185 | | * client-initiated header block. Use reply decompressor. */ |
1186 | 0 | decomp = conv_data->rply_decompressor; |
1187 | 0 | } else if (frame->type == SPDY_HEADERS) { |
1188 | | /* Odd streams are client-initiated, but may have HEADERS from either |
1189 | | * side. Currently, no known clients send HEADERS so we assume they are |
1190 | | * all from the server. */ |
1191 | 0 | decomp = conv_data->rply_decompressor; |
1192 | 0 | } else if (frame->type == SPDY_SYN_STREAM) { |
1193 | 0 | decomp = conv_data->rqst_decompressor; |
1194 | 0 | } else if (frame->type == SPDY_SYN_REPLY) { |
1195 | 0 | decomp = conv_data->rply_decompressor; |
1196 | 0 | } else { |
1197 | | /* Unhandled case. This should never happen. */ |
1198 | 0 | DISSECTOR_ASSERT_NOT_REACHED(); |
1199 | 0 | } |
1200 | | |
1201 | | /* Decompress. */ |
1202 | 0 | uncomp_ptr = spdy_decompress_header_block(tvb, |
1203 | 0 | pinfo, |
1204 | 0 | decomp, |
1205 | 0 | conv_data->dictionary_id, |
1206 | 0 | offset, |
1207 | 0 | header_block_length, |
1208 | 0 | &uncomp_length); |
1209 | | |
1210 | | /* Catch decompression failures. */ |
1211 | 0 | if (uncomp_ptr == NULL) { |
1212 | 0 | expert_add_info(pinfo, frame_tree, &ei_spdy_inflation_failed); |
1213 | |
|
1214 | 0 | proto_item_append_text(frame_tree, " [Error: Header decompression failed]"); |
1215 | 0 | return -1; |
1216 | 0 | } |
1217 | 0 | #endif |
1218 | | |
1219 | | /* Store decompressed data. */ |
1220 | 0 | header_info = spdy_save_header_block(pinfo, stream_id, frame->type, uncomp_ptr, uncomp_length); |
1221 | 0 | } |
1222 | | |
1223 | | /* Create a tvb containing the uncompressed data. */ |
1224 | 0 | header_tvb = tvb_new_child_real_data(tvb, header_info->header_block, |
1225 | 0 | header_info->header_block_len, |
1226 | 0 | header_info->header_block_len); |
1227 | 0 | add_new_data_source(pinfo, header_tvb, "Uncompressed headers"); |
1228 | 0 | hdr_offset = 0; |
1229 | 0 | } |
1230 | | |
1231 | | /* Get header block details. */ |
1232 | 0 | if (header_tvb == NULL || !spdy_decompress_headers) { |
1233 | 0 | num_headers = 0; |
1234 | 0 | } else { |
1235 | 0 | num_headers = tvb_get_ntohl(header_tvb, hdr_offset); |
1236 | 0 | /*ti = */ proto_tree_add_item(header_block_tree, |
1237 | 0 | hf_spdy_num_headers, |
1238 | 0 | header_tvb, |
1239 | 0 | hdr_offset, |
1240 | 0 | 4, |
1241 | 0 | ENC_BIG_ENDIAN); |
1242 | 0 | } |
1243 | 0 | hdr_offset += 4; |
1244 | | |
1245 | | /* Process headers. */ |
1246 | 0 | while (num_headers--) { |
1247 | 0 | char *header_name; |
1248 | 0 | const char *header_value; |
1249 | 0 | proto_tree *header_tree; |
1250 | 0 | proto_item *header; |
1251 | 0 | int header_name_offset; |
1252 | 0 | int header_value_offset; |
1253 | 0 | int header_name_length; |
1254 | 0 | int header_value_length; |
1255 | | |
1256 | | /* Get header name details. */ |
1257 | 0 | if (tvb_reported_length_remaining(header_tvb, hdr_offset) < 4) { |
1258 | 0 | expert_add_info_format(pinfo, frame_tree, &ei_spdy_mal_frame_data, |
1259 | 0 | "Not enough frame data for header name size."); |
1260 | 0 | break; |
1261 | 0 | } |
1262 | 0 | header_name_offset = hdr_offset; |
1263 | 0 | header_name_length = tvb_get_ntohl(header_tvb, hdr_offset); |
1264 | 0 | hdr_offset += 4; |
1265 | 0 | if (tvb_reported_length_remaining(header_tvb, hdr_offset) < header_name_length) { |
1266 | 0 | expert_add_info_format(pinfo, frame_tree, &ei_spdy_mal_frame_data, |
1267 | 0 | "Not enough frame data for header name."); |
1268 | 0 | break; |
1269 | 0 | } |
1270 | 0 | header_name = (char *)tvb_get_string_enc(pinfo->pool, header_tvb, |
1271 | 0 | hdr_offset, |
1272 | 0 | header_name_length, ENC_ASCII|ENC_NA); |
1273 | 0 | hdr_offset += header_name_length; |
1274 | | |
1275 | | /* Get header value details. */ |
1276 | 0 | if (tvb_reported_length_remaining(header_tvb, hdr_offset) < 4) { |
1277 | 0 | expert_add_info_format(pinfo, frame_tree, &ei_spdy_mal_frame_data, |
1278 | 0 | "Not enough frame data for header value size."); |
1279 | 0 | break; |
1280 | 0 | } |
1281 | 0 | header_value_offset = hdr_offset; |
1282 | 0 | header_value_length = tvb_get_ntohl(header_tvb, hdr_offset); |
1283 | 0 | hdr_offset += 4; |
1284 | 0 | if (tvb_reported_length_remaining(header_tvb, hdr_offset) < header_value_length) { |
1285 | 0 | expert_add_info_format(pinfo, frame_tree, &ei_spdy_mal_frame_data, |
1286 | 0 | "Not enough frame data for header value."); |
1287 | 0 | break; |
1288 | 0 | } |
1289 | 0 | header_value = (char *)tvb_get_string_enc(pinfo->pool,header_tvb, |
1290 | 0 | hdr_offset, |
1291 | 0 | header_value_length, ENC_ASCII|ENC_NA); |
1292 | 0 | hdr_offset += header_value_length; |
1293 | | |
1294 | | /* Populate tree with header name/value details. */ |
1295 | 0 | if (frame_tree) { |
1296 | | /* Add 'Header' subtree with description. */ |
1297 | 0 | header = proto_tree_add_item(frame_tree, |
1298 | 0 | hf_spdy_header, |
1299 | 0 | header_tvb, |
1300 | 0 | header_name_offset, |
1301 | 0 | hdr_offset - header_name_offset, |
1302 | 0 | ENC_NA); |
1303 | 0 | proto_item_append_text(header, ": %s: %s", header_name, header_value); |
1304 | 0 | header_tree = proto_item_add_subtree(header, ett_spdy_header); |
1305 | | |
1306 | | /* Add header name. */ |
1307 | 0 | proto_tree_add_item(header_tree, hf_spdy_header_name, header_tvb, |
1308 | 0 | header_name_offset, 4, ENC_ASCII|ENC_BIG_ENDIAN); |
1309 | | |
1310 | | /* Add header value. */ |
1311 | 0 | proto_tree_add_item(header_tree, hf_spdy_header_value, header_tvb, |
1312 | 0 | header_value_offset, 4, ENC_ASCII|ENC_BIG_ENDIAN); |
1313 | 0 | } |
1314 | | |
1315 | | /* |
1316 | | * TODO(ers) check that the header name contains only legal characters. |
1317 | | */ |
1318 | | /* TODO(hkhalil): Make sure that prohibited headers aren't sent. */ |
1319 | 0 | if (g_strcmp0(header_name, ":method") == 0) { |
1320 | 0 | hdr_method = header_value; |
1321 | 0 | } else if (g_strcmp0(header_name, ":path") == 0) { |
1322 | 0 | hdr_path = header_value; |
1323 | 0 | } else if (g_strcmp0(header_name, ":version") == 0) { |
1324 | 0 | hdr_version = header_value; |
1325 | 0 | } else if (g_strcmp0(header_name, ":host") == 0) { |
1326 | 0 | hdr_host = header_value; |
1327 | 0 | } else if (g_strcmp0(header_name, ":scheme") == 0) { |
1328 | 0 | hdr_scheme = header_value; |
1329 | 0 | } else if (g_strcmp0(header_name, ":status") == 0) { |
1330 | 0 | hdr_status = header_value; |
1331 | 0 | } else if (g_strcmp0(header_name, "content-type") == 0) { |
1332 | 0 | content_type = wmem_strdup(wmem_file_scope(), header_value); |
1333 | 0 | } else if (g_strcmp0(header_name, "content-encoding") == 0) { |
1334 | 0 | content_encoding = wmem_strdup(wmem_file_scope(), header_value); |
1335 | 0 | } |
1336 | 0 | } |
1337 | | |
1338 | | /* Set Info column. */ |
1339 | 0 | if (hdr_version != NULL) { |
1340 | 0 | if (hdr_status == NULL) { |
1341 | 0 | proto_item_append_text(frame_tree, ", Request: %s %s://%s%s %s", |
1342 | 0 | hdr_method, hdr_scheme, hdr_host, hdr_path, hdr_version); |
1343 | 0 | } else { |
1344 | 0 | proto_item_append_text(frame_tree, ", Response: %s %s", |
1345 | 0 | hdr_status, hdr_version); |
1346 | 0 | } |
1347 | 0 | } |
1348 | | |
1349 | | /* |
1350 | | * If we expect data on this stream, we need to remember the content |
1351 | | * type and content encoding. |
1352 | | */ |
1353 | 0 | if (content_type != NULL && !pinfo->fd->visited) { |
1354 | 0 | char *content_type_params = spdy_parse_content_type(content_type); |
1355 | 0 | spdy_save_stream_info(conv_data, stream_id, |
1356 | 0 | (hdr_status == NULL) ? MEDIA_CONTAINER_HTTP_REQUEST : MEDIA_CONTAINER_HTTP_RESPONSE, |
1357 | 0 | content_type, content_type_params, content_encoding); |
1358 | 0 | } |
1359 | |
|
1360 | 0 | return frame->length; |
1361 | 0 | } |
1362 | | |
1363 | | static int dissect_spdy_rst_stream_payload( |
1364 | | tvbuff_t *tvb, |
1365 | | int offset, |
1366 | | packet_info *pinfo, |
1367 | | proto_tree *frame_tree, |
1368 | 0 | const spdy_control_frame_info_t *frame) { |
1369 | 0 | uint32_t rst_status; |
1370 | 0 | proto_item *ti; |
1371 | 0 | const char* str; |
1372 | | |
1373 | | /* Get stream ID and add to info column and tree. */ |
1374 | 0 | dissect_spdy_stream_id_field(tvb, offset, pinfo, frame_tree, hf_spdy_streamid); |
1375 | 0 | offset += 4; |
1376 | | |
1377 | | /* Get status. */ |
1378 | |
|
1379 | 0 | ti = proto_tree_add_item(frame_tree, hf_spdy_rst_stream_status, tvb, offset, 4, ENC_BIG_ENDIAN); |
1380 | 0 | rst_status = tvb_get_ntohl(tvb, offset); |
1381 | 0 | if (try_val_to_str(rst_status, rst_stream_status_names) == NULL) { |
1382 | | /* Handle boundary conditions. */ |
1383 | 0 | expert_add_info_format(pinfo, ti, &ei_spdy_invalid_rst_stream, |
1384 | 0 | "Invalid status code for RST_STREAM: %u", rst_status); |
1385 | 0 | } |
1386 | |
|
1387 | 0 | str = val_to_str(rst_status, rst_stream_status_names, "Unknown (%d)"); |
1388 | |
|
1389 | 0 | proto_item_append_text(frame_tree, ", Status: %s", str); |
1390 | |
|
1391 | 0 | return frame->length; |
1392 | 0 | } |
1393 | | |
1394 | | static int dissect_spdy_settings_payload( |
1395 | | tvbuff_t *tvb, |
1396 | | int offset, |
1397 | | packet_info *pinfo, |
1398 | | proto_tree *frame_tree, |
1399 | 0 | const spdy_control_frame_info_t *frame) { |
1400 | 0 | uint32_t num_entries; |
1401 | 0 | proto_item *ti, *ti_setting; |
1402 | 0 | proto_tree *setting_tree; |
1403 | 0 | proto_tree *flags_tree; |
1404 | | |
1405 | | /* Make sure that we have enough room for our number of entries field. */ |
1406 | 0 | if (frame->length < 4) { |
1407 | 0 | expert_add_info(pinfo, frame_tree, &ei_spdy_mal_setting_frame); |
1408 | 0 | return -1; |
1409 | 0 | } |
1410 | | |
1411 | | /* Get number of entries, and make sure we have enough room for them. */ |
1412 | 0 | num_entries = tvb_get_ntohl(tvb, offset); |
1413 | 0 | if (frame->length < num_entries * 8) { |
1414 | 0 | expert_add_info_format(pinfo, frame_tree, &ei_spdy_mal_setting_frame, |
1415 | 0 | "SETTINGS frame too small [num_entries=%d]", num_entries); |
1416 | 0 | return -1; |
1417 | 0 | } |
1418 | | |
1419 | 0 | proto_tree_add_item(frame_tree, hf_spdy_num_settings, tvb, offset, 4, ENC_BIG_ENDIAN); |
1420 | 0 | offset += 4; |
1421 | | |
1422 | | /* Dissect each entry. */ |
1423 | 0 | while (num_entries > 0) { |
1424 | 0 | const char *setting_id_str; |
1425 | 0 | uint32_t setting_value; |
1426 | | |
1427 | | /* Create key/value pair subtree. */ |
1428 | 0 | ti_setting = proto_tree_add_item(frame_tree, hf_spdy_setting, tvb, offset, 8, ENC_NA); |
1429 | 0 | setting_tree = proto_item_add_subtree(ti_setting, ett_spdy_setting); |
1430 | | |
1431 | | /* Set flags. */ |
1432 | 0 | if (setting_tree) { |
1433 | 0 | ti = proto_tree_add_item(setting_tree, hf_spdy_flags, tvb, offset, 1, ENC_BIG_ENDIAN); |
1434 | | |
1435 | | /* TODO(hkhalil): Prettier output for flags sub-tree description. */ |
1436 | 0 | flags_tree = proto_item_add_subtree(ti, ett_spdy_flags); |
1437 | 0 | proto_tree_add_item(flags_tree, hf_spdy_flags_persist_value, tvb, offset, 1, ENC_BIG_ENDIAN); |
1438 | 0 | proto_tree_add_item(flags_tree, hf_spdy_flags_persisted, tvb, offset, 1, ENC_BIG_ENDIAN); |
1439 | 0 | } |
1440 | 0 | offset += 1; |
1441 | | |
1442 | | /* Set ID. */ |
1443 | 0 | setting_id_str = val_to_str(tvb_get_ntoh24(tvb, offset), setting_id_names, "Unknown(%d)"); |
1444 | |
|
1445 | 0 | proto_tree_add_item(setting_tree, hf_spdy_setting_id, tvb, offset, 3, ENC_BIG_ENDIAN); |
1446 | 0 | offset += 3; |
1447 | | |
1448 | | /* Set Value. */ |
1449 | 0 | setting_value = tvb_get_ntohl(tvb, offset); |
1450 | |
|
1451 | 0 | proto_tree_add_item(setting_tree, hf_spdy_setting_value, tvb, offset, 4, ENC_BIG_ENDIAN); |
1452 | 0 | proto_item_append_text(ti_setting, ", %s: %u", setting_id_str, setting_value); |
1453 | 0 | proto_item_append_text(frame_tree, ", %s: %u", setting_id_str, setting_value); |
1454 | 0 | offset += 4; |
1455 | | |
1456 | | /* Increment. */ |
1457 | 0 | --num_entries; |
1458 | 0 | } |
1459 | |
|
1460 | 0 | return frame->length; |
1461 | 0 | } |
1462 | | |
1463 | | static int dissect_spdy_ping_payload(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, |
1464 | | proto_tree *frame_tree, const spdy_control_frame_info_t *frame) |
1465 | 0 | { |
1466 | | /* Get ping ID. */ |
1467 | 0 | uint32_t ping_id = tvb_get_ntohl(tvb, offset); |
1468 | | |
1469 | | /* Add proto item for ping ID. */ |
1470 | 0 | proto_tree_add_item(frame_tree, hf_spdy_ping_id, tvb, offset, 4, ENC_BIG_ENDIAN); |
1471 | 0 | proto_item_append_text(frame_tree, ", ID: %u", ping_id); |
1472 | |
|
1473 | 0 | return frame->length; |
1474 | 0 | } |
1475 | | |
1476 | | static int dissect_spdy_goaway_payload(tvbuff_t *tvb, |
1477 | | int offset, |
1478 | | packet_info *pinfo, |
1479 | | proto_tree *frame_tree, |
1480 | 0 | const spdy_control_frame_info_t *frame) { |
1481 | 0 | uint32_t goaway_status; |
1482 | 0 | proto_item* ti; |
1483 | | |
1484 | | /* Get last good stream ID and add to info column and tree. */ |
1485 | 0 | dissect_spdy_stream_id_field(tvb, offset, pinfo, frame_tree, hf_spdy_goaway_last_good_stream_id); |
1486 | 0 | offset += 4; |
1487 | | |
1488 | | /* Add proto item for goaway_status. */ |
1489 | 0 | ti = proto_tree_add_item(frame_tree, hf_spdy_goaway_status, tvb, offset, 4, ENC_BIG_ENDIAN); |
1490 | 0 | goaway_status = tvb_get_ntohl(tvb, offset); |
1491 | |
|
1492 | 0 | if (try_val_to_str(goaway_status, goaway_status_names) == NULL) { |
1493 | | /* Handle boundary conditions. */ |
1494 | 0 | expert_add_info_format(pinfo, ti, &ei_spdy_invalid_go_away, |
1495 | 0 | "Invalid status code for GOAWAY: %u", goaway_status); |
1496 | 0 | } |
1497 | | |
1498 | | /* Add status to info column. */ |
1499 | 0 | proto_item_append_text(frame_tree, " Status=%s)", |
1500 | 0 | val_to_str(goaway_status, rst_stream_status_names, "Unknown (%d)")); |
1501 | |
|
1502 | 0 | return frame->length; |
1503 | 0 | } |
1504 | | |
1505 | | static int dissect_spdy_window_update_payload( |
1506 | | tvbuff_t *tvb, |
1507 | | int offset, |
1508 | | packet_info *pinfo, |
1509 | | proto_tree *frame_tree, |
1510 | | const spdy_control_frame_info_t *frame) |
1511 | 0 | { |
1512 | 0 | uint32_t window_update_delta; |
1513 | | |
1514 | | /* Get stream ID. */ |
1515 | 0 | dissect_spdy_stream_id_field(tvb, offset, pinfo, frame_tree, hf_spdy_streamid); |
1516 | 0 | offset += 4; |
1517 | | |
1518 | | /* Get window update delta. */ |
1519 | 0 | window_update_delta = tvb_get_ntohl(tvb, offset) & 0x7FFFFFFF; |
1520 | | |
1521 | | /* Add proto item for window update delta. */ |
1522 | 0 | proto_tree_add_item(frame_tree, hf_spdy_window_update_delta, tvb, offset, 4, ENC_BIG_ENDIAN); |
1523 | 0 | proto_item_append_text(frame_tree, ", Delta: %u", window_update_delta); |
1524 | |
|
1525 | 0 | return frame->length; |
1526 | 0 | } |
1527 | | |
1528 | | /* |
1529 | | * Performs SPDY frame dissection. |
1530 | | */ |
1531 | | static int dissect_spdy_frame(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) |
1532 | 16 | { |
1533 | 16 | uint8_t control_bit; |
1534 | 16 | spdy_control_frame_info_t frame; |
1535 | 16 | uint32_t stream_id = 0; |
1536 | 16 | const char *frame_type_name; |
1537 | 16 | proto_tree *spdy_tree; |
1538 | 16 | proto_item *spdy_item, *type_item = NULL; |
1539 | 16 | int offset = 0; |
1540 | 16 | spdy_conv_t *conv_data; |
1541 | | |
1542 | 16 | conv_data = get_or_create_spdy_conversation_data(pinfo); |
1543 | | |
1544 | 16 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "SPDY"); |
1545 | | |
1546 | | /* Create frame root. */ |
1547 | 16 | spdy_item = proto_tree_add_item(tree, proto_spdy, tvb, offset, -1, ENC_NA); |
1548 | 16 | spdy_tree = proto_item_add_subtree(spdy_item, ett_spdy); |
1549 | | |
1550 | | /* Add control bit. */ |
1551 | 16 | control_bit = tvb_get_uint8(tvb, offset) & 0x80; |
1552 | 16 | proto_tree_add_item(spdy_tree, hf_spdy_control_bit, tvb, offset, 2, ENC_NA); |
1553 | | |
1554 | | /* Process first four bytes of frame, formatted depending on control bit. */ |
1555 | 16 | if (control_bit) { |
1556 | | /* Add version. */ |
1557 | 1 | frame.version = tvb_get_ntohs(tvb, offset) & 0x7FFF; |
1558 | 1 | proto_tree_add_item(spdy_tree, hf_spdy_version, tvb, offset, 2, ENC_BIG_ENDIAN); |
1559 | 1 | offset += 2; |
1560 | | |
1561 | | /* Add control frame type. */ |
1562 | 1 | type_item = proto_tree_add_item(spdy_tree, hf_spdy_type, tvb, offset, 2, ENC_BIG_ENDIAN); |
1563 | 1 | frame.type = tvb_get_ntohs(tvb, offset); |
1564 | 1 | if (frame.type >= SPDY_INVALID) { |
1565 | 1 | expert_add_info_format(pinfo, type_item, &ei_spdy_invalid_frame_type, |
1566 | 1 | "Invalid SPDY control frame type: %d", frame.type); |
1567 | 1 | return -1; |
1568 | 1 | } |
1569 | 0 | offset += 2; |
1570 | |
|
1571 | 15 | } else { |
1572 | 15 | frame.type = SPDY_DATA; |
1573 | 15 | frame.version = 0; /* Version doesn't apply to DATA. */ |
1574 | | |
1575 | | /* Add stream ID. */ |
1576 | 15 | stream_id = tvb_get_ntohl(tvb, offset) & SPDY_STREAM_ID_MASK; |
1577 | 15 | proto_tree_add_item(spdy_tree, hf_spdy_streamid, tvb, offset, 4, ENC_BIG_ENDIAN); |
1578 | 15 | offset += 4; |
1579 | 15 | } |
1580 | | |
1581 | | /* Add frame info. */ |
1582 | 15 | frame_type_name = val_to_str(frame.type, frame_type_names, "Unknown(%d)"); |
1583 | 15 | col_append_sep_str(pinfo->cinfo, COL_INFO, ", ", frame_type_name); |
1584 | | |
1585 | 15 | proto_item_append_text(spdy_tree, ": %s", frame_type_name); |
1586 | | |
1587 | | /* Add flags. */ |
1588 | 15 | frame.flags = tvb_get_uint8(tvb, offset); |
1589 | 15 | if (spdy_tree) { |
1590 | 15 | dissect_spdy_flags(tvb, offset, spdy_tree, &frame); |
1591 | 15 | } |
1592 | 15 | offset += 1; |
1593 | | |
1594 | | /* Add length. */ |
1595 | 15 | frame.length = tvb_get_ntoh24(tvb, offset); |
1596 | | |
1597 | 15 | proto_item_set_len(spdy_item, frame.length + 8); |
1598 | 15 | proto_tree_add_item(spdy_tree, hf_spdy_length, tvb, offset, 3, ENC_BIG_ENDIAN); |
1599 | 15 | offset += 3; |
1600 | | |
1601 | | /* |
1602 | | * Make sure there's as much data as the frame header says there is. |
1603 | | */ |
1604 | 15 | if ((unsigned)tvb_reported_length_remaining(tvb, offset) < frame.length) { |
1605 | 0 | expert_add_info_format(pinfo, tree, &ei_spdy_mal_frame_data, |
1606 | 0 | "Not enough frame data: %d vs. %d", |
1607 | 0 | frame.length, tvb_reported_length_remaining(tvb, offset)); |
1608 | 0 | return -1; |
1609 | 0 | } |
1610 | | |
1611 | | /* Dissect DATA payload as necessary. */ |
1612 | 15 | if (!control_bit) { |
1613 | 15 | return offset + dissect_spdy_data_payload(tvb, offset, pinfo, tree, spdy_tree, |
1614 | 15 | spdy_item, conv_data, stream_id, &frame); |
1615 | 15 | } |
1616 | | |
1617 | | /* Abort here if the version is too low. */ |
1618 | 0 | if (frame.version < MIN_SPDY_VERSION) { |
1619 | 0 | proto_item_append_text(spdy_item, " [Unsupported Version]"); |
1620 | 0 | return frame.length + 8; |
1621 | 0 | } |
1622 | | |
1623 | 0 | switch (frame.type) { |
1624 | 0 | case SPDY_SYN_STREAM: |
1625 | 0 | case SPDY_SYN_REPLY: |
1626 | 0 | case SPDY_HEADERS: |
1627 | 0 | dissect_spdy_header_payload(tvb, offset, pinfo, spdy_tree, &frame, conv_data); |
1628 | 0 | break; |
1629 | | |
1630 | 0 | case SPDY_RST_STREAM: |
1631 | 0 | dissect_spdy_rst_stream_payload(tvb, offset, pinfo, spdy_tree, &frame); |
1632 | 0 | break; |
1633 | | |
1634 | 0 | case SPDY_SETTINGS: |
1635 | 0 | dissect_spdy_settings_payload(tvb, offset, pinfo, spdy_tree, &frame); |
1636 | 0 | break; |
1637 | | |
1638 | 0 | case SPDY_PING: |
1639 | 0 | dissect_spdy_ping_payload(tvb, offset, pinfo, spdy_tree, &frame); |
1640 | 0 | break; |
1641 | | |
1642 | 0 | case SPDY_GOAWAY: |
1643 | 0 | dissect_spdy_goaway_payload(tvb, offset, pinfo, spdy_tree, &frame); |
1644 | 0 | break; |
1645 | | |
1646 | 0 | case SPDY_WINDOW_UPDATE: |
1647 | 0 | dissect_spdy_window_update_payload(tvb, offset, pinfo, spdy_tree, &frame); |
1648 | 0 | break; |
1649 | | |
1650 | 0 | case SPDY_CREDENTIAL: |
1651 | | /* TODO(hkhalil): Show something meaningful. */ |
1652 | 0 | break; |
1653 | | |
1654 | 0 | default: |
1655 | 0 | expert_add_info_format(pinfo, type_item, &ei_spdy_invalid_frame_type, |
1656 | 0 | "Unhandled SPDY frame type: %d", frame.type); |
1657 | 0 | break; |
1658 | 0 | } |
1659 | | |
1660 | | /* |
1661 | | * OK, we've set the Protocol and Info columns for the |
1662 | | * first SPDY message; set a fence so that subsequent |
1663 | | * SPDY messages don't overwrite the Info column. |
1664 | | */ |
1665 | 0 | col_set_fence(pinfo->cinfo, COL_INFO); |
1666 | | |
1667 | | /* Assume that we've consumed the whole frame. */ |
1668 | 0 | return 8 + frame.length; |
1669 | 0 | } |
1670 | | |
1671 | | static unsigned get_spdy_message_len(packet_info *pinfo _U_, tvbuff_t *tvb, |
1672 | | int offset, void *data _U_) |
1673 | 16 | { |
1674 | 16 | return (unsigned)tvb_get_ntoh24(tvb, offset + 5) + 8; |
1675 | 16 | } |
1676 | | |
1677 | | /* |
1678 | | * Wrapper for dissect_spdy_frame, sets fencing and desegments as necessary. |
1679 | | */ |
1680 | | static int dissect_spdy(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) |
1681 | 5 | { |
1682 | 5 | col_clear(pinfo->cinfo, COL_INFO); |
1683 | | |
1684 | 5 | tcp_dissect_pdus(tvb, pinfo, tree, true, 8, get_spdy_message_len, dissect_spdy_frame, data); |
1685 | 5 | return tvb_captured_length(tvb); |
1686 | 5 | } |
1687 | | |
1688 | | /* |
1689 | | * Looks for SPDY frame at tvb start. |
1690 | | * If not enough data for either, requests more via desegment struct. |
1691 | | */ |
1692 | | static bool dissect_spdy_heur(tvbuff_t *tvb, |
1693 | | packet_info *pinfo, |
1694 | | proto_tree *tree, |
1695 | | void *data _U_) |
1696 | 0 | { |
1697 | | /* |
1698 | | * The first byte of a SPDY frame must be either 0 or |
1699 | | * 0x80. If it's not, assume that this is not SPDY. |
1700 | | * (In theory, a data frame could have a stream ID |
1701 | | * >= 2^24, in which case it won't have 0 for a first |
1702 | | * byte, but this is a pretty reliable heuristic for |
1703 | | * now.) |
1704 | | */ |
1705 | 0 | uint8_t first_byte = tvb_get_uint8(tvb, 0); |
1706 | 0 | if (first_byte != 0x80 && first_byte != 0x0) { |
1707 | 0 | return false; |
1708 | 0 | } |
1709 | | |
1710 | | /* Attempt dissection. */ |
1711 | 0 | if (dissect_spdy(tvb, pinfo, tree, NULL) != 0) { |
1712 | 0 | return true; |
1713 | 0 | } |
1714 | | |
1715 | 0 | return false; |
1716 | 0 | } |
1717 | | |
1718 | | /* |
1719 | | * Performs plugin registration. |
1720 | | */ |
1721 | | void proto_register_spdy(void) |
1722 | 14 | { |
1723 | 14 | static hf_register_info hf[] = { |
1724 | 14 | { &hf_spdy_data, |
1725 | 14 | { "Data", "spdy.data", |
1726 | 14 | FT_BYTES, BASE_NONE, NULL, 0x0, |
1727 | 14 | NULL, HFILL |
1728 | 14 | } |
1729 | 14 | }, |
1730 | 14 | { &hf_spdy_control_bit, |
1731 | 14 | { "Control frame", "spdy.control_bit", |
1732 | 14 | FT_BOOLEAN, 16, TFS(&tfs_yes_no), 0x8000, |
1733 | 14 | "true if SPDY control frame", HFILL |
1734 | 14 | } |
1735 | 14 | }, |
1736 | 14 | { &hf_spdy_version, |
1737 | 14 | { "Version", "spdy.version", |
1738 | 14 | FT_UINT16, BASE_DEC, NULL, 0x7FFF, |
1739 | 14 | NULL, HFILL |
1740 | 14 | } |
1741 | 14 | }, |
1742 | 14 | { &hf_spdy_type, |
1743 | 14 | { "Type", "spdy.type", |
1744 | 14 | FT_UINT16, BASE_DEC, |
1745 | 14 | VALS(frame_type_names), 0x0, |
1746 | 14 | NULL, HFILL |
1747 | 14 | } |
1748 | 14 | }, |
1749 | 14 | { &hf_spdy_flags, |
1750 | 14 | { "Flags", "spdy.flags", |
1751 | 14 | FT_UINT8, BASE_HEX, NULL, 0x0, |
1752 | 14 | NULL, HFILL |
1753 | 14 | } |
1754 | 14 | }, |
1755 | 14 | { &hf_spdy_flags_fin, |
1756 | 14 | { "FIN", "spdy.flags.fin", |
1757 | 14 | FT_BOOLEAN, 8, |
1758 | 14 | TFS(&tfs_set_notset), SPDY_FLAG_FIN, |
1759 | 14 | NULL, HFILL |
1760 | 14 | } |
1761 | 14 | }, |
1762 | 14 | { &hf_spdy_flags_unidirectional, |
1763 | 14 | { "Unidirectional", "spdy.flags.unidirectional", |
1764 | 14 | FT_BOOLEAN, 8, |
1765 | 14 | TFS(&tfs_set_notset), SPDY_FLAG_UNIDIRECTIONAL, |
1766 | 14 | NULL, HFILL |
1767 | 14 | } |
1768 | 14 | }, |
1769 | 14 | { &hf_spdy_flags_clear_settings, |
1770 | 14 | { "Persist Value", "spdy.flags.clear_settings", |
1771 | 14 | FT_BOOLEAN, 8, |
1772 | 14 | TFS(&tfs_set_notset), SPDY_FLAG_SETTINGS_CLEAR_SETTINGS, |
1773 | 14 | NULL, HFILL |
1774 | 14 | } |
1775 | 14 | }, |
1776 | 14 | { &hf_spdy_flags_persist_value, |
1777 | 14 | { "Persist Value", "spdy.flags.persist_value", |
1778 | 14 | FT_BOOLEAN, 8, |
1779 | 14 | TFS(&tfs_set_notset), SPDY_FLAG_SETTINGS_PERSIST_VALUE, |
1780 | 14 | NULL, HFILL |
1781 | 14 | } |
1782 | 14 | }, |
1783 | 14 | { &hf_spdy_flags_persisted, |
1784 | 14 | { "Persisted", "spdy.flags.persisted", |
1785 | 14 | FT_BOOLEAN, 8, |
1786 | 14 | TFS(&tfs_set_notset), SPDY_FLAG_SETTINGS_PERSISTED, |
1787 | 14 | NULL, HFILL |
1788 | 14 | } |
1789 | 14 | }, |
1790 | 14 | { &hf_spdy_length, |
1791 | 14 | { "Length", "spdy.length", |
1792 | 14 | FT_UINT24, BASE_DEC, NULL, 0x0, |
1793 | 14 | NULL, HFILL |
1794 | 14 | } |
1795 | 14 | }, |
1796 | 14 | { &hf_spdy_header_block, |
1797 | 14 | { "Header block", "spdy.header_block", |
1798 | 14 | FT_BYTES, BASE_NONE, NULL, 0x0, |
1799 | 14 | NULL, HFILL |
1800 | 14 | } |
1801 | 14 | }, |
1802 | 14 | { &hf_spdy_header, |
1803 | 14 | { "Header", "spdy.header", |
1804 | 14 | FT_NONE, BASE_NONE, NULL, 0x0, |
1805 | 14 | NULL, HFILL |
1806 | 14 | } |
1807 | 14 | }, |
1808 | 14 | { &hf_spdy_header_name, |
1809 | 14 | { "Name", "spdy.header.name", |
1810 | 14 | FT_UINT_STRING, BASE_NONE, NULL, 0x0, |
1811 | 14 | NULL, HFILL |
1812 | 14 | } |
1813 | 14 | }, |
1814 | 14 | { &hf_spdy_header_value, |
1815 | 14 | { "Value", "spdy.header.value", |
1816 | 14 | FT_UINT_STRING, BASE_NONE, NULL, 0x0, |
1817 | 14 | NULL, HFILL |
1818 | 14 | } |
1819 | 14 | }, |
1820 | 14 | { &hf_spdy_streamid, |
1821 | 14 | { "Stream ID", "spdy.streamid", |
1822 | 14 | FT_UINT32, BASE_DEC, NULL, SPDY_STREAM_ID_MASK, |
1823 | 14 | NULL, HFILL |
1824 | 14 | } |
1825 | 14 | }, |
1826 | 14 | { &hf_spdy_associated_streamid, |
1827 | 14 | { "Associated Stream ID", "spdy.associated.streamid", |
1828 | 14 | FT_UINT32, BASE_DEC, NULL, SPDY_STREAM_ID_MASK, |
1829 | 14 | NULL, HFILL |
1830 | 14 | } |
1831 | 14 | }, |
1832 | 14 | { &hf_spdy_priority, |
1833 | 14 | { "Priority", "spdy.priority", |
1834 | 14 | FT_UINT16, BASE_DEC, NULL, 0xE000, |
1835 | 14 | NULL, HFILL |
1836 | 14 | } |
1837 | 14 | }, |
1838 | 14 | { &hf_spdy_unused, |
1839 | 14 | { "Unused", "spdy.unused", |
1840 | 14 | FT_UINT16, BASE_HEX, NULL, 0x1F00, |
1841 | 14 | "Reserved for future use", HFILL |
1842 | 14 | } |
1843 | 14 | }, |
1844 | 14 | { &hf_spdy_slot, |
1845 | 14 | { "Slot", "spdy.slot", |
1846 | 14 | FT_UINT16, BASE_DEC, NULL, 0x00FF, |
1847 | 14 | "Specifying the index in the server's CREDENTIAL vector of the client certificate to be used for this request", HFILL |
1848 | 14 | } |
1849 | 14 | }, |
1850 | 14 | { &hf_spdy_num_headers, |
1851 | 14 | { "Number of headers", "spdy.numheaders", |
1852 | 14 | FT_UINT32, BASE_DEC, NULL, 0x0, |
1853 | 14 | NULL, HFILL |
1854 | 14 | } |
1855 | 14 | }, |
1856 | 14 | { &hf_spdy_rst_stream_status, |
1857 | 14 | { "Reset Status", "spdy.rst_stream_status", |
1858 | 14 | FT_UINT32, BASE_DEC, VALS(rst_stream_status_names), 0x0, |
1859 | 14 | NULL, HFILL |
1860 | 14 | } |
1861 | 14 | }, |
1862 | 14 | { &hf_spdy_num_settings, |
1863 | 14 | { "Number of Settings", "spdy.num_settings", |
1864 | 14 | FT_UINT32, BASE_DEC, NULL, 0x0, |
1865 | 14 | NULL, HFILL |
1866 | 14 | } |
1867 | 14 | }, |
1868 | 14 | { &hf_spdy_setting, |
1869 | 14 | { "Setting", "spdy.setting", |
1870 | 14 | FT_NONE, BASE_NONE, NULL, 0x0, |
1871 | 14 | NULL, HFILL |
1872 | 14 | } |
1873 | 14 | }, |
1874 | 14 | { &hf_spdy_setting_id, |
1875 | 14 | { "ID", "spdy.setting.id", |
1876 | 14 | FT_UINT24, BASE_DEC, VALS(setting_id_names), 0x0, |
1877 | 14 | NULL, HFILL |
1878 | 14 | } |
1879 | 14 | }, |
1880 | 14 | { &hf_spdy_setting_value, |
1881 | 14 | { "Value", "spdy.setting.value", |
1882 | 14 | FT_UINT32, BASE_DEC, NULL, 0x0, |
1883 | 14 | NULL, HFILL |
1884 | 14 | } |
1885 | 14 | }, |
1886 | 14 | { &hf_spdy_ping_id, |
1887 | 14 | { "Ping ID", "spdy.ping_id", |
1888 | 14 | FT_UINT32, BASE_DEC, NULL, 0x0, |
1889 | 14 | NULL, HFILL |
1890 | 14 | } |
1891 | 14 | }, |
1892 | 14 | { &hf_spdy_goaway_last_good_stream_id, |
1893 | 14 | { "Last Good Stream ID", "spdy.goaway_last_good_stream_id", |
1894 | 14 | FT_UINT32, BASE_DEC, NULL, SPDY_STREAM_ID_MASK, |
1895 | 14 | NULL, HFILL |
1896 | 14 | } |
1897 | 14 | }, |
1898 | 14 | { &hf_spdy_goaway_status, |
1899 | 14 | { "Go Away Status", "spdy.goaway_status", |
1900 | 14 | FT_UINT32, BASE_DEC, VALS(goaway_status_names), 0x0, |
1901 | 14 | NULL, HFILL |
1902 | 14 | } |
1903 | 14 | }, |
1904 | 14 | { &hf_spdy_window_update_delta, |
1905 | 14 | { "Window Update Delta", "spdy.window_update_delta", |
1906 | 14 | FT_UINT32, BASE_DEC, NULL, 0x7FFFFFFF, |
1907 | 14 | NULL, HFILL |
1908 | 14 | } |
1909 | 14 | }, |
1910 | 14 | }; |
1911 | 14 | static int *ett[] = { |
1912 | 14 | &ett_spdy, |
1913 | 14 | &ett_spdy_flags, |
1914 | 14 | &ett_spdy_header_block, |
1915 | 14 | &ett_spdy_header, |
1916 | 14 | &ett_spdy_setting, |
1917 | 14 | &ett_spdy_encoded_entity, |
1918 | 14 | }; |
1919 | | |
1920 | 14 | static ei_register_info ei[] = { |
1921 | 14 | { &ei_spdy_inflation_failed, { "spdy.inflation_failed", PI_UNDECODED, PI_ERROR, "Inflation failed. Aborting.", EXPFILL }}, |
1922 | 14 | { &ei_spdy_mal_frame_data, { "spdy.malformed.frame_data", PI_MALFORMED, PI_ERROR, "Not enough frame data", EXPFILL }}, |
1923 | 14 | { &ei_spdy_mal_setting_frame, { "spdy.malformed.setting_frame", PI_MALFORMED, PI_ERROR, "SETTINGS frame too small for number of entries field.", EXPFILL }}, |
1924 | 14 | { &ei_spdy_invalid_rst_stream, { "spdy.rst_stream.invalid", PI_PROTOCOL, PI_WARN, "Invalid status code for RST_STREAM", EXPFILL }}, |
1925 | 14 | { &ei_spdy_invalid_go_away, { "spdy.goaway.invalid", PI_PROTOCOL, PI_WARN, "Invalid status code for GOAWAY", EXPFILL }}, |
1926 | 14 | { &ei_spdy_invalid_frame_type, { "spdy.type.invalid", PI_PROTOCOL, PI_WARN, "Invalid SPDY frame type", EXPFILL }}, |
1927 | 14 | { &ei_spdy_reassembly_info, { "spdy.reassembly_info", PI_REASSEMBLE, PI_CHAT, "Assembled from frames in packet(s)", EXPFILL }}, |
1928 | 14 | }; |
1929 | | |
1930 | 14 | module_t *spdy_module; |
1931 | 14 | expert_module_t* expert_spdy; |
1932 | | |
1933 | 14 | proto_spdy = proto_register_protocol("SPDY", "SPDY", "spdy"); |
1934 | 14 | proto_register_field_array(proto_spdy, hf, array_length(hf)); |
1935 | 14 | proto_register_subtree_array(ett, array_length(ett)); |
1936 | 14 | expert_spdy = expert_register_protocol(proto_spdy); |
1937 | 14 | expert_register_field_array(expert_spdy, ei, array_length(ei)); |
1938 | | |
1939 | 14 | spdy_handle = register_dissector("spdy", dissect_spdy, proto_spdy); |
1940 | | |
1941 | 14 | spdy_module = prefs_register_protocol(proto_spdy, NULL); |
1942 | 14 | prefs_register_bool_preference(spdy_module, "assemble_data_frames", |
1943 | 14 | "Assemble SPDY bodies that consist of multiple DATA frames", |
1944 | 14 | "Whether the SPDY dissector should reassemble multiple " |
1945 | 14 | "data frames into an entity body.", |
1946 | 14 | &spdy_assemble_entity_bodies); |
1947 | | |
1948 | 14 | prefs_register_bool_preference(spdy_module, "decompress_headers", |
1949 | 14 | "Uncompress SPDY headers", |
1950 | 14 | "Whether to uncompress SPDY headers.", |
1951 | 14 | &spdy_decompress_headers); |
1952 | 14 | prefs_register_bool_preference(spdy_module, "decompress_body", |
1953 | 14 | "Uncompress entity bodies", |
1954 | 14 | "Whether to uncompress entity bodies that are compressed " |
1955 | 14 | "using \"Content-Encoding: \"", |
1956 | 14 | &spdy_decompress_body); |
1957 | | |
1958 | 14 | register_init_routine(&spdy_init_protocol); |
1959 | | |
1960 | | /* |
1961 | | * Register for tapping |
1962 | | */ |
1963 | 14 | spdy_tap = register_tap("spdy"); /* SPDY statistics tap */ |
1964 | 14 | spdy_eo_tap = register_tap("spdy_eo"); /* SPDY Export Object tap */ |
1965 | 14 | } |
1966 | | |
1967 | 14 | void proto_reg_handoff_spdy(void) { |
1968 | | |
1969 | 14 | dissector_add_uint_with_preference("tcp.port", TCP_PORT_SPDY, spdy_handle); |
1970 | | /* Use "0" to avoid overwriting HTTPS port and still offer support over TLS */ |
1971 | 14 | ssl_dissector_add(0, spdy_handle); |
1972 | 14 | dissector_add_string("http.upgrade", "spdy", spdy_handle); |
1973 | | |
1974 | 14 | media_handle = find_dissector_add_dependency("media", proto_spdy); |
1975 | 14 | port_subdissector_table = find_dissector_table("http.port"); |
1976 | 14 | media_type_subdissector_table = find_dissector_table("media_type"); |
1977 | | |
1978 | | /* Weak heuristic, so disabled by default */ |
1979 | 14 | heur_dissector_add("tcp", dissect_spdy_heur, "SPDY over TCP", "spdy_tcp", proto_spdy, HEURISTIC_DISABLE); |
1980 | 14 | } |
1981 | | |
1982 | | /* |
1983 | | * Editor modelines |
1984 | | * |
1985 | | * Local Variables: |
1986 | | * c-basic-offset: 2 |
1987 | | * tab-width: 8 |
1988 | | * indent-tabs-mode: nil |
1989 | | * End: |
1990 | | * |
1991 | | * ex: set shiftwidth=2 tabstop=8 expandtab: |
1992 | | * :indentSize=4:tabSize=8:noTabs=true: |
1993 | | */ |