aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-websocket.c
diff options
context:
space:
mode:
Diffstat (limited to 'epan/dissectors/packet-websocket.c')
-rw-r--r--epan/dissectors/packet-websocket.c329
1 files changed, 124 insertions, 205 deletions
diff --git a/epan/dissectors/packet-websocket.c b/epan/dissectors/packet-websocket.c
index 432034a1a3..5ff78a71d2 100644
--- a/epan/dissectors/packet-websocket.c
+++ b/epan/dissectors/packet-websocket.c
@@ -1,6 +1,7 @@
/* packet-websocket.c
* Routines for WebSocket dissection
* Copyright 2012, Alexis La Goutte <alexis.lagoutte@gmail.com>
+ * 2015, Peter Wu <peter@lekensteyn.nl>
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
@@ -41,6 +42,7 @@
void proto_register_websocket(void);
void proto_reg_handoff_websocket(void);
+static dissector_handle_t data_handle;
static dissector_handle_t text_lines_handle;
static dissector_handle_t json_handle;
static dissector_handle_t sip_handle;
@@ -65,30 +67,19 @@ static int hf_ws_payload_length_ext_16 = -1;
static int hf_ws_payload_length_ext_64 = -1;
static int hf_ws_masking_key = -1;
static int hf_ws_payload = -1;
-static int hf_ws_payload_unmask = -1;
+static int hf_ws_masked_payload = -1;
static int hf_ws_payload_continue = -1;
-static int hf_ws_payload_text = -1;
-static int hf_ws_payload_text_mask = -1;
-static int hf_ws_payload_text_unmask = -1;
-static int hf_ws_payload_binary = -1;
-static int hf_ws_payload_binary_mask = -1;
-static int hf_ws_payload_binary_unmask = -1;
static int hf_ws_payload_close = -1;
-static int hf_ws_payload_close_mask = -1;
-static int hf_ws_payload_close_unmask = -1;
static int hf_ws_payload_close_status_code = -1;
static int hf_ws_payload_close_reason = -1;
static int hf_ws_payload_ping = -1;
-static int hf_ws_payload_ping_mask = -1;
-static int hf_ws_payload_ping_unmask = -1;
static int hf_ws_payload_pong = -1;
-static int hf_ws_payload_pong_mask = -1;
-static int hf_ws_payload_pong_unmask = -1;
static int hf_ws_payload_unknown = -1;
static gint ett_ws = -1;
static gint ett_ws_pl = -1;
static gint ett_ws_mask = -1;
+static gint ett_ws_control_close = -1;
static expert_field ei_ws_payload_unknown = EI_INIT;
@@ -155,161 +146,139 @@ tvb_unmasked(tvbuff_t *tvb, packet_info *pinfo, const guint offset, guint payloa
return tvb_new_real_data(data_unmask, unmasked_length, payload_length);
}
-static int
-dissect_websocket_payload(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_tree *ws_tree, guint8 opcode, guint payload_length, gboolean mask, const guint8* masking_key)
+static void
+dissect_websocket_control_frame(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint8 opcode)
{
- guint offset = 0;
- proto_item *ti_unmask, *ti;
+ proto_item *ti;
+ proto_tree *subtree;
+ const guint offset = 0, length = tvb_reported_length(tvb);
+
+ switch (opcode) {
+ case WS_CLOSE: /* Close */
+ ti = proto_tree_add_item(tree, hf_ws_payload_close, tvb, offset, length, ENC_NA);
+ subtree = proto_item_add_subtree(ti, ett_ws_control_close);
+ /* Close frame MAY contain a body. */
+ if (length >= 2) {
+ proto_tree_add_item(subtree, hf_ws_payload_close_status_code, tvb, offset, 2, ENC_BIG_ENDIAN);
+ if (length > 2)
+ proto_tree_add_item(subtree, hf_ws_payload_close_reason, tvb, offset+2, length-2, ENC_UTF_8|ENC_NA);
+ }
+ break;
+
+ case WS_PING: /* Ping */
+ proto_tree_add_item(tree, hf_ws_payload_ping, tvb, offset, length, ENC_NA);
+ break;
+
+ case WS_PONG: /* Pong */
+ proto_tree_add_item(tree, hf_ws_payload_pong, tvb, offset, length, ENC_NA);
+ break;
+
+ default: /* Unknown */
+ ti = proto_tree_add_item(tree, hf_ws_payload_unknown, tvb, offset, length, ENC_NA);
+ expert_add_info_format(pinfo, ti, &ei_ws_payload_unknown, "Dissector for Websocket Opcode (%d)"
+ " code not implemented, Contact Wireshark developers"
+ " if you want this supported", opcode);
+ break;
+ }
+}
+
+static void
+dissect_websocket_data_frame(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_tree *pl_tree, guint8 opcode)
+{
+ proto_item *ti;
+ const guint offset = 0, length = tvb_reported_length(tvb);
dissector_handle_t handle = NULL;
- proto_tree *pl_tree, *mask_tree = NULL;
- tvbuff_t *payload_tvb = NULL;
heur_dtbl_entry_t *hdtbl_entry;
conversation_t *conv;
http_conv_t *http_conv = NULL;
- /* Payload */
- ti = proto_tree_add_item(ws_tree, hf_ws_payload, tvb, offset, payload_length, ENC_NA);
- pl_tree = proto_item_add_subtree(ti, ett_ws_pl);
- if (mask) {
- payload_tvb = tvb_unmasked(tvb, pinfo, offset, payload_length, masking_key);
- tvb_set_child_real_data_tvbuff(tvb, payload_tvb);
- add_new_data_source(pinfo, payload_tvb, payload_length > tvb_captured_length(payload_tvb) ? "Unmasked Data (truncated)" : "Unmasked Data");
- ti = proto_tree_add_item(ws_tree, hf_ws_payload_unmask, payload_tvb, offset, payload_length, ENC_NA);
- if (payload_length > tvb_captured_length(payload_tvb)) {
- proto_item_append_text(ti, " [truncated]");
- }
- mask_tree = proto_item_add_subtree(ti, ett_ws_mask);
- } else {
- payload_tvb = tvb_new_subset(tvb, offset, payload_length, -1);
- }
-
+ /* try to find a dissector which accepts the data. */
conv = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
- if (conv)
+ if (conv) {
http_conv = (http_conv_t *)conversation_get_proto_data(conv, proto_http);
- if (http_conv)
- handle = dissector_get_uint_handle(port_subdissector_table, http_conv->server_port);
-
- if (handle)
- call_dissector_only(handle, payload_tvb, pinfo, tree, NULL);
- else
- dissector_try_heuristic(heur_subdissector_list, payload_tvb, pinfo, tree, &hdtbl_entry, NULL);
+ if (http_conv)
+ handle = dissector_get_uint_handle(port_subdissector_table, http_conv->server_port);
+ }
- /* Extension Data */
- /* TODO: Add dissector of Extension (not extension available for the moment...) */
+ if (handle) {
+ call_dissector_only(handle, tvb, pinfo, tree, NULL);
+ return; /* handle found, assume dissector took care of it. */
+ } else if (dissector_try_heuristic(heur_subdissector_list, tvb, pinfo, tree, &hdtbl_entry, NULL)) {
+ return; /* heuristics dissector handled it. */
+ }
- /* Application Data */
+ /* no dissector wanted it, try to print something appropriate. */
switch (opcode) {
-
- case WS_CONTINUE: /* Continue */
- proto_tree_add_item(pl_tree, hf_ws_payload_continue, tvb, offset, payload_length, ENC_NA);
- /* TODO: Add Fragmentation support... */
- break;
-
case WS_TEXT: /* Text */
- if (mask) {
-
- proto_tree_add_item(pl_tree, hf_ws_payload_text_mask, tvb, offset, payload_length, ENC_NA);
- ti_unmask = proto_tree_add_item(mask_tree, hf_ws_payload_text_unmask, payload_tvb, offset, payload_length, ENC_UTF_8|ENC_NA);
- PROTO_ITEM_SET_GENERATED(ti_unmask);
- ti_unmask = proto_tree_add_item(mask_tree, hf_ws_payload_text, payload_tvb, offset, payload_length, ENC_UTF_8|ENC_NA);
- PROTO_ITEM_SET_HIDDEN(ti_unmask);
- } else {
+ {
const gchar *saved_match_string = pinfo->match_string;
pinfo->match_string = NULL;
switch (pref_text_type) {
case WEBSOCKET_TEXT:
- call_dissector(text_lines_handle, payload_tvb, pinfo, pl_tree);
- break;
- case WEBSOCKET_JSON:
- call_dissector(json_handle, payload_tvb, pinfo, pl_tree);
- break;
- case WEBSOCKET_SIP:
- call_dissector(sip_handle, payload_tvb, pinfo, pl_tree);
- break;
case WEBSOCKET_NONE:
- /* falltrough */
default:
- proto_tree_add_item(pl_tree, hf_ws_payload_text, tvb, offset, payload_length, ENC_UTF_8|ENC_NA);
- break;
+ /* Assume that most text protocols are line-based. */
+ call_dissector(text_lines_handle, tvb, pinfo, tree);
+ break;
+ case WEBSOCKET_JSON:
+ call_dissector(json_handle, tvb, pinfo, tree);
+ break;
+ case WEBSOCKET_SIP:
+ call_dissector(sip_handle, tvb, pinfo, tree);
+ break;
}
pinfo->match_string = saved_match_string;
}
- offset += payload_length;
break;
case WS_BINARY: /* Binary */
- if (mask) {
- proto_tree_add_item(pl_tree, hf_ws_payload_binary_mask, tvb, offset, payload_length, ENC_NA);
- ti_unmask = proto_tree_add_item(mask_tree, hf_ws_payload_binary_unmask, payload_tvb, offset, payload_length, ENC_NA);
- PROTO_ITEM_SET_GENERATED(ti_unmask);
- ti_unmask = proto_tree_add_item(mask_tree, hf_ws_payload_binary, payload_tvb, offset, payload_length, ENC_NA);
- PROTO_ITEM_SET_HIDDEN(ti_unmask);
- } else {
- proto_tree_add_item(pl_tree, hf_ws_payload_binary, tvb, offset, payload_length, ENC_NA);
- }
- offset += payload_length;
- break;
-
- case WS_CLOSE: /* Close */
- if (mask) {
- proto_tree_add_item(pl_tree, hf_ws_payload_close_mask, tvb, offset, payload_length, ENC_NA);
- ti_unmask = proto_tree_add_item(mask_tree, hf_ws_payload_close_unmask, payload_tvb, offset, payload_length, ENC_NA);
- PROTO_ITEM_SET_GENERATED(ti_unmask);
- ti_unmask = proto_tree_add_item(mask_tree, hf_ws_payload_close, payload_tvb, offset, payload_length, ENC_NA);
- PROTO_ITEM_SET_HIDDEN(ti_unmask);
- ti_unmask = proto_tree_add_item(mask_tree, hf_ws_payload_close_status_code, payload_tvb, offset, 2, ENC_BIG_ENDIAN);
- PROTO_ITEM_SET_GENERATED(ti_unmask);
-
- if (payload_length > 2) {
- ti_unmask = proto_tree_add_item(mask_tree, hf_ws_payload_close_reason, payload_tvb, offset+2, payload_length-2, ENC_ASCII|ENC_NA);
- PROTO_ITEM_SET_GENERATED(ti_unmask);
- }
- } else {
- proto_tree_add_item(pl_tree, hf_ws_payload_close, tvb, offset, payload_length, ENC_NA);
- proto_tree_add_item(pl_tree, hf_ws_payload_close_status_code, tvb, offset, 2, ENC_BIG_ENDIAN);
- if (payload_length > 2) {
- proto_tree_add_item(pl_tree, hf_ws_payload_close_reason, tvb, offset+2, payload_length-2, ENC_ASCII|ENC_NA);
- }
- }
- offset += payload_length;
- break;
-
- case WS_PING: /* Ping */
- if (mask) {
- proto_tree_add_item(pl_tree, hf_ws_payload_ping_mask, tvb, offset, payload_length, ENC_NA);
- ti_unmask = proto_tree_add_item(mask_tree, hf_ws_payload_ping_unmask, payload_tvb, offset, payload_length, ENC_NA);
- PROTO_ITEM_SET_GENERATED(ti_unmask);
- ti_unmask = proto_tree_add_item(mask_tree, hf_ws_payload_ping, payload_tvb, offset, payload_length, ENC_NA);
- PROTO_ITEM_SET_HIDDEN(ti_unmask);
- } else {
- proto_tree_add_item(pl_tree, hf_ws_payload_ping, tvb, offset, payload_length, ENC_NA);
- }
- offset += payload_length;
- break;
-
- case WS_PONG: /* Pong */
- if (mask) {
- proto_tree_add_item(pl_tree, hf_ws_payload_pong_mask, tvb, offset, payload_length, ENC_NA);
- ti_unmask = proto_tree_add_item(mask_tree, hf_ws_payload_pong_unmask, payload_tvb, offset, payload_length, ENC_NA);
- PROTO_ITEM_SET_GENERATED(ti_unmask);
- ti_unmask = proto_tree_add_item(mask_tree, hf_ws_payload_pong, payload_tvb, offset, payload_length, ENC_NA);
- PROTO_ITEM_SET_HIDDEN(ti_unmask);
- } else {
- proto_tree_add_item(pl_tree, hf_ws_payload_pong, tvb, offset, payload_length, ENC_NA);
- }
- offset += payload_length;
- break;
+ call_dissector(data_handle, tvb, pinfo, tree);
+ break;
default: /* Unknown */
- ti = proto_tree_add_item(pl_tree, hf_ws_payload_unknown, tvb, offset, payload_length, ENC_NA);
+ ti = proto_tree_add_item(pl_tree, hf_ws_payload_unknown, tvb, offset, length, ENC_NA);
expert_add_info_format(pinfo, ti, &ei_ws_payload_unknown, "Dissector for Websocket Opcode (%d)"
" code not implemented, Contact Wireshark developers"
" if you want this supported", opcode);
- break;
+ break;
+ }
+}
+
+static void
+dissect_websocket_payload(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_tree *ws_tree, guint8 opcode)
+{
+ const guint offset = 0, length = tvb_reported_length(tvb);
+ proto_item *ti;
+ proto_tree *pl_tree;
+ tvbuff_t *tvb_appdata;
+
+ /* Payload */
+ ti = proto_tree_add_item(ws_tree, hf_ws_payload, tvb, offset, length, ENC_NA);
+ pl_tree = proto_item_add_subtree(ti, ett_ws_pl);
+
+ /* Extension Data */
+ /* TODO: Add dissector of Extension (not extension available for the moment...) */
+
+
+ /* Application Data */
+ if (opcode == WS_CONTINUE) {
+ proto_tree_add_item(tree, hf_ws_payload_continue, tvb, offset, length, ENC_NA);
+ /* TODO: Add Fragmentation support (needs FIN bit)
+ * https://tools.ietf.org/html/rfc6455#section-5.4 */
+ return;
+ }
+ /* Right now this is exactly the same, this may change when exts. are added.
+ tvb_appdata = tvb_new_subset(tvb, offset, length, length);
+ */
+ tvb_appdata = tvb;
+
+ if (opcode & 8) { /* Control frames have MSB set. */
+ dissect_websocket_control_frame(tvb_appdata, pinfo, pl_tree, opcode);
+ } else {
+ dissect_websocket_data_frame(tvb_appdata, pinfo, tree, pl_tree, opcode);
}
- return offset;
}
@@ -381,8 +350,16 @@ dissect_websocket_frame(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, voi
}
if (payload_length > 0) {
- tvb_payload = tvb_new_subset_remaining(tvb, payload_offset);
- dissect_websocket_payload(tvb_payload, pinfo, tree, ws_tree, opcode, payload_length, mask, masking_key);
+ /* Always unmask payload data before analysing it. */
+ if (mask) {
+ ti = proto_tree_add_item(ws_tree, hf_ws_masked_payload, tvb, payload_offset, payload_length, ENC_NA);
+ tvb_payload = tvb_unmasked(tvb, pinfo, payload_offset, payload_length, masking_key);
+ tvb_set_child_real_data_tvbuff(tvb, tvb_payload);
+ add_new_data_source(pinfo, tvb_payload, "Unmasked data");
+ } else {
+ tvb_payload = tvb_new_subset(tvb, payload_offset, payload_length, payload_length);
+ }
+ dissect_websocket_payload(tvb_payload, pinfo, tree, ws_tree, opcode);
}
return tvb_captured_length(tvb);
@@ -479,10 +456,10 @@ proto_register_websocket(void)
{ &hf_ws_payload,
{ "Payload", "websocket.payload",
FT_NONE, BASE_NONE, NULL, 0x0,
- NULL, HFILL }
+ "Payload (after unmasking)", HFILL }
},
- { &hf_ws_payload_unmask,
- { "Unmask Payload", "websocket.payload.unmask",
+ { &hf_ws_masked_payload,
+ { "Masked payload", "websocket.masked_payload",
FT_NONE, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
@@ -491,53 +468,13 @@ proto_register_websocket(void)
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
- { &hf_ws_payload_text,
- { "Text", "websocket.payload.text",
- FT_STRING, BASE_NONE, NULL, 0x0,
- NULL, HFILL }
- },
- { &hf_ws_payload_text_mask,
- { "Text", "websocket.payload.text_mask",
- FT_BYTES, BASE_NONE, NULL, 0x0,
- NULL, HFILL }
- },
- { &hf_ws_payload_text_unmask,
- { "Text unmask", "websocket.payload.text_unmask",
- FT_STRING, BASE_NONE, NULL, 0x0,
- NULL, HFILL }
- },
- { &hf_ws_payload_binary,
- { "Binary", "websocket.payload.binary",
- FT_BYTES, BASE_NONE, NULL, 0x0,
- NULL, HFILL }
- },
- { &hf_ws_payload_binary_mask,
- { "Binary", "websocket.payload.binary_mask",
- FT_BYTES, BASE_NONE, NULL, 0x0,
- NULL, HFILL }
- },
- { &hf_ws_payload_binary_unmask,
- { "Binary", "websocket.payload.binary_unmask",
- FT_BYTES, BASE_NONE, NULL, 0x0,
- NULL, HFILL }
- },
{ &hf_ws_payload_close,
{ "Close", "websocket.payload.close",
- FT_BYTES, BASE_NONE, NULL, 0x0,
- NULL, HFILL }
- },
- { &hf_ws_payload_close_mask,
- { "Close", "websocket.payload.close_mask",
- FT_BYTES, BASE_NONE, NULL, 0x0,
- NULL, HFILL }
- },
- { &hf_ws_payload_close_unmask,
- { "Unmask Close", "websocket.payload.close_unmask",
- FT_BYTES, BASE_NONE, NULL, 0x0,
+ FT_NONE, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_ws_payload_close_status_code,
- { "Close", "websocket.payload.close.status_code",
+ { "Status code", "websocket.payload.close.status_code",
FT_UINT16, BASE_DEC, VALS(ws_close_status_code_vals), 0x0,
NULL, HFILL }
},
@@ -551,31 +488,11 @@ proto_register_websocket(void)
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
- { &hf_ws_payload_ping_mask,
- { "Ping", "websocket.payload.ping_mask",
- FT_BYTES, BASE_NONE, NULL, 0x0,
- NULL, HFILL }
- },
- { &hf_ws_payload_ping_unmask,
- { "Ping", "websocket.payload.ping_unmask",
- FT_BYTES, BASE_NONE, NULL, 0x0,
- NULL, HFILL }
- },
{ &hf_ws_payload_pong,
{ "Pong", "websocket.payload.pong",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
- { &hf_ws_payload_pong_mask,
- { "Pong", "websocket.payload.pong_mask",
- FT_BYTES, BASE_NONE, NULL, 0x0,
- NULL, HFILL }
- },
- { &hf_ws_payload_pong_unmask,
- { "Pong", "websocket.payload.pong_unmask",
- FT_BYTES, BASE_NONE, NULL, 0x0,
- NULL, HFILL }
- },
{ &hf_ws_payload_unknown,
{ "Unknown", "websocket.payload.unknown",
FT_BYTES, BASE_NONE, NULL, 0x0,
@@ -587,7 +504,8 @@ proto_register_websocket(void)
static gint *ett[] = {
&ett_ws,
&ett_ws_pl,
- &ett_ws_mask
+ &ett_ws_mask,
+ &ett_ws_control_close,
};
static ei_register_info ei[] = {
@@ -638,6 +556,7 @@ proto_register_websocket(void)
void
proto_reg_handoff_websocket(void)
{
+ data_handle = find_dissector("data");
text_lines_handle = find_dissector("data-text-lines");
json_handle = find_dissector("json");
sip_handle = find_dissector("sip");
@@ -645,7 +564,7 @@ proto_reg_handoff_websocket(void)
proto_http = proto_get_id_by_filter_name("http");
}
/*
- * Editor modelines - http://www.wireshark.org/tools/modelines.html
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 2