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 * SPDX-License-Identifier: GPL-2.0-or-later
15 #include <epan/conversation.h>
16 #include <epan/proto_data.h>
17 #include <epan/packet.h>
18 #include <epan/expert.h>
19 #include <epan/prefs.h>
20 #include <wsutil/strtoi.h>
22 #include "packet-http.h"
23 #include "packet-tcp.h"
31 * The information used comes from:
32 * RFC6455: The WebSocket Protocol
33 * http://www.iana.org/assignments/websocket (last updated 2012-04-12)
36 void proto_register_websocket(void);
37 void proto_reg_handoff_websocket(void);
39 static dissector_handle_t websocket_handle;
40 static dissector_handle_t text_lines_handle;
41 static dissector_handle_t json_handle;
42 static dissector_handle_t sip_handle;
44 #define WEBSOCKET_NONE 0
45 #define WEBSOCKET_TEXT 1
46 #define WEBSOCKET_JSON 2
47 #define WEBSOCKET_SIP 3
49 static gint pref_text_type = WEBSOCKET_NONE;
50 static gboolean pref_decompress = TRUE;
53 const char *subprotocol;
55 gboolean permessage_deflate;
57 gboolean permessage_deflate_ok;
60 z_streamp server_take_over_context;
61 z_streamp client_take_over_context;
67 guint8 *decompr_payload;
72 /* Initialize the protocol and registered fields */
73 static int proto_websocket = -1;
74 static int proto_http = -1;
76 static int hf_ws_fin = -1;
77 static int hf_ws_reserved = -1;
78 static int hf_ws_pmc = -1;
79 static int hf_ws_opcode = -1;
80 static int hf_ws_mask = -1;
81 static int hf_ws_payload_length = -1;
82 static int hf_ws_payload_length_ext_16 = -1;
83 static int hf_ws_payload_length_ext_64 = -1;
84 static int hf_ws_masking_key = -1;
85 static int hf_ws_payload = -1;
86 static int hf_ws_masked_payload = -1;
87 static int hf_ws_payload_continue = -1;
88 static int hf_ws_payload_close = -1;
89 static int hf_ws_payload_close_status_code = -1;
90 static int hf_ws_payload_close_reason = -1;
91 static int hf_ws_payload_ping = -1;
92 static int hf_ws_payload_pong = -1;
93 static int hf_ws_payload_unknown = -1;
95 static gint ett_ws = -1;
96 static gint ett_ws_pl = -1;
97 static gint ett_ws_mask = -1;
98 static gint ett_ws_control_close = -1;
100 static expert_field ei_ws_payload_unknown = EI_INIT;
101 static expert_field ei_ws_decompression_failed = EI_INIT;
103 #define WS_CONTINUE 0x0
105 #define WS_BINARY 0x2
110 static const value_string ws_opcode_vals[] = {
111 { WS_CONTINUE, "Continuation" },
113 { WS_BINARY, "Binary" },
114 { WS_CLOSE, "Connection Close" },
120 #define MASK_WS_FIN 0x80
121 #define MASK_WS_RSV 0x70
122 #define MASK_WS_RSV1 0x40
123 #define MASK_WS_OPCODE 0x0F
124 #define MASK_WS_MASK 0x80
125 #define MASK_WS_PAYLOAD_LEN 0x7F
127 static const value_string ws_close_status_code_vals[] = {
128 { 1000, "Normal Closure" },
129 { 1001, "Going Away" },
130 { 1002, "Protocol error" },
131 { 1003, "Unsupported Data" },
132 { 1004, "---Reserved----" },
133 { 1005, "No Status Rcvd" },
134 { 1006, "Abnormal Closure" },
135 { 1007, "Invalid frame payload data" },
136 { 1008, "Policy Violation" },
137 { 1009, "Message Too Big" },
138 { 1010, "Mandatory Ext." },
139 { 1011, "Internal Server" },
140 { 1015, "TLS handshake" },
144 static dissector_table_t port_subdissector_table;
145 static dissector_table_t protocol_subdissector_table;
146 static heur_dissector_list_t heur_subdissector_list;
148 #define MAX_UNMASKED_LEN (1024 * 256)
150 tvb_unmasked(tvbuff_t *tvb, packet_info *pinfo, const guint offset, guint payload_length, const guint8 *masking_key)
155 const guint8 *data_mask;
156 guint unmasked_length = payload_length > MAX_UNMASKED_LEN ? MAX_UNMASKED_LEN : payload_length;
158 data_unmask = (gchar *)wmem_alloc(pinfo->pool, unmasked_length);
159 data_mask = tvb_get_ptr(tvb, offset, unmasked_length);
160 /* Unmasked(XOR) Data... */
161 for(i=0; i < unmasked_length; i++) {
162 data_unmask[i] = data_mask[i] ^ masking_key[i%4];
165 return tvb_new_real_data(data_unmask, unmasked_length, payload_length);
170 websocket_extract_wbits(const gchar *str)
175 if (str && ws_strtou8(str, &end, &wbits) &&
176 (*end == '\0' || strchr(";\t ", *end))) {
179 } else if (wbits > 15) {
189 websocket_zalloc(void *opaque _U_, unsigned int items, unsigned int size)
191 return wmem_alloc(wmem_file_scope(), items*size);
195 websocket_zfree(void *opaque _U_, void *addr)
197 wmem_free(wmem_file_scope(), addr);
201 websocket_init_z_stream_context(gint8 wbits)
203 z_streamp z_strm = wmem_new0(wmem_file_scope(), z_stream);
205 z_strm->zalloc = websocket_zalloc;
206 z_strm->zfree = websocket_zfree;
208 if (inflateInit2(z_strm, wbits) != Z_OK) {
210 wmem_free(wmem_file_scope(), z_strm);
217 * Decompress the given buffer using the given zlib context. On success, the
218 * (possibly empty) buffer is stored as "proto data" and TRUE is returned.
219 * Otherwise FALSE is returned.
222 websocket_uncompress(tvbuff_t *tvb, packet_info *pinfo, z_streamp z_strm, tvbuff_t **uncompressed_tvb, guint32 key)
225 * Decompression a message: append "0x00 0x00 0xff 0xff" to the end of
226 * message, then apply DEFLATE to the result.
227 * https://tools.ietf.org/html/rfc7692#section-7.2.2
229 guint8 *decompr_payload = NULL;
230 guint decompr_len = 0;
231 guint compr_len, decompr_buf_len;
232 guint8 *compr_payload, *decompr_buf;
235 compr_len = tvb_captured_length(tvb) + 4;
236 compr_payload = (guint8 *)wmem_alloc(wmem_packet_scope(), compr_len);
237 tvb_memcpy(tvb, compr_payload, 0, compr_len-4);
238 compr_payload[compr_len-4] = compr_payload[compr_len-3] = 0x00;
239 compr_payload[compr_len-2] = compr_payload[compr_len-1] = 0xff;
240 decompr_buf_len = 2*compr_len;
241 decompr_buf = (guint8 *)wmem_alloc(wmem_packet_scope(), decompr_buf_len);
243 z_strm->next_in = compr_payload;
244 z_strm->avail_in = compr_len;
245 /* Decompress all available data. */
247 z_strm->next_out = decompr_buf;
248 z_strm->avail_out = decompr_buf_len;
250 err = inflate(z_strm, Z_SYNC_FLUSH);
252 if (err == Z_OK || err == Z_STREAM_END || err == Z_BUF_ERROR) {
253 guint avail_bytes = decompr_buf_len - z_strm->avail_out;
255 decompr_payload = (guint8 *)wmem_realloc(wmem_file_scope(), decompr_payload,
256 decompr_len + avail_bytes);
257 memcpy(&decompr_payload[decompr_len], decompr_buf, avail_bytes);
258 decompr_len += avail_bytes;
261 } while (err == Z_OK);
263 if (err == Z_STREAM_END || err == Z_BUF_ERROR) {
264 /* Data was (partially) uncompressed. */
265 websocket_packet_t *pkt_info = wmem_new0(wmem_file_scope(), websocket_packet_t);
266 if (decompr_len > 0) {
267 pkt_info->decompr_payload = decompr_payload;
268 pkt_info->decompr_len = decompr_len;
269 *uncompressed_tvb = tvb_new_real_data(decompr_payload, decompr_len, decompr_len);
271 p_add_proto_data(wmem_file_scope(), pinfo, proto_websocket, key, pkt_info);
274 /* decompression failed */
275 wmem_free(wmem_file_scope(), decompr_payload);
282 dissect_websocket_control_frame(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint8 opcode)
286 const guint offset = 0, length = tvb_reported_length(tvb);
289 case WS_CLOSE: /* Close */
290 ti = proto_tree_add_item(tree, hf_ws_payload_close, tvb, offset, length, ENC_NA);
291 subtree = proto_item_add_subtree(ti, ett_ws_control_close);
292 /* Close frame MAY contain a body. */
294 proto_tree_add_item(subtree, hf_ws_payload_close_status_code, tvb, offset, 2, ENC_BIG_ENDIAN);
296 proto_tree_add_item(subtree, hf_ws_payload_close_reason, tvb, offset+2, length-2, ENC_UTF_8|ENC_NA);
300 case WS_PING: /* Ping */
301 proto_tree_add_item(tree, hf_ws_payload_ping, tvb, offset, length, ENC_NA);
304 case WS_PONG: /* Pong */
305 proto_tree_add_item(tree, hf_ws_payload_pong, tvb, offset, length, ENC_NA);
308 default: /* Unknown */
309 ti = proto_tree_add_item(tree, hf_ws_payload_unknown, tvb, offset, length, ENC_NA);
310 expert_add_info_format(pinfo, ti, &ei_ws_payload_unknown, "Dissector for Websocket Opcode (%d)"
311 " code not implemented, Contact Wireshark developers"
312 " if you want this supported", opcode);
318 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_, gint raw_offset _U_)
321 dissector_handle_t handle = NULL;
322 heur_dtbl_entry_t *hdtbl_entry;
324 /* try to find a dissector which accepts the data. */
325 if (websocket_conv->subprotocol) {
326 handle = dissector_get_string_handle(protocol_subdissector_table, websocket_conv->subprotocol);
327 } else if (websocket_conv->server_port) {
328 handle = dissector_get_uint_handle(port_subdissector_table, websocket_conv->server_port);
332 if (websocket_conv->permessage_deflate_ok && pmc) {
333 tvbuff_t *uncompressed = NULL;
334 gboolean uncompress_ok = FALSE;
336 if (!PINFO_FD_VISITED(pinfo)) {
340 if (pinfo->destport == websocket_conv->server_port) {
341 z_strm = websocket_conv->server_take_over_context;
342 wbits = websocket_conv->server_wbits;
344 z_strm = websocket_conv->client_take_over_context;
345 wbits = websocket_conv->client_wbits;
349 uncompress_ok = websocket_uncompress(tvb, pinfo, z_strm, &uncompressed, raw_offset);
351 /* no context take over, initialize a new context */
352 z_strm = wmem_new0(wmem_packet_scope(), z_stream);
353 if (inflateInit2(z_strm, wbits) == Z_OK) {
354 uncompress_ok = websocket_uncompress(tvb, pinfo, z_strm, &uncompressed, raw_offset);
359 websocket_packet_t *pkt_info =
360 (websocket_packet_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_websocket, raw_offset);
362 uncompress_ok = TRUE;
363 if (pkt_info->decompr_len > 0) {
364 uncompressed = tvb_new_real_data(pkt_info->decompr_payload, pkt_info->decompr_len, pkt_info->decompr_len);
369 if (!uncompress_ok) {
370 proto_tree_add_expert(tree, pinfo, &ei_ws_decompression_failed, tvb, 0, -1);
374 add_new_data_source(pinfo, uncompressed, "Decompressed payload");
381 call_dissector_only(handle, tvb, pinfo, tree, NULL);
382 return; /* handle found, assume dissector took care of it. */
383 } else if (dissector_try_heuristic(heur_subdissector_list, tvb, pinfo, tree, &hdtbl_entry, NULL)) {
384 return; /* heuristics dissector handled it. */
387 /* no dissector wanted it, try to print something appropriate. */
389 case WS_TEXT: /* Text */
391 const gchar *saved_match_string = pinfo->match_string;
393 pinfo->match_string = NULL;
394 switch (pref_text_type) {
398 /* Assume that most text protocols are line-based. */
399 call_dissector(text_lines_handle, tvb, pinfo, tree);
402 call_dissector(json_handle, tvb, pinfo, tree);
405 call_dissector(sip_handle, tvb, pinfo, tree);
408 pinfo->match_string = saved_match_string;
412 case WS_BINARY: /* Binary */
413 call_data_dissector(tvb, pinfo, tree);
416 default: /* Unknown */
417 ti = proto_tree_add_item(pl_tree, hf_ws_payload_unknown, tvb, 0, -1, ENC_NA);
418 expert_add_info_format(pinfo, ti, &ei_ws_payload_unknown, "Dissector for Websocket Opcode (%d)"
419 " code not implemented, Contact Wireshark developers"
420 " if you want this supported", opcode);
426 websocket_parse_extensions(websocket_conv_t *websocket_conv, const char *str)
429 * Grammar for the header:
431 * Sec-WebSocket-Extensions = extension-list
432 * extension-list = 1#extension
433 * extension = extension-token *( ";" extension-param )
434 * extension-token = registered-token
435 * registered-token = token
436 * extension-param = token [ "=" (token | quoted-string) ]
440 * RFC 7692 permessage-deflate parsing.
443 websocket_conv->permessage_deflate = !!strstr(str, "permessage-deflate");
445 websocket_conv->permessage_deflate_ok = pref_decompress &&
446 websocket_conv->permessage_deflate;
447 if (websocket_conv->permessage_deflate_ok) {
448 websocket_conv->server_wbits =
449 websocket_extract_wbits(strstr(str, "server_max_window_bits="));
450 if (!strstr(str, "server_no_context_takeover")) {
451 websocket_conv->server_take_over_context =
452 websocket_init_z_stream_context(websocket_conv->server_wbits);
454 websocket_conv->client_wbits =
455 websocket_extract_wbits(strstr(str, "client_max_window_bits="));
456 if (!strstr(str, "client_no_context_takeover")) {
457 websocket_conv->client_take_over_context =
458 websocket_init_z_stream_context(websocket_conv->client_wbits);
465 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, gint raw_offset)
467 const guint offset = 0, length = tvb_reported_length(tvb);
470 tvbuff_t *tvb_appdata;
473 ti = proto_tree_add_item(ws_tree, hf_ws_payload, tvb, offset, length, ENC_NA);
474 pl_tree = proto_item_add_subtree(ti, ett_ws_pl);
477 /* TODO: Add dissector of Extension (not extension available for the moment...) */
480 /* Application Data */
481 if (opcode == WS_CONTINUE) {
482 proto_tree_add_item(tree, hf_ws_payload_continue, tvb, offset, length, ENC_NA);
483 /* TODO: Add Fragmentation support (needs FIN bit)
484 * https://tools.ietf.org/html/rfc6455#section-5.4 */
487 /* Right now this is exactly the same, this may change when exts. are added.
488 tvb_appdata = tvb_new_subset_length_caplen(tvb, offset, length, length);
492 if (opcode & 8) { /* Control frames have MSB set. */
493 dissect_websocket_control_frame(tvb_appdata, pinfo, pl_tree, opcode);
495 dissect_websocket_data_frame(tvb_appdata, pinfo, tree, pl_tree, opcode, websocket_conv, pmc, raw_offset);
501 dissect_websocket_frame(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
503 proto_item *ti, *ti_len;
506 guint short_length, payload_length;
507 guint payload_offset, mask_offset;
509 const guint8 *masking_key = NULL;
510 tvbuff_t *tvb_payload;
511 conversation_t *conv;
512 websocket_conv_t *websocket_conv;
513 gboolean pmc = FALSE;
516 * If this is a new Websocket session, try to parse HTTP Sec-Websocket-*
519 conv = find_or_create_conversation(pinfo);
520 websocket_conv = (websocket_conv_t *)conversation_get_proto_data(conv, proto_websocket);
521 if (!websocket_conv) {
522 websocket_conv = wmem_new0(wmem_file_scope(), websocket_conv_t);
524 http_conv_t *http_conv = (http_conv_t *)conversation_get_proto_data(conv, proto_http);
526 websocket_conv->subprotocol = http_conv->websocket_protocol;
527 websocket_conv->server_port = http_conv->server_port;
528 if ( http_conv->websocket_extensions) {
529 websocket_parse_extensions(websocket_conv, http_conv->websocket_extensions);
533 conversation_add_proto_data(conv, proto_websocket, websocket_conv);
536 short_length = tvb_get_guint8(tvb, 1) & MASK_WS_PAYLOAD_LEN;
538 if (short_length == 126) {
539 payload_length = tvb_get_ntohs(tvb, 2);
541 } else if (short_length == 127) {
542 /* warning C4244: '=' : conversion from 'guint64' to 'guint ', possible loss of data */
543 payload_length = (guint)tvb_get_ntoh64(tvb, 2);
546 payload_length = short_length;
550 mask = (tvb_get_guint8(tvb, 1) & MASK_WS_MASK) != 0;
551 payload_offset = mask_offset + (mask ? 4 : 0);
553 col_set_str(pinfo->cinfo, COL_PROTOCOL, "WebSocket");
554 col_set_str(pinfo->cinfo, COL_INFO, "WebSocket");
556 ti = proto_tree_add_item(tree, proto_websocket, tvb, 0, payload_offset, ENC_NA);
557 ws_tree = proto_item_add_subtree(ti, ett_ws);
560 proto_tree_add_item(ws_tree, hf_ws_fin, tvb, 0, 1, ENC_NA);
561 fin = (tvb_get_guint8(tvb, 0) & MASK_WS_FIN) >> 4;
562 proto_tree_add_item(ws_tree, hf_ws_reserved, tvb, 0, 1, ENC_BIG_ENDIAN);
563 if (websocket_conv->permessage_deflate) {
564 /* RSV1 is Per-Message Compressed bit (RFC 7692). */
565 pmc = !!(tvb_get_guint8(tvb, 0) & MASK_WS_RSV1);
566 proto_tree_add_item(ws_tree, hf_ws_pmc, tvb, 0, 1, ENC_BIG_ENDIAN);
570 proto_tree_add_item(ws_tree, hf_ws_opcode, tvb, 0, 1, ENC_BIG_ENDIAN);
571 opcode = tvb_get_guint8(tvb, 0) & MASK_WS_OPCODE;
572 col_append_fstr(pinfo->cinfo, COL_INFO, " %s", val_to_str_const(opcode, ws_opcode_vals, "Unknown Opcode"));
573 col_append_str(pinfo->cinfo, COL_INFO, fin ? " [FIN]" : " ");
575 /* Add Mask bit to the tree */
576 proto_tree_add_item(ws_tree, hf_ws_mask, tvb, 1, 1, ENC_NA);
577 col_append_str(pinfo->cinfo, COL_INFO, mask ? " [MASKED]" : " ");
579 /* (Extended) Payload Length */
580 ti_len = proto_tree_add_item(ws_tree, hf_ws_payload_length, tvb, 1, 1, ENC_BIG_ENDIAN);
581 if (short_length == 126) {
582 proto_item_append_text(ti_len, " Extended Payload Length (16 bits)");
583 proto_tree_add_item(ws_tree, hf_ws_payload_length_ext_16, tvb, 2, 2, ENC_BIG_ENDIAN);
585 else if (short_length == 127) {
586 proto_item_append_text(ti_len, " Extended Payload Length (64 bits)");
587 proto_tree_add_item(ws_tree, hf_ws_payload_length_ext_64, tvb, 2, 8, ENC_BIG_ENDIAN);
592 proto_tree_add_item(ws_tree, hf_ws_masking_key, tvb, mask_offset, 4, ENC_NA);
593 masking_key = tvb_get_ptr(tvb, mask_offset, 4);
596 if (payload_length > 0) {
597 /* Always unmask payload data before analysing it. */
599 proto_tree_add_item(ws_tree, hf_ws_masked_payload, tvb, payload_offset, payload_length, ENC_NA);
600 tvb_payload = tvb_unmasked(tvb, pinfo, payload_offset, payload_length, masking_key);
601 tvb_set_child_real_data_tvbuff(tvb, tvb_payload);
602 add_new_data_source(pinfo, tvb_payload, "Unmasked data");
604 tvb_payload = tvb_new_subset_length_caplen(tvb, payload_offset, payload_length, payload_length);
606 dissect_websocket_payload(tvb_payload, pinfo, tree, ws_tree, opcode, websocket_conv, pmc, tvb_raw_offset(tvb));
609 return tvb_captured_length(tvb);
613 get_websocket_frame_length(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
615 guint frame_length, payload_length;
618 frame_length = 2; /* flags, opcode and Payload length */
619 mask = tvb_get_guint8(tvb, offset + 1) & MASK_WS_MASK;
621 payload_length = tvb_get_guint8(tvb, offset + 1) & MASK_WS_PAYLOAD_LEN;
622 offset += 2; /* Skip flags, opcode and Payload length */
624 /* Check for Extended Payload Length. */
625 if (payload_length == 126) {
626 if (tvb_reported_length_remaining(tvb, offset) < 2)
627 return 0; /* Need more data. */
629 payload_length = tvb_get_ntohs(tvb, offset);
630 frame_length += 2; /* Extended payload length */
631 } else if (payload_length == 127) {
632 if (tvb_reported_length_remaining(tvb, offset) < 8)
633 return 0; /* Need more data. */
635 payload_length = (guint)tvb_get_ntoh64(tvb, offset);
636 frame_length += 8; /* Extended payload length */
640 frame_length += 4; /* Masking-key */
641 frame_length += payload_length; /* Payload data */
646 dissect_websocket(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
648 /* Need at least two bytes for flags, opcode and Payload length. */
649 tcp_dissect_pdus(tvb, pinfo, tree, TRUE, 2,
650 get_websocket_frame_length, dissect_websocket_frame, data);
651 return tvb_captured_length(tvb);
656 proto_register_websocket(void)
659 static hf_register_info hf[] = {
661 { "Fin", "websocket.fin",
662 FT_BOOLEAN, 8, NULL, MASK_WS_FIN,
663 "Indicates that this is the final fragment in a message", HFILL }
666 { "Reserved", "websocket.rsv",
667 FT_UINT8, BASE_HEX, NULL, MASK_WS_RSV,
668 "Must be zero", HFILL }
671 { "Per-Message Compressed", "websocket.pmc",
672 FT_BOOLEAN, 8, NULL, MASK_WS_RSV1,
673 "Whether a message is compressed or not", HFILL }
676 { "Opcode", "websocket.opcode",
677 FT_UINT8, BASE_DEC, VALS(ws_opcode_vals), MASK_WS_OPCODE,
678 "Defines the interpretation of the Payload data", HFILL }
681 { "Mask", "websocket.mask",
682 FT_BOOLEAN, 8, NULL, MASK_WS_MASK,
683 "Defines whether the Payload data is masked", HFILL }
685 { &hf_ws_payload_length,
686 { "Payload length", "websocket.payload_length",
687 FT_UINT8, BASE_DEC, NULL, MASK_WS_PAYLOAD_LEN,
688 "The length of the Payload data", HFILL }
690 { &hf_ws_payload_length_ext_16,
691 { "Extended Payload length (16 bits)", "websocket.payload_length_ext_16",
692 FT_UINT16, BASE_DEC, NULL, 0x0,
693 "The length (16 bits) of the Payload data", HFILL }
695 { &hf_ws_payload_length_ext_64,
696 { "Extended Payload length (64 bits)", "websocket.payload_length_ext_64",
697 FT_UINT64, BASE_DEC, NULL, 0x0,
698 "The length (64 bits) of the Payload data", HFILL }
700 { &hf_ws_masking_key,
701 { "Masking-Key", "websocket.masking_key",
702 FT_BYTES, BASE_NONE, NULL, 0x0,
703 "All frames sent from the client to the server are masked by a 32-bit value that is contained within the frame", HFILL }
706 { "Payload", "websocket.payload",
707 FT_NONE, BASE_NONE, NULL, 0x0,
708 "Payload (after unmasking)", HFILL }
710 { &hf_ws_masked_payload,
711 { "Masked payload", "websocket.masked_payload",
712 FT_NONE, BASE_NONE, NULL, 0x0,
715 { &hf_ws_payload_continue,
716 { "Continue", "websocket.payload.continue",
717 FT_BYTES, BASE_NONE, NULL, 0x0,
720 { &hf_ws_payload_close,
721 { "Close", "websocket.payload.close",
722 FT_NONE, BASE_NONE, NULL, 0x0,
725 { &hf_ws_payload_close_status_code,
726 { "Status code", "websocket.payload.close.status_code",
727 FT_UINT16, BASE_DEC, VALS(ws_close_status_code_vals), 0x0,
730 { &hf_ws_payload_close_reason,
731 { "Reason", "websocket.payload.close.reason",
732 FT_STRING, BASE_NONE, NULL, 0x0,
735 { &hf_ws_payload_ping,
736 { "Ping", "websocket.payload.ping",
737 FT_BYTES, BASE_NONE, NULL, 0x0,
740 { &hf_ws_payload_pong,
741 { "Pong", "websocket.payload.pong",
742 FT_BYTES, BASE_NONE, NULL, 0x0,
745 { &hf_ws_payload_unknown,
746 { "Unknown", "websocket.payload.unknown",
747 FT_BYTES, BASE_NONE, NULL, 0x0,
753 static gint *ett[] = {
757 &ett_ws_control_close,
760 static ei_register_info ei[] = {
761 { &ei_ws_payload_unknown, { "websocket.payload.unknown.expert", PI_UNDECODED, PI_NOTE, "Dissector for Websocket Opcode", EXPFILL }},
762 { &ei_ws_decompression_failed, { "websocket.decompression.failed.expert", PI_PROTOCOL, PI_WARN, "Decompression failed", EXPFILL }},
765 static const enum_val_t text_types[] = {
766 {"None", "No subdissection", WEBSOCKET_NONE},
767 {"Line based text", "Line based text", WEBSOCKET_TEXT},
768 {"As JSON", "As json", WEBSOCKET_JSON},
769 {"As SIP", "As SIP", WEBSOCKET_SIP},
773 module_t *websocket_module;
774 expert_module_t* expert_websocket;
776 proto_websocket = proto_register_protocol("WebSocket",
777 "WebSocket", "websocket");
780 * Heuristic dissectors SHOULD register themselves in
781 * this table using the standard heur_dissector_add()
784 heur_subdissector_list = register_heur_dissector_list("ws", proto_websocket);
786 port_subdissector_table = register_dissector_table("ws.port",
787 "TCP port for protocols using WebSocket", proto_websocket, FT_UINT16, BASE_DEC);
789 protocol_subdissector_table = register_dissector_table("ws.protocol",
790 "Negotiated WebSocket protocol", proto_websocket, FT_STRING, BASE_NONE);
792 proto_register_field_array(proto_websocket, hf, array_length(hf));
793 proto_register_subtree_array(ett, array_length(ett));
794 expert_websocket = expert_register_protocol(proto_websocket);
795 expert_register_field_array(expert_websocket, ei, array_length(ei));
797 websocket_handle = register_dissector("websocket", dissect_websocket, proto_websocket);
799 websocket_module = prefs_register_protocol(proto_websocket, proto_reg_handoff_websocket);
801 prefs_register_enum_preference(websocket_module, "text_type",
802 "Dissect websocket text as",
803 "Select dissector for websocket text",
804 &pref_text_type, text_types, WEBSOCKET_NONE);
805 prefs_register_bool_preference(websocket_module, "decompress",
806 "Try to decompress permessage-deflate payload", NULL, &pref_decompress);
810 proto_reg_handoff_websocket(void)
812 dissector_add_string("http.upgrade", "websocket", websocket_handle);
814 text_lines_handle = find_dissector_add_dependency("data-text-lines", proto_websocket);
815 json_handle = find_dissector_add_dependency("json", proto_websocket);
816 sip_handle = find_dissector_add_dependency("sip", proto_websocket);
818 proto_http = proto_get_id_by_filter_name("http");
821 * Editor modelines - https://www.wireshark.org/tools/modelines.html
826 * indent-tabs-mode: nil
829 * vi: set shiftwidth=2 tabstop=8 expandtab:
830 * :indentSize=2:tabSize=8:noTabs=true: