Make it compil by adding a cast, not sure if payload_length = (int)tvb_get_ntoh64...
[metze/wireshark/wip.git] / epan / dissectors / packet-websocket.c
1 /* packet-websocket.c
2  * Routines for WebSocket dissection
3  * Copyright 2012, Alexis La Goutte <alexis.lagoutte@gmail.com>
4  *
5  * $Id$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License along
22  * with this program; if not, write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24  */
25
26 #include "config.h"
27
28 #include <glib.h>
29
30 #include <epan/packet.h>
31 #include <epan/expert.h>
32
33 /*
34  * The information used comes from:
35  * RFC6455: The WebSocket Protocol
36  * http://www.iana.org/assignments/websocket (last updated 2012-04-12)
37  */
38
39 /* Initialize the protocol and registered fields */
40 static int proto_websocket = -1;
41 static int hf_ws_fin = -1;
42 static int hf_ws_reserved = -1;
43 static int hf_ws_opcode = -1;
44 static int hf_ws_mask = -1;
45 static int hf_ws_payload_length = -1;
46 static int hf_ws_payload_length_ext_16 = -1;
47 static int hf_ws_payload_length_ext_64 = -1;
48 static int hf_ws_masking_key = -1;
49 static int hf_ws_payload = -1;
50 static int hf_ws_payload_unmask = -1;
51 static int hf_ws_payload_continue = -1;
52 static int hf_ws_payload_text = -1;
53 static int hf_ws_payload_text_mask = -1;
54 static int hf_ws_payload_text_unmask = -1;
55 static int hf_ws_payload_binary = -1;
56 static int hf_ws_payload_binary_mask = -1;
57 static int hf_ws_payload_binary_unmask = -1;
58 static int hf_ws_payload_close = -1;
59 static int hf_ws_payload_close_mask = -1;
60 static int hf_ws_payload_close_unmask = -1;
61 static int hf_ws_payload_close_status_code = -1;
62 static int hf_ws_payload_close_reason = -1;
63 static int hf_ws_payload_ping = -1;
64 static int hf_ws_payload_ping_mask = -1;
65 static int hf_ws_payload_ping_unmask = -1;
66 static int hf_ws_payload_pong = -1;
67 static int hf_ws_payload_pong_mask = -1;
68 static int hf_ws_payload_pong_unmask = -1;
69 static int hf_ws_payload_unknown = -1;
70
71 static gint ett_ws = -1;
72 static gint ett_ws_pl = -1;
73 static gint ett_ws_mask = -1;
74
75 #define WS_CONTINUE 0x0
76 #define WS_TEXT     0x1
77 #define WS_BINARY   0x2
78 #define WS_CLOSE    0x8
79 #define WS_PING     0x9
80 #define WS_PONG     0xA
81
82 static const value_string ws_opcode_vals[] = {
83   { WS_CONTINUE, "Continuation" },
84   { WS_TEXT, "Text" },
85   { WS_BINARY, "Binary" },
86   { WS_CLOSE, "Connection Close" },
87   { WS_PING, "Ping" },
88   { WS_PONG, "Pong" },
89   { 0, NULL}
90 };
91
92 #define MASK_WS_FIN 0x80
93 #define MASK_WS_RSV 0x70
94 #define MASK_WS_OPCODE 0x0F
95 #define MASK_WS_MASK 0x80
96 #define MASK_WS_PAYLOAD_LEN 0x7F
97
98 static const value_string ws_close_status_code_vals[] = {
99   { 1000, "Normal Closure" },
100   { 1001, "Going Away" },
101   { 1002, "Protocol error" },
102   { 1003, "Unsupported Data" },
103   { 1004, "---Reserved----" },
104   { 1005, "No Status Rcvd" },
105   { 1006, "Abnormal Closure" },
106   { 1007, "Invalid frame payload data" },
107   { 1008, "Policy Violation" },
108   { 1009, "Message Too Big" },
109   { 1010, "Mandatory Ext." },
110   { 1011, "Internal Server" },
111   { 1015, "TLS handshake" },
112   { 0,    NULL}
113 };
114
115 static dissector_table_t port_subdissector_table;
116 static heur_dissector_list_t heur_subdissector_list;
117
118 #define MAX_UNMASKED_LEN (1024 * 64)
119 tvbuff_t *
120 tvb_unmasked(tvbuff_t *tvb, const int offset, int payload_length, const guint8 *masking_key)
121 {
122
123   gchar *data_unmask;
124   tvbuff_t *tvb_unmask = NULL;
125   int i;
126   const guint8 *data_mask;
127   int unmasked_length = payload_length > MAX_UNMASKED_LEN ? MAX_UNMASKED_LEN : payload_length;
128
129   data_unmask = g_malloc(unmasked_length);
130   data_mask = tvb_get_ptr(tvb, offset, unmasked_length);
131   /* Unmasked(XOR) Data... */
132   for(i=0; i < unmasked_length; i++){
133     data_unmask[i] = data_mask[i] ^ masking_key[i%4];
134   }
135
136   tvb_unmask = tvb_new_real_data(data_unmask, unmasked_length, unmasked_length);
137   tvb_set_free_cb(tvb_unmask, g_free);
138   return tvb_unmask;
139 }
140
141 static int
142 dissect_websocket_payload(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_tree *ws_tree, guint8 opcode, int payload_length, guint8 mask, const guint8* masking_key)
143 {
144   int offset = 0;
145   proto_item *ti_unmask, *ti;
146   dissector_handle_t handle;
147   proto_tree *pl_tree, *mask_tree = NULL;
148   tvbuff_t *payload_tvb = NULL;
149
150   /* Payload */
151   ti = proto_tree_add_item(ws_tree, hf_ws_payload, tvb, offset, payload_length, ENC_NA);
152   pl_tree = proto_item_add_subtree(ti, ett_ws_pl);
153   if(mask){
154     payload_tvb = tvb_unmasked(tvb, offset, payload_length, masking_key);
155     tvb_set_child_real_data_tvbuff(tvb, payload_tvb);
156     add_new_data_source(pinfo, payload_tvb, payload_length > (int) tvb_length(payload_tvb) ? "Unmasked Data (truncated)" : "Unmasked Data");
157     ti = proto_tree_add_item(ws_tree, hf_ws_payload_unmask, payload_tvb, offset, payload_length, ENC_NA);
158     mask_tree = proto_item_add_subtree(ti, ett_ws_mask);
159   }else{
160     payload_tvb = tvb_new_subset(tvb, offset, payload_length, -1);
161   }
162
163   handle = dissector_get_uint_handle(port_subdissector_table, pinfo->match_uint);
164   if(handle != NULL){
165     call_dissector_only(handle, payload_tvb, pinfo, tree, NULL);
166   }else{
167     dissector_try_heuristic(heur_subdissector_list, payload_tvb, pinfo, tree, NULL);
168   }
169
170   /* Extension Data */
171   /* TODO: Add dissector of Extension (not extension available for the moment...) */
172
173   /* Application Data */
174   switch(opcode){
175
176     case WS_CONTINUE: /* Continue */
177       proto_tree_add_item(pl_tree, hf_ws_payload_continue, tvb, offset, payload_length, ENC_NA);
178       /* TODO: Add Fragmentation support... */
179     break;
180
181     case WS_TEXT: /* Text */
182     if(mask){
183
184       proto_tree_add_item(pl_tree, hf_ws_payload_text_mask, tvb, offset, payload_length, ENC_NA);
185       ti_unmask = proto_tree_add_item(mask_tree, hf_ws_payload_text_unmask, payload_tvb, offset, payload_length, ENC_UTF_8|ENC_NA);
186       PROTO_ITEM_SET_GENERATED(ti_unmask);
187       ti_unmask = proto_tree_add_item(mask_tree, hf_ws_payload_text, payload_tvb, offset, payload_length, ENC_UTF_8|ENC_NA);
188       PROTO_ITEM_SET_HIDDEN(ti_unmask);
189     }else{
190       proto_tree_add_item(pl_tree, hf_ws_payload_text, tvb, offset, payload_length, ENC_UTF_8|ENC_NA);
191
192     }
193     offset += payload_length;
194     break;
195
196     case WS_BINARY: /* Binary */
197     if(mask){
198       proto_tree_add_item(pl_tree, hf_ws_payload_binary_mask, tvb, offset, payload_length, ENC_NA);
199       ti_unmask = proto_tree_add_item(mask_tree, hf_ws_payload_binary_unmask, payload_tvb, offset, payload_length, ENC_NA);
200       PROTO_ITEM_SET_GENERATED(ti_unmask);
201       ti_unmask = proto_tree_add_item(mask_tree, hf_ws_payload_binary, payload_tvb, offset, payload_length, ENC_NA);
202       PROTO_ITEM_SET_HIDDEN(ti_unmask);
203     }else{
204       proto_tree_add_item(pl_tree, hf_ws_payload_binary, tvb, offset, payload_length, ENC_NA);
205     }
206     offset += payload_length;
207     break;
208
209     case WS_CLOSE: /* Close */
210     if(mask){
211       proto_tree_add_item(pl_tree, hf_ws_payload_close_mask, tvb, offset, payload_length, ENC_NA);
212       ti_unmask = proto_tree_add_item(mask_tree, hf_ws_payload_close_unmask, payload_tvb, offset, payload_length, ENC_NA);
213       PROTO_ITEM_SET_GENERATED(ti_unmask);
214       ti_unmask = proto_tree_add_item(mask_tree, hf_ws_payload_close, payload_tvb, offset, payload_length, ENC_NA);
215       PROTO_ITEM_SET_HIDDEN(ti_unmask);
216       ti_unmask = proto_tree_add_item(mask_tree, hf_ws_payload_close_status_code, payload_tvb, offset, 2, ENC_BIG_ENDIAN);
217       PROTO_ITEM_SET_GENERATED(ti_unmask);
218
219       if(payload_length > 2){
220         ti_unmask = proto_tree_add_item(mask_tree, hf_ws_payload_close_reason, payload_tvb, offset+2, payload_length-2, ENC_ASCII|ENC_NA);
221         PROTO_ITEM_SET_GENERATED(ti_unmask);
222       }
223     }else{
224       proto_tree_add_item(pl_tree, hf_ws_payload_close, tvb, offset, payload_length, ENC_NA);
225       proto_tree_add_item(pl_tree, hf_ws_payload_close_status_code, tvb, offset, 2, ENC_BIG_ENDIAN);
226       if(payload_length > 2){
227         proto_tree_add_item(pl_tree, hf_ws_payload_close_reason, tvb, offset+2, payload_length-2, ENC_ASCII|ENC_NA);
228       }
229     }
230     offset += payload_length;
231     break;
232
233     case WS_PING: /* Ping */
234     if(mask){
235       proto_tree_add_item(pl_tree, hf_ws_payload_ping_mask, tvb, offset, payload_length, ENC_NA);
236       ti_unmask = proto_tree_add_item(mask_tree, hf_ws_payload_ping_unmask, payload_tvb, offset, payload_length, ENC_NA);
237       PROTO_ITEM_SET_GENERATED(ti_unmask);
238       ti_unmask = proto_tree_add_item(mask_tree, hf_ws_payload_ping, payload_tvb, offset, payload_length, ENC_NA);
239       PROTO_ITEM_SET_HIDDEN(ti_unmask);
240     }else{
241       proto_tree_add_item(pl_tree, hf_ws_payload_ping, tvb, offset, payload_length, ENC_NA);
242     }
243     offset += payload_length;
244     break;
245
246     case WS_PONG: /* Pong */
247     if(mask){
248       proto_tree_add_item(pl_tree, hf_ws_payload_pong_mask, tvb, offset, payload_length, ENC_NA);
249       ti_unmask = proto_tree_add_item(mask_tree, hf_ws_payload_pong_unmask, payload_tvb, offset, payload_length, ENC_NA);
250       PROTO_ITEM_SET_GENERATED(ti_unmask);
251       ti_unmask = proto_tree_add_item(mask_tree, hf_ws_payload_pong, payload_tvb, offset, payload_length, ENC_NA);
252       PROTO_ITEM_SET_HIDDEN(ti_unmask);
253     }else{
254       proto_tree_add_item(pl_tree, hf_ws_payload_pong, tvb, offset, payload_length, ENC_NA);
255     }
256     offset += payload_length;
257     break;
258
259     default: /* Unknown */
260       ti = proto_tree_add_item(pl_tree, hf_ws_payload_unknown, tvb, offset, payload_length, ENC_NA);
261       expert_add_info_format(pinfo, ti, PI_UNDECODED, PI_NOTE, "Dissector for Websocket Opcode (%d)"
262         " code not implemented, Contact Wireshark developers"
263         " if you want this supported", opcode);
264     break;
265   }
266   return offset;
267 }
268
269
270 static int
271 dissect_websocket(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
272 {
273   proto_item *ti, *ti_len;
274   guint8 fin, opcode, mask;
275   int length, short_length, payload_length, recurse_length;
276   int payload_offset, mask_offset, recurse_offset;
277   proto_tree *ws_tree = NULL;
278   const guint8 *masking_key = NULL;
279   tvbuff_t *tvb_payload = NULL;
280
281   length = tvb_length(tvb);
282   if(length<2){
283     pinfo->desegment_len = 2;
284     return 0;
285   }
286
287   short_length = tvb_get_guint8(tvb, 1) & MASK_WS_PAYLOAD_LEN;
288   if(short_length==126){
289     if(length < 2+2){
290       pinfo->desegment_len = 2+2;
291       return 0;
292     }
293     payload_length = tvb_get_ntohs(tvb, 2);
294     mask_offset = 2+2;
295   }
296   else if(short_length==127){
297     if(length < 2+8){
298       pinfo->desegment_len = 2+8;
299       return 0;
300     }
301         /* warning C4244: '=' : conversion from 'guint64' to 'int ', possible loss of data */
302     payload_length = (int)tvb_get_ntoh64(tvb, 2);
303     mask_offset = 2+8;
304   }
305   else{
306     payload_length = short_length;
307     mask_offset = 2;
308   }
309
310   /* Mask */
311   mask = (tvb_get_guint8(tvb, 1) & MASK_WS_MASK) >> 4;
312   payload_offset = mask_offset + (mask ? 4 : 0);
313
314   if(length < payload_offset + payload_length){
315     /* XXXX Warning desegment_len is 32 bits */
316     pinfo->desegment_len = payload_offset + payload_length - length;
317     return 0;
318   }
319
320   /* We've got the entire message! */
321
322   col_set_str(pinfo->cinfo, COL_PROTOCOL, "WebSocket");
323   col_set_str(pinfo->cinfo, COL_INFO, "WebSocket");
324
325   if(tree){
326     ti = proto_tree_add_item(tree, proto_websocket, tvb, 0, payload_offset, ENC_NA);
327     ws_tree = proto_item_add_subtree(ti, ett_ws);
328   }
329
330   /* Flags */
331   proto_tree_add_item(ws_tree, hf_ws_fin, tvb, 0, 1, ENC_NA);
332   fin = (tvb_get_guint8(tvb, 0) & MASK_WS_FIN) >> 4;
333   proto_tree_add_item(ws_tree, hf_ws_reserved, tvb, 0, 1, ENC_NA);
334
335   /* Opcode */
336   proto_tree_add_item(ws_tree, hf_ws_opcode, tvb, 0, 1, ENC_NA);
337   opcode = tvb_get_guint8(tvb, 0) & MASK_WS_OPCODE;
338   col_append_fstr(pinfo->cinfo, COL_INFO, " %s", val_to_str_const(opcode, ws_opcode_vals, "Unknown Opcode"));
339   col_append_fstr(pinfo->cinfo, COL_INFO, " %s", fin ? "[FIN]" : "");
340
341   /* Add Mask bit to the tree */
342   proto_tree_add_item(ws_tree, hf_ws_mask, tvb, 1, 1, ENC_NA);
343   col_append_fstr(pinfo->cinfo, COL_INFO, " %s", mask ? "[MASKED]" : "");
344
345   /* (Extended) Payload Length */
346   ti_len = proto_tree_add_item(ws_tree, hf_ws_payload_length, tvb, 1, 1, ENC_NA);
347   if(short_length==126){
348     proto_item_append_text(ti_len, " Extended Payload Length (16 bits)");
349     proto_tree_add_item(ws_tree, hf_ws_payload_length_ext_16, tvb, 2, 2, ENC_BIG_ENDIAN);
350   }
351   else if(short_length==127){
352     proto_item_append_text(ti_len, " Extended Payload Length (64 bits)");
353     proto_tree_add_item(ws_tree, hf_ws_payload_length_ext_64, tvb, 2, 8, ENC_BIG_ENDIAN);
354   }
355
356   /* Masking-key */
357   if(mask){
358     proto_tree_add_item(ws_tree, hf_ws_masking_key, tvb, mask_offset, 4, ENC_NA);
359     masking_key = tvb_get_ptr(tvb, mask_offset, 4);
360   }
361
362   tvb_payload = tvb_new_subset_remaining(tvb, payload_offset);
363   dissect_websocket_payload(tvb_payload, pinfo, tree, ws_tree, opcode, payload_length, mask, masking_key);
364
365   /* Call this function recursively, to see if we have enough data to parse another websocket message */
366
367   recurse_offset = payload_offset + payload_length;
368   if(length > recurse_offset){
369     recurse_length = dissect_websocket(tvb_new_subset_remaining(tvb, payload_offset+payload_length), pinfo, tree, data);
370     if(pinfo->desegment_len) pinfo->desegment_offset += recurse_offset;
371     return recurse_offset + recurse_length;
372   }
373   return recurse_offset;
374 }
375
376
377 void
378 proto_register_websocket(void)
379 {
380
381   static hf_register_info hf[] = {
382     { &hf_ws_fin,
383       { "Fin", "websocket.fin",
384       FT_BOOLEAN, 8, NULL, MASK_WS_FIN,
385       "Indicates that this is the final fragment in a message", HFILL }
386     },
387     { &hf_ws_reserved,
388       { "Reserved", "websocket.rsv",
389       FT_UINT8, BASE_HEX, NULL, MASK_WS_RSV,
390       "Must be zero", HFILL }
391     },
392     { &hf_ws_opcode,
393       { "Opcode", "websocket.opcode",
394       FT_UINT8, BASE_DEC, VALS(ws_opcode_vals), MASK_WS_OPCODE,
395       "Defines the interpretation of the Payload data", HFILL }
396     },
397     { &hf_ws_mask,
398       { "Mask", "websocket.mask",
399       FT_BOOLEAN, 8, NULL, MASK_WS_MASK,
400       "Defines whether the Payload data is masked", HFILL }
401     },
402     { &hf_ws_payload_length,
403       { "Payload length", "websocket.payload_length",
404       FT_UINT8, BASE_DEC, NULL, MASK_WS_PAYLOAD_LEN,
405       "The length of the Payload data", HFILL }
406     },
407     { &hf_ws_payload_length_ext_16,
408       { "Extended Payload length (16 bits)", "websocket.payload_length_ext_16",
409       FT_UINT16, BASE_DEC, NULL, 0x0,
410       "The length (16 bits) of the Payload data", HFILL }
411     },
412     { &hf_ws_payload_length_ext_64,
413       { "Extended Payload length (16 bits)", "websocket.payload_length_ext_64",
414       FT_UINT64, BASE_DEC, NULL, 0x0,
415       "The length (64 bits) of the Payload data", HFILL }
416     },
417     { &hf_ws_masking_key,
418       { "Masking-Key", "websocket.masking_key",
419       FT_BYTES, BASE_NONE, NULL, 0x0,
420       "All frames sent from the client to the server are masked by a 32-bit value that is contained within the frame", HFILL }
421     },
422     { &hf_ws_payload,
423       { "Payload", "websocket.payload",
424       FT_NONE, BASE_NONE, NULL, 0x0,
425       NULL, HFILL }
426     },
427     { &hf_ws_payload_unmask,
428       { "Unmask Payload", "websocket.payload.unmask",
429       FT_NONE, BASE_NONE, NULL, 0x0,
430       NULL, HFILL }
431     },
432     { &hf_ws_payload_continue,
433       { "Continue", "websocket.payload.continue",
434       FT_BYTES, BASE_NONE, NULL, 0x0,
435       NULL, HFILL }
436     },
437     { &hf_ws_payload_text,
438       { "Text", "websocket.payload.text",
439       FT_STRING, BASE_NONE, NULL, 0x0,
440       NULL, HFILL }
441     },
442     { &hf_ws_payload_text_mask,
443       { "Text", "websocket.payload.text_mask",
444       FT_BYTES, BASE_NONE, NULL, 0x0,
445       NULL, HFILL }
446     },
447     { &hf_ws_payload_text_unmask,
448       { "Text unmask", "websocket.payload.text_unmask",
449       FT_STRING, BASE_NONE, NULL, 0x0,
450       NULL, HFILL }
451     },
452     { &hf_ws_payload_binary,
453       { "Binary", "websocket.payload.binary",
454       FT_BYTES, BASE_NONE, NULL, 0x0,
455       NULL, HFILL }
456     },
457     { &hf_ws_payload_binary_mask,
458       { "Binary", "websocket.payload.binary_mask",
459       FT_BYTES, BASE_NONE, NULL, 0x0,
460       NULL, HFILL }
461     },
462     { &hf_ws_payload_binary_unmask,
463       { "Binary", "websocket.payload.binary_unmask",
464       FT_BYTES, BASE_NONE, NULL, 0x0,
465       NULL, HFILL }
466     },
467     { &hf_ws_payload_close,
468       { "Close", "websocket.payload.close",
469       FT_BYTES, BASE_NONE, NULL, 0x0,
470       NULL, HFILL }
471     },
472     { &hf_ws_payload_close_mask,
473       { "Close", "websocket.payload.close_mask",
474       FT_BYTES, BASE_NONE, NULL, 0x0,
475       NULL, HFILL }
476     },
477     { &hf_ws_payload_close_unmask,
478       { "Unmask Close", "websocket.payload.close_unmask",
479       FT_BYTES, BASE_NONE, NULL, 0x0,
480       NULL, HFILL }
481     },
482     { &hf_ws_payload_close_status_code,
483       { "Close", "websocket.payload.close.status_code",
484       FT_UINT16, BASE_DEC, VALS(ws_close_status_code_vals), 0x0,
485       NULL, HFILL }
486     },
487     { &hf_ws_payload_close_reason,
488       { "Reason", "websocket.payload.close.reason",
489       FT_STRING, BASE_NONE, NULL, 0x0,
490       NULL, HFILL }
491     },
492     { &hf_ws_payload_ping,
493       { "Ping", "websocket.payload.ping",
494       FT_BYTES, BASE_NONE, NULL, 0x0,
495       NULL, HFILL }
496     },
497     { &hf_ws_payload_ping_mask,
498       { "Ping", "websocket.payload.ping_mask",
499       FT_BYTES, BASE_NONE, NULL, 0x0,
500       NULL, HFILL }
501     },
502     { &hf_ws_payload_ping_unmask,
503       { "Ping", "websocket.payload.ping_unmask",
504       FT_BYTES, BASE_NONE, NULL, 0x0,
505       NULL, HFILL }
506     },
507     { &hf_ws_payload_pong,
508       { "Pong", "websocket.payload.pong",
509       FT_BYTES, BASE_NONE, NULL, 0x0,
510       NULL, HFILL }
511     },
512     { &hf_ws_payload_pong_mask,
513       { "Pong", "websocket.payload.pong_mask",
514       FT_BYTES, BASE_NONE, NULL, 0x0,
515       NULL, HFILL }
516     },
517     { &hf_ws_payload_pong_unmask,
518       { "Pong", "websocket.payload.pong_unmask",
519       FT_BYTES, BASE_NONE, NULL, 0x0,
520       NULL, HFILL }
521     },
522     { &hf_ws_payload_unknown,
523       { "Unknown", "websocket.payload.unknown",
524       FT_BYTES, BASE_NONE, NULL, 0x0,
525       NULL, HFILL }
526     },
527   };
528
529
530   static gint *ett[] = {
531     &ett_ws,
532     &ett_ws_pl,
533     &ett_ws_mask
534   };
535
536   proto_websocket = proto_register_protocol("WebSocket",
537       "WebSocket", "websocket");
538   
539   /*
540    * Heuristic dissectors SHOULD register themselves in
541    * this table using the standard heur_dissector_add()
542    * function.
543    */
544   register_heur_dissector_list("ws", &heur_subdissector_list);
545
546   port_subdissector_table = register_dissector_table("ws.port",
547       "TCP port for protocols using WebSocket", FT_UINT16, BASE_DEC);
548
549   proto_register_field_array(proto_websocket, hf, array_length(hf));
550   proto_register_subtree_array(ett, array_length(ett));
551
552   new_register_dissector("websocket", dissect_websocket, proto_websocket);
553 }
554
555
556 /*
557  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
558  *
559  * Local variables:
560  * c-basic-offset: 2
561  * tab-width: 8
562  * indent-tabs-mode: nil
563  * End:
564  *
565  * vi: set shiftwidth=2 tabstop=8 expandtab:
566  * :indentSize=2:tabSize=8:noTabs=true:
567  */