2 * Routines for WebSocket dissection
3 * Copyright 2012, Alexis La Goutte <alexis.lagoutte@gmail.com>
4 * 2015, Peter Wu <peter@lekensteyn.nl>
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 #include <epan/conversation.h>
28 #include <epan/proto_data.h>
29 #include <epan/packet.h>
30 #include <epan/expert.h>
31 #include <epan/prefs.h>
32 #include <wsutil/strtoi.h>
34 #include "packet-http.h"
35 #include "packet-tcp.h"
43 * The information used comes from:
44 * RFC6455: The WebSocket Protocol
45 * http://www.iana.org/assignments/websocket (last updated 2012-04-12)
48 void proto_register_websocket(void);
49 void proto_reg_handoff_websocket(void);
51 static dissector_handle_t text_lines_handle;
52 static dissector_handle_t json_handle;
53 static dissector_handle_t sip_handle;
55 #define WEBSOCKET_NONE 0
56 #define WEBSOCKET_TEXT 1
57 #define WEBSOCKET_JSON 2
58 #define WEBSOCKET_SIP 3
60 static gint pref_text_type = WEBSOCKET_NONE;
61 static gboolean pref_decompress = TRUE;
64 const char *subprotocol;
66 gboolean permessage_deflate;
68 gboolean permessage_deflate_ok;
71 z_streamp server_take_over_context;
72 z_streamp client_take_over_context;
78 guint8 *decompr_payload;
83 /* Initialize the protocol and registered fields */
84 static int proto_websocket = -1;
85 static int proto_http = -1;
87 static int hf_ws_fin = -1;
88 static int hf_ws_reserved = -1;
89 static int hf_ws_pmc = -1;
90 static int hf_ws_opcode = -1;
91 static int hf_ws_mask = -1;
92 static int hf_ws_payload_length = -1;
93 static int hf_ws_payload_length_ext_16 = -1;
94 static int hf_ws_payload_length_ext_64 = -1;
95 static int hf_ws_masking_key = -1;
96 static int hf_ws_payload = -1;
97 static int hf_ws_masked_payload = -1;
98 static int hf_ws_payload_continue = -1;
99 static int hf_ws_payload_close = -1;
100 static int hf_ws_payload_close_status_code = -1;
101 static int hf_ws_payload_close_reason = -1;
102 static int hf_ws_payload_ping = -1;
103 static int hf_ws_payload_pong = -1;
104 static int hf_ws_payload_unknown = -1;
106 static gint ett_ws = -1;
107 static gint ett_ws_pl = -1;
108 static gint ett_ws_mask = -1;
109 static gint ett_ws_control_close = -1;
111 static expert_field ei_ws_payload_unknown = EI_INIT;
112 static expert_field ei_ws_decompression_failed = EI_INIT;
114 #define WS_CONTINUE 0x0
116 #define WS_BINARY 0x2
121 static const value_string ws_opcode_vals[] = {
122 { WS_CONTINUE, "Continuation" },
124 { WS_BINARY, "Binary" },
125 { WS_CLOSE, "Connection Close" },
131 #define MASK_WS_FIN 0x80
132 #define MASK_WS_RSV 0x70
133 #define MASK_WS_RSV1 0x40
134 #define MASK_WS_OPCODE 0x0F
135 #define MASK_WS_MASK 0x80
136 #define MASK_WS_PAYLOAD_LEN 0x7F
138 static const value_string ws_close_status_code_vals[] = {
139 { 1000, "Normal Closure" },
140 { 1001, "Going Away" },
141 { 1002, "Protocol error" },
142 { 1003, "Unsupported Data" },
143 { 1004, "---Reserved----" },
144 { 1005, "No Status Rcvd" },
145 { 1006, "Abnormal Closure" },
146 { 1007, "Invalid frame payload data" },
147 { 1008, "Policy Violation" },
148 { 1009, "Message Too Big" },
149 { 1010, "Mandatory Ext." },
150 { 1011, "Internal Server" },
151 { 1015, "TLS handshake" },
155 static dissector_table_t port_subdissector_table;
156 static dissector_table_t protocol_subdissector_table;
157 static heur_dissector_list_t heur_subdissector_list;
159 #define MAX_UNMASKED_LEN (1024 * 256)
161 tvb_unmasked(tvbuff_t *tvb, packet_info *pinfo, const guint offset, guint payload_length, const guint8 *masking_key)
166 const guint8 *data_mask;
167 guint unmasked_length = payload_length > MAX_UNMASKED_LEN ? MAX_UNMASKED_LEN : payload_length;
169 data_unmask = (gchar *)wmem_alloc(pinfo->pool, unmasked_length);
170 data_mask = tvb_get_ptr(tvb, offset, unmasked_length);
171 /* Unmasked(XOR) Data... */
172 for(i=0; i < unmasked_length; i++) {
173 data_unmask[i] = data_mask[i] ^ masking_key[i%4];
176 return tvb_new_real_data(data_unmask, unmasked_length, payload_length);
181 websocket_extract_wbits(const gchar *str)
186 if (str && ws_strtou8(str, &end, &wbits) &&
187 (*end == '\0' || strchr(";\t ", *end))) {
190 } else if (wbits > 15) {
200 websocket_zalloc(void *opaque _U_, unsigned int items, unsigned int size)
202 return wmem_alloc(wmem_file_scope(), items*size);
206 websocket_zfree(void *opaque _U_, void *addr)
208 wmem_free(wmem_file_scope(), addr);
212 websocket_init_z_stream_context(gint8 wbits)
214 z_streamp z_strm = wmem_new0(wmem_file_scope(), z_stream);
216 z_strm->zalloc = websocket_zalloc;
217 z_strm->zfree = websocket_zfree;
219 if (inflateInit2(z_strm, wbits) != Z_OK) {
221 wmem_free(wmem_file_scope(), z_strm);
228 * Decompress the given buffer using the given zlib context. On success, the
229 * (possibly empty) buffer is stored as "proto data" and TRUE is returned.
230 * Otherwise FALSE is returned.
233 websocket_uncompress(tvbuff_t *tvb, packet_info *pinfo, z_streamp z_strm, tvbuff_t **uncompressed_tvb)
236 * Decompression a message: append "0x00 0x00 0xff 0xff" to the end of
237 * message, then apply DEFLATE to the result.
238 * https://tools.ietf.org/html/rfc7692#section-7.2.2
240 guint8 *decompr_payload = NULL;
241 guint decompr_len = 0;
242 guint compr_len, decompr_buf_len;
243 guint8 *compr_payload, *decompr_buf;
246 compr_len = tvb_captured_length(tvb) + 4;
247 compr_payload = (guint8 *)wmem_alloc(wmem_packet_scope(), compr_len);
248 tvb_memcpy(tvb, compr_payload, 0, compr_len-4);
249 compr_payload[compr_len-4] = compr_payload[compr_len-3] = 0x00;
250 compr_payload[compr_len-2] = compr_payload[compr_len-1] = 0xff;
251 decompr_buf_len = 2*compr_len;
252 decompr_buf = (guint8 *)wmem_alloc(wmem_packet_scope(), decompr_buf_len);
254 z_strm->next_in = compr_payload;
255 z_strm->avail_in = compr_len;
256 /* Decompress all available data. */
258 z_strm->next_out = decompr_buf;
259 z_strm->avail_out = decompr_buf_len;
261 err = inflate(z_strm, Z_SYNC_FLUSH);
263 if (err == Z_OK || err == Z_STREAM_END || err == Z_BUF_ERROR) {
264 guint avail_bytes = decompr_buf_len - z_strm->avail_out;
266 decompr_payload = (guint8 *)wmem_realloc(wmem_file_scope(), decompr_payload,
267 decompr_len + avail_bytes);
268 memcpy(&decompr_payload[decompr_len], decompr_buf, avail_bytes);
269 decompr_len += avail_bytes;
272 } while (err == Z_OK);
274 if (err == Z_STREAM_END || err == Z_BUF_ERROR) {
275 /* Data was (partially) uncompressed. */
276 websocket_packet_t *pkt_info = wmem_new0(wmem_file_scope(), websocket_packet_t);
277 if (decompr_len > 0) {
278 pkt_info->decompr_payload = decompr_payload;
279 pkt_info->decompr_len = decompr_len;
280 *uncompressed_tvb = tvb_new_real_data(decompr_payload, decompr_len, decompr_len);
282 p_add_proto_data(wmem_file_scope(), pinfo, proto_websocket, 0, pkt_info);
285 /* decompression failed */
286 wmem_free(wmem_file_scope(), decompr_payload);
293 dissect_websocket_control_frame(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint8 opcode)
297 const guint offset = 0, length = tvb_reported_length(tvb);
300 case WS_CLOSE: /* Close */
301 ti = proto_tree_add_item(tree, hf_ws_payload_close, tvb, offset, length, ENC_NA);
302 subtree = proto_item_add_subtree(ti, ett_ws_control_close);
303 /* Close frame MAY contain a body. */
305 proto_tree_add_item(subtree, hf_ws_payload_close_status_code, tvb, offset, 2, ENC_BIG_ENDIAN);
307 proto_tree_add_item(subtree, hf_ws_payload_close_reason, tvb, offset+2, length-2, ENC_UTF_8|ENC_NA);
311 case WS_PING: /* Ping */
312 proto_tree_add_item(tree, hf_ws_payload_ping, tvb, offset, length, ENC_NA);
315 case WS_PONG: /* Pong */
316 proto_tree_add_item(tree, hf_ws_payload_pong, tvb, offset, length, ENC_NA);
319 default: /* Unknown */
320 ti = proto_tree_add_item(tree, hf_ws_payload_unknown, tvb, offset, length, ENC_NA);
321 expert_add_info_format(pinfo, ti, &ei_ws_payload_unknown, "Dissector for Websocket Opcode (%d)"
322 " code not implemented, Contact Wireshark developers"
323 " if you want this supported", opcode);
329 dissect_websocket_data_frame(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_tree *pl_tree, guint8 opcode, websocket_conv_t *websocket_conv, gboolean pmc _U_)
332 dissector_handle_t handle = NULL;
333 heur_dtbl_entry_t *hdtbl_entry;
335 /* try to find a dissector which accepts the data. */
336 if (websocket_conv->subprotocol) {
337 handle = dissector_get_string_handle(protocol_subdissector_table, websocket_conv->subprotocol);
338 } else if (websocket_conv->server_port) {
339 handle = dissector_get_uint_handle(port_subdissector_table, websocket_conv->server_port);
343 if (websocket_conv->permessage_deflate_ok && pmc) {
344 tvbuff_t *uncompressed = NULL;
345 gboolean uncompress_ok = FALSE;
347 if (!PINFO_FD_VISITED(pinfo)) {
351 if (pinfo->destport == websocket_conv->server_port) {
352 z_strm = websocket_conv->server_take_over_context;
353 wbits = websocket_conv->server_wbits;
355 z_strm = websocket_conv->client_take_over_context;
356 wbits = websocket_conv->client_wbits;
360 uncompress_ok = websocket_uncompress(tvb, pinfo, z_strm, &uncompressed);
362 /* no context take over, initialize a new context */
363 z_strm = wmem_new0(wmem_packet_scope(), z_stream);
364 if (inflateInit2(z_strm, wbits) == Z_OK) {
365 uncompress_ok = websocket_uncompress(tvb, pinfo, z_strm, &uncompressed);
370 websocket_packet_t *pkt_info =
371 (websocket_packet_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_websocket, 0);
373 uncompress_ok = TRUE;
374 if (pkt_info->decompr_len > 0) {
375 uncompressed = tvb_new_real_data(pkt_info->decompr_payload, pkt_info->decompr_len, pkt_info->decompr_len);
380 if (!uncompress_ok) {
381 proto_tree_add_expert(tree, pinfo, &ei_ws_decompression_failed, tvb, 0, -1);
385 add_new_data_source(pinfo, uncompressed, "Decompressed payload");
392 call_dissector_only(handle, tvb, pinfo, tree, NULL);
393 return; /* handle found, assume dissector took care of it. */
394 } else if (dissector_try_heuristic(heur_subdissector_list, tvb, pinfo, tree, &hdtbl_entry, NULL)) {
395 return; /* heuristics dissector handled it. */
398 /* no dissector wanted it, try to print something appropriate. */
400 case WS_TEXT: /* Text */
402 const gchar *saved_match_string = pinfo->match_string;
404 pinfo->match_string = NULL;
405 switch (pref_text_type) {
409 /* Assume that most text protocols are line-based. */
410 call_dissector(text_lines_handle, tvb, pinfo, tree);
413 call_dissector(json_handle, tvb, pinfo, tree);
416 call_dissector(sip_handle, tvb, pinfo, tree);
419 pinfo->match_string = saved_match_string;
423 case WS_BINARY: /* Binary */
424 call_data_dissector(tvb, pinfo, tree);
427 default: /* Unknown */
428 ti = proto_tree_add_item(pl_tree, hf_ws_payload_unknown, tvb, 0, -1, ENC_NA);
429 expert_add_info_format(pinfo, ti, &ei_ws_payload_unknown, "Dissector for Websocket Opcode (%d)"
430 " code not implemented, Contact Wireshark developers"
431 " if you want this supported", opcode);
437 websocket_parse_extensions(websocket_conv_t *websocket_conv, const char *str)
440 * Grammar for the header:
442 * Sec-WebSocket-Extensions = extension-list
443 * extension-list = 1#extension
444 * extension = extension-token *( ";" extension-param )
445 * extension-token = registered-token
446 * registered-token = token
447 * extension-param = token [ "=" (token | quoted-string) ]
451 * RFC 7692 permessage-deflate parsing.
454 websocket_conv->permessage_deflate = !!strstr(str, "permessage-deflate");
456 websocket_conv->permessage_deflate_ok = pref_decompress &&
457 websocket_conv->permessage_deflate;
458 if (websocket_conv->permessage_deflate_ok) {
459 websocket_conv->server_wbits =
460 websocket_extract_wbits(strstr(str, "server_max_window_bits="));
461 if (!strstr(str, "server_no_context_takeover")) {
462 websocket_conv->server_take_over_context =
463 websocket_init_z_stream_context(websocket_conv->server_wbits);
465 websocket_conv->client_wbits =
466 websocket_extract_wbits(strstr(str, "client_max_window_bits="));
467 if (!strstr(str, "client_no_context_takeover")) {
468 websocket_conv->client_take_over_context =
469 websocket_init_z_stream_context(websocket_conv->client_wbits);
476 dissect_websocket_payload(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_tree *ws_tree, guint8 opcode, websocket_conv_t *websocket_conv, gboolean pmc)
478 const guint offset = 0, length = tvb_reported_length(tvb);
481 tvbuff_t *tvb_appdata;
484 ti = proto_tree_add_item(ws_tree, hf_ws_payload, tvb, offset, length, ENC_NA);
485 pl_tree = proto_item_add_subtree(ti, ett_ws_pl);
488 /* TODO: Add dissector of Extension (not extension available for the moment...) */
491 /* Application Data */
492 if (opcode == WS_CONTINUE) {
493 proto_tree_add_item(tree, hf_ws_payload_continue, tvb, offset, length, ENC_NA);
494 /* TODO: Add Fragmentation support (needs FIN bit)
495 * https://tools.ietf.org/html/rfc6455#section-5.4 */
498 /* Right now this is exactly the same, this may change when exts. are added.
499 tvb_appdata = tvb_new_subset_length_caplen(tvb, offset, length, length);
503 if (opcode & 8) { /* Control frames have MSB set. */
504 dissect_websocket_control_frame(tvb_appdata, pinfo, pl_tree, opcode);
506 dissect_websocket_data_frame(tvb_appdata, pinfo, tree, pl_tree, opcode, websocket_conv, pmc);
512 dissect_websocket_frame(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
514 proto_item *ti, *ti_len;
517 guint short_length, payload_length;
518 guint payload_offset, mask_offset;
520 const guint8 *masking_key = NULL;
521 tvbuff_t *tvb_payload;
522 conversation_t *conv;
523 websocket_conv_t *websocket_conv;
524 gboolean pmc = FALSE;
527 * If this is a new Websocket session, try to parse HTTP Sec-Websocket-*
530 conv = find_or_create_conversation(pinfo);
531 websocket_conv = (websocket_conv_t *)conversation_get_proto_data(conv, proto_websocket);
532 if (!websocket_conv) {
533 websocket_conv = wmem_new0(wmem_file_scope(), websocket_conv_t);
535 http_conv_t *http_conv = (http_conv_t *)conversation_get_proto_data(conv, proto_http);
537 websocket_conv->subprotocol = http_conv->websocket_protocol;
538 websocket_conv->server_port = http_conv->server_port;
539 if ( http_conv->websocket_extensions) {
540 websocket_parse_extensions(websocket_conv, http_conv->websocket_extensions);
544 conversation_add_proto_data(conv, proto_websocket, websocket_conv);
547 short_length = tvb_get_guint8(tvb, 1) & MASK_WS_PAYLOAD_LEN;
549 if (short_length == 126) {
550 payload_length = tvb_get_ntohs(tvb, 2);
552 } else if (short_length == 127) {
553 /* warning C4244: '=' : conversion from 'guint64' to 'guint ', possible loss of data */
554 payload_length = (guint)tvb_get_ntoh64(tvb, 2);
557 payload_length = short_length;
561 mask = (tvb_get_guint8(tvb, 1) & MASK_WS_MASK) != 0;
562 payload_offset = mask_offset + (mask ? 4 : 0);
564 col_set_str(pinfo->cinfo, COL_PROTOCOL, "WebSocket");
565 col_set_str(pinfo->cinfo, COL_INFO, "WebSocket");
567 ti = proto_tree_add_item(tree, proto_websocket, tvb, 0, payload_offset, ENC_NA);
568 ws_tree = proto_item_add_subtree(ti, ett_ws);
571 proto_tree_add_item(ws_tree, hf_ws_fin, tvb, 0, 1, ENC_NA);
572 fin = (tvb_get_guint8(tvb, 0) & MASK_WS_FIN) >> 4;
573 proto_tree_add_item(ws_tree, hf_ws_reserved, tvb, 0, 1, ENC_BIG_ENDIAN);
574 if (websocket_conv->permessage_deflate) {
575 /* RSV1 is Per-Message Compressed bit (RFC 7692). */
576 pmc = !!(tvb_get_guint8(tvb, 0) & MASK_WS_RSV1);
577 proto_tree_add_item(ws_tree, hf_ws_pmc, tvb, 0, 1, ENC_BIG_ENDIAN);
581 proto_tree_add_item(ws_tree, hf_ws_opcode, tvb, 0, 1, ENC_BIG_ENDIAN);
582 opcode = tvb_get_guint8(tvb, 0) & MASK_WS_OPCODE;
583 col_append_fstr(pinfo->cinfo, COL_INFO, " %s", val_to_str_const(opcode, ws_opcode_vals, "Unknown Opcode"));
584 col_append_str(pinfo->cinfo, COL_INFO, fin ? " [FIN]" : " ");
586 /* Add Mask bit to the tree */
587 proto_tree_add_item(ws_tree, hf_ws_mask, tvb, 1, 1, ENC_NA);
588 col_append_str(pinfo->cinfo, COL_INFO, mask ? " [MASKED]" : " ");
590 /* (Extended) Payload Length */
591 ti_len = proto_tree_add_item(ws_tree, hf_ws_payload_length, tvb, 1, 1, ENC_BIG_ENDIAN);
592 if (short_length == 126) {
593 proto_item_append_text(ti_len, " Extended Payload Length (16 bits)");
594 proto_tree_add_item(ws_tree, hf_ws_payload_length_ext_16, tvb, 2, 2, ENC_BIG_ENDIAN);
596 else if (short_length == 127) {
597 proto_item_append_text(ti_len, " Extended Payload Length (64 bits)");
598 proto_tree_add_item(ws_tree, hf_ws_payload_length_ext_64, tvb, 2, 8, ENC_BIG_ENDIAN);
603 proto_tree_add_item(ws_tree, hf_ws_masking_key, tvb, mask_offset, 4, ENC_NA);
604 masking_key = tvb_get_ptr(tvb, mask_offset, 4);
607 if (payload_length > 0) {
608 /* Always unmask payload data before analysing it. */
610 proto_tree_add_item(ws_tree, hf_ws_masked_payload, tvb, payload_offset, payload_length, ENC_NA);
611 tvb_payload = tvb_unmasked(tvb, pinfo, payload_offset, payload_length, masking_key);
612 tvb_set_child_real_data_tvbuff(tvb, tvb_payload);
613 add_new_data_source(pinfo, tvb_payload, "Unmasked data");
615 tvb_payload = tvb_new_subset_length_caplen(tvb, payload_offset, payload_length, payload_length);
617 dissect_websocket_payload(tvb_payload, pinfo, tree, ws_tree, opcode, websocket_conv, pmc);
620 return tvb_captured_length(tvb);
624 get_websocket_frame_length(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
626 guint frame_length, payload_length;
629 frame_length = 2; /* flags, opcode and Payload length */
630 mask = tvb_get_guint8(tvb, offset + 1) & MASK_WS_MASK;
632 payload_length = tvb_get_guint8(tvb, offset + 1) & MASK_WS_PAYLOAD_LEN;
633 offset += 2; /* Skip flags, opcode and Payload length */
635 /* Check for Extended Payload Length. */
636 if (payload_length == 126) {
637 if (tvb_reported_length_remaining(tvb, offset) < 2)
638 return 0; /* Need more data. */
640 payload_length = tvb_get_ntohs(tvb, offset);
641 frame_length += 2; /* Extended payload length */
642 } else if (payload_length == 127) {
643 if (tvb_reported_length_remaining(tvb, offset) < 8)
644 return 0; /* Need more data. */
646 payload_length = (guint)tvb_get_ntoh64(tvb, offset);
647 frame_length += 8; /* Extended payload length */
651 frame_length += 4; /* Masking-key */
652 frame_length += payload_length; /* Payload data */
657 dissect_websocket(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
659 /* Need at least two bytes for flags, opcode and Payload length. */
660 tcp_dissect_pdus(tvb, pinfo, tree, TRUE, 2,
661 get_websocket_frame_length, dissect_websocket_frame, data);
662 return tvb_captured_length(tvb);
667 proto_register_websocket(void)
670 static hf_register_info hf[] = {
672 { "Fin", "websocket.fin",
673 FT_BOOLEAN, 8, NULL, MASK_WS_FIN,
674 "Indicates that this is the final fragment in a message", HFILL }
677 { "Reserved", "websocket.rsv",
678 FT_UINT8, BASE_HEX, NULL, MASK_WS_RSV,
679 "Must be zero", HFILL }
682 { "Per-Message Compressed", "websocket.pmc",
683 FT_BOOLEAN, 8, NULL, MASK_WS_RSV1,
684 "Whether a message is compressed or not", HFILL }
687 { "Opcode", "websocket.opcode",
688 FT_UINT8, BASE_DEC, VALS(ws_opcode_vals), MASK_WS_OPCODE,
689 "Defines the interpretation of the Payload data", HFILL }
692 { "Mask", "websocket.mask",
693 FT_BOOLEAN, 8, NULL, MASK_WS_MASK,
694 "Defines whether the Payload data is masked", HFILL }
696 { &hf_ws_payload_length,
697 { "Payload length", "websocket.payload_length",
698 FT_UINT8, BASE_DEC, NULL, MASK_WS_PAYLOAD_LEN,
699 "The length of the Payload data", HFILL }
701 { &hf_ws_payload_length_ext_16,
702 { "Extended Payload length (16 bits)", "websocket.payload_length_ext_16",
703 FT_UINT16, BASE_DEC, NULL, 0x0,
704 "The length (16 bits) of the Payload data", HFILL }
706 { &hf_ws_payload_length_ext_64,
707 { "Extended Payload length (64 bits)", "websocket.payload_length_ext_64",
708 FT_UINT64, BASE_DEC, NULL, 0x0,
709 "The length (64 bits) of the Payload data", HFILL }
711 { &hf_ws_masking_key,
712 { "Masking-Key", "websocket.masking_key",
713 FT_BYTES, BASE_NONE, NULL, 0x0,
714 "All frames sent from the client to the server are masked by a 32-bit value that is contained within the frame", HFILL }
717 { "Payload", "websocket.payload",
718 FT_NONE, BASE_NONE, NULL, 0x0,
719 "Payload (after unmasking)", HFILL }
721 { &hf_ws_masked_payload,
722 { "Masked payload", "websocket.masked_payload",
723 FT_NONE, BASE_NONE, NULL, 0x0,
726 { &hf_ws_payload_continue,
727 { "Continue", "websocket.payload.continue",
728 FT_BYTES, BASE_NONE, NULL, 0x0,
731 { &hf_ws_payload_close,
732 { "Close", "websocket.payload.close",
733 FT_NONE, BASE_NONE, NULL, 0x0,
736 { &hf_ws_payload_close_status_code,
737 { "Status code", "websocket.payload.close.status_code",
738 FT_UINT16, BASE_DEC, VALS(ws_close_status_code_vals), 0x0,
741 { &hf_ws_payload_close_reason,
742 { "Reason", "websocket.payload.close.reason",
743 FT_STRING, BASE_NONE, NULL, 0x0,
746 { &hf_ws_payload_ping,
747 { "Ping", "websocket.payload.ping",
748 FT_BYTES, BASE_NONE, NULL, 0x0,
751 { &hf_ws_payload_pong,
752 { "Pong", "websocket.payload.pong",
753 FT_BYTES, BASE_NONE, NULL, 0x0,
756 { &hf_ws_payload_unknown,
757 { "Unknown", "websocket.payload.unknown",
758 FT_BYTES, BASE_NONE, NULL, 0x0,
764 static gint *ett[] = {
768 &ett_ws_control_close,
771 static ei_register_info ei[] = {
772 { &ei_ws_payload_unknown, { "websocket.payload.unknown.expert", PI_UNDECODED, PI_NOTE, "Dissector for Websocket Opcode", EXPFILL }},
773 { &ei_ws_decompression_failed, { "websocket.decompression.failed.expert", PI_PROTOCOL, PI_WARN, "Decompression failed", EXPFILL }},
776 static const enum_val_t text_types[] = {
777 {"None", "No subdissection", WEBSOCKET_NONE},
778 {"Line based text", "Line based text", WEBSOCKET_TEXT},
779 {"As JSON", "As json", WEBSOCKET_JSON},
780 {"As SIP", "As SIP", WEBSOCKET_SIP},
784 module_t *websocket_module;
785 expert_module_t* expert_websocket;
787 proto_websocket = proto_register_protocol("WebSocket",
788 "WebSocket", "websocket");
791 * Heuristic dissectors SHOULD register themselves in
792 * this table using the standard heur_dissector_add()
795 heur_subdissector_list = register_heur_dissector_list("ws", proto_websocket);
797 port_subdissector_table = register_dissector_table("ws.port",
798 "TCP port for protocols using WebSocket", proto_websocket, FT_UINT16, BASE_DEC);
800 protocol_subdissector_table = register_dissector_table("ws.protocol",
801 "Negotiated WebSocket protocol", proto_websocket, FT_STRING, BASE_NONE);
803 proto_register_field_array(proto_websocket, hf, array_length(hf));
804 proto_register_subtree_array(ett, array_length(ett));
805 expert_websocket = expert_register_protocol(proto_websocket);
806 expert_register_field_array(expert_websocket, ei, array_length(ei));
808 register_dissector("websocket", dissect_websocket, proto_websocket);
810 websocket_module = prefs_register_protocol(proto_websocket, proto_reg_handoff_websocket);
812 prefs_register_enum_preference(websocket_module, "text_type",
813 "Dissect websocket text as",
814 "Select dissector for websocket text",
815 &pref_text_type, text_types, WEBSOCKET_NONE);
816 prefs_register_bool_preference(websocket_module, "decompress",
817 "Try to decompress permessage-deflate payload", NULL, &pref_decompress);
821 proto_reg_handoff_websocket(void)
823 text_lines_handle = find_dissector_add_dependency("data-text-lines", proto_websocket);
824 json_handle = find_dissector_add_dependency("json", proto_websocket);
825 sip_handle = find_dissector_add_dependency("sip", proto_websocket);
827 proto_http = proto_get_id_by_filter_name("http");
830 * Editor modelines - https://www.wireshark.org/tools/modelines.html
835 * indent-tabs-mode: nil
838 * vi: set shiftwidth=2 tabstop=8 expandtab:
839 * :indentSize=2:tabSize=8:noTabs=true: