From me for https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=6843
authorAlexis La Goutte <alexis.lagoutte@gmail.com>
Fri, 20 Apr 2012 15:31:15 +0000 (15:31 -0000)
committerAlexis La Goutte <alexis.lagoutte@gmail.com>
Fri, 20 Apr 2012 15:31:15 +0000 (15:31 -0000)
Add WebSocket Protocol dissector (RFC6455)
* Support Base Framing Protocol
* Support of major opcode (Text, Binary, Close, Ping, Pong...)
* Support of unmask Payload (Client-to-Server Masking)

TODO
* Add fragmentation support
* Add WebSocket Extensions

svn path=/trunk/; revision=42163

AUTHORS
epan/CMakeLists.txt
epan/dissectors/Makefile.common
epan/dissectors/packet-http.c
epan/dissectors/packet-http.h
epan/dissectors/packet-websocket.c [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
index e8fd9f25e233ea5c7c067f02f108f47268a2a4e5..0b47e84bc75bce56a90deecde939e57b254086a9 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -2977,6 +2977,7 @@ Alexis La Goutte  <alexis.lagoutte [AT] gmail.com> {
        CAPWAP dissector
        PAPI dissector
        MONGO dissector
+       WebSocket dissector
        Miscellaneous ISAKMP enhancements
        Miscellaneous ICMPv6 enhancements
        Miscellaneous 802.11 enhancements
index 297ff49690b5c9237cc07cc496ff1fa4ecabeab6..65ba08c63548fae952796dd980f92208bcbc9001 100644 (file)
@@ -1163,6 +1163,7 @@ set(DISSECTOR_SRC
        dissectors/packet-wbxml.c
        dissectors/packet-wccp.c
        dissectors/packet-wcp.c
+       dissectors/packet-websocket.c
        dissectors/packet-wfleet-hdlc.c
        dissectors/packet-who.c
        dissectors/packet-wifi-p2p.c
index c16888110ce1e6151333fe9f460e45acc407ad0a..d59e82021d37e19c23757380d1aee80a18665e16 100644 (file)
@@ -1082,6 +1082,7 @@ DISSECTOR_SRC = \
        packet-wbxml.c          \
        packet-wccp.c           \
        packet-wcp.c            \
+       packet-websocket.c      \
        packet-wfleet-hdlc.c    \
        packet-who.c            \
        packet-wifi-p2p.c       \
index c2cb5bfb0152c829a4923755c519db088db3fc85..07e84a0e554919f1873bb52af1111be751f542ca 100644 (file)
@@ -90,6 +90,7 @@ static int hf_http_content_length_header = -1;
 static int hf_http_content_length = -1;
 static int hf_http_content_encoding = -1;
 static int hf_http_transfer_encoding = -1;
+static int hf_http_upgrade = -1;
 static int hf_http_user_agent = -1;
 static int hf_http_host = -1;
 static int hf_http_connection = -1;
@@ -117,6 +118,7 @@ static gint ett_http_header_item = -1;
 
 static dissector_handle_t data_handle;
 static dissector_handle_t media_handle;
+static dissector_handle_t websocket_handle;
 static dissector_handle_t http_handle;
 
 /* Stuff for generation/handling of fields for custom HTTP headers */
@@ -1075,6 +1077,14 @@ dissect_http_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
                next_tvb = tvb_new_subset(tvb, offset, datalen,
                    reported_datalen);
 
+               /*
+                *      Check if Websocket 
+                */
+               if (conv_data->upgrade != NULL &&
+                   g_ascii_strcasecmp(conv_data->upgrade, "WebSocket") == 0) {
+                       call_dissector_only(websocket_handle, next_tvb, pinfo, tree);
+                       goto body_dissected;
+               }
                /*
                 * Handle *transfer* encodings other than "identity".
                 */
@@ -2029,7 +2039,8 @@ typedef struct {
 #define HDR_CONTENT_LENGTH     4
 #define HDR_CONTENT_ENCODING   5
 #define HDR_TRANSFER_ENCODING  6
-#define HDR_HOST  7
+#define HDR_HOST               7
+#define HDR_UPGRADE            8
 
 static const header_info headers[] = {
        { "Authorization", &hf_http_authorization, HDR_AUTHORIZATION },
@@ -2040,6 +2051,7 @@ static const header_info headers[] = {
        { "Content-Length", &hf_http_content_length_header, HDR_CONTENT_LENGTH },
        { "Content-Encoding", &hf_http_content_encoding, HDR_CONTENT_ENCODING },
        { "Transfer-Encoding", &hf_http_transfer_encoding, HDR_TRANSFER_ENCODING },
+       { "Upgrade", &hf_http_upgrade, HDR_UPGRADE },
        { "User-Agent", &hf_http_user_agent, HDR_NO_SPECIAL },
        { "Host", &hf_http_host, HDR_HOST },
        { "Connection", &hf_http_connection, HDR_NO_SPECIAL },
@@ -2317,6 +2329,9 @@ process_header(tvbuff_t *tvb, int offset, int next_offset,
                        conv_data->http_host = se_strndup(value, value_len);
                        break;
 
+               case HDR_UPGRADE:
+                       conv_data->upgrade = se_strndup(value, value_len);
+                       break;
                }
        }
 }
@@ -2593,6 +2608,10 @@ proto_register_http(void)
              { "Transfer-Encoding",    "http.transfer_encoding",
                FT_STRING, BASE_NONE, NULL, 0x0,
                "HTTP Transfer-Encoding header", HFILL }},
+           { &hf_http_upgrade,
+             { "Upgrade",      "http.upgrade",
+               FT_STRING, BASE_NONE, NULL, 0x0,
+               "HTTP Upgrade header", HFILL }},
            { &hf_http_user_agent,
              { "User-Agent",   "http.user_agent",
                FT_STRING, BASE_NONE, NULL, 0x0,
@@ -2803,7 +2822,7 @@ proto_reg_handoff_http(void)
 
        data_handle = find_dissector("data");
        media_handle = find_dissector("media");
-
+       websocket_handle = find_dissector("websocket");
        /*
         * XXX - is there anything to dissect in the body of an SSDP
         * request or reply?  I.e., should there be an SSDP dissector?
index 1ff19501a9bf841afab8187b24a65e29f9edacb0..ee45c77adc5e62650288ec73fe8eb936a56fcb80 100644 (file)
@@ -31,7 +31,7 @@ void http_dissector_add(guint32 port, dissector_handle_t handle);
 /* Used for HTTP statistics */
 typedef struct _http_info_value_t {
        guint32 framenum;
-       gchar   *request_method;        
+       gchar   *request_method;
        guint    response_code;
        gchar   *http_host;
        gchar   *request_uri;
@@ -53,6 +53,7 @@ typedef struct _http_conv_t {
        gchar   *http_host;
        gchar   *request_method;
        gchar   *request_uri;
+       gchar   *upgrade;
        guint32 startframe;     /* First frame of proxied connection */
 } http_conv_t;
 
diff --git a/epan/dissectors/packet-websocket.c b/epan/dissectors/packet-websocket.c
new file mode 100644 (file)
index 0000000..ead05d5
--- /dev/null
@@ -0,0 +1,507 @@
+/* packet-websocket.c
+ * Routines for WebSocket dissection
+ * Copyright 2012, Alexis La Goutte <alexis.lagoutte@gmail.com>
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <glib.h>
+
+#include <epan/packet.h>
+#include <epan/prefs.h>
+#include <epan/expert.h>
+
+/*
+ * The information used comes from:
+ * RFC6455: The WebSocket Protocol
+ * http://www.iana.org/assignments/websocket (last updated 2012-04-12)
+ */
+
+/* Initialize the protocol and registered fields */
+static int proto_websocket = -1;
+static int hf_ws_fin = -1;
+static int hf_ws_reserved = -1;
+static int hf_ws_opcode = -1;
+static int hf_ws_mask = -1;
+static int hf_ws_payload_length = -1;
+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_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;
+
+#define WS_CONTINUE 0x0
+#define WS_TEXT     0x1
+#define WS_BINARY   0x2
+#define WS_CLOSE    0x8
+#define WS_PING     0x9
+#define WS_PONG     0xA
+
+static const value_string ws_opcode_vals[] = {
+  { WS_CONTINUE, "Continuation" },
+  { WS_TEXT, "Text" },
+  { WS_BINARY, "Binary" },
+  { WS_CLOSE, "Connection Close" },
+  { WS_PING, "Ping" },
+  { WS_PONG, "Pong" },
+  { 0,         NULL}
+};
+
+#define MASK_WS_FIN 0x80
+#define MASK_WS_RSV 0x70
+#define MASK_WS_OPCODE  0x0F
+#define MASK_WS_MASK  0x80
+#define MASK_WS_PAYLOAD_LEN 0x7F
+
+static const value_string ws_close_status_code_vals[] = {
+  { 1000, "Normal Closure" },
+  { 1001, "Going Away" },
+  { 1002, "Protocol error" },
+  { 1003, "Unsupported Data" },
+  { 1004, "---Reserved----" },
+  { 1005, "No Status Rcvd" },
+  { 1006, "Abnormal Closure" },
+  { 1007, "Invalid frame payload data" },
+  { 1008, "Policy Violation" },
+  { 1009, "Message Too Big" },
+  { 1010, "Mandatory Ext." },
+  { 1011, "Internal Server" },
+  { 1015, "TLS handshake" },
+  { 0,         NULL}
+};
+
+tvbuff_t *
+tvb_unmasked(tvbuff_t *tvb, const int offset, guint64 payload_length, const guint8 *masking_key)
+{
+
+  gchar *data_unmask;
+  tvbuff_t  *tvb_unmask    = NULL;
+  guint64 i;
+  const guint8 *data_mask;
+
+  data_unmask = ep_alloc(payload_length);
+  data_mask = tvb_get_ptr(tvb, offset, payload_length);
+  /* Unmasked(XOR) Data... */
+  for(i=0; i < payload_length; i++){
+    data_unmask[i] = data_mask[i] ^ masking_key[i%4];
+  }
+
+  tvb_unmask = tvb_new_real_data(data_unmask, payload_length, payload_length);
+  tvb_set_free_cb(tvb_unmask, g_free);
+  return tvb_unmask;
+}
+
+static int 
+dissect_websocket_payload(tvbuff_t *tvb, packet_info *pinfo, proto_tree *ws_tree, guint8 opcode, guint64 payload_length, guint8 mask, const guint8* masking_key)
+{
+  int offset = 0;
+  proto_item *ti_unmask, *ti;
+  proto_tree *pl_tree, *mask_tree = NULL;
+  tvbuff_t  *tvb_unmask    = 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){
+    tvb_unmask = tvb_unmasked(tvb, offset, payload_length, masking_key);
+    add_new_data_source(pinfo, tvb_unmask, "Unmasked Data");
+    ti = proto_tree_add_item(ws_tree, hf_ws_payload_unmask, tvb_unmask, offset, payload_length, ENC_NA);
+    mask_tree = proto_item_add_subtree(ti, ett_ws_mask);
+  }
+
+  /* Extension Data */
+  /* TODO: Add dissector of Extension (not extension available for the moment...) */
+
+  /* Application Data */
+  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, tvb_unmask, 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, tvb_unmask, offset, payload_length,  ENC_UTF_8|ENC_NA);
+      PROTO_ITEM_SET_HIDDEN(ti_unmask);
+    }else{
+      proto_tree_add_item(pl_tree, hf_ws_payload_text, tvb, offset, payload_length, ENC_UTF_8|ENC_NA);
+
+    }
+    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, tvb_unmask, offset, payload_length, ENC_NA);
+      PROTO_ITEM_SET_GENERATED(ti_unmask);
+      ti_unmask = proto_tree_add_item(mask_tree, hf_ws_payload_binary, tvb_unmask, 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, tvb_unmask, offset, payload_length, ENC_NA);
+      PROTO_ITEM_SET_GENERATED(ti_unmask);
+      ti_unmask = proto_tree_add_item(mask_tree, hf_ws_payload_close, tvb_unmask, 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, tvb_unmask, 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, tvb_unmask, 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, tvb_unmask, offset, payload_length, ENC_NA);
+      PROTO_ITEM_SET_GENERATED(ti_unmask);
+      ti_unmask = proto_tree_add_item(mask_tree, hf_ws_payload_ping, tvb_unmask, 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, tvb_unmask, offset, payload_length, ENC_NA);
+      PROTO_ITEM_SET_GENERATED(ti_unmask);
+      ti_unmask = proto_tree_add_item(mask_tree, hf_ws_payload_pong, tvb_unmask, 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;
+
+    default: /* Unknown */
+      ti = proto_tree_add_item(pl_tree, hf_ws_payload_unknown, tvb, offset, payload_length, ENC_NA);
+      expert_add_info_format(pinfo, ti, PI_UNDECODED, PI_NOTE, "Dissector for Websocket Opcode (%d)"
+                                       " code not implemented, Contact Wireshark developers"
+                                       " if you want this supported", opcode);
+    break;
+  }
+  return offset;
+}
+static int
+dissect_websocket(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+
+  proto_item *ti, *ti_len;
+  proto_tree *ws_tree = NULL;
+  int offset = 0;
+  guint8 fin, opcode, mask;
+  const guint8 *masking_key = NULL;
+  guint64 payload_length;
+  tvbuff_t  *tvb_payload    = NULL;
+  col_set_str(pinfo->cinfo, COL_PROTOCOL, "WebSocket");
+  col_set_str(pinfo->cinfo, COL_INFO, "WebSocket");
+  if (tree) {
+
+    ti = proto_tree_add_item(tree, proto_websocket, tvb, 0, -1, ENC_NA);
+    ws_tree = proto_item_add_subtree(ti, ett_ws);
+
+  }
+  /* Flags */
+  proto_tree_add_item(ws_tree, hf_ws_fin, tvb, offset, 1, ENC_NA);
+  fin = (tvb_get_guint8(tvb, offset) & MASK_WS_FIN) >> 4;
+  proto_tree_add_item(ws_tree, hf_ws_reserved, tvb, offset, 1, ENC_NA);
+
+  /* Opcode */
+  proto_tree_add_item(ws_tree, hf_ws_opcode, tvb, offset, 1, ENC_NA);
+  opcode = tvb_get_guint8(tvb, offset) & MASK_WS_OPCODE;
+  col_append_fstr(pinfo->cinfo, COL_INFO, " %s", val_to_str_const(opcode, ws_opcode_vals, "Unknown Opcode"));
+  col_append_fstr(pinfo->cinfo, COL_INFO, " %s", fin ? "[FIN]" : "");
+  offset +=1;
+
+  /* Mask */
+  proto_tree_add_item(ws_tree, hf_ws_mask, tvb, offset, 1, ENC_NA);
+  mask = (tvb_get_guint8(tvb, offset) & MASK_WS_MASK) >> 4;
+  col_append_fstr(pinfo->cinfo, COL_INFO, " %s", mask ? "[MASKED]" : "");
+
+  /* (Extended) Payload Length */
+  ti_len = proto_tree_add_item(ws_tree, hf_ws_payload_length, tvb, offset, 1, ENC_NA);
+  payload_length = (guint64)tvb_get_guint8(tvb, offset) & MASK_WS_PAYLOAD_LEN;
+  offset +=1;
+  switch(payload_length){
+    case 126:
+      proto_item_append_text(ti_len, " Extended Payload Length (16 bits)");
+      proto_tree_add_item(ws_tree, hf_ws_payload_length_ext_16, tvb, offset, 2, ENC_BIG_ENDIAN);
+      payload_length = (guint64)tvb_get_ntohs(tvb, offset);
+      offset += 2;
+    break;
+    case 127:
+      proto_item_append_text(ti_len, " Extended Payload Length (64 bits)");
+      proto_tree_add_item(ws_tree, hf_ws_payload_length_ext_64, tvb, offset, 8, ENC_BIG_ENDIAN);
+      payload_length = tvb_get_ntoh64(tvb, offset);
+      offset += 8;
+    break;
+    default:
+    break;
+  }
+
+  /* Masking-key */
+  if(mask){
+    proto_tree_add_item(ws_tree, hf_ws_masking_key, tvb, offset, 4, ENC_NA);
+    masking_key = tvb_get_ptr(tvb, offset, 4);
+    offset += 4;
+  }
+
+  tvb_payload = tvb_new_subset_remaining(tvb, offset);
+  offset += dissect_websocket_payload(tvb_payload, pinfo, ws_tree, opcode, payload_length, mask, masking_key);
+
+  return offset;
+}
+
+
+void
+proto_register_websocket(void)
+{
+
+  static hf_register_info hf[] = {
+    { &hf_ws_fin,
+      { "Fin", "websocket.fin",
+      FT_BOOLEAN, 8, NULL, MASK_WS_FIN,
+      "Indicates that this is the final fragment in a message", HFILL }
+    },
+    { &hf_ws_reserved,
+      { "Reserved", "websocket.rsv",
+      FT_UINT8, BASE_HEX, NULL, MASK_WS_RSV,
+      "Must be zero", HFILL }
+    },
+    { &hf_ws_opcode,
+      { "Opcode", "websocket.opcode",
+      FT_UINT8, BASE_DEC, VALS(ws_opcode_vals), MASK_WS_OPCODE,
+      "Defines the interpretation of the Payload data", HFILL }
+    },
+    { &hf_ws_mask,
+      { "Mask", "websocket.mask",
+      FT_BOOLEAN, 8, NULL, MASK_WS_MASK,
+      "Defines whether the Payload data is masked", HFILL }
+    },
+    { &hf_ws_payload_length,
+      { "Payload length", "websocket.payload_length",
+      FT_UINT8, BASE_DEC, NULL, MASK_WS_PAYLOAD_LEN,
+      "The length of the Payload data", HFILL }
+    },
+    { &hf_ws_payload_length_ext_16,
+      { "Extended Payload length (16 bits)", "websocket.payload_length_ext_16",
+      FT_UINT16, BASE_DEC, NULL, 0x0,
+      "The length (16 bits) of the Payload data", HFILL }
+    },
+    { &hf_ws_payload_length_ext_64,
+      { "Extended Payload length (16 bits)", "websocket.payload_length_ext_64",
+      FT_UINT64, BASE_DEC, NULL, 0x0,
+      "The length (64 bits) of the Payload data", HFILL }
+    },
+    { &hf_ws_masking_key,
+      { "Masking-Key", "websocket.masking_key",
+      FT_BYTES, BASE_NONE, NULL, 0x0,
+      "All frames sent from the client to the server are masked by a 32-bit value that is contained within the frame", HFILL }
+    },
+    { &hf_ws_payload,
+      { "Payload", "websocket.payload",
+      FT_NONE, BASE_NONE, NULL, 0x0,
+      NULL, HFILL }
+    },
+    { &hf_ws_payload_unmask,
+      { "Unmask Payload", "websocket.payload.unmask",
+      FT_NONE, BASE_NONE, NULL, 0x0,
+      NULL, HFILL }
+    },
+    { &hf_ws_payload_continue,
+      { "Continue", "websocket.payload.continue",
+      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,
+      NULL, HFILL }
+    },
+    { &hf_ws_payload_close_status_code,
+      { "Close", "websocket.payload.close.status_code",
+      FT_UINT16, BASE_DEC, VALS(ws_close_status_code_vals), 0x0,
+      NULL, HFILL }
+    },
+    { &hf_ws_payload_close_reason,
+      { "Reason", "websocket.payload.close.reason",
+      FT_STRING, BASE_NONE, NULL, 0x0,
+      NULL, HFILL }
+    },
+    { &hf_ws_payload_ping,
+      { "Ping", "websocket.payload.ping",
+      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,
+      NULL, HFILL }
+    },
+  };
+
+
+  static gint *ett[] = {
+    &ett_ws,
+    &ett_ws_pl,
+    &ett_ws_mask
+  };
+
+  proto_websocket = proto_register_protocol("WebSocket",
+      "WebSocket", "websocket");
+
+  proto_register_field_array(proto_websocket, hf, array_length(hf));
+  proto_register_subtree_array(ett, array_length(ett));
+
+  new_register_dissector("websocket", dissect_websocket, proto_websocket);
+}
+
+
+/*
+ * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 2
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=2 tabstop=8 expandtab:
+ * :indentSize=2:tabSize=8:noTabs=true:
+ */