nexmon – Rev 1

Subversion Repositories:
Rev:
/* packet-vnc.c
 * Routines for VNC dissection (Virtual Network Computing)
 * Copyright 2005, Ulf Lamping <ulf.lamping@web.de>
 * Copyright 2006-2007, Stephen Fisher (see AUTHORS file)
 *
 * 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.
 */

/* Dissection of the VNC (Virtual Network Computing) network traffic.
 *
 * All versions of RealVNC and TightVNC are supported.
 * Note: The addition of TightVNC support is not yet complete.
 *
 * Several VNC implementations available, see:
 * http://www.realvnc.com/
 * http://www.tightvnc.com/
 * http://ultravnc.sourceforge.net/
 * [Fedora TigerVNC]
 * ...
 *
 * The protocol itself is known as RFB - Remote Frame Buffer Protocol.
 *
 * This code is based on the protocol specification:
 *   http://www.realvnc.com/docs/rfbproto.pdf
 *  and the RealVNC free edition & TightVNC source code
 *  Note: rfbproto.rst [ https://github.com/svn2github/tigervnc/blob/master/rfbproto/rfbproto.rst ]
 *        seems to have additional information over rfbproto.pdf.
 */

/* XXX:
 *  This dissector adds items to the protocol tree before completing re-assembly
 *   of a VNC PDU which extends over more than one TCP segment.
 *  This is not correct. As noted in Bug #5366 (and elsewhere), the correct method
 *  is that:
 *   "one must walk over all the message first, to determine its exact length
 *    (one of the characteristics of the protocol, hard to determine prior to
 *    going over the message what its final size would be)".
 *
 *  The original method of reassembly:
 *    When more data is needed to continue dissecting a PDU, repeatedly request
 *    a few additional bytes (for one or a few more fields of the PDU).
 *   This resulted in 'one-pass' tshark dissection redissecting
 *    the PDU repeatedly many, many times with each time dissecting
 *    the PDU with one or a few more additional fields.
 *    This generated *lots* of (repeated) output since a reassembled
 *    VNC PDU can contain many fields (each of short length).
 *    It also resulted in the fragment table containing many, many small fragments
 *     for VNC PDUS containing many small fields.
 *
 *  The current reassembly method:
 *    Use DESEGMENT_ONE_MORE_SEGMENT when requesting additional data for a PDU.
 *    This significantly reduces the amount of repeated data in a dissection,
 *    but will still result in "partial" repeated dissection output in some cases.
 */

/* (Somewhat random notes while reviewing the code):
   Check types, etc against IANA list
   Optimize: Do col_set(..., COL_INFO) once (after fetching message type & before dispatching ?)
   Dispatch via a message table (instead of using a switch(...)
   Worry about globals (vnc_bytes_per_pixel & nc_depth): "Global so they keep their value between packets"
   Msg type 150: client-server: enable/disable (1+9 bytes); server-client: endofContinousUpdates(1+0 bytes) ?
*/

#include "config.h"

#include <epan/packet.h>
#include <epan/conversation.h>
#include <epan/prefs.h>
#include <epan/expert.h>
#include <epan/proto_data.h>
#include "packet-x11.h" /* This contains the extern for the X11 value_string_ext
                         * "x11_keysym_vals_source_ext" that VNC uses. */

void proto_register_vnc(void);

typedef enum {
        VNC_SECURITY_TYPE_INVALID       =  0,
        VNC_SECURITY_TYPE_NONE          =  1,
        VNC_SECURITY_TYPE_VNC           =  2,
        VNC_SECURITY_TYPE_RA2           =  5,
        VNC_SECURITY_TYPE_RA2ne         =  6,
        VNC_SECURITY_TYPE_TIGHT         = 16,
        VNC_SECURITY_TYPE_ULTRA         = 17,
        VNC_SECURITY_TYPE_TLS           = 18,
        VNC_SECURITY_TYPE_VENCRYPT      = 19,
        VNC_SECURITY_TYPE_GTK_VNC_SASL  = 20,
        VNC_SECURITY_TYPE_MD5_HASH_AUTH = 21,
        VNC_SECURITY_TYPE_XVP           = 22,
        VNC_SECURITY_TYPE_ARD           = 30,
        VNC_TIGHT_AUTH_TGHT_ULGNAUTH    = 119,
        VNC_TIGHT_AUTH_TGHT_XTRNAUTH    = 130
} vnc_security_types_e;

static const value_string vnc_security_types_vs[] = {
        { VNC_SECURITY_TYPE_INVALID,      "Invalid"              },
        { VNC_SECURITY_TYPE_NONE,         "None"                 },
        { VNC_SECURITY_TYPE_VNC,          "VNC"                  },
        { VNC_SECURITY_TYPE_RA2,          "RA2"                  },
        { VNC_SECURITY_TYPE_RA2ne,        "RA2ne"                },
        { VNC_SECURITY_TYPE_TIGHT,        "Tight"                },
        { VNC_SECURITY_TYPE_ULTRA,        "Ultra"                },
        { VNC_SECURITY_TYPE_TLS,          "TLS"                  },
        { VNC_SECURITY_TYPE_VENCRYPT,     "VeNCrypt"             },
        { VNC_SECURITY_TYPE_GTK_VNC_SASL, "GTK-VNC SASL"         },
        { VNC_SECURITY_TYPE_ARD,          "Apple Remote Desktop" },
        { 0,  NULL                     }
};

static const true_false_string auth_result_tfs = {
        "Failed",
        "OK"
};

static const value_string yes_no_vs[] = {
        { 0, "No"  },
        { 1, "Yes" },
        { 0,  NULL }
};

typedef enum {
        /* Required */
        VNC_CLIENT_MESSAGE_TYPE_SET_PIXEL_FORMAT          =   0,
        VNC_CLIENT_MESSAGE_TYPE_SET_ENCODINGS             =   2,
        VNC_CLIENT_MESSAGE_TYPE_FRAMEBUF_UPDATE_REQ       =   3,
        VNC_CLIENT_MESSAGE_TYPE_KEY_EVENT                 =   4,
        VNC_CLIENT_MESSAGE_TYPE_POINTER_EVENT             =   5,
        VNC_CLIENT_MESSAGE_TYPE_CLIENT_CUT_TEXT           =   6,
        /* Optional */
        VNC_CLIENT_MESSAGE_TYPE_MIRRORLINK                = 128,
        VNC_CLIENT_MESSAGE_TYPE_ENABLE_CONTINUOUS_UPDATES = 150,  /* TightVNC */
        VNC_CLIENT_MESSAGE_TYPE_FENCE                     = 248,  /* TigerVNC */
        VNC_CLIENT_MESSAGE_TYPE_XVP                       = 250,
        VNC_CLIENT_MESSAGE_TYPE_SETR_DESKTOP_SIZE         = 251,
        VNC_CLIENT_MESSAGE_TYPE_TIGHT                     = 252,
        VNC_CLIENT_MESSAGE_TYPE_GII                       = 253,
        VNC_CLIENT_MESSAGE_TYPE_QEMU                      = 255
} vnc_client_message_types_e;

static const value_string vnc_client_message_types_vs[] = {
        /* Required */
        { VNC_CLIENT_MESSAGE_TYPE_SET_PIXEL_FORMAT,          "Set Pixel Format"           },
        { VNC_CLIENT_MESSAGE_TYPE_SET_ENCODINGS,             "Set Encodings"              },
        { VNC_CLIENT_MESSAGE_TYPE_FRAMEBUF_UPDATE_REQ,       "Framebuffer Update Request" },
        { VNC_CLIENT_MESSAGE_TYPE_KEY_EVENT,                 "Key Event"                  },
        { VNC_CLIENT_MESSAGE_TYPE_POINTER_EVENT,             "Pointer Event"              },
        { VNC_CLIENT_MESSAGE_TYPE_CLIENT_CUT_TEXT,           "Cut Text"                   },
        /* Optional */
        { VNC_CLIENT_MESSAGE_TYPE_MIRRORLINK,                "MirrorLink"                 },
        { VNC_CLIENT_MESSAGE_TYPE_ENABLE_CONTINUOUS_UPDATES, "Enable Continuous Updates"  },
        { VNC_CLIENT_MESSAGE_TYPE_FENCE,                     "Fence"                      },
        { VNC_CLIENT_MESSAGE_TYPE_XVP,                       "Xvp"                        },
        { VNC_CLIENT_MESSAGE_TYPE_SETR_DESKTOP_SIZE,         "Setr Desktop Size"          },
        { VNC_CLIENT_MESSAGE_TYPE_TIGHT,                     "Tight"                      },
        { VNC_CLIENT_MESSAGE_TYPE_GII,                       "Gii"                        },
        { VNC_CLIENT_MESSAGE_TYPE_QEMU,                      "Qemu"                       },
        { 0,  NULL                                                                        }
};

typedef enum {
        VNC_SERVER_MESSAGE_TYPE_FRAMEBUFFER_UPDATE        =   0,
        VNC_SERVER_MESSAGE_TYPE_SET_COLORMAP_ENTRIES      =   1,
        VNC_SERVER_MESSAGE_TYPE_RING_BELL                 =   2,
        VNC_SERVER_MESSAGE_TYPE_CUT_TEXT                  =   3,
        VNC_SERVER_MESSAGE_TYPE_MIRRORLINK                = 128,
        VNC_SERVER_MESSAGE_TYPE_END_CONTINUOUS_UPDATES    = 150,  /* TightVNC */
        VNC_SERVER_MESSAGE_TYPE_FENCE                     = 248,  /* TigerVNC */
        VNC_SERVER_MESSAGE_TYPE_XVP                       = 250,
        VNC_SERVER_MESSAGE_TYPE_TIGHT                     = 252,
        VNC_SERVER_MESSAGE_TYPE_GII                       = 253,
        VNC_SERVER_MESSAGE_TYPE_QEMU                      = 255
} vnc_server_message_types_e;

static const value_string vnc_server_message_types_vs[] = {
        { VNC_SERVER_MESSAGE_TYPE_FRAMEBUFFER_UPDATE,        "Framebuffer Update"        },
        { VNC_SERVER_MESSAGE_TYPE_SET_COLORMAP_ENTRIES,      "Set Colormap Entries"      },
        { VNC_SERVER_MESSAGE_TYPE_RING_BELL,                 "Ring Bell"                 },
        { VNC_SERVER_MESSAGE_TYPE_CUT_TEXT,                  "Cut Text"                  },
        { VNC_SERVER_MESSAGE_TYPE_MIRRORLINK,                "MirrorLink"                },
        { VNC_SERVER_MESSAGE_TYPE_END_CONTINUOUS_UPDATES,    "End Continuous Updates" },
        { VNC_SERVER_MESSAGE_TYPE_FENCE,                     "Fence"                     },
        { VNC_SERVER_MESSAGE_TYPE_XVP,                       "Xvp"                       },
        { VNC_SERVER_MESSAGE_TYPE_TIGHT,                     "Tight"                     },
        { VNC_SERVER_MESSAGE_TYPE_GII,                       "Gii"                       },
        { VNC_SERVER_MESSAGE_TYPE_QEMU,                      "Qemu"                      },
        { 0,  NULL                                                                       }
};

static const true_false_string button_mask_tfs = {
        "Pressed",
        "Not pressed"
};

#define VNC_ENCODING_TYPE_DESKTOP_SIZE       0xFFFFFF21
#define VNC_ENCODING_TYPE_LAST_RECT          0xFFFFFF20
#define VNC_ENCODING_TYPE_POINTER_POS        0xFFFFFF18
#define VNC_ENCODING_TYPE_RICH_CURSOR        0xFFFFFF11
#define VNC_ENCODING_TYPE_X_CURSOR           0xFFFFFF10
#define VNC_ENCODING_TYPE_RAW                0
#define VNC_ENCODING_TYPE_COPY_RECT          1
#define VNC_ENCODING_TYPE_RRE                2
#define VNC_ENCODING_TYPE_CORRE              4
#define VNC_ENCODING_TYPE_HEXTILE            5
#define VNC_ENCODING_TYPE_ZLIB               6
#define VNC_ENCODING_TYPE_TIGHT              7
#define VNC_ENCODING_TYPE_ZLIBHEX            8
#define VNC_ENCODING_TYPE_ULTRA              9
#define VNC_ENCODING_TYPE_TRLE               15
#define VNC_ENCODING_TYPE_RLE                16
#define VNC_ENCODING_TYPE_HITACHI_ZYWRLE     17
#define VNC_ENCODING_TYPE_JPEG_0             -32
#define VNC_ENCODING_TYPE_JPEG_1             -31
#define VNC_ENCODING_TYPE_JPEG_2             -30
#define VNC_ENCODING_TYPE_JPEG_3             -29
#define VNC_ENCODING_TYPE_JPEG_4             -28
#define VNC_ENCODING_TYPE_JPEG_5             -27
#define VNC_ENCODING_TYPE_JPEG_6             -26
#define VNC_ENCODING_TYPE_JPEG_7             -25
#define VNC_ENCODING_TYPE_JPEG_8             -24
#define VNC_ENCODING_TYPE_JPEG_9             -23
#define VNC_ENCODING_TYPE_COMPRESSION_0      0xFFFFFF00
#define VNC_ENCODING_TYPE_COMPRESSION_1      0xFFFFFF01
#define VNC_ENCODING_TYPE_COMPRESSION_2      0xFFFFFF02
#define VNC_ENCODING_TYPE_COMPRESSION_3      0xFFFFFF03
#define VNC_ENCODING_TYPE_COMPRESSION_4      0xFFFFFF04
#define VNC_ENCODING_TYPE_COMPRESSION_5      0xFFFFFF05
#define VNC_ENCODING_TYPE_COMPRESSION_6      0xFFFFFF06
#define VNC_ENCODING_TYPE_COMPRESSION_7      0xFFFFFF07
#define VNC_ENCODING_TYPE_COMPRESSION_8      0xFFFFFF08
#define VNC_ENCODING_TYPE_COMPRESSION_9      0xFFFFFF09
#define VNC_ENCODING_TYPE_WMVi               0x574D5669
#define VNC_ENCODING_TYPE_CACHE              0xFFFF0000
#define VNC_ENCODING_TYPE_CACHE_ENABLE       0xFFFF0001
#define VNC_ENCODING_TYPE_XOR_ZLIB           0xFFFF0002
#define VNC_ENCODING_TYPE_XOR_MONO_ZLIB      0xFFFF0003
#define VNC_ENCODING_TYPE_XOR_MULTI_ZLIB     0xFFFF0004
#define VNC_ENCODING_TYPE_SOLID_COLOR        0xFFFF0005
#define VNC_ENCODING_TYPE_XOR_ENABLE         0xFFFF0006
#define VNC_ENCODING_TYPE_CACHE_ZIP          0xFFFF0007
#define VNC_ENCODING_TYPE_SOL_MONO_ZIP       0xFFFF0008
#define VNC_ENCODING_TYPE_ULTRA_ZIP          0xFFFF0009
#define VNC_ENCODING_TYPE_SERVER_STATE       0xFFFF8000
#define VNC_ENCODING_TYPE_ENABLE_KEEP_ALIVE  0xFFFF8001
#define VNC_ENCODING_TYPE_FTP_PROTO_VER      0xFFFF8002
#define VNC_ENCODING_TYPE_POINTER_CHANGE     -257
#define VNC_ENCODING_TYPE_EXT_KEY_EVENT      -258
#define VNC_ENCODING_TYPE_AUDIO               259
#define VNC_ENCODING_TYPE_DESKTOP_NAME       -307
#define VNC_ENCODING_TYPE_EXTENDED_DESK_SIZE -308
#define VNC_ENCODING_TYPE_KEYBOARD_LED_STATE 0XFFFE0000
#define VNC_ENCODING_TYPE_SUPPORTED_MESSAGES 0XFFFE0001
#define VNC_ENCODING_TYPE_SUPPORTED_ENCODINGS 0XFFFE0002
#define VNC_ENCODING_TYPE_SERVER_IDENTITY    0XFFFE0003
#define VNC_ENCODING_TYPE_MIRRORLINK         0xFFFFFDF5
#define VNC_ENCODING_TYPE_CONTEXT_INFORMATION 0xFFFFFDF4
#define VNC_ENCODING_TYPE_SLRLE              0xFFFFFDF3
#define VNC_ENCODING_TYPE_TRANSFORM          0xFFFFFDF2
#define VNC_ENCODING_TYPE_HSML               0xFFFFFDF1
#define VNC_ENCODING_TYPE_H264               0X48323634

static const value_string encoding_types_vs[] = {
        { VNC_ENCODING_TYPE_DESKTOP_SIZE,       "DesktopSize (pseudo)" },
        { VNC_ENCODING_TYPE_LAST_RECT,          "LastRect (pseudo)"    },
        { VNC_ENCODING_TYPE_POINTER_POS,        "Pointer pos (pseudo)" },
        { VNC_ENCODING_TYPE_RICH_CURSOR,        "Rich Cursor (pseudo)" },
        { VNC_ENCODING_TYPE_X_CURSOR,           "X Cursor (pseudo)"    },
        { VNC_ENCODING_TYPE_RAW,                "Raw"                  },
        { VNC_ENCODING_TYPE_COPY_RECT,          "CopyRect"             },
        { VNC_ENCODING_TYPE_RRE,                "RRE"                  },
        { VNC_ENCODING_TYPE_CORRE,              "CoRRE"                },
        { VNC_ENCODING_TYPE_HEXTILE,            "Hextile"              },
        { VNC_ENCODING_TYPE_ZLIB,               "Zlib"                 },
        { VNC_ENCODING_TYPE_TIGHT,              "Tight"                },
        { VNC_ENCODING_TYPE_ZLIBHEX,            "ZlibHex"              },
        { VNC_ENCODING_TYPE_ULTRA,              "Ultra"                },
        { VNC_ENCODING_TYPE_RLE,                "ZRLE"                 },
        { VNC_ENCODING_TYPE_HITACHI_ZYWRLE,     "Hitachi ZYWRLE"       },
        { VNC_ENCODING_TYPE_JPEG_0,             "JPEG quality level 0" },
        { VNC_ENCODING_TYPE_JPEG_1,             "JPEG quality level 1" },
        { VNC_ENCODING_TYPE_JPEG_2,             "JPEG quality level 2" },
        { VNC_ENCODING_TYPE_JPEG_3,             "JPEG quality level 3" },
        { VNC_ENCODING_TYPE_JPEG_4,             "JPEG quality level 4" },
        { VNC_ENCODING_TYPE_JPEG_5,             "JPEG quality level 5" },
        { VNC_ENCODING_TYPE_JPEG_6,             "JPEG quality level 6" },
        { VNC_ENCODING_TYPE_JPEG_7,             "JPEG quality level 7" },
        { VNC_ENCODING_TYPE_JPEG_8,             "JPEG quality level 8" },
        { VNC_ENCODING_TYPE_JPEG_9,             "JPEG quality level 9" },
        { VNC_ENCODING_TYPE_COMPRESSION_0,      "Compression level 0"  },
        { VNC_ENCODING_TYPE_COMPRESSION_1,      "Compression level 1"  },
        { VNC_ENCODING_TYPE_COMPRESSION_2,      "Compression level 2"  },
        { VNC_ENCODING_TYPE_COMPRESSION_3,      "Compression level 3"  },
        { VNC_ENCODING_TYPE_COMPRESSION_4,      "Compression level 4"  },
        { VNC_ENCODING_TYPE_COMPRESSION_5,      "Compression level 5"  },
        { VNC_ENCODING_TYPE_COMPRESSION_6,      "Compression level 6"  },
        { VNC_ENCODING_TYPE_COMPRESSION_7,      "Compression level 7"  },
        { VNC_ENCODING_TYPE_COMPRESSION_8,      "Compression level 8"  },
        { VNC_ENCODING_TYPE_COMPRESSION_9,      "Compression level 9"  },
        /* FIXME understand for real what the below mean. Taken from Ultra VNC source code */
/*      { VNC_ENCODING_TYPE_CACHE,     */
        { VNC_ENCODING_TYPE_CACHE_ENABLE,       "Enable Caching"},
/*      { VNC_ENCODING_TYPE_XOR_ZLIB,
        { VNC_ENCODING_TYPE_XOR_MONO_ZLIB,
        { VNC_ENCODING_TYPE_XOR_MULTI_ZLIB,
        { VNC_ENCODING_TYPE_SOLID_COLOR,
        { VNC_ENCODING_TYPE_XOR_ENABLE,
        { VNC_ENCODING_TYPE_CACHE_ZIP,
        { VNC_ENCODING_TYPE_SOL_MONO_ZIP,
        { VNC_ENCODING_TYPE_ULTRA_ZIP,
*/      { VNC_ENCODING_TYPE_SERVER_STATE,       "Server State"         },
        { VNC_ENCODING_TYPE_ENABLE_KEEP_ALIVE,  "Enable Keep Alive"    },
        { VNC_ENCODING_TYPE_FTP_PROTO_VER,      "FTP protocol version" },
        { VNC_ENCODING_TYPE_EXTENDED_DESK_SIZE, "Extended Desktop Size"},
        { VNC_ENCODING_TYPE_DESKTOP_NAME,       "Desktop Name"         },
        { VNC_ENCODING_TYPE_KEYBOARD_LED_STATE, "Keyboard LED State"   },
        { VNC_ENCODING_TYPE_SUPPORTED_MESSAGES, "Supported Messages"   },
        { VNC_ENCODING_TYPE_SUPPORTED_ENCODINGS, "Supported Encodings" },
        { VNC_ENCODING_TYPE_SERVER_IDENTITY,    "Server Identity"      },
        { VNC_ENCODING_TYPE_MIRRORLINK,         "MirrorLink"           },
        { VNC_ENCODING_TYPE_CONTEXT_INFORMATION, "Context Information" },
        { VNC_ENCODING_TYPE_SLRLE,              "SLRLE"                },
        { VNC_ENCODING_TYPE_TRANSFORM,          "Transform"            },
        { VNC_ENCODING_TYPE_HSML,               "HSML"                 },
        { VNC_ENCODING_TYPE_H264,               "H264"                 },
        { 0,                            NULL                   }
};

/* Rectangle types for Tight encoding.  These come in the "control byte" at the
 * start of a rectangle's payload.  Note that these are with respect to the most
 * significant bits 4-7 of the control byte, so you must shift it to the right 4
 * bits before comparing against these values.
 */
#define TIGHT_RECT_FILL      0x08
#define TIGHT_RECT_JPEG      0x09
#define TIGHT_RECT_MAX_VALUE 0x09

#define TIGHT_RECT_EXPLICIT_FILTER_FLAG 0x04

/* Filter types for Basic encoding of Tight rectangles */
#define TIGHT_RECT_FILTER_COPY     0x00
#define TIGHT_RECT_FILTER_PALETTE  0x01
#define TIGHT_RECT_FILTER_GRADIENT 0x02

/* Minimum number of bytes to compress for Tight encoding */
#define TIGHT_MIN_BYTES_TO_COMPRESS 12

static const value_string tight_filter_ids_vs[] = {
        { TIGHT_RECT_FILTER_COPY,     "Copy"     },
        { TIGHT_RECT_FILTER_PALETTE,  "Palette"  },
        { TIGHT_RECT_FILTER_GRADIENT, "Gradient" },
        { 0, NULL }
};

/* MirrorLink messages */
typedef enum {
        VNC_ML_EXT_BYE_BYE                      = 0,
        VNC_ML_EXT_SERVER_DISPLAY_CONFIGURATION = 1,
        VNC_ML_EXT_CLIENT_DISPLAY_CONFIGURATION = 2,
        VNC_ML_EXT_SERVER_EVENT_CONFIGURATION   = 3,
        VNC_ML_EXT_CLIENT_EVENT_CONFIGURATION   = 4,
        VNC_ML_EXT_EVENT_MAPPING                = 5,
        VNC_ML_EXT_EVENT_MAPPING_REQUEST        = 6,
        VNC_ML_EXT_KEY_EVENT_LISTING            = 7,
        VNC_ML_EXT_KEY_EVENT_LISTING_REQUEST    = 8,
        VNC_ML_EXT_VIRTUAL_KEYBOARD             = 9,
        VNC_ML_EXT_VIRTUAL_KEYBOARD_REQUEST     = 10,
        VNC_ML_EXT_DEVICE_STATUS                = 11,
        VNC_ML_EXT_DEVICE_STATUS_REQUEST        = 12,
        VNC_ML_EXT_CONTENT_ATTESTATION          = 13,
        VNC_ML_EXT_CONTENT_ATTESTATION_REQUEST  = 14,
        VNC_ML_EXT_FB_BLOCKING_NOTIFICATION     = 16,
        VNC_ML_EXT_AUDIO_BLOCKING_NOTIFICATION  = 18,
        VNC_ML_EXT_TOUCH_EVENT                  = 20,
        VNC_ML_EXT_FB_ALTERNATIVE_TEXT          = 21,
        VNC_ML_EXT_FB_ALTERNATIVE_TEXT_REQUEST  = 22
} vnc_mirrorlink_ext_types_e;

static const value_string vnc_mirrorlink_types_vs[] = {
        { VNC_ML_EXT_BYE_BYE,                      "ByeBye"                               },
        { VNC_ML_EXT_SERVER_DISPLAY_CONFIGURATION, "Server Display Configuration"         },
        { VNC_ML_EXT_CLIENT_DISPLAY_CONFIGURATION, "Client Display Configuration"         },
        { VNC_ML_EXT_SERVER_EVENT_CONFIGURATION,   "Server Event Configuration"           },
        { VNC_ML_EXT_CLIENT_EVENT_CONFIGURATION,   "Client Event Configuration"           },
        { VNC_ML_EXT_EVENT_MAPPING,                "Event Mapping"                        },
        { VNC_ML_EXT_EVENT_MAPPING_REQUEST,        "Event Mapping Request"                },
        { VNC_ML_EXT_KEY_EVENT_LISTING,            "Key Event Listing"                    },
        { VNC_ML_EXT_KEY_EVENT_LISTING_REQUEST,    "Key Event Listing Request"            },
        { VNC_ML_EXT_VIRTUAL_KEYBOARD,             "Virtual Keyboard Trigger"             },
        { VNC_ML_EXT_VIRTUAL_KEYBOARD_REQUEST,     "Virtual Keyboard Trigger Request"     },
        { VNC_ML_EXT_DEVICE_STATUS,                "Device Status"                        },
        { VNC_ML_EXT_DEVICE_STATUS_REQUEST,        "Device Status Request"                },
        { VNC_ML_EXT_CONTENT_ATTESTATION,          "Content Attestation"                  },
        { VNC_ML_EXT_CONTENT_ATTESTATION_REQUEST,  "Content Attestation Request"          },
        { VNC_ML_EXT_FB_BLOCKING_NOTIFICATION,     "Framebuffer Blocking Notification"    },
        { VNC_ML_EXT_AUDIO_BLOCKING_NOTIFICATION,  "Audio Blocking Notification"          },
        { VNC_ML_EXT_TOUCH_EVENT,                  "Touch Event"                          },
        { VNC_ML_EXT_FB_ALTERNATIVE_TEXT,          "Framebuffer Alternative Text"         },
        { VNC_ML_EXT_FB_ALTERNATIVE_TEXT_REQUEST,  "Framebuffer Alternative Text Request" },
        { 0,  NULL                                                                        }
};

/* Slice types for H.264 encoding */
typedef enum {
        VNC_H264_SLICE_TYPE_P = 0,
        VNC_H264_SLICE_TYPE_B = 1,
        VNC_H264_SLICE_TYPE_I = 2
} vnc_h264_slice_types_e;

static const value_string vnc_h264_slice_types_vs[] = {
        { VNC_H264_SLICE_TYPE_P, "Predicted"    },
        { VNC_H264_SLICE_TYPE_B, "Bi-predicted" },
        { VNC_H264_SLICE_TYPE_I, "Intra coded"  },
        { 0, NULL                               }
};


typedef enum {
        VNC_SESSION_STATE_SERVER_VERSION,
        VNC_SESSION_STATE_CLIENT_VERSION,

        VNC_SESSION_STATE_SECURITY,
        VNC_SESSION_STATE_SECURITY_TYPES,

        VNC_SESSION_STATE_TIGHT_TUNNELING_CAPABILITIES,
        VNC_SESSION_STATE_TIGHT_TUNNEL_TYPE_REPLY,
        VNC_SESSION_STATE_TIGHT_AUTH_CAPABILITIES,
        VNC_SESSION_STATE_TIGHT_AUTH_TYPE_REPLY,
        VNC_SESSION_STATE_TIGHT_UNKNOWN_PACKET3,

        VNC_SESSION_STATE_VNC_AUTHENTICATION_CHALLENGE,
        VNC_SESSION_STATE_VNC_AUTHENTICATION_RESPONSE,

        VNC_SESSION_STATE_ARD_AUTHENTICATION_CHALLENGE,
        VNC_SESSION_STATE_ARD_AUTHENTICATION_RESPONSE,

        VNC_SESSION_STATE_SECURITY_RESULT,

        VNC_SESSION_STATE_CLIENT_INIT,
        VNC_SESSION_STATE_SERVER_INIT,

        VNC_SESSION_STATE_TIGHT_INTERACTION_CAPS,

        VNC_SESSION_STATE_NORMAL_TRAFFIC
} vnc_session_state_e;

#define VNC_FENCE_BLOCK_BEFORE   0x00000001
#define VNC_FENCE_BLOCK_AFTER    0x00000002
#define VNC_FENCE_SYNC_NEXT      0x00000004
#define VNC_FENCE_REQUEST        0x80000000

/* This structure will be tied to each conversation. */
typedef struct {
        gdouble server_proto_ver, client_proto_ver;
        vnc_session_state_e vnc_next_state;
        guint32 server_port;
        /* These are specific to TightVNC */
        gint num_server_message_types;
        gint num_client_message_types;
        gint num_encoding_types;
        guint8 security_type_selected;
        gboolean tight_enabled;
        /* This is specific to Apple Remote Desktop */
        guint16 ard_key_length;
} vnc_conversation_t;

/* This structure will be tied to each packet */
typedef struct {
        vnc_session_state_e state;
        gint preferred_encoding;
        guint8 bytes_per_pixel;
        guint8 depth;
} vnc_packet_t;

void proto_reg_handoff_vnc(void);

static gboolean vnc_startup_messages(tvbuff_t *tvb, packet_info *pinfo,
                                     gint offset, proto_tree *tree,
                                     vnc_conversation_t *per_conversation_info);
static void vnc_client_to_server(tvbuff_t *tvb, packet_info *pinfo,
                                 gint *offset, proto_tree *tree);
static void vnc_server_to_client(tvbuff_t *tvb, packet_info *pinfo,
                                 gint *offset, proto_tree *tree);
static void vnc_client_set_pixel_format(tvbuff_t *tvb, packet_info *pinfo,
                                        gint *offset, proto_tree *tree);
static void vnc_client_set_encodings(tvbuff_t *tvb, packet_info *pinfo,
                                     gint *offset, proto_tree *tree);
static void vnc_client_framebuffer_update_request(tvbuff_t *tvb,
                                                  packet_info *pinfo,
                                                  gint *offset,
                                                  proto_tree *tree);
static void vnc_client_key_event(tvbuff_t *tvb, packet_info *pinfo,
                                 gint *offset, proto_tree *tree);
static void vnc_client_pointer_event(tvbuff_t *tvb, packet_info *pinfo,
                                     gint *offset, proto_tree *tree);
static void vnc_client_cut_text(tvbuff_t *tvb, packet_info *pinfo, gint *offset,
                                proto_tree *tree);

static guint vnc_server_framebuffer_update(tvbuff_t *tvb, packet_info *pinfo,
                                           gint *offset, proto_tree *tree);
static guint vnc_raw_encoding(tvbuff_t *tvb, packet_info *pinfo, gint *offset,
                              proto_tree *tree, const guint16 width, const guint16 height);
static guint vnc_copyrect_encoding(tvbuff_t *tvb, packet_info *pinfo,
                                   gint *offset, proto_tree *tree,
                                   const guint16 width, const guint16 height);
static guint vnc_rre_encoding(tvbuff_t *tvb, packet_info *pinfo, gint *offset,
                              proto_tree *tree, const guint16 width, const guint16 height);
static guint vnc_hextile_encoding(tvbuff_t *tvb, packet_info *pinfo,
                                  gint *offset, proto_tree *tree,
                                  const guint16 width, const guint16 height);
static guint vnc_zrle_encoding(tvbuff_t *tvb, packet_info *pinfo, gint *offset,
                               proto_tree *tree, const guint16 width, const guint16 height);
static guint vnc_tight_encoding(tvbuff_t *tvb, packet_info *pinfo, gint *offset,
                                proto_tree *tree, const guint16 width, const guint16 height);
static guint vnc_rich_cursor_encoding(tvbuff_t *tvb, packet_info *pinfo,
                                      gint *offset, proto_tree *tree, const guint16 width,
                                      const guint16 height);
static guint vnc_x_cursor_encoding(tvbuff_t *tvb, packet_info *pinfo,
                                   gint *offset, proto_tree *tree, const guint16 width,
                                   const guint16 height);
static guint vnc_server_set_colormap_entries(tvbuff_t *tvb, packet_info *pinfo,
                                             gint *offset, proto_tree *tree);
static void vnc_server_ring_bell(tvbuff_t *tvb, packet_info *pinfo,
                                 gint *offset, proto_tree *tree);
static guint vnc_server_cut_text(tvbuff_t *tvb, packet_info *pinfo,
                                 gint *offset, proto_tree *tree);
static void vnc_set_bytes_per_pixel(packet_info *pinfo, const guint8 bytes_per_pixel);
static void vnc_set_depth(packet_info *pinfo, const guint8 depth);
static guint8 vnc_get_bytes_per_pixel(packet_info *pinfo);
static guint8 vnc_get_depth(packet_info *pinfo);
static guint32 vnc_extended_desktop_size(tvbuff_t *tvb, gint *offset, proto_tree *tree);

static guint vnc_supported_messages(tvbuff_t *tvb, gint *offset,
                                    proto_tree *tree, const guint16 width);
static guint vnc_supported_encodings(tvbuff_t *tvb, gint *offset,
                                     proto_tree *tree, const guint16 width,
                                     const guint16 height);
static guint vnc_server_identity(tvbuff_t *tvb, gint *offset,
                                 proto_tree *tree, const guint16 width);

static guint vnc_fence(tvbuff_t *tvb, packet_info *pinfo, gint *offset,
                            proto_tree *tree);
static guint vnc_mirrorlink(tvbuff_t *tvb, packet_info *pinfo, gint *offset,
                            proto_tree *tree);
static guint vnc_context_information(tvbuff_t *tvb, gint *offset,
                                     proto_tree *tree);
static guint vnc_slrle_encoding(tvbuff_t *tvb, packet_info *pinfo, gint *offset,
                                proto_tree *tree, const guint16 height);

static guint vnc_h264_encoding(tvbuff_t *tvb, gint *offset, proto_tree *tree);

#define DEST_PORT_VNC pinfo->destport == 5500 || pinfo->destport == 5501 || \
                pinfo->destport == 5900 || pinfo->destport == 5901 ||   \
                pinfo->destport == vnc_preference_alternate_port

#define VNC_BYTES_NEEDED(a)                                     \
        if((a) > (guint)tvb_reported_length_remaining(tvb, *offset))    \
                return (a);

/* Variables for our preferences */
static guint vnc_preference_alternate_port = 0;

/* Initialize the protocol and registered fields */
static int proto_vnc = -1; /* Protocol subtree */
static int hf_vnc_padding = -1;
static int hf_vnc_server_proto_ver = -1;
static int hf_vnc_client_proto_ver = -1;
static int hf_vnc_num_security_types = -1;
static int hf_vnc_security_type = -1;
static int hf_vnc_server_security_type = -1;
static int hf_vnc_client_security_type = -1;
static int hf_vnc_auth_challenge = -1;
static int hf_vnc_auth_response = -1;
static int hf_vnc_auth_result = -1;
static int hf_vnc_auth_error = -1;
static int hf_vnc_auth_error_length = -1;

static int hf_vnc_ard_auth_generator = -1;
static int hf_vnc_ard_auth_key_len = -1;
static int hf_vnc_ard_auth_modulus = -1;
static int hf_vnc_ard_auth_server_key = -1;
static int hf_vnc_ard_auth_credentials = -1;
static int hf_vnc_ard_auth_client_key = -1;

static int hf_vnc_share_desktop_flag = -1;
static int hf_vnc_width = -1;
static int hf_vnc_height = -1;
static int hf_vnc_server_bits_per_pixel = -1;
static int hf_vnc_server_depth = -1;
static int hf_vnc_server_big_endian_flag = -1;
static int hf_vnc_server_true_color_flag = -1;
static int hf_vnc_server_red_max = -1;
static int hf_vnc_server_green_max = -1;
static int hf_vnc_server_blue_max = -1;
static int hf_vnc_server_red_shift = -1;
static int hf_vnc_server_green_shift = -1;
static int hf_vnc_server_blue_shift = -1;
static int hf_vnc_desktop_name = -1;
static int hf_vnc_desktop_name_len = -1;
static int hf_vnc_desktop_screen_num = -1;
static int hf_vnc_desktop_screen_id = -1;
static int hf_vnc_desktop_screen_x = -1;
static int hf_vnc_desktop_screen_y = -1;
static int hf_vnc_desktop_screen_width = -1;
static int hf_vnc_desktop_screen_height = -1;
static int hf_vnc_desktop_screen_flags = -1;
static int hf_vnc_num_server_message_types = -1;
static int hf_vnc_num_client_message_types = -1;
static int hf_vnc_num_encoding_types = -1;

/********** Client Message Types **********/

static int hf_vnc_client_message_type = -1; /* A subtree under VNC */
static int hf_vnc_client_bits_per_pixel = -1;
static int hf_vnc_client_depth = -1;
static int hf_vnc_client_big_endian_flag = -1;
static int hf_vnc_client_true_color_flag = -1;
static int hf_vnc_client_red_max = -1;
static int hf_vnc_client_green_max = -1;
static int hf_vnc_client_blue_max = -1;
static int hf_vnc_client_red_shift = -1;
static int hf_vnc_client_green_shift = -1;
static int hf_vnc_client_blue_shift = -1;

/* Client Key Event */
static int hf_vnc_key_down = -1;
static int hf_vnc_key = -1;

/* Client Pointer Event */
static int hf_vnc_button_1_pos = -1;
static int hf_vnc_button_2_pos = -1;
static int hf_vnc_button_3_pos = -1;
static int hf_vnc_button_4_pos = -1;
static int hf_vnc_button_5_pos = -1;
static int hf_vnc_button_6_pos = -1;
static int hf_vnc_button_7_pos = -1;
static int hf_vnc_button_8_pos = -1;
static int hf_vnc_pointer_x_pos = -1;
static int hf_vnc_pointer_y_pos = -1;

/* Client Framebuffer Update Request */
static int hf_vnc_update_req_incremental = -1;
static int hf_vnc_update_req_x_pos = -1;
static int hf_vnc_update_req_y_pos = -1;
static int hf_vnc_update_req_width = -1;
static int hf_vnc_update_req_height = -1;

/* Client Set Encodings */
static int hf_vnc_encoding_num = -1;
static int hf_vnc_client_set_encodings_encoding_type = -1;

/* Client Cut Text */
static int hf_vnc_client_cut_text_len = -1;
static int hf_vnc_client_cut_text = -1;

/********** Server Message Types **********/

static int hf_vnc_server_message_type = -1; /* Subtree */

/* Tunneling capabilities (TightVNC extension) */
static int hf_vnc_tight_num_tunnel_types = -1;
static int hf_vnc_tight_tunnel_type = -1;

/* Authentication capabilities (TightVNC extension) */
static int hf_vnc_tight_num_auth_types = -1;
static int hf_vnc_tight_auth_code = -1;
/* TightVNC capabilities */
static int hf_vnc_tight_server_message_type = -1;
static int hf_vnc_tight_server_vendor = -1;
static int hf_vnc_tight_signature = -1;
static int hf_vnc_tight_server_name = -1;

static int hf_vnc_tight_client_message_type = -1;
static int hf_vnc_tight_client_vendor = -1;
static int hf_vnc_tight_client_name = -1;

static int hf_vnc_tight_encoding_type = -1;
static int hf_vnc_tight_encoding_vendor = -1;
static int hf_vnc_tight_encoding_name = -1;

/* Tight compression parameters */
static int hf_vnc_tight_reset_stream0 = -1;
static int hf_vnc_tight_reset_stream1 = -1;
static int hf_vnc_tight_reset_stream2 = -1;
static int hf_vnc_tight_reset_stream3 = -1;

static int hf_vnc_tight_rect_type = -1;

static int hf_vnc_tight_image_len = -1;
static int hf_vnc_tight_image_data = -1;

static int hf_vnc_tight_fill_color = -1;

static int hf_vnc_tight_filter_flag = -1;
static int hf_vnc_tight_filter_id = -1;

static int hf_vnc_tight_palette_num_colors = -1;
static int hf_vnc_tight_palette_data = -1;

/* Server Framebuffer Update */
static int hf_vnc_rectangle_num = -1;
static int hf_vnc_fb_update_x_pos = -1;
static int hf_vnc_fb_update_y_pos = -1;
static int hf_vnc_fb_update_width = -1;
static int hf_vnc_fb_update_height = -1;
static int hf_vnc_fb_update_encoding_type = -1;

/* Raw Encoding */
static int hf_vnc_raw_pixel_data = -1;

/* CopyRect Encoding */
static int hf_vnc_copyrect_src_x_pos = -1;
static int hf_vnc_copyrect_src_y_pos = -1;

/* RRE Encoding */
static int hf_vnc_rre_num_subrects = -1;
static int hf_vnc_rre_bg_pixel = -1;

static int hf_vnc_rre_subrect_pixel = -1;
static int hf_vnc_rre_subrect_x_pos = -1;
static int hf_vnc_rre_subrect_y_pos = -1;
static int hf_vnc_rre_subrect_width = -1;
static int hf_vnc_rre_subrect_height = -1;

/* Hextile Encoding */
static int hf_vnc_hextile_subencoding_mask = -1;
static int hf_vnc_hextile_raw = -1;
static int hf_vnc_hextile_raw_value = -1;
static int hf_vnc_hextile_bg = -1;
static int hf_vnc_hextile_bg_value = -1;
static int hf_vnc_hextile_fg = -1;
static int hf_vnc_hextile_fg_value = -1;
static int hf_vnc_hextile_anysubrects = -1;
static int hf_vnc_hextile_num_subrects = -1;
static int hf_vnc_hextile_subrectscolored = -1;
static int hf_vnc_hextile_subrect_pixel_value = -1;
static int hf_vnc_hextile_subrect_x_pos = -1;
static int hf_vnc_hextile_subrect_y_pos = -1;
static int hf_vnc_hextile_subrect_width = -1;
static int hf_vnc_hextile_subrect_height = -1;

/* ZRLE Encoding */
static int hf_vnc_zrle_len = -1;
static int hf_vnc_zrle_subencoding = -1;
static int hf_vnc_zrle_rle = -1;
static int hf_vnc_zrle_palette_size = -1;
static int hf_vnc_zrle_data = -1;
static int hf_vnc_zrle_raw = -1;
static int hf_vnc_zrle_palette = -1;

/* Cursor Encoding */
static int hf_vnc_cursor_x_fore_back = -1;
static int hf_vnc_cursor_encoding_pixels = -1;
static int hf_vnc_cursor_encoding_bitmask = -1;

/* Server Set Colormap Entries */
static int hf_vnc_color_groups = -1;
static int hf_vnc_colormap_first_color = -1;
static int hf_vnc_colormap_num_colors = -1;
static int hf_vnc_colormap_red = -1;
static int hf_vnc_colormap_green = -1;
static int hf_vnc_colormap_blue = -1;

/* Server Cut Text */
static int hf_vnc_server_cut_text_len = -1;
static int hf_vnc_server_cut_text = -1;

/* LibVNCServer additions */
static int hf_vnc_supported_messages_client2server = -1;
static int hf_vnc_supported_messages_server2client = -1;
static int hf_vnc_num_supported_encodings = -1;
static int hf_vnc_supported_encodings = -1;
static int hf_vnc_server_identity = -1;

/* MirrorLink */
static int hf_vnc_mirrorlink_type = -1;
static int hf_vnc_mirrorlink_length = -1;
static int hf_vnc_mirrorlink_version_major = -1;
static int hf_vnc_mirrorlink_version_minor = -1;
static int hf_vnc_mirrorlink_framebuffer_configuration = -1;
static int hf_vnc_mirrorlink_pixel_width = -1;
static int hf_vnc_mirrorlink_pixel_height = -1;
static int hf_vnc_mirrorlink_pixel_format = -1;
static int hf_vnc_mirrorlink_display_width = -1;
static int hf_vnc_mirrorlink_display_height = -1;
static int hf_vnc_mirrorlink_display_distance = -1;
static int hf_vnc_mirrorlink_keyboard_language = -1;
static int hf_vnc_mirrorlink_keyboard_country = -1;
static int hf_vnc_mirrorlink_ui_language = -1;
static int hf_vnc_mirrorlink_ui_country = -1;
static int hf_vnc_mirrorlink_knob_keys = -1;
static int hf_vnc_mirrorlink_device_keys = -1;
static int hf_vnc_mirrorlink_multimedia_keys = -1;
static int hf_vnc_mirrorlink_key_related = -1;
static int hf_vnc_mirrorlink_pointer_related = -1;
static int hf_vnc_mirrorlink_key_symbol_value_client = -1;
static int hf_vnc_mirrorlink_key_symbol_value_server = -1;
static int hf_vnc_mirrorlink_key_configuration = -1;
static int hf_vnc_mirrorlink_key_num_events = -1;
static int hf_vnc_mirrorlink_key_event_counter = -1;
static int hf_vnc_mirrorlink_key_symbol_value = -1;
static int hf_vnc_mirrorlink_key_request_configuration = -1;
static int hf_vnc_mirrorlink_keyboard_configuration = -1;
static int hf_vnc_mirrorlink_cursor_x = -1;
static int hf_vnc_mirrorlink_cursor_y = -1;
static int hf_vnc_mirrorlink_text_x = -1;
static int hf_vnc_mirrorlink_text_y = -1;
static int hf_vnc_mirrorlink_text_width = -1;
static int hf_vnc_mirrorlink_text_height = -1;
static int hf_vnc_mirrorlink_keyboard_request_configuration = -1;
static int hf_vnc_mirrorlink_device_status = -1;
static int hf_vnc_mirrorlink_app_id = -1;
static int hf_vnc_mirrorlink_fb_block_x = -1;
static int hf_vnc_mirrorlink_fb_block_y = -1;
static int hf_vnc_mirrorlink_fb_block_width = -1;
static int hf_vnc_mirrorlink_fb_block_height = -1;
static int hf_vnc_mirrorlink_fb_block_reason = -1;
static int hf_vnc_mirrorlink_audio_block_reason = -1;
static int hf_vnc_mirrorlink_touch_num_events = -1;
static int hf_vnc_mirrorlink_touch_x = -1;
static int hf_vnc_mirrorlink_touch_y = -1;
static int hf_vnc_mirrorlink_touch_id = -1;
static int hf_vnc_mirrorlink_touch_pressure = -1;
static int hf_vnc_mirrorlink_text = -1;
static int hf_vnc_mirrorlink_text_length = -1;
static int hf_vnc_mirrorlink_text_max_length = -1;
static int hf_vnc_mirrorlink_unknown = -1;

/* Fence */
static int hf_vnc_fence_flags = -1;
static int hf_vnc_fence_request = -1;
static int hf_vnc_fence_sync_next = -1;
static int hf_vnc_fence_block_after = -1;
static int hf_vnc_fence_block_before = -1;
static int hf_vnc_fence_payload_length = -1;
static int hf_vnc_fence_payload = -1;

static const int *vnc_fence_flags[] = {
        &hf_vnc_fence_request,
        &hf_vnc_fence_sync_next,
        &hf_vnc_fence_block_after,
        &hf_vnc_fence_block_before,
        NULL
};

/* Context Information */
static int hf_vnc_context_information_app_id = -1;
static int hf_vnc_context_information_app_category = -1;
static int hf_vnc_context_information_app_trust_level = -1;
static int hf_vnc_context_information_content_category = -1;
static int hf_vnc_context_information_content_rules = -1;
static int hf_vnc_context_information_content_trust_level = -1;

/* Scan Line based Run-Length Encoding */
static int hf_vnc_slrle_run_num = -1;
static int hf_vnc_slrle_run_data = -1;

/* H.264 Encoding */
static int hf_vnc_h264_slice_type = -1;
static int hf_vnc_h264_nbytes = -1;
static int hf_vnc_h264_width = -1;
static int hf_vnc_h264_height = -1;
static int hf_vnc_h264_data = -1;

/********** End of Server Message Types **********/

static gboolean vnc_preference_desegment = TRUE;

/* Initialize the subtree pointers */
static gint ett_vnc = -1;
static gint ett_vnc_client_message_type = -1;
static gint ett_vnc_server_message_type = -1;
static gint ett_vnc_rect = -1;
static gint ett_vnc_encoding_type = -1;
static gint ett_vnc_rre_subrect = -1;
static gint ett_vnc_hextile_subencoding_mask = -1;
static gint ett_vnc_hextile_num_subrects = -1;
static gint ett_vnc_hextile_subrect = -1;
static gint ett_vnc_hextile_tile = -1;
static gint ett_vnc_zrle_subencoding = -1;
static gint ett_vnc_colormap_num_groups = -1;
static gint ett_vnc_colormap_color_group = -1;
static gint ett_vnc_desktop_screen = -1;
static gint ett_vnc_key_events = -1;
static gint ett_vnc_touch_events = -1;
static gint ett_vnc_slrle_subline = -1;
static gint ett_vnc_fence_flags = -1;

static expert_field ei_vnc_possible_gtk_vnc_bug = EI_INIT;
static expert_field ei_vnc_auth_code_mismatch = EI_INIT;
static expert_field ei_vnc_unknown_tight_vnc_auth = EI_INIT;
static expert_field ei_vnc_too_many_rectangles = EI_INIT;
static expert_field ei_vnc_too_many_sub_rectangles = EI_INIT;
static expert_field ei_vnc_invalid_encoding = EI_INIT;
static expert_field ei_vnc_too_many_colors = EI_INIT;
static expert_field ei_vnc_too_many_cut_text = EI_INIT;
static expert_field ei_vnc_zrle_failed = EI_INIT;
static expert_field ei_vnc_unknown_tight = EI_INIT;
static expert_field ei_vnc_reassemble = EI_INIT;

/* Global so they keep their value between packets */
guint8 vnc_bytes_per_pixel;
guint8 vnc_depth;


static dissector_handle_t vnc_handle;

/* Code to dissect the packets */
static int
dissect_vnc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
        gboolean ret;
        gint     offset = 0;

        /* Set up structures needed to add the protocol subtree and manage it */
        proto_item         *ti;
        proto_tree         *vnc_tree;

        conversation_t     *conversation;
        vnc_conversation_t *per_conversation_info;

        conversation = find_or_create_conversation(pinfo);

        /* Retrieve information from conversation, or add it if it isn't
         * there yet */
        per_conversation_info = (vnc_conversation_t *)conversation_get_proto_data(conversation, proto_vnc);
        if(!per_conversation_info) {
                per_conversation_info = wmem_new(wmem_file_scope(), vnc_conversation_t);

                per_conversation_info->vnc_next_state = VNC_SESSION_STATE_SERVER_VERSION;
                per_conversation_info->security_type_selected = VNC_SECURITY_TYPE_INVALID;
                per_conversation_info->tight_enabled = FALSE;

                conversation_add_proto_data(conversation, proto_vnc, per_conversation_info);
        }


        /* Make entries in Protocol column and Info column on summary display */
        col_set_str(pinfo->cinfo, COL_PROTOCOL, "VNC");

        /* First, clear the info column */
        col_clear(pinfo->cinfo, COL_INFO);

        /* create display subtree for the protocol */
        ti = proto_tree_add_item(tree, proto_vnc, tvb, 0, -1, ENC_NA);
        vnc_tree = proto_item_add_subtree(ti, ett_vnc);

        /* Dissect any remaining session startup messages */
        ret = vnc_startup_messages(tvb, pinfo, offset, vnc_tree,
                                   per_conversation_info);

        vnc_set_bytes_per_pixel(pinfo, vnc_bytes_per_pixel);
        vnc_set_depth(pinfo, vnc_depth);

        if (ret) {
                return tvb_captured_length(tvb);  /* We're in a "startup" state; Cannot yet do "normal" processing */
        }

        if(DEST_PORT_VNC || per_conversation_info->server_port == pinfo->destport) {
                vnc_client_to_server(tvb, pinfo, &offset, vnc_tree);
        }
        else {
                vnc_server_to_client(tvb, pinfo, &offset, vnc_tree);
        }
        return tvb_captured_length(tvb);
}

/* Returns the new offset after processing the 4-byte vendor string */
static gint
process_vendor(proto_tree *tree, gint hfindex, tvbuff_t *tvb, gint offset)
{
        const guint8 *vendor;
        proto_item *ti;

        if (tree) {
                ti = proto_tree_add_item_ret_string(tree, hfindex, tvb, offset, 4, ENC_ASCII|ENC_NA, wmem_packet_scope(), &vendor);

                if(g_ascii_strcasecmp(vendor, "STDV") == 0)
                        proto_item_append_text(ti, " (Standard VNC vendor)");
                else if(g_ascii_strcasecmp(vendor, "TRDV") == 0)
                        proto_item_append_text(ti, " (Tridia VNC vendor)");
                else if(g_ascii_strcasecmp(vendor, "TGHT") == 0)
                        proto_item_append_text(ti, " (Tight VNC vendor)");
        }

        offset += 4;
        return offset;
}

/* Returns the new offset after processing the specified number of capabilities */
static gint
process_tight_capabilities(proto_tree *tree,
                           gint type_index, gint vendor_index, gint name_index,
                           tvbuff_t *tvb, gint offset, const gint num_capabilities)
{
        gint i;
        /* See vnc_unixsrc/include/rfbproto.h:rfbCapabilityInfo */

        for (i = 0; i < num_capabilities; i++) {

                proto_tree_add_item(tree, type_index, tvb, offset, 4, ENC_BIG_ENDIAN);
                offset += 4;

                offset = process_vendor(tree, vendor_index, tvb, offset);

                proto_tree_add_item(tree, name_index, tvb, offset, 8, ENC_ASCII|ENC_NA);
                offset += 8;
        }

        return offset;
}

/* Returns true if this looks like a client or server version packet: 12 bytes, in the format "RFB xxx.yyy\n" .
* Will check for the 12 bytes exact length, the 'RFB ' string and that it ends with a '\n'.
* The exact 'xxx.yyy' is checked later, by trying to convert it to a double using g_ascii_strtod.
* pinfo and tree are NULL when using this function to check the heuristics for dissection.  If we're
* checking the heuristics, we don't need to add expert_info, we just reject that packet as not
* being a VNC packet.
*/
static gboolean
vnc_is_client_or_server_version_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
        if(tvb_captured_length(tvb) != 12) {
                return FALSE;
        }

        if(tvb_strncaseeql(tvb, 0, "RFB ", 4) != 0) {
                return FALSE;
        }

        /* 0x2e = '.'   0xa = '\n' */
        if (tvb_get_guint8(tvb, 7) != 0x2e) {
                return FALSE;
        }

        if (tvb_get_guint8(tvb,11) != 0xa) {
                if (tvb_get_guint8(tvb,11) == 0) {
                        /* Per bug 5469,  It appears that any VNC clients using gtk-vnc before [1] was
                        * fixed will exhibit the described protocol violation that prevents wireshark
                        * from dissecting the session.
                        *
                        * [1] http://git.gnome.org/browse/gtk-vnc/commit/?id=bc9e2b19167686dd381a0508af1a5113675d08a2
                        */
                        if ((pinfo != NULL) && (tree != NULL)) {
                                proto_tree_add_expert(tree, pinfo, &ei_vnc_possible_gtk_vnc_bug, tvb, -1, 0);
                        }

                        return TRUE;
                }

                return FALSE;
        }

        return TRUE;
}

static gboolean test_vnc_protocol(tvbuff_t *tvb, packet_info *pinfo,
                                  proto_tree *tree, void *data _U_)
{
        conversation_t *conversation;

        if (vnc_is_client_or_server_version_message(tvb, NULL, NULL)) {
                conversation = conversation_new(pinfo->num, &pinfo->src,
                                                &pinfo->dst, pinfo->ptype,
                                                pinfo->srcport,
                                                pinfo->destport, 0);
                conversation_set_dissector(conversation, vnc_handle);
                dissect_vnc(tvb, pinfo, tree, data);
                return TRUE;
        }
        return FALSE;
}

/* Returns true if additional session startup messages follow */
static gboolean
vnc_startup_messages(tvbuff_t *tvb, packet_info *pinfo, gint offset,
                     proto_tree *tree, vnc_conversation_t
                     *per_conversation_info)
{
        guint8 num_security_types;
        guint32 desktop_name_len, auth_result, text_len, auth_code;
        vnc_packet_t *per_packet_info;
        gint num_tunnel_types;
        gint num_auth_types;
        proto_item* auth_item;

        per_packet_info = (vnc_packet_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_vnc, 0);

        if(!per_packet_info) {
                per_packet_info = wmem_new(wmem_file_scope(), vnc_packet_t);

                per_packet_info->state = per_conversation_info->vnc_next_state;
                per_packet_info->preferred_encoding = -1;

                p_add_proto_data(wmem_file_scope(), pinfo, proto_vnc, 0, per_packet_info);
        }

        /* Packet dissection follows */
        switch(per_packet_info->state) {

        case VNC_SESSION_STATE_SERVER_VERSION :
                if (!vnc_is_client_or_server_version_message(tvb, pinfo, tree))
                        return TRUE; /* we still hope to get a SERVER_VERSION message some day. Do not proceed yet */

                proto_tree_add_item(tree, hf_vnc_server_proto_ver, tvb, 4,
                                    7, ENC_ASCII|ENC_NA);
                per_conversation_info->server_proto_ver =
                        g_ascii_strtod((char *)tvb_get_string_enc(wmem_packet_scope(), tvb, 4, 7, ENC_ASCII), NULL);
                per_conversation_info->server_port = pinfo->srcport;

                col_add_fstr(pinfo->cinfo, COL_INFO,
                                     "Server protocol version: %s",
                                     tvb_format_text(tvb, 4, 7));

                per_conversation_info->vnc_next_state = VNC_SESSION_STATE_CLIENT_VERSION;
                break;

        case VNC_SESSION_STATE_CLIENT_VERSION :
                if (!vnc_is_client_or_server_version_message(tvb, pinfo, tree))
                        return TRUE; /* we still hope to get a CLIENT_VERSION message some day. Do not proceed yet */

                proto_tree_add_item(tree, hf_vnc_client_proto_ver, tvb,
                                    4, 7, ENC_ASCII|ENC_NA);
                per_conversation_info->client_proto_ver =
                        g_ascii_strtod((char *)tvb_get_string_enc(wmem_packet_scope(), tvb, 4, 7, ENC_ASCII), NULL);

                col_add_fstr(pinfo->cinfo, COL_INFO,
                                     "Client protocol version: %s",
                                     tvb_format_text(tvb, 4, 7));

                per_conversation_info->vnc_next_state = VNC_SESSION_STATE_SECURITY;
                break;

        case VNC_SESSION_STATE_SECURITY :
                col_set_str(pinfo->cinfo, COL_INFO, "Security types supported");

                /* We're checking against the client protocol version because
                 * the client is the final decider on which version to use
                 * after the server offers the highest version it supports. */

                if(per_conversation_info->client_proto_ver >= 3.007) {
                        num_security_types = tvb_get_guint8(tvb, offset);
                        if (tree) {
                                proto_tree_add_item(tree,
                                                    hf_vnc_num_security_types,
                                                    tvb, offset, 1, ENC_BIG_ENDIAN);

                                for(offset = 1; offset <= num_security_types; offset++){
                                        proto_tree_add_item(tree,
                                                            hf_vnc_security_type, tvb,
                                                            offset, 1, ENC_BIG_ENDIAN);
                                }
                        }
                        per_conversation_info->vnc_next_state = VNC_SESSION_STATE_SECURITY_TYPES;
                } else {
                        /* Version < 3.007: The server decides the
                         * authentication type for us to use */
                        proto_tree_add_item(tree, hf_vnc_server_security_type,
                                            tvb, offset, 4, ENC_BIG_ENDIAN);
                        /* The cast below is possible since in older versions of the protocol the only possible values are 0,1,2 */
                        per_conversation_info->security_type_selected = (guint8)tvb_get_ntohl(tvb, offset);
                        switch(per_conversation_info->security_type_selected) {

                        case VNC_SECURITY_TYPE_INVALID:
                                /* TODO: In this case (INVALID) the connection has failed */
                                /* and there should be an error string describing the error */
                                per_conversation_info->vnc_next_state = VNC_SESSION_STATE_SECURITY_TYPES;
                                break;

                        case VNC_SECURITY_TYPE_NONE:
                                per_conversation_info->vnc_next_state = VNC_SESSION_STATE_CLIENT_INIT;
                                break;

                        case VNC_SECURITY_TYPE_VNC:
                                per_conversation_info->vnc_next_state = VNC_SESSION_STATE_VNC_AUTHENTICATION_CHALLENGE;
                                break;

                        case VNC_SECURITY_TYPE_ARD:
                                per_conversation_info->vnc_next_state = VNC_SESSION_STATE_ARD_AUTHENTICATION_CHALLENGE;
                                break;

                        default:
                                /* Security type not supported by this dissector */
                                break;
                        }
                }

                break;

        case VNC_SESSION_STATE_SECURITY_TYPES :
                col_set_str(pinfo->cinfo, COL_INFO, "Authentication type selected by client");
                proto_tree_add_item(tree, hf_vnc_client_security_type, tvb, offset, 1, ENC_BIG_ENDIAN);
                per_conversation_info->security_type_selected =
                        tvb_get_guint8(tvb, offset);

                switch(per_conversation_info->security_type_selected) {

                case VNC_SECURITY_TYPE_NONE :
                        if(per_conversation_info->client_proto_ver >= 3.008)
                                per_conversation_info->vnc_next_state =
                                        VNC_SESSION_STATE_SECURITY_RESULT;
                        else
                                per_conversation_info->vnc_next_state =
                                        VNC_SESSION_STATE_CLIENT_INIT;

                        break;

                case VNC_SECURITY_TYPE_VNC :
                        per_conversation_info->vnc_next_state =
                                VNC_SESSION_STATE_VNC_AUTHENTICATION_CHALLENGE;
                        break;

                case VNC_SECURITY_TYPE_TIGHT :
                        per_conversation_info->vnc_next_state =
                                VNC_SESSION_STATE_TIGHT_TUNNELING_CAPABILITIES;
                        per_conversation_info->tight_enabled = TRUE;
                        break;

                case VNC_SECURITY_TYPE_ARD:
                        per_conversation_info->vnc_next_state = VNC_SESSION_STATE_ARD_AUTHENTICATION_CHALLENGE;
                        break;

                default :
                        /* Security type not supported by this dissector */
                        break;
                }

                break;

        case VNC_SESSION_STATE_TIGHT_TUNNELING_CAPABILITIES :
        {
                gint i;

                col_set_str(pinfo->cinfo, COL_INFO, "TightVNC tunneling capabilities supported");

                proto_tree_add_item(tree, hf_vnc_tight_num_tunnel_types, tvb, offset, 4, ENC_BIG_ENDIAN);
                num_tunnel_types = tvb_get_ntohl(tvb, offset);

                offset += 4;

                for(i = 0; i < num_tunnel_types; i++) {
                        /* TightVNC and Xvnc don't support any tunnel capabilities yet, but each capability
                         * is 16 bytes, so skip them.
                         */

                        proto_tree_add_item(tree, hf_vnc_tight_tunnel_type, tvb, offset, 16, ENC_BIG_ENDIAN);
                        offset += 16;
                }

                if (num_tunnel_types == 0)
                        per_conversation_info->vnc_next_state = VNC_SESSION_STATE_TIGHT_AUTH_CAPABILITIES;
                else
                        per_conversation_info->vnc_next_state = VNC_SESSION_STATE_TIGHT_TUNNEL_TYPE_REPLY;
                break;
        }
        case VNC_SESSION_STATE_TIGHT_TUNNEL_TYPE_REPLY:
                /* Neither TightVNC nor Xvnc implement this; they just have a placeholder that emits an error
                 * message and closes the connection (xserver/hw/vnc/auth.c:rfbProcessClientTunnelingType).
                 * We should actually never get here...
                 */
                break;

        case VNC_SESSION_STATE_TIGHT_AUTH_CAPABILITIES:
                col_set_str(pinfo->cinfo, COL_INFO, "TightVNC authentication capabilities supported");

                proto_tree_add_item(tree, hf_vnc_tight_num_auth_types, tvb, offset, 4, ENC_BIG_ENDIAN);
                num_auth_types = tvb_get_ntohl(tvb, offset);
                offset += 4;

                {
                        int i;
                        const guint8 *vendor, *signature;
                        for (i = 0; i < 1; i++) {
                                auth_code = tvb_get_ntohl(tvb, offset);
                                auth_item = proto_tree_add_item(tree, hf_vnc_tight_auth_code, tvb, offset, 4, ENC_BIG_ENDIAN);
                                offset += 4;
                                vendor = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, 4, ENC_ASCII);
                                process_vendor(tree, hf_vnc_tight_server_vendor, tvb, offset);
                                offset += 4;
                                proto_tree_add_item_ret_string(tree, hf_vnc_tight_signature, tvb, offset, 8, ENC_ASCII|ENC_NA, wmem_packet_scope(), &signature);
                                offset += 8;

                                switch(auth_code) {
                                        case VNC_SECURITY_TYPE_NONE:
                                                if ((g_ascii_strcasecmp(vendor, "STDV") != 0) || (g_ascii_strcasecmp(signature, "NOAUTH__") != 0)) {
                                                        expert_add_info(pinfo, auth_item, &ei_vnc_auth_code_mismatch);
                                                }
                                                break;
                                        case VNC_SECURITY_TYPE_VNC:
                                                if ((g_ascii_strcasecmp(vendor, "STDV") != 0) || (g_ascii_strcasecmp(signature, "VNCAUTH_") != 0)) {
                                                        expert_add_info(pinfo, auth_item, &ei_vnc_auth_code_mismatch);
                                                }
                                                break;
                                        case VNC_SECURITY_TYPE_VENCRYPT:
                                                if ((g_ascii_strcasecmp(vendor, "VENC") != 0) || (g_ascii_strcasecmp(signature, "VENCRYPT") != 0)) {
                                                        expert_add_info(pinfo, auth_item, &ei_vnc_auth_code_mismatch);
                                                }
                                                break;
                                        case VNC_SECURITY_TYPE_GTK_VNC_SASL:
                                                if ((g_ascii_strcasecmp(vendor, "GTKV") != 0) || (g_ascii_strcasecmp(signature, "SASL____") != 0)) {
                                                        expert_add_info(pinfo, auth_item, &ei_vnc_auth_code_mismatch);
                                                }
                                                break;
                                        case VNC_TIGHT_AUTH_TGHT_ULGNAUTH:
                                                if ((g_ascii_strcasecmp(vendor, "TGHT") != 0) || (g_ascii_strcasecmp(signature, "ULGNAUTH") != 0)) {
                                                        expert_add_info(pinfo, auth_item, &ei_vnc_auth_code_mismatch);
                                                }
                                                break;
                                        case VNC_TIGHT_AUTH_TGHT_XTRNAUTH:
                                                if ((g_ascii_strcasecmp(vendor, "TGHT") != 0) || (g_ascii_strcasecmp(signature, "XTRNAUTH") != 0)) {
                                                        expert_add_info(pinfo, auth_item, &ei_vnc_auth_code_mismatch);
                                                }
                                                break;
                                        default:
                                                expert_add_info(pinfo, auth_item, &ei_vnc_unknown_tight_vnc_auth);
                                                break;
                                }
                        }
                }

                if (num_auth_types == 0)
                        per_conversation_info->vnc_next_state = VNC_SESSION_STATE_CLIENT_INIT;
                else
                        per_conversation_info->vnc_next_state = VNC_SESSION_STATE_TIGHT_AUTH_TYPE_REPLY;
                break;

        case VNC_SESSION_STATE_TIGHT_AUTH_TYPE_REPLY:
                col_set_str(pinfo->cinfo, COL_INFO, "TightVNC authentication type selected by client");
                auth_code = tvb_get_ntohl(tvb, offset);
                auth_item = proto_tree_add_item(tree, hf_vnc_tight_auth_code, tvb, offset, 4, ENC_BIG_ENDIAN);

                switch(auth_code) {
                        case VNC_SECURITY_TYPE_NONE:
                                per_conversation_info->security_type_selected = VNC_SECURITY_TYPE_NONE;
                                per_conversation_info->vnc_next_state = VNC_SESSION_STATE_CLIENT_INIT;
                        break;
                        case VNC_SECURITY_TYPE_VNC:
                                per_conversation_info->security_type_selected = VNC_SECURITY_TYPE_VNC;
                                per_conversation_info->vnc_next_state = VNC_SESSION_STATE_VNC_AUTHENTICATION_CHALLENGE;
                        break;
                        case VNC_SECURITY_TYPE_GTK_VNC_SASL:
                                per_conversation_info->security_type_selected = VNC_SECURITY_TYPE_GTK_VNC_SASL;
                                /* TODO: dissection not implemented yet */
                                per_conversation_info->vnc_next_state = VNC_SESSION_STATE_TIGHT_UNKNOWN_PACKET3;
                                break;
                        case VNC_TIGHT_AUTH_TGHT_ULGNAUTH:
                                per_conversation_info->security_type_selected = VNC_TIGHT_AUTH_TGHT_ULGNAUTH;
                                /* TODO: dissection not implemented yet */
                                per_conversation_info->vnc_next_state = VNC_SESSION_STATE_TIGHT_UNKNOWN_PACKET3;
                                break;
                        case VNC_TIGHT_AUTH_TGHT_XTRNAUTH:
                                per_conversation_info->security_type_selected = VNC_TIGHT_AUTH_TGHT_XTRNAUTH;
                                /* TODO: dissection not implemented yet */
                                per_conversation_info->vnc_next_state = VNC_SESSION_STATE_TIGHT_UNKNOWN_PACKET3;
                                break;
                        default:
                                expert_add_info(pinfo, auth_item, &ei_vnc_unknown_tight_vnc_auth);
                                per_conversation_info->vnc_next_state = VNC_SESSION_STATE_TIGHT_UNKNOWN_PACKET3;
                                break;
                }

                break;

        case VNC_SESSION_STATE_TIGHT_UNKNOWN_PACKET3 :
                col_set_str(pinfo->cinfo, COL_INFO, "Unknown packet (TightVNC)");

                proto_tree_add_expert(tree, pinfo, &ei_vnc_unknown_tight, tvb, offset, -1);

                per_conversation_info->vnc_next_state =
                        VNC_SESSION_STATE_VNC_AUTHENTICATION_CHALLENGE;

                break;

        case VNC_SESSION_STATE_VNC_AUTHENTICATION_CHALLENGE :
                col_set_str(pinfo->cinfo, COL_INFO, "Authentication challenge from server");

                proto_tree_add_item(tree, hf_vnc_auth_challenge, tvb,
                                    offset, 16, ENC_NA);

                per_conversation_info->vnc_next_state =
                        VNC_SESSION_STATE_VNC_AUTHENTICATION_RESPONSE;
                break;

        case VNC_SESSION_STATE_VNC_AUTHENTICATION_RESPONSE :
                col_set_str(pinfo->cinfo, COL_INFO, "Authentication response from client");

                proto_tree_add_item(tree, hf_vnc_auth_response, tvb,
                                    offset, 16, ENC_NA);

                per_conversation_info->vnc_next_state = VNC_SESSION_STATE_SECURITY_RESULT;
                break;

        case VNC_SESSION_STATE_ARD_AUTHENTICATION_CHALLENGE :
                {
                        gint key_len;

                        col_set_str(pinfo->cinfo, COL_INFO, "ARD authentication challenge");

                        proto_tree_add_item(tree, hf_vnc_ard_auth_generator, tvb,
                                                offset, 2, ENC_BIG_ENDIAN);
                        proto_tree_add_item(tree, hf_vnc_ard_auth_key_len, tvb,
                                                offset + 2, 2, ENC_BIG_ENDIAN);

                        key_len = tvb_get_ntohs(tvb, offset + 2);

                        offset += 4;

                        proto_tree_add_item(tree, hf_vnc_ard_auth_modulus, tvb,
                                                offset, key_len, ENC_NA);
                        proto_tree_add_item(tree, hf_vnc_ard_auth_server_key, tvb,
                                                offset + key_len, key_len, ENC_NA);

                        per_conversation_info->ard_key_length = key_len;
                        per_conversation_info->vnc_next_state =
                                VNC_SESSION_STATE_ARD_AUTHENTICATION_RESPONSE;
                }
                break;

        case VNC_SESSION_STATE_ARD_AUTHENTICATION_RESPONSE :
                col_set_str(pinfo->cinfo, COL_INFO, "ARD authentication response");

                proto_tree_add_item(tree, hf_vnc_ard_auth_credentials, tvb,
                                        offset, 128, ENC_NA);
                proto_tree_add_item(tree, hf_vnc_ard_auth_client_key, tvb,
                                        offset + 128, per_conversation_info->ard_key_length, ENC_NA);

                per_conversation_info->vnc_next_state = VNC_SESSION_STATE_SECURITY_RESULT;
                break;

        case VNC_SESSION_STATE_SECURITY_RESULT :
                col_set_str(pinfo->cinfo, COL_INFO, "Authentication result");

                proto_tree_add_item(tree, hf_vnc_auth_result, tvb, offset,
                                    4, ENC_BIG_ENDIAN);
                auth_result = tvb_get_ntohl(tvb, offset);
                offset += 4;

                switch(auth_result) {

                case 0 : /* OK */
                        per_conversation_info->vnc_next_state = VNC_SESSION_STATE_CLIENT_INIT;
                        break;

                case 1 : /* Failed */
                        if(per_conversation_info->client_proto_ver >= 3.008) {
                                text_len = tvb_get_ntohl(tvb, offset);
                                proto_tree_add_item(tree, hf_vnc_auth_error_length, tvb, offset, 4, ENC_BIG_ENDIAN);
                                offset += 4;

                                proto_tree_add_item(tree, hf_vnc_auth_error, tvb,
                                                    offset, text_len, ENC_ASCII|ENC_NA);
                        }

                        return TRUE; /* All versions: Do not continue
                                        processing VNC packets as connection
                                        will be closed after this packet. */

                        break;
                }

                break;

        case VNC_SESSION_STATE_CLIENT_INIT :
                col_set_str(pinfo->cinfo, COL_INFO, "Share desktop flag");

                proto_tree_add_item(tree, hf_vnc_share_desktop_flag, tvb,
                                    offset, 1, ENC_BIG_ENDIAN);

                per_conversation_info->vnc_next_state = VNC_SESSION_STATE_SERVER_INIT;

                break;

        case VNC_SESSION_STATE_SERVER_INIT :
                col_set_str(pinfo->cinfo, COL_INFO, "Server framebuffer parameters");

                proto_tree_add_item(tree, hf_vnc_width, tvb, offset, 2,
                                    ENC_BIG_ENDIAN);
                offset += 2;

                proto_tree_add_item(tree, hf_vnc_height, tvb, offset, 2,
                                    ENC_BIG_ENDIAN);
                offset += 2;

                proto_tree_add_item(tree, hf_vnc_server_bits_per_pixel,
                                    tvb, offset, 1, ENC_BIG_ENDIAN);
                vnc_bytes_per_pixel = tvb_get_guint8(tvb, offset)/8;
                vnc_set_bytes_per_pixel(pinfo, vnc_bytes_per_pixel);
                offset += 1;

                proto_tree_add_item(tree, hf_vnc_server_depth, tvb, offset,
                                    1, ENC_BIG_ENDIAN);
                offset += 1;

                proto_tree_add_item(tree, hf_vnc_server_big_endian_flag,
                                    tvb, offset, 1, ENC_BIG_ENDIAN);
                offset += 1;

                proto_tree_add_item(tree, hf_vnc_server_true_color_flag,
                                    tvb, offset, 1, ENC_BIG_ENDIAN);
                offset += 1;

                proto_tree_add_item(tree, hf_vnc_server_red_max,
                                    tvb, offset, 2, ENC_BIG_ENDIAN);
                offset += 2;

                proto_tree_add_item(tree, hf_vnc_server_green_max,
                                    tvb, offset, 2, ENC_BIG_ENDIAN);
                offset += 2;

                proto_tree_add_item(tree, hf_vnc_server_blue_max,
                                    tvb, offset, 2, ENC_BIG_ENDIAN);
                offset += 2;

                proto_tree_add_item(tree, hf_vnc_server_red_shift,
                                    tvb, offset, 1, ENC_BIG_ENDIAN);
                offset += 1;

                proto_tree_add_item(tree, hf_vnc_server_green_shift,
                                    tvb, offset, 1, ENC_BIG_ENDIAN);
                offset += 1;

                proto_tree_add_item(tree, hf_vnc_server_blue_shift,
                                    tvb, offset, 1, ENC_BIG_ENDIAN);
                offset += 1;

                proto_tree_add_item(tree, hf_vnc_padding,
                                    tvb, offset, 3, ENC_NA);
                offset += 3; /* Skip over 3 bytes of padding */

                if(tvb_reported_length_remaining(tvb, offset) > 4) {
                        /* Sometimes the desktop name & length is skipped */
                        proto_tree_add_item(tree, hf_vnc_desktop_name_len,
                                            tvb, offset, 4, ENC_BIG_ENDIAN);
                        desktop_name_len = tvb_get_ntohl(tvb, offset);
                        offset += 4;

                        proto_tree_add_item(tree, hf_vnc_desktop_name,
                                            tvb, offset, desktop_name_len,
                                            ENC_ASCII|ENC_NA);
                }

                if(per_conversation_info->tight_enabled == TRUE)
                        per_conversation_info->vnc_next_state =
                                VNC_SESSION_STATE_TIGHT_INTERACTION_CAPS;
                else
                        per_conversation_info->vnc_next_state = VNC_SESSION_STATE_NORMAL_TRAFFIC;
                break;

        case VNC_SESSION_STATE_TIGHT_INTERACTION_CAPS :
                col_set_str(pinfo->cinfo, COL_INFO, "TightVNC Interaction Capabilities");

                proto_tree_add_item(tree, hf_vnc_num_server_message_types,
                                    tvb, offset, 2, ENC_BIG_ENDIAN);
                per_conversation_info->num_server_message_types = tvb_get_ntohs(tvb, offset);
                offset += 2;

                proto_tree_add_item(tree, hf_vnc_num_client_message_types,
                                    tvb, offset, 2, ENC_BIG_ENDIAN);
                per_conversation_info->num_client_message_types = tvb_get_ntohs(tvb, offset);
                offset += 2;

                proto_tree_add_item(tree, hf_vnc_num_encoding_types,
                                    tvb, offset, 2, ENC_BIG_ENDIAN);
                per_conversation_info->num_encoding_types = tvb_get_ntohs(tvb, offset);
                offset += 2;

                proto_tree_add_item(tree, hf_vnc_padding, tvb, offset, 2, ENC_NA);
                offset += 2;

                offset = process_tight_capabilities(tree,
                                                    hf_vnc_tight_server_message_type,
                                                    hf_vnc_tight_server_vendor,
                                                    hf_vnc_tight_server_name,
                                                    tvb, offset, per_conversation_info->num_server_message_types);
                offset = process_tight_capabilities(tree,
                                                    hf_vnc_tight_client_message_type,
                                                    hf_vnc_tight_client_vendor,
                                                    hf_vnc_tight_client_name,
                                                    tvb, offset, per_conversation_info->num_client_message_types);
                process_tight_capabilities(tree,
                                                    hf_vnc_tight_encoding_type,
                                                    hf_vnc_tight_encoding_vendor,
                                                    hf_vnc_tight_encoding_name,
                                                    tvb, offset, per_conversation_info->num_encoding_types);

                per_conversation_info->vnc_next_state = VNC_SESSION_STATE_NORMAL_TRAFFIC;
                break;

        case VNC_SESSION_STATE_NORMAL_TRAFFIC :
                return FALSE;
        }

        return TRUE;
}


static void
vnc_client_to_server(tvbuff_t *tvb, packet_info *pinfo, gint *offset,
                     proto_tree *tree)
{
        guint8 message_type;

        proto_item *ti;
        proto_tree *vnc_client_message_type_tree;

        message_type = tvb_get_guint8(tvb, *offset);

        ti = proto_tree_add_item(tree, hf_vnc_client_message_type, tvb,
                                 *offset, 1, ENC_BIG_ENDIAN);

        vnc_client_message_type_tree =
                proto_item_add_subtree(ti, ett_vnc_client_message_type);

        *offset += 1;

        switch(message_type) {

        case VNC_CLIENT_MESSAGE_TYPE_SET_PIXEL_FORMAT :
                vnc_client_set_pixel_format(tvb, pinfo, offset,
                                            vnc_client_message_type_tree);
                break;

        case VNC_CLIENT_MESSAGE_TYPE_SET_ENCODINGS :
                vnc_client_set_encodings(tvb, pinfo, offset,
                                         vnc_client_message_type_tree);
                break;

        case VNC_CLIENT_MESSAGE_TYPE_FRAMEBUF_UPDATE_REQ :
                vnc_client_framebuffer_update_request(tvb, pinfo, offset,
                                                      vnc_client_message_type_tree);
                break;

        case VNC_CLIENT_MESSAGE_TYPE_KEY_EVENT :
                vnc_client_key_event(tvb, pinfo, offset,
                                     vnc_client_message_type_tree);
                break;

        case VNC_CLIENT_MESSAGE_TYPE_POINTER_EVENT:
                vnc_client_pointer_event(tvb, pinfo, offset,
                                         vnc_client_message_type_tree);
                break;

        case VNC_CLIENT_MESSAGE_TYPE_CLIENT_CUT_TEXT :
                vnc_client_cut_text(tvb, pinfo, offset,
                                    vnc_client_message_type_tree);
                break;

        case VNC_CLIENT_MESSAGE_TYPE_MIRRORLINK :
                vnc_mirrorlink(tvb, pinfo, offset,
                               vnc_client_message_type_tree);
                break;

        case VNC_CLIENT_MESSAGE_TYPE_ENABLE_CONTINUOUS_UPDATES :
                col_append_sep_str(pinfo->cinfo, COL_INFO, "; ", "Client Enable Continuous Updates");
                *offset += 9;
                break;

        case VNC_CLIENT_MESSAGE_TYPE_FENCE :
                vnc_fence(tvb, pinfo, offset,
                          vnc_client_message_type_tree);
                break;

        default :
                col_append_sep_fstr(pinfo->cinfo, COL_INFO, "; ",
                                "Unknown client message type (%u)",
                                message_type);
                break;
        }
}

static void
vnc_server_to_client(tvbuff_t *tvb, packet_info *pinfo, gint *offset,
                     proto_tree *tree)
{
        gint start_offset;
        guint8 message_type;
        gint bytes_needed = 0;

        proto_item *ti;
        proto_tree *vnc_server_message_type_tree;

again:
        start_offset = *offset;

        message_type = tvb_get_guint8(tvb, *offset);

        ti = proto_tree_add_item(tree, hf_vnc_server_message_type, tvb,
                                 *offset, 1, ENC_BIG_ENDIAN);
        vnc_server_message_type_tree =
                proto_item_add_subtree(ti, ett_vnc_server_message_type);

        *offset += 1;

        switch(message_type) {

        case VNC_SERVER_MESSAGE_TYPE_FRAMEBUFFER_UPDATE :
                bytes_needed =
                        vnc_server_framebuffer_update(tvb, pinfo, offset,
                                                      vnc_server_message_type_tree);
                break;

        case VNC_SERVER_MESSAGE_TYPE_SET_COLORMAP_ENTRIES :
                bytes_needed = vnc_server_set_colormap_entries(tvb, pinfo, offset, vnc_server_message_type_tree);
                break;

        case VNC_SERVER_MESSAGE_TYPE_RING_BELL :
                vnc_server_ring_bell(tvb, pinfo, offset,
                                     vnc_server_message_type_tree);
                break;

        case VNC_SERVER_MESSAGE_TYPE_CUT_TEXT :
                bytes_needed = vnc_server_cut_text(tvb, pinfo, offset,
                                                   vnc_server_message_type_tree);
                break;

        case VNC_SERVER_MESSAGE_TYPE_MIRRORLINK :
                bytes_needed = vnc_mirrorlink(tvb, pinfo, offset,
                                              vnc_server_message_type_tree);
                break;

        case VNC_SERVER_MESSAGE_TYPE_END_CONTINUOUS_UPDATES :
                col_append_sep_str(pinfo->cinfo, COL_INFO, "; ", "Server End Continuous Updates");
                *offset += 1;
                break;

        case VNC_SERVER_MESSAGE_TYPE_FENCE :
                bytes_needed = vnc_fence(tvb, pinfo, offset,
                          vnc_server_message_type_tree);
                break;

        default :
                col_append_sep_str(pinfo->cinfo, COL_INFO, "; ",
                                       "Unknown server message type");
                *offset = tvb_reported_length(tvb);  /* Swallow the rest of the segment */
                break;
        }

        if(bytes_needed > 0 && vnc_preference_desegment && pinfo->can_desegment) {
                proto_tree_add_expert(vnc_server_message_type_tree, pinfo, &ei_vnc_reassemble, tvb, start_offset, -1);
                pinfo->desegment_offset = start_offset;
                pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
                return;
        }

        if ((unsigned)*offset < tvb_reported_length(tvb)) {
                goto again;
        }
}


static void
vnc_client_set_pixel_format(tvbuff_t *tvb, packet_info *pinfo, gint *offset,
                            proto_tree *tree)
{
        col_set_str(pinfo->cinfo, COL_INFO, "Client set pixel format");

        proto_tree_add_item(tree, hf_vnc_padding, tvb, *offset, 3, ENC_NA);
        *offset += 3; /* Skip over 3 bytes of padding */

        proto_tree_add_item(tree, hf_vnc_client_bits_per_pixel, tvb, *offset,
                            1, ENC_BIG_ENDIAN);
        vnc_bytes_per_pixel = tvb_get_guint8(tvb, *offset)/8;
        vnc_set_bytes_per_pixel(pinfo, vnc_bytes_per_pixel);
        *offset += 1;

        proto_tree_add_item(tree, hf_vnc_client_depth, tvb, *offset,
                            1, ENC_BIG_ENDIAN);
        vnc_depth = tvb_get_guint8(tvb, *offset);
        vnc_set_depth(pinfo, vnc_depth);
        *offset += 1;

        proto_tree_add_item(tree, hf_vnc_client_big_endian_flag, tvb, *offset,
                            1, ENC_BIG_ENDIAN);
        *offset += 1;

        proto_tree_add_item(tree, hf_vnc_client_true_color_flag, tvb, *offset,
                            1, ENC_BIG_ENDIAN);
        *offset += 1;

        proto_tree_add_item(tree, hf_vnc_client_red_max, tvb, *offset,
                            2, ENC_BIG_ENDIAN);
        *offset += 2;

        proto_tree_add_item(tree, hf_vnc_client_green_max, tvb, *offset,
                            2, ENC_BIG_ENDIAN);
        *offset += 2;

        proto_tree_add_item(tree, hf_vnc_client_blue_max, tvb, *offset,
                            2, ENC_BIG_ENDIAN);
        *offset += 2;

        proto_tree_add_item(tree, hf_vnc_client_red_shift, tvb, *offset,
                            1, ENC_BIG_ENDIAN);
        *offset += 1;

        proto_tree_add_item(tree, hf_vnc_client_green_shift, tvb, *offset,
                            1, ENC_BIG_ENDIAN);
        *offset += 1;

        proto_tree_add_item(tree, hf_vnc_client_blue_shift, tvb, *offset,
                            1, ENC_BIG_ENDIAN);
        *offset += 1;

        proto_tree_add_item(tree, hf_vnc_padding, tvb, *offset, 3, ENC_NA);
        *offset += 3; /* Skip over 3 bytes of padding */
}


static void
vnc_client_set_encodings(tvbuff_t *tvb, packet_info *pinfo, gint *offset,
                         proto_tree *tree)
{
        guint16       number_of_encodings;
        guint         counter;
        vnc_packet_t *per_packet_info;

        per_packet_info = (vnc_packet_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_vnc, 0);
        /* Our calling function should have set the packet's proto data already */
        DISSECTOR_ASSERT(per_packet_info != NULL);

        col_set_str(pinfo->cinfo, COL_INFO, "Client set encodings");

        proto_tree_add_item(tree, hf_vnc_padding, tvb, *offset, 1, ENC_NA);
        *offset += 1; /* Skip over 1 byte of padding */

        number_of_encodings = tvb_get_ntohs(tvb, *offset);
        proto_tree_add_item(tree, hf_vnc_encoding_num, tvb, *offset, 2, ENC_BIG_ENDIAN);
        *offset += 2;

        per_packet_info->preferred_encoding = -1;

        for(counter = 0; counter < number_of_encodings; counter++) {
                proto_tree_add_item(tree,
                                    hf_vnc_client_set_encodings_encoding_type,
                                    tvb, *offset, 4, ENC_BIG_ENDIAN);

                /* Remember the first real encoding as the preferred encoding,
                 * per xserver/hw/vnc/rfbserver.c:rfbProcessClientNormalMessage().
                 * Otherwise, use RAW as the preferred encoding.
                 */
                if (per_packet_info->preferred_encoding == -1) {
                        int encoding;

                        encoding = tvb_get_ntohl(tvb, *offset);

                        switch(encoding) {
                        case VNC_ENCODING_TYPE_RAW:
                        case VNC_ENCODING_TYPE_RRE:
                        case VNC_ENCODING_TYPE_CORRE:
                        case VNC_ENCODING_TYPE_HEXTILE:
                        case VNC_ENCODING_TYPE_ZLIB:
                        case VNC_ENCODING_TYPE_TIGHT:
                                per_packet_info->preferred_encoding = encoding;
                                break;
                        }
                }

                *offset += 4;
        }

        if (per_packet_info->preferred_encoding == -1)
                per_packet_info->preferred_encoding = VNC_ENCODING_TYPE_RAW;
}


static void
vnc_client_framebuffer_update_request(tvbuff_t *tvb, packet_info *pinfo,
                                      gint *offset, proto_tree *tree)
{
        col_set_str(pinfo->cinfo, COL_INFO, "Client framebuffer update request");

        proto_tree_add_item(tree, hf_vnc_update_req_incremental,
                            tvb, *offset, 1, ENC_BIG_ENDIAN);
        *offset += 1;

        proto_tree_add_item(tree, hf_vnc_update_req_x_pos,
                            tvb, *offset, 2, ENC_BIG_ENDIAN);
        *offset += 2;

        proto_tree_add_item(tree, hf_vnc_update_req_y_pos,
                            tvb, *offset, 2, ENC_BIG_ENDIAN);
        *offset += 2;

        proto_tree_add_item(tree, hf_vnc_update_req_width, tvb,
                            *offset, 2, ENC_BIG_ENDIAN);
        *offset += 2;

        proto_tree_add_item(tree, hf_vnc_update_req_height, tvb,
                            *offset, 2, ENC_BIG_ENDIAN);
        *offset += 2;
}


static void
vnc_client_key_event(tvbuff_t *tvb, packet_info *pinfo, gint *offset,
                     proto_tree *tree)
{
        col_set_str(pinfo->cinfo, COL_INFO, "Client key event");

        proto_tree_add_item(tree, hf_vnc_key_down, tvb, *offset, 1, ENC_BIG_ENDIAN);
        *offset += 1;

        proto_tree_add_item(tree, hf_vnc_padding, tvb, *offset, 2, ENC_NA);
        *offset += 2; /* Skip over 2 bytes of padding */

        proto_tree_add_item(tree, hf_vnc_key, tvb, *offset, 4, ENC_BIG_ENDIAN);
        *offset += 4;
}


static void
vnc_client_pointer_event(tvbuff_t *tvb, packet_info *pinfo, gint *offset,
                         proto_tree *tree)
{
        col_set_str(pinfo->cinfo, COL_INFO, "Client pointer event");

        proto_tree_add_item(tree, hf_vnc_button_1_pos, tvb, *offset, 1, ENC_BIG_ENDIAN);
        proto_tree_add_item(tree, hf_vnc_button_2_pos, tvb, *offset, 1, ENC_BIG_ENDIAN);
        proto_tree_add_item(tree, hf_vnc_button_3_pos, tvb, *offset, 1, ENC_BIG_ENDIAN);
        proto_tree_add_item(tree, hf_vnc_button_4_pos, tvb, *offset, 1, ENC_BIG_ENDIAN);
        proto_tree_add_item(tree, hf_vnc_button_5_pos, tvb, *offset, 1, ENC_BIG_ENDIAN);
        proto_tree_add_item(tree, hf_vnc_button_6_pos, tvb, *offset, 1, ENC_BIG_ENDIAN);
        proto_tree_add_item(tree, hf_vnc_button_7_pos, tvb, *offset, 1, ENC_BIG_ENDIAN);
        proto_tree_add_item(tree, hf_vnc_button_8_pos, tvb, *offset, 1, ENC_BIG_ENDIAN);
        *offset += 1;

        proto_tree_add_item(tree, hf_vnc_pointer_x_pos, tvb, *offset, 2, ENC_BIG_ENDIAN);
        *offset += 2;

        proto_tree_add_item(tree, hf_vnc_pointer_y_pos, tvb, *offset, 2, ENC_BIG_ENDIAN);
        *offset += 2;
}


static void
vnc_client_cut_text(tvbuff_t *tvb, packet_info *pinfo, gint *offset,
                    proto_tree *tree)
{
        guint32 text_len;

        col_set_str(pinfo->cinfo, COL_INFO, "Client cut text");

        proto_tree_add_item(tree, hf_vnc_padding, tvb, *offset, 3, ENC_NA);
        *offset += 3; /* Skip over 3 bytes of padding */

        text_len = tvb_get_ntohl(tvb, *offset);
        proto_tree_add_item(tree, hf_vnc_client_cut_text_len, tvb, *offset, 4,
                            ENC_BIG_ENDIAN);
        *offset += 4;

        proto_tree_add_item(tree, hf_vnc_client_cut_text, tvb, *offset,
                            text_len, ENC_ASCII|ENC_NA);
        *offset += text_len;

}


static guint
vnc_server_framebuffer_update(tvbuff_t *tvb, packet_info *pinfo, gint *offset,
                              proto_tree *tree)
{
        guint       ii;
        guint       num_rects;
        guint16     width, height;
        guint       bytes_needed = 0;
        guint32     encoding_type;
        proto_item *ti, *ti_x, *ti_y, *ti_width, *ti_height;
        proto_tree *vnc_rect_tree, *vnc_encoding_type_tree;

        col_append_sep_str(pinfo->cinfo, COL_INFO, "; ", "Server framebuffer update");

        proto_tree_add_item(tree, hf_vnc_padding, tvb, *offset, 1, ENC_NA);
        *offset += 1;

        num_rects = tvb_get_ntohs(tvb, *offset);
        ti = proto_tree_add_item(tree, hf_vnc_rectangle_num, tvb, *offset, 2, ENC_BIG_ENDIAN);

        /* In some cases, TIGHT encoding ignores the "number of rectangles" field;        */
        /* VNC_ENCODING_TYPE_LAST_RECT is used to indicate the end of the rectangle list. */
        /* (It appears that TIGHT encoding uses 0xFFFF for the num_rects field when the   */
        /*  field is not being used). For now: we'll assume that a value 0f 0xFFFF means  */
        /*  that the field is not being used.                                             */
        if (num_rects == 0xFFFF) {
                proto_item_append_text(ti, " [TIGHT encoding assumed (field is not used)]");
        }
        if ((num_rects != 0xFFFF) && (num_rects > 5000)) {
                expert_add_info_format(pinfo, ti, &ei_vnc_too_many_rectangles,
                                "Too many rectangles (%d), aborting dissection", num_rects);
                return(0);
        }

        *offset += 2;

        for(ii = 0; ii < num_rects; ii++) {
                if (ii > 5000) {
                        expert_add_info_format(pinfo, ti, &ei_vnc_too_many_rectangles,
                                               "Too many rectangles (%d), aborting dissection", ii);
                        return(0);
                }
                VNC_BYTES_NEEDED(12);

                vnc_rect_tree = proto_tree_add_subtree_format(tree, tvb, *offset, 12,
                                         ett_vnc_rect, NULL, "Rectangle #%d", ii+1);


                ti_x = proto_tree_add_item(vnc_rect_tree, hf_vnc_fb_update_x_pos,
                                           tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;

                ti_y = proto_tree_add_item(vnc_rect_tree, hf_vnc_fb_update_y_pos,
                                           tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;

                ti_width = proto_tree_add_item(vnc_rect_tree, hf_vnc_fb_update_width,
                                               tvb, *offset, 2, ENC_BIG_ENDIAN);
                width = tvb_get_ntohs(tvb, *offset);
                *offset += 2;

                ti_height = proto_tree_add_item(vnc_rect_tree, hf_vnc_fb_update_height,
                                                tvb, *offset, 2, ENC_BIG_ENDIAN);
                height = tvb_get_ntohs(tvb, *offset);
                *offset += 2;

                ti = proto_tree_add_item(vnc_rect_tree,
                                         hf_vnc_fb_update_encoding_type,
                                         tvb, *offset, 4, ENC_BIG_ENDIAN);

                encoding_type = tvb_get_ntohl(tvb, *offset);
                *offset += 4;

                if (encoding_type == VNC_ENCODING_TYPE_LAST_RECT)
                        break; /* exit the loop */

                vnc_encoding_type_tree =
                        proto_item_add_subtree(ti, ett_vnc_encoding_type);

                switch(encoding_type) {

                case VNC_ENCODING_TYPE_RAW:
                        bytes_needed = vnc_raw_encoding(tvb, pinfo, offset,
                                                        vnc_encoding_type_tree,
                                                        width, height);
                        break;

                case VNC_ENCODING_TYPE_COPY_RECT:
                        bytes_needed =
                                vnc_copyrect_encoding(tvb, pinfo, offset,
                                                      vnc_encoding_type_tree,
                                                      width, height);
                        break;

                case VNC_ENCODING_TYPE_RRE:
                        bytes_needed =
                                vnc_rre_encoding(tvb, pinfo, offset,
                                                 vnc_encoding_type_tree,
                                                 width, height);
                        break;

                case VNC_ENCODING_TYPE_HEXTILE:
                        bytes_needed =
                                vnc_hextile_encoding(tvb, pinfo, offset,
                                                     vnc_encoding_type_tree,
                                                     width, height);
                        break;

                case VNC_ENCODING_TYPE_RLE:
                        bytes_needed =
                                vnc_zrle_encoding(tvb, pinfo, offset,
                                                  vnc_encoding_type_tree,
                                                  width, height);
                        break;

                case VNC_ENCODING_TYPE_TIGHT:
                        bytes_needed =
                                vnc_tight_encoding(tvb, pinfo, offset,
                                                   vnc_encoding_type_tree,
                                                   width, height);
                        break;

                case VNC_ENCODING_TYPE_RICH_CURSOR:
                case VNC_ENCODING_TYPE_X_CURSOR:
                        proto_item_append_text (ti_x,      " (hotspot X)");
                        proto_item_append_text (ti_y,      " (hotspot Y)");
                        proto_item_append_text (ti_width,  " (cursor width)");
                        proto_item_append_text (ti_height, " (cursor height)");

                        if (encoding_type == VNC_ENCODING_TYPE_RICH_CURSOR)
                                bytes_needed = vnc_rich_cursor_encoding(tvb, pinfo, offset, vnc_encoding_type_tree, width, height);
                        else
                                bytes_needed = vnc_x_cursor_encoding(tvb, pinfo, offset, vnc_encoding_type_tree, width, height);

                        break;

                case VNC_ENCODING_TYPE_POINTER_POS:
                        proto_item_append_text (ti_x,      " (pointer X)");
                        proto_item_append_text (ti_y,      " (pointer Y)");
                        proto_item_append_text (ti_width,  " (unused)");
                        proto_item_append_text (ti_height, " (unused)");
                        bytes_needed = 0;
                        break;

                case VNC_ENCODING_TYPE_DESKTOP_SIZE:

                        /* There is no payload for this message type */

                        bytes_needed = 0;
                        break;

                case VNC_ENCODING_TYPE_EXTENDED_DESK_SIZE :
                        bytes_needed = vnc_extended_desktop_size(tvb, offset, vnc_encoding_type_tree);
                        break;

                case VNC_ENCODING_TYPE_KEYBOARD_LED_STATE :

                        /* There is no payload for this message type */

                        bytes_needed = 0;
                        break;

                case VNC_ENCODING_TYPE_SUPPORTED_MESSAGES :
                        bytes_needed = vnc_supported_messages(tvb, offset,
                                                              vnc_encoding_type_tree,
                                                              width);
                        break;

                case VNC_ENCODING_TYPE_SUPPORTED_ENCODINGS :
                        bytes_needed = vnc_supported_encodings(tvb, offset,
                                                               vnc_encoding_type_tree,
                                                               width, height);
                        break;

                case VNC_ENCODING_TYPE_SERVER_IDENTITY :
                        bytes_needed = vnc_server_identity(tvb, offset,
                                                           vnc_encoding_type_tree,
                                                           width);
                        break;

                case VNC_ENCODING_TYPE_CONTEXT_INFORMATION :
                        bytes_needed = vnc_context_information(tvb, offset,
                                                               vnc_encoding_type_tree);
                        break;

                case VNC_ENCODING_TYPE_SLRLE :
                        bytes_needed = vnc_slrle_encoding(tvb, pinfo, offset,
                                                          vnc_encoding_type_tree,
                                                          height);
                        break;

                case VNC_ENCODING_TYPE_H264 :
                        bytes_needed = vnc_h264_encoding(tvb, offset,
                                                         vnc_encoding_type_tree);
                        break;

                }

                /* Check if the routines above requested more bytes to
                 * be desegmented. */
                if(bytes_needed > 0)
                        return bytes_needed;
        }

        return 0;
}

static guint32
vnc_extended_desktop_size(tvbuff_t *tvb, gint *offset, proto_tree *tree)
{

        guint8      i, num_of_screens;
        proto_tree *screen_tree;

        num_of_screens = tvb_get_guint8(tvb, *offset);
        proto_tree_add_item(tree, hf_vnc_desktop_screen_num, tvb, *offset, 1, ENC_BIG_ENDIAN);
        *offset += 1;
        proto_tree_add_item(tree, hf_vnc_padding, tvb, *offset, 3, ENC_NA);

        VNC_BYTES_NEEDED((guint32)(3 + (num_of_screens * 16)));
        *offset += 3;
        for(i = 0; i < num_of_screens; i++) {
                screen_tree = proto_tree_add_subtree_format(tree, tvb, *offset, 16, ett_vnc_desktop_screen, NULL, "Screen #%u", i+1);

                proto_tree_add_item(screen_tree, hf_vnc_desktop_screen_id, tvb, *offset, 4, ENC_BIG_ENDIAN);
                *offset += 4;
                proto_tree_add_item(screen_tree, hf_vnc_desktop_screen_x, tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
                proto_tree_add_item(screen_tree, hf_vnc_desktop_screen_y, tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
                proto_tree_add_item(screen_tree, hf_vnc_desktop_screen_width, tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
                proto_tree_add_item(screen_tree, hf_vnc_desktop_screen_height, tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
                proto_tree_add_item(screen_tree, hf_vnc_desktop_screen_flags, tvb, *offset, 4, ENC_BIG_ENDIAN);
                *offset += 4;
        }

        return 0;
}

static guint
vnc_raw_encoding(tvbuff_t *tvb, packet_info *pinfo, gint *offset,
                 proto_tree *tree, const guint16 width, const guint16 height)
{
        guint8 bytes_per_pixel = vnc_get_bytes_per_pixel(pinfo);
        guint length;

        length = width * height * bytes_per_pixel;
        VNC_BYTES_NEEDED(length);

        proto_tree_add_item(tree, hf_vnc_raw_pixel_data, tvb, *offset,
                            length, ENC_NA);
        *offset += length;

        return 0; /* bytes_needed */
}


static guint
vnc_copyrect_encoding(tvbuff_t *tvb, packet_info *pinfo _U_, gint *offset,
                      proto_tree *tree, const guint16 width _U_, const guint16 height _U_)
{
        proto_tree_add_item(tree, hf_vnc_copyrect_src_x_pos, tvb, *offset,
                            2, ENC_BIG_ENDIAN);
        *offset += 2;

        proto_tree_add_item(tree, hf_vnc_copyrect_src_y_pos, tvb, *offset,
                            2, ENC_BIG_ENDIAN);
        *offset += 2;

        return 0; /* bytes_needed */
}


static guint
vnc_rre_encoding(tvbuff_t *tvb, packet_info *pinfo, gint *offset,
                 proto_tree *tree, const guint16 width _U_, const guint16 height _U_)
{
        guint8      bytes_per_pixel = vnc_get_bytes_per_pixel(pinfo);
        guint32     num_subrects, i;
        guint       bytes_needed;
        proto_item *ti;
        proto_tree *subrect_tree;

        VNC_BYTES_NEEDED(4);
        ti = proto_tree_add_item(tree, hf_vnc_rre_num_subrects, tvb, *offset,
                                 4, ENC_BIG_ENDIAN);
        num_subrects = tvb_get_ntohl(tvb, *offset);
        *offset += 4;

        if (num_subrects > 10000) {
                expert_add_info_format(pinfo, ti, &ei_vnc_too_many_sub_rectangles,
                                "Too many sub-rectangles (%d), aborting dissection", num_subrects);
                return(0);
        }

        *offset += 2;
        VNC_BYTES_NEEDED(bytes_per_pixel);
        proto_tree_add_item(tree, hf_vnc_rre_bg_pixel, tvb, *offset,
                            bytes_per_pixel, ENC_NA);
        *offset += bytes_per_pixel;

        /*  We know we need (at least) all these bytes, so ask for them now
         *  (instead of a few at a time...).
         */
        bytes_needed = bytes_per_pixel + 8;
        VNC_BYTES_NEEDED(bytes_needed * num_subrects);
        for(i = 0; i < num_subrects; i++) {

                subrect_tree = proto_tree_add_subtree_format(tree, tvb, *offset, bytes_per_pixel +
                                         8, ett_vnc_rre_subrect, NULL, "Subrectangle #%d", i+1);

                proto_tree_add_item(subrect_tree, hf_vnc_rre_subrect_pixel,
                                    tvb, *offset, bytes_per_pixel, ENC_NA);
                *offset += bytes_per_pixel;

                proto_tree_add_item(subrect_tree, hf_vnc_rre_subrect_x_pos,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;

                proto_tree_add_item(subrect_tree, hf_vnc_rre_subrect_y_pos,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;

                proto_tree_add_item(subrect_tree, hf_vnc_rre_subrect_width,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;

                proto_tree_add_item(subrect_tree, hf_vnc_rre_subrect_height,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
        }

        return 0; /* bytes_needed */
}


static guint
vnc_hextile_encoding(tvbuff_t *tvb, packet_info *pinfo, gint *offset,
                     proto_tree *tree, const guint16 width, const guint16 height)
{
        guint8      bytes_per_pixel = vnc_get_bytes_per_pixel(pinfo);
        guint8      i, subencoding_mask, num_subrects, subrect_len, tile_height, tile_width;
        guint32     raw_length;
        proto_tree *tile_tree, *subencoding_mask_tree, *subrect_tree, *num_subrects_tree;
        proto_item *ti;
        guint16     current_height  = 0, current_width;

        while(current_height != height) {
                if (current_height + 16 > height)
                        tile_height = height - current_height;
                else
                        tile_height = 16;
                current_height += tile_height;
                current_width = 0;
                while(current_width != width) {
                        if (current_width + 16 > width)
                                tile_width = width - current_width;
                        else
                                tile_width = 16;

                        current_width += tile_width;

                        VNC_BYTES_NEEDED(1);
                        subencoding_mask = tvb_get_guint8(tvb, *offset);

                        tile_tree = proto_tree_add_subtree_format(tree, tvb, *offset, 1, ett_vnc_hextile_tile, NULL,
                                                        "Tile {%d:%d}, sub encoding mask %u", current_width, current_height, subencoding_mask);

                        ti = proto_tree_add_item(tile_tree, hf_vnc_hextile_subencoding_mask, tvb,
                                                 *offset, 1, ENC_BIG_ENDIAN);

                        subencoding_mask_tree =
                                proto_item_add_subtree(ti, ett_vnc_hextile_subencoding_mask);

                        proto_tree_add_item(subencoding_mask_tree,
                                            hf_vnc_hextile_raw, tvb, *offset, 1,
                                            ENC_BIG_ENDIAN);
                        proto_tree_add_item(subencoding_mask_tree,
                                            hf_vnc_hextile_bg, tvb, *offset, 1,
                                            ENC_BIG_ENDIAN);
                        proto_tree_add_item(subencoding_mask_tree,
                                            hf_vnc_hextile_fg, tvb, *offset, 1,
                                            ENC_BIG_ENDIAN);
                        proto_tree_add_item(subencoding_mask_tree,
                                            hf_vnc_hextile_anysubrects, tvb, *offset, 1,
                                            ENC_BIG_ENDIAN);
                        proto_tree_add_item(subencoding_mask_tree,
                                            hf_vnc_hextile_subrectscolored, tvb, *offset, 1,
                                            ENC_BIG_ENDIAN);
                        *offset += 1;

                        if(subencoding_mask & 0x1) { /* Raw */
                                raw_length = tile_width * tile_height * bytes_per_pixel;

                                proto_tree_add_item(tile_tree, hf_vnc_hextile_raw_value, tvb,
                                                    *offset, raw_length, ENC_NA);
                                VNC_BYTES_NEEDED(raw_length);
                                *offset += raw_length;
                        } else {
                                if(subencoding_mask & 0x2) { /* Background Specified */
                                        VNC_BYTES_NEEDED(bytes_per_pixel);
                                        proto_tree_add_item(tile_tree, hf_vnc_hextile_bg_value,
                                                            tvb, *offset, bytes_per_pixel,
                                                            ENC_NA);
                                        *offset += bytes_per_pixel;
                                }

                                if(subencoding_mask & 0x4) { /* Foreground Specified */
                                        VNC_BYTES_NEEDED(bytes_per_pixel);
                                        proto_tree_add_item(tile_tree, hf_vnc_hextile_fg_value,
                                                            tvb, *offset, bytes_per_pixel,
                                                            ENC_NA);
                                        *offset += bytes_per_pixel;
                                }

                                if(subencoding_mask & 0x8) { /* Any Subrects */
                                        VNC_BYTES_NEEDED(3); /* 1 byte for number of subrects field, +2 at least for 1 subrect */
                                        ti = proto_tree_add_item(tile_tree,
                                                                 hf_vnc_hextile_num_subrects,
                                                                 tvb, *offset, 1,
                                                                 ENC_BIG_ENDIAN);
                                        num_subrects = tvb_get_guint8(tvb, *offset);
                                        *offset += 1;

                                        if(subencoding_mask & 0x10)
                                                subrect_len = bytes_per_pixel + 2;
                                        else
                                                subrect_len = 2;
                                        VNC_BYTES_NEEDED((guint)(subrect_len * num_subrects));

                                        num_subrects_tree =
                                                proto_item_add_subtree(ti, ett_vnc_hextile_num_subrects);

                                        for(i = 0; i < num_subrects; i++) {
                                                subrect_tree = proto_tree_add_subtree_format(num_subrects_tree, tvb,
                                                                         *offset, subrect_len, ett_vnc_hextile_subrect, NULL,
                                                                         "Subrectangle #%d", i+1);

                                                if(subencoding_mask & 0x10) {
                                                        /* Subrects Colored */
                                                        proto_tree_add_item(subrect_tree, hf_vnc_hextile_subrect_pixel_value, tvb, *offset, bytes_per_pixel, ENC_NA);

                                                        *offset += bytes_per_pixel;
                                                }

                                                proto_tree_add_item(subrect_tree,
                                                                    hf_vnc_hextile_subrect_x_pos, tvb, *offset, 1, ENC_BIG_ENDIAN);

                                                proto_tree_add_item(subrect_tree, hf_vnc_hextile_subrect_y_pos, tvb, *offset, 1, ENC_BIG_ENDIAN);

                                                *offset += 1;

                                                proto_tree_add_item(subrect_tree, hf_vnc_hextile_subrect_width, tvb, *offset, 1, ENC_BIG_ENDIAN);

                                                proto_tree_add_item(subrect_tree, hf_vnc_hextile_subrect_height, tvb, *offset, 1, ENC_BIG_ENDIAN);

                                                *offset += 1;
                                        }
                                }
                        }
                }
        }
        return 0; /* bytes_needed */
}

static guint
vnc_supported_messages(tvbuff_t *tvb, gint *offset, proto_tree *tree,
                       const guint16 width)
{
        VNC_BYTES_NEEDED(width);
        if (width >= 64) {
                proto_tree_add_item(tree,
                                    hf_vnc_supported_messages_client2server,
                                    tvb, *offset, 32, ENC_NA);
                *offset += 32;
                proto_tree_add_item(tree,
                                    hf_vnc_supported_messages_server2client,
                                    tvb, *offset, 32, ENC_NA);
                *offset += 32;
                *offset += width - 64;
        } else {
                *offset += width;
        }

        return 0; /* bytes_needed */
}

static guint
vnc_supported_encodings(tvbuff_t *tvb, gint *offset, proto_tree *tree,
                        const guint16 width, const guint16 height)
{
        guint16 i = width;

        proto_tree_add_uint(tree, hf_vnc_num_supported_encodings, tvb, *offset, 0, height);

        VNC_BYTES_NEEDED(width);
        for (; i >= 4; i -= 4) {
                proto_tree_add_item(tree, hf_vnc_supported_encodings,
                                    tvb, *offset, 4, ENC_BIG_ENDIAN);
                *offset += 4;
        }
        *offset += i;

        return 0; /* bytes_needed */
}

static guint
vnc_server_identity(tvbuff_t *tvb, gint *offset, proto_tree *tree,
                    const guint16 width)
{
        VNC_BYTES_NEEDED(width);
        proto_tree_add_item(tree, hf_vnc_server_identity,
                            tvb, *offset, width, ENC_ASCII|ENC_NA);
        *offset += width;

        return 0; /* bytes_needed */
}

static guint
vnc_mirrorlink(tvbuff_t *tvb, packet_info *pinfo, gint *offset,
               proto_tree *tree)
{
        guint8 type;
        guint16 length;
        guint16 num, i;
        gint end;
        proto_tree *sub_tree;

        /* Header */
        VNC_BYTES_NEEDED(3);

        type = tvb_get_guint8(tvb, *offset);
        proto_tree_add_item(tree, hf_vnc_mirrorlink_type,
                            tvb, *offset, 1, ENC_BIG_ENDIAN);
        *offset += 1;

        length = tvb_get_ntohs(tvb, *offset);
        proto_tree_add_item(tree, hf_vnc_mirrorlink_length,
                            tvb, *offset, 2, ENC_BIG_ENDIAN);
        *offset += 2;

        col_add_fstr(pinfo->cinfo, COL_INFO, "MirrorLink (%s)",
                     val_to_str_const(type, vnc_mirrorlink_types_vs,
                                      "Unknown"));

        /* Payload */
        end = *offset + length;

        switch(type) {

        case VNC_ML_EXT_BYE_BYE :
                break;

        case VNC_ML_EXT_SERVER_DISPLAY_CONFIGURATION :
                VNC_BYTES_NEEDED(12);
                proto_tree_add_item(tree, hf_vnc_mirrorlink_version_major,
                                    tvb, *offset, 1, ENC_BIG_ENDIAN);
                *offset += 1;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_version_minor,
                                    tvb, *offset, 1, ENC_BIG_ENDIAN);
                *offset += 1;
                proto_tree_add_item(tree,
                                    hf_vnc_mirrorlink_framebuffer_configuration,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_pixel_width,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_pixel_height,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_pixel_format,
                                    tvb, *offset, 4, ENC_BIG_ENDIAN);
                *offset += 4;
                break;

        case VNC_ML_EXT_CLIENT_DISPLAY_CONFIGURATION :
                VNC_BYTES_NEEDED(14);
                proto_tree_add_item(tree, hf_vnc_mirrorlink_version_major,
                                    tvb, *offset, 1, ENC_BIG_ENDIAN);
                *offset += 1;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_version_minor,
                                    tvb, *offset, 1, ENC_BIG_ENDIAN);
                *offset += 1;
                proto_tree_add_item(tree,
                                    hf_vnc_mirrorlink_framebuffer_configuration,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_pixel_width,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_pixel_height,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_display_width,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_display_height,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_display_distance,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
                break;

        case VNC_ML_EXT_SERVER_EVENT_CONFIGURATION :
        case VNC_ML_EXT_CLIENT_EVENT_CONFIGURATION :
                VNC_BYTES_NEEDED(28);
                proto_tree_add_item(tree, hf_vnc_mirrorlink_keyboard_language,
                                    tvb, *offset, 2, ENC_ASCII|ENC_NA);
                *offset += 2;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_keyboard_country,
                                    tvb, *offset, 2, ENC_ASCII|ENC_NA);
                *offset += 2;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_ui_language,
                                    tvb, *offset, 2, ENC_ASCII|ENC_NA);
                *offset += 2;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_ui_country,
                                    tvb, *offset, 2, ENC_ASCII|ENC_NA);
                *offset += 2;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_knob_keys,
                                    tvb, *offset, 4, ENC_BIG_ENDIAN);
                *offset += 4;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_device_keys,
                                    tvb, *offset, 4, ENC_BIG_ENDIAN);
                *offset += 4;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_multimedia_keys,
                                    tvb, *offset, 4, ENC_BIG_ENDIAN);
                *offset += 4;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_key_related,
                                    tvb, *offset, 4, ENC_BIG_ENDIAN);
                *offset += 4;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_pointer_related,
                                    tvb, *offset, 4, ENC_BIG_ENDIAN);
                *offset += 4;
                break;

        case VNC_ML_EXT_EVENT_MAPPING :
        case VNC_ML_EXT_EVENT_MAPPING_REQUEST :
                VNC_BYTES_NEEDED(8);
                proto_tree_add_item(tree,
                                    hf_vnc_mirrorlink_key_symbol_value_client,
                                    tvb, *offset, 4, ENC_BIG_ENDIAN);
                *offset += 4;
                proto_tree_add_item(tree,
                                    hf_vnc_mirrorlink_key_symbol_value_server,
                                    tvb, *offset, 4, ENC_BIG_ENDIAN);
                *offset += 4;
                break;

        case VNC_ML_EXT_KEY_EVENT_LISTING :
                VNC_BYTES_NEEDED(4);
                proto_tree_add_item(tree, hf_vnc_mirrorlink_key_configuration,
                                    tvb, *offset, 1, ENC_BIG_ENDIAN);
                *offset += 1;
                num = tvb_get_guint8(tvb, *offset);
                proto_tree_add_item(tree, hf_vnc_mirrorlink_key_num_events,
                                    tvb, *offset, 1, ENC_BIG_ENDIAN);
                *offset += 1;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_key_event_counter,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
                VNC_BYTES_NEEDED((guint)(4 * num));
                sub_tree = proto_tree_add_subtree(tree, tvb, *offset, 4 * num,
                                         ett_vnc_key_events, NULL, "Key Event List");
                for (; num > 0; num--) {
                        proto_tree_add_item(sub_tree,
                                            hf_vnc_mirrorlink_key_symbol_value,
                                            tvb, *offset, 4, ENC_BIG_ENDIAN);
                        *offset += 4 ;
                }
                break;

        case VNC_ML_EXT_KEY_EVENT_LISTING_REQUEST :
                VNC_BYTES_NEEDED(4);
                proto_tree_add_item(tree,
                                    hf_vnc_mirrorlink_key_request_configuration,
                                    tvb, *offset, 4, ENC_BIG_ENDIAN);
                *offset += 4;
                break;

        case VNC_ML_EXT_VIRTUAL_KEYBOARD :
                VNC_BYTES_NEEDED(16);
                proto_tree_add_item(tree,
                                    hf_vnc_mirrorlink_keyboard_configuration,
                                    tvb, *offset, 4, ENC_BIG_ENDIAN);
                *offset += 4;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_cursor_x,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_cursor_y,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_text_x,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_text_y,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_text_width,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_text_height,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
                break;

        case VNC_ML_EXT_VIRTUAL_KEYBOARD_REQUEST :
                VNC_BYTES_NEEDED(4);
                proto_tree_add_item(tree,
                                    hf_vnc_mirrorlink_keyboard_request_configuration,
                                    tvb, *offset, 4, ENC_BIG_ENDIAN);
                *offset += 4;
                break;

        case VNC_ML_EXT_DEVICE_STATUS :
        case VNC_ML_EXT_DEVICE_STATUS_REQUEST :
                VNC_BYTES_NEEDED(4);
                proto_tree_add_item(tree, hf_vnc_mirrorlink_device_status,
                                    tvb, *offset, 4, ENC_BIG_ENDIAN);
                *offset += 4;
                break;
/*
        case VNC_ML_EXT_CONTENT_ATTESTATION :
                break;

        case VNC_ML_EXT_CONTENT_ATTESTATION_REQUEST :
                break;
*/
        case VNC_ML_EXT_FB_BLOCKING_NOTIFICATION :
                VNC_BYTES_NEEDED(14);
                proto_tree_add_item(tree, hf_vnc_mirrorlink_fb_block_x,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_fb_block_y,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_fb_block_width,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_fb_block_height,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_app_id,
                                    tvb, *offset, 4, ENC_BIG_ENDIAN);
                *offset += 4;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_fb_block_reason,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
                break;

        case VNC_ML_EXT_AUDIO_BLOCKING_NOTIFICATION :
                VNC_BYTES_NEEDED(6);
                proto_tree_add_item(tree, hf_vnc_mirrorlink_app_id,
                                    tvb, *offset, 4, ENC_BIG_ENDIAN);
                *offset += 4;
                proto_tree_add_item(tree, hf_vnc_mirrorlink_audio_block_reason,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
                break;

        case VNC_ML_EXT_TOUCH_EVENT :
                VNC_BYTES_NEEDED(1);
                num = tvb_get_guint8(tvb, *offset);
                proto_tree_add_item(tree, hf_vnc_mirrorlink_touch_num_events,
                                    tvb, *offset, 1, ENC_BIG_ENDIAN);
                *offset += 1;
                VNC_BYTES_NEEDED((guint)(6 * num));
                /*sub_tree = proto_item_add_subtree(tree, ett_vnc_touch_events);*/
                for (i = 0; i < num; i++) {
                        sub_tree = proto_tree_add_subtree_format(tree, tvb, *offset, 6,
                                                 ett_vnc_touch_events, NULL, "Touch Event #%d", i + 1);

                        proto_tree_add_item(sub_tree, hf_vnc_mirrorlink_touch_x,
                                            tvb, *offset, 2, ENC_BIG_ENDIAN);
                        *offset += 2;
                        proto_tree_add_item(sub_tree, hf_vnc_mirrorlink_touch_y,
                                            tvb, *offset, 2, ENC_BIG_ENDIAN);
                        *offset += 2;
                        proto_tree_add_item(sub_tree,
                                            hf_vnc_mirrorlink_touch_id,
                                            tvb, *offset, 1, ENC_BIG_ENDIAN);
                        *offset += 1;
                        proto_tree_add_item(sub_tree,
                                            hf_vnc_mirrorlink_touch_pressure,
                                            tvb, *offset, 1, ENC_BIG_ENDIAN);
                        *offset += 1;
                }
                break;

        case VNC_ML_EXT_FB_ALTERNATIVE_TEXT :
                VNC_BYTES_NEEDED(6);
                proto_tree_add_item(tree, hf_vnc_mirrorlink_app_id,
                                    tvb, *offset, 4, ENC_BIG_ENDIAN);
                *offset += 4;
                num = tvb_get_ntohs(tvb, *offset);
                proto_tree_add_item(tree, hf_vnc_mirrorlink_text_length,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
                VNC_BYTES_NEEDED(num);
                proto_tree_add_item(tree, hf_vnc_mirrorlink_text,
                                    tvb, *offset, num, ENC_ASCII|ENC_NA);
                *offset += num;
                break;

        case VNC_ML_EXT_FB_ALTERNATIVE_TEXT_REQUEST :
                VNC_BYTES_NEEDED(2);
                proto_tree_add_item(tree, hf_vnc_mirrorlink_text_max_length,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
                break;

        }

        if (end > *offset) {
                length = end - *offset;
                VNC_BYTES_NEEDED(length);
                proto_tree_add_item(tree, hf_vnc_mirrorlink_unknown,
                                    tvb, *offset, length, ENC_NA);
                *offset = end;
        }

        return 0; /* bytes_needed */
}

static guint
vnc_fence(tvbuff_t *tvb, packet_info *pinfo, gint *offset,
          proto_tree *tree)
{
        guint payload_length;

        VNC_BYTES_NEEDED(8);

        payload_length = tvb_get_guint8(tvb, *offset+7);
        VNC_BYTES_NEEDED((8+payload_length));

        col_append_sep_str(pinfo->cinfo, COL_INFO, "; ", "Fence");

        proto_tree_add_item(tree, hf_vnc_padding, tvb, *offset, 3, ENC_NA);
        *offset += 3;  /* skip padding */

        proto_tree_add_bitmask(tree, tvb, *offset, hf_vnc_fence_flags,
                               ett_vnc_fence_flags, vnc_fence_flags, ENC_BIG_ENDIAN);

        *offset += 4;

        proto_tree_add_item(tree, hf_vnc_fence_payload_length,
                            tvb, *offset, 1, ENC_BIG_ENDIAN);

        *offset += 1;

        if (payload_length > 0) {
                proto_tree_add_item(tree, hf_vnc_fence_payload,
                                    tvb, *offset, payload_length, ENC_NA);
                *offset += payload_length;
        }
        return 0;
}

static guint
vnc_context_information(tvbuff_t *tvb, gint *offset, proto_tree *tree)
{
        VNC_BYTES_NEEDED(20);

        proto_tree_add_item(tree, hf_vnc_context_information_app_id,
                            tvb, *offset, 4, ENC_BIG_ENDIAN);
        *offset += 4;

        proto_tree_add_item(tree, hf_vnc_context_information_app_trust_level,
                            tvb, *offset, 2, ENC_BIG_ENDIAN);
        *offset += 2;

        proto_tree_add_item(tree,
                            hf_vnc_context_information_content_trust_level,
                            tvb, *offset, 2, ENC_BIG_ENDIAN);
        *offset += 2;

        proto_tree_add_item(tree, hf_vnc_context_information_app_category,
                            tvb, *offset, 4, ENC_BIG_ENDIAN);
        *offset += 4;

        proto_tree_add_item(tree, hf_vnc_context_information_content_category,
                            tvb, *offset, 4, ENC_BIG_ENDIAN);
        *offset += 4;

        proto_tree_add_item(tree, hf_vnc_context_information_content_rules,
                            tvb, *offset, 4, ENC_BIG_ENDIAN);
        *offset += 4;

        return 0; /* bytes_needed */
}

static guint
vnc_slrle_encoding(tvbuff_t *tvb, packet_info *pinfo, gint *offset,
                   proto_tree *tree, const guint16 height)
{
        guint8 depth = vnc_get_depth(pinfo);
        guint8 depth_mod = depth % 8;
        guint8 bytes_per_run;
        guint16 num_runs, i;
        guint length;
        proto_tree *sub_tree;

        if (depth_mod <= 4)
                bytes_per_run = ( 8 - depth_mod + depth) / 8;
        else
                bytes_per_run = (16 - depth_mod + depth) / 8;

        for (i = 0; i < height; i++) {
                VNC_BYTES_NEEDED(2);
                num_runs = tvb_get_ntohs(tvb, *offset);

                length = num_runs * bytes_per_run;

                sub_tree = proto_tree_add_subtree_format(tree, tvb, *offset, 2 + length,
                                         ett_vnc_slrle_subline, NULL, "Scanline #%d", i+1);

                proto_tree_add_item(sub_tree, hf_vnc_slrle_run_num,
                                    tvb, *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;

                VNC_BYTES_NEEDED(length);
                proto_tree_add_item(sub_tree, hf_vnc_slrle_run_data,
                                    tvb, *offset, length, ENC_NA);
                *offset += length;
        }

        return 0; /* bytes_needed */
}

static guint
vnc_h264_encoding(tvbuff_t *tvb, gint *offset, proto_tree *tree)
{
        guint32 nbytes;

        VNC_BYTES_NEEDED(16);

        /*0 == P-Frame; 1 == B-Frame; 2 == I-Frame*/
        proto_tree_add_item(tree, hf_vnc_h264_slice_type,
                            tvb, *offset, 4, ENC_BIG_ENDIAN);
        *offset += 4;

        nbytes = tvb_get_ntohl(tvb, *offset);
        proto_tree_add_item(tree, hf_vnc_h264_nbytes,
                            tvb, *offset, 4, ENC_BIG_ENDIAN);
        *offset += 4;

        proto_tree_add_item(tree, hf_vnc_h264_width,
                            tvb, *offset, 4, ENC_BIG_ENDIAN);
        *offset += 4;

        proto_tree_add_item(tree, hf_vnc_h264_height,
                            tvb, *offset, 4, ENC_BIG_ENDIAN);
        *offset += 4;

        VNC_BYTES_NEEDED(nbytes);
        proto_tree_add_item(tree, hf_vnc_h264_data,
                            tvb, *offset, nbytes, ENC_NA);
        *offset += nbytes;

        return 0; /* bytes_needed */
}

#ifdef HAVE_ZLIB
static guint
vnc_zrle_encoding(tvbuff_t *tvb, packet_info *pinfo, gint *offset,
                  proto_tree *tree, const guint16 width, const guint16 height)
#else
static guint
vnc_zrle_encoding(tvbuff_t *tvb, packet_info *pinfo _U_, gint *offset,
                  proto_tree *tree, const guint16 width _U_, const guint16 height _U_)
#endif
{
        guint32 data_len;
#ifdef HAVE_ZLIB
        guint8 palette_size;
        guint8 bytes_per_cpixel = vnc_get_bytes_per_pixel(pinfo);
        gint uncomp_offset = 0;
        guint length;
        gint subencoding_type;
        tvbuff_t *uncomp_tvb;
        proto_tree *zrle_subencoding_tree;
        proto_item *ti;
#endif

        VNC_BYTES_NEEDED(4);
        proto_tree_add_item(tree, hf_vnc_zrle_len, tvb, *offset,
                            4, ENC_BIG_ENDIAN);
        data_len = tvb_get_ntohl(tvb, *offset);

        *offset += 4;

        VNC_BYTES_NEEDED(data_len);

        proto_tree_add_item(tree, hf_vnc_zrle_data, tvb, *offset,
                            data_len, ENC_NA);

#ifdef HAVE_ZLIB
        uncomp_tvb = tvb_child_uncompress(tvb, tvb, *offset, data_len);

        if(uncomp_tvb != NULL) {
                add_new_data_source(pinfo, uncomp_tvb,
                                    "Uncompressed ZRLE data");

                ti = proto_tree_add_item(tree, hf_vnc_zrle_subencoding,
                                         uncomp_tvb, uncomp_offset, 1, ENC_BIG_ENDIAN);
                zrle_subencoding_tree =
                        proto_item_add_subtree(ti, ett_vnc_zrle_subencoding);

                proto_tree_add_item(zrle_subencoding_tree, hf_vnc_zrle_rle,
                                    uncomp_tvb, uncomp_offset, 1, ENC_BIG_ENDIAN);

                proto_tree_add_item(zrle_subencoding_tree,
                                    hf_vnc_zrle_palette_size, uncomp_tvb,
                                    uncomp_offset, 1, ENC_BIG_ENDIAN);

                subencoding_type = tvb_get_guint8(uncomp_tvb, uncomp_offset);
                palette_size = subencoding_type & 0x7F;

                uncomp_offset += 1;

                if(subencoding_type == 0) { /* Raw */
                        length = width * height * bytes_per_cpixel;
                        VNC_BYTES_NEEDED(length);

                        /* XXX - not working yet! */

                        proto_tree_add_item(zrle_subencoding_tree,
                                            hf_vnc_zrle_raw, uncomp_tvb,
                                            uncomp_offset, length, ENC_NA);

                } else if(subencoding_type >= 130 && subencoding_type <= 255) {
                        length = palette_size * bytes_per_cpixel;
                        VNC_BYTES_NEEDED(length);

                        proto_tree_add_item(zrle_subencoding_tree,
                                            hf_vnc_zrle_palette, uncomp_tvb,
                                            uncomp_offset, length, ENC_NA);

                        /* XXX - Not complete! */
                }

        } else {
                proto_tree_add_expert(tree, pinfo, &ei_vnc_zrle_failed, tvb, *offset, data_len);
        }
#endif /* HAVE_ZLIB */

        *offset += data_len;

        return 0; /* bytes_needed */
}


static guint
read_compact_len(tvbuff_t *tvb, gint *offset, gint *length, gint *value_length)
{
        gint b;

        VNC_BYTES_NEEDED(1);

        *value_length = 0;

        b = tvb_get_guint8(tvb, *offset);
        *offset += 1;
        *value_length += 1;

        *length = b & 0x7f;
        if ((b & 0x80) != 0) {
                VNC_BYTES_NEEDED(1);

                b = tvb_get_guint8(tvb, *offset);
                *offset += 1;
                *value_length += 1;

                *length |= (b & 0x7f) << 7;

                if ((b & 0x80) != 0) {
                        VNC_BYTES_NEEDED (1);

                        b = tvb_get_guint8(tvb, *offset);
                        *offset += 1;
                        *value_length += 1;

                        *length |= (b & 0xff) << 14;
                }
        }

        return 0;
}


static guint
process_compact_length_and_image_data(tvbuff_t *tvb, gint *offset, proto_tree *tree)
{
        guint bytes_needed;
        guint length, value_length;

        bytes_needed = read_compact_len (tvb, offset, &length, &value_length);
        if (bytes_needed != 0)
                return bytes_needed;

        proto_tree_add_uint(tree, hf_vnc_tight_image_len, tvb, *offset - value_length, value_length, length);

        VNC_BYTES_NEEDED(length);
        proto_tree_add_item(tree, hf_vnc_tight_image_data, tvb, *offset, length, ENC_NA);
        *offset += length;

        return 0; /* bytes_needed */
}


static guint
process_tight_rect_filter_palette(tvbuff_t *tvb, packet_info *pinfo, gint *offset,
                                  proto_tree *tree, gint *bits_per_pixel)
{
        vnc_packet_t *per_packet_info;
        gint num_colors;
        guint palette_bytes;

        /* See TightVNC's vnc_unixsrc/vncviewer/tight.c:InitFilterPaletteBPP() */

        per_packet_info = (vnc_packet_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_vnc, 0);
        /* Our calling function should have set the packet's proto data already */
        DISSECTOR_ASSERT(per_packet_info != NULL);

        VNC_BYTES_NEEDED(1);
        proto_tree_add_item(tree, hf_vnc_tight_palette_num_colors, tvb, *offset, 1, ENC_BIG_ENDIAN);
        num_colors = tvb_get_guint8(tvb, *offset);
        *offset += 1;

        num_colors++;
        if (num_colors < 2)
                return 0;

        if (per_packet_info->depth == 24)
                palette_bytes = num_colors * 3;
        else
                palette_bytes = num_colors * per_packet_info->depth / 8;

        VNC_BYTES_NEEDED(palette_bytes);
        proto_tree_add_item(tree, hf_vnc_tight_palette_data, tvb, *offset, palette_bytes, ENC_NA);
        *offset += palette_bytes;

        /* This is the number of bits per pixel *in the image data*, not the actual client depth */
        if (num_colors == 2)
                *bits_per_pixel = 1;
        else
                *bits_per_pixel = 8;

        return 0;
}

static guint
vnc_tight_encoding(tvbuff_t *tvb, packet_info *pinfo, gint *offset,
                   proto_tree *tree, const guint16 width _U_, const guint16 height _U_)
{
        vnc_packet_t *per_packet_info;
        guint8 comp_ctl;
        proto_item *compression_type_ti;
        gint bit_offset;
        gint bytes_needed = -1;

        per_packet_info = (vnc_packet_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_vnc, 0);
        /* Our calling function should have set the packet's proto data already */
        DISSECTOR_ASSERT(per_packet_info != NULL);

        /* See xserver/hw/vnc/rfbproto.h and grep for "Tight Encoding." for the following layout */

        VNC_BYTES_NEEDED(1);

        /* least significant bits 0-3 are "reset compression stream N" */
        bit_offset = *offset * 8;
        proto_tree_add_bits_item(tree, hf_vnc_tight_reset_stream0, tvb, bit_offset + 7, 1, ENC_BIG_ENDIAN);
        proto_tree_add_bits_item(tree, hf_vnc_tight_reset_stream1, tvb, bit_offset + 6, 1, ENC_BIG_ENDIAN);
        proto_tree_add_bits_item(tree, hf_vnc_tight_reset_stream2, tvb, bit_offset + 5, 1, ENC_BIG_ENDIAN);
        proto_tree_add_bits_item(tree, hf_vnc_tight_reset_stream3, tvb, bit_offset + 4, 1, ENC_BIG_ENDIAN);

        /* most significant bits 4-7 are "compression type" */
        compression_type_ti = proto_tree_add_bits_item(tree, hf_vnc_tight_rect_type, tvb, bit_offset + 0, 4, ENC_BIG_ENDIAN);

        comp_ctl = tvb_get_guint8(tvb, *offset);
        *offset += 1;

        comp_ctl >>= 4; /* skip over the "reset compression" bits from above */

        /* compression format */

        if (comp_ctl == TIGHT_RECT_FILL) {
                /* "fill" encoding (solid rectangle) */

                proto_item_append_text(compression_type_ti, " (fill encoding - solid rectangle)");

                if (per_packet_info->depth == 24) {
                        VNC_BYTES_NEEDED(3);
                        proto_tree_add_item(tree, hf_vnc_tight_fill_color, tvb, *offset, 3, ENC_NA);
                        *offset += 3;
                } else {
                        VNC_BYTES_NEEDED(per_packet_info->bytes_per_pixel);
                        proto_tree_add_item(tree, hf_vnc_tight_fill_color, tvb, *offset, per_packet_info->bytes_per_pixel, ENC_NA);
                        *offset += per_packet_info->bytes_per_pixel;
                }

                bytes_needed = 0;
        } else if (comp_ctl == TIGHT_RECT_JPEG) {
                /* jpeg encoding */

                proto_item_append_text(compression_type_ti, " (JPEG encoding)");
                bytes_needed = process_compact_length_and_image_data(tvb, offset, tree);
                if (bytes_needed != 0)
                        return bytes_needed;
        } else if (comp_ctl > TIGHT_RECT_MAX_VALUE) {
                /* invalid encoding */

                expert_add_info(pinfo, compression_type_ti, &ei_vnc_invalid_encoding);
        } else {
                guint row_size;
                gint bits_per_pixel;

                /* basic encoding */

                proto_item_append_text(compression_type_ti, " (basic encoding)");

                proto_tree_add_bits_item(tree, hf_vnc_tight_filter_flag, tvb, bit_offset + 1, 1, ENC_BIG_ENDIAN);

                bits_per_pixel = per_packet_info->depth;

                if ((comp_ctl & TIGHT_RECT_EXPLICIT_FILTER_FLAG) != 0) {
                        guint8 filter_id;

                        /* explicit filter */

                        VNC_BYTES_NEEDED(1);
                        proto_tree_add_item(tree, hf_vnc_tight_filter_id, tvb, *offset, 1, ENC_BIG_ENDIAN);
                        filter_id = tvb_get_guint8(tvb, *offset);
                        *offset += 1;

                        switch (filter_id) {
                        case TIGHT_RECT_FILTER_COPY:
                                /* nothing to do */
                                break;

                        case TIGHT_RECT_FILTER_PALETTE:
                                bytes_needed = process_tight_rect_filter_palette(tvb, pinfo, offset, tree, &bits_per_pixel);
                                if (bytes_needed != 0)
                                        return bytes_needed;

                                break;

                        case TIGHT_RECT_FILTER_GRADIENT:
                                /* nothing to do */
                                break;
                        }
                } else {
                        /* this is the same case as TIGHT_RECT_FILTER_COPY, so there's nothing special to do */
                }

                row_size = ((guint) width * bits_per_pixel + 7) / 8;
                if (row_size * height < TIGHT_MIN_BYTES_TO_COMPRESS) {
                        guint num_bytes;

                        /* The data is not compressed; just skip over it */

                        num_bytes = row_size * height;
                        VNC_BYTES_NEEDED(num_bytes);
                        proto_tree_add_item(tree, hf_vnc_tight_image_data, tvb, *offset, num_bytes, ENC_NA);
                        *offset += num_bytes;

                        bytes_needed = 0;
                } else {
                        /* The data is compressed; read its length and data */
                        bytes_needed = process_compact_length_and_image_data(tvb, offset, tree);
                        if (bytes_needed != 0)
                                return bytes_needed;
                }
        }

        DISSECTOR_ASSERT(bytes_needed != -1);

        return bytes_needed;
}


static guint
decode_cursor(tvbuff_t *tvb, gint *offset, proto_tree *tree,
              guint pixels_bytes, guint mask_bytes)
{
        guint total_bytes;

        total_bytes = pixels_bytes + mask_bytes;
        VNC_BYTES_NEEDED (total_bytes);

        proto_tree_add_item(tree, hf_vnc_cursor_encoding_pixels, tvb, *offset,
                            pixels_bytes, ENC_NA);
        *offset += pixels_bytes;

        proto_tree_add_item(tree, hf_vnc_cursor_encoding_bitmask, tvb, *offset,
                            mask_bytes, ENC_NA);
        *offset += mask_bytes;

        return 0; /* bytes_needed */
}


static guint
vnc_rich_cursor_encoding(tvbuff_t *tvb, packet_info *pinfo, gint *offset,
                         proto_tree *tree, const guint16 width, const guint16 height)
{
        guint8 bytes_per_pixel = vnc_get_bytes_per_pixel(pinfo);
        guint  pixels_bytes, mask_bytes;

        pixels_bytes = width * height * bytes_per_pixel;
        mask_bytes   = ((width + 7) / 8) * height;

        return decode_cursor(tvb, offset, tree,
                             pixels_bytes, mask_bytes);
}


static guint
vnc_x_cursor_encoding(tvbuff_t *tvb, packet_info *pinfo _U_, gint *offset,
                      proto_tree *tree, const guint16 width, const guint16 height)
{
        gint bitmap_row_bytes = (width + 7) / 8;
        gint mask_bytes       = bitmap_row_bytes * height;

        VNC_BYTES_NEEDED (6);
        proto_tree_add_item(tree, hf_vnc_cursor_x_fore_back, tvb, *offset, 6, ENC_NA);
        *offset += 6;

        /* The length of the pixel data is the same as the length of the mask data (X cursors are strictly black/white) */
        return decode_cursor(tvb, offset, tree,
                             mask_bytes, mask_bytes);
}


static guint
vnc_server_set_colormap_entries(tvbuff_t *tvb, packet_info *pinfo, gint *offset,
                                proto_tree *tree)
{
        guint16 number_of_colors;
        guint counter, bytes_needed;
        proto_item *ti;
        proto_tree *vnc_colormap_num_groups, *vnc_colormap_color_group;

        col_append_sep_str(pinfo->cinfo, COL_INFO, "; ", "Server set colormap entries");

        number_of_colors = tvb_get_ntohs(tvb, 4);

        VNC_BYTES_NEEDED(3);
        proto_tree_add_item(tree, hf_vnc_padding, tvb, *offset, 1, ENC_NA);
        *offset += 1; /* Skip over 1 byte of padding */

        proto_tree_add_item(tree, hf_vnc_colormap_first_color,
                            tvb, *offset, 2, ENC_BIG_ENDIAN);
        *offset += 2;

        /*  XXX - this is 3 bytes into the tvb, but number_of_colors is set off
         *  of 4 bytes in... Bug???
         */
        ti = proto_tree_add_item(tree, hf_vnc_colormap_num_colors, tvb,
                                 *offset, 2, ENC_BIG_ENDIAN);

        if (number_of_colors > 10000) {
                expert_add_info_format(pinfo, ti, &ei_vnc_too_many_colors,"Too many colors (%d), aborting dissection",
                                       number_of_colors);
                return(0);
        }

        bytes_needed = (number_of_colors * 6) + 5;
        VNC_BYTES_NEEDED(bytes_needed);

        *offset += 2;

        ti = proto_tree_add_item(tree, hf_vnc_color_groups, tvb,
                                *offset, number_of_colors * 6, ENC_NA);
        vnc_colormap_num_groups =
                proto_item_add_subtree(ti, ett_vnc_colormap_num_groups);

        for(counter = 0; counter < number_of_colors; counter++) {
                vnc_colormap_color_group = proto_tree_add_subtree_format(vnc_colormap_num_groups, tvb,
                                         *offset, 6, ett_vnc_colormap_color_group, NULL,
                                         "Color group #%d", counter+1);

                proto_tree_add_item(vnc_colormap_color_group,
                                    hf_vnc_colormap_red, tvb,
                                    *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;

                proto_tree_add_item(vnc_colormap_color_group,
                                    hf_vnc_colormap_green, tvb,
                                    *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;

                proto_tree_add_item(vnc_colormap_color_group,
                                    hf_vnc_colormap_blue, tvb,
                                    *offset, 2, ENC_BIG_ENDIAN);
                *offset += 2;
        }
        return 0;
}


static void
vnc_server_ring_bell(tvbuff_t *tvb _U_, packet_info *pinfo, gint *offset _U_,
                     proto_tree *tree _U_)
{
        col_append_sep_str(pinfo->cinfo, COL_INFO, "; ", "Server ring bell on client");
        /* This message type has no payload... */
}


static guint
vnc_server_cut_text(tvbuff_t *tvb, packet_info *pinfo, gint *offset,
                    proto_tree *tree)
{
        guint32     text_len;
        proto_item *pi;

        col_append_sep_str(pinfo->cinfo, COL_INFO, "; ", "Server cut text");

        text_len = tvb_get_ntohl(tvb, *offset);
        pi = proto_tree_add_item(tree, hf_vnc_server_cut_text_len, tvb, *offset, 4,
                            ENC_BIG_ENDIAN);
        *offset += 4;

        if (text_len > 100000) {
                expert_add_info_format(pinfo, pi, &ei_vnc_too_many_cut_text,
                                "Too much cut text (%d), aborting dissection", text_len);
                return(0);
        }

        VNC_BYTES_NEEDED(text_len);

        proto_tree_add_item(tree, hf_vnc_server_cut_text, tvb, *offset,
                            text_len, ENC_ASCII|ENC_NA);
        *offset += text_len;

        return *offset;
}


static void
vnc_set_bytes_per_pixel(packet_info *pinfo, const guint8 bytes_per_pixel)
{
        vnc_packet_t *per_packet_info;

        per_packet_info = (vnc_packet_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_vnc, 0);
        /* Our calling function should have set the packet's proto data already */
        DISSECTOR_ASSERT(per_packet_info != NULL);

        per_packet_info->bytes_per_pixel = bytes_per_pixel;
}


static void
vnc_set_depth(packet_info *pinfo, const guint8 depth)
{
        vnc_packet_t *per_packet_info;

        per_packet_info = (vnc_packet_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_vnc, 0);
        /* Our calling function should have set the packet's proto data already */
        DISSECTOR_ASSERT(per_packet_info != NULL);

        per_packet_info->depth = depth;
}


static guint8
vnc_get_bytes_per_pixel(packet_info *pinfo)
{
        vnc_packet_t *per_packet_info;

        per_packet_info = (vnc_packet_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_vnc, 0);
        /* Our calling function should have set the packet's proto data already */
        DISSECTOR_ASSERT(per_packet_info != NULL);

        return per_packet_info->bytes_per_pixel;
}


static guint8
vnc_get_depth(packet_info *pinfo)
{
        vnc_packet_t *per_packet_info;

        per_packet_info = (vnc_packet_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_vnc, 0);
        /* Our calling function should have set the packet's proto data already */
        DISSECTOR_ASSERT(per_packet_info != NULL);

        return per_packet_info->depth;
}


/* Register the protocol with Wireshark */
void
proto_register_vnc(void)
{
        module_t *vnc_module; /* To handle our preferences */
        expert_module_t* expert_vnc;

        /* Setup list of header fields */
        static hf_register_info hf[] = {
                { &hf_vnc_padding,
                  { "Padding", "vnc.padding",
                    FT_NONE, BASE_NONE, NULL, 0x0,
                    "Unused space", HFILL }
                },

                { &hf_vnc_server_proto_ver,
                  { "Server protocol version", "vnc.server_proto_ver",
                    FT_STRING, BASE_NONE, NULL, 0x0,
                    "VNC protocol version on server", HFILL }
                },
                { &hf_vnc_client_proto_ver,
                  { "Client protocol version", "vnc.client_proto_ver",
                    FT_STRING, BASE_NONE, NULL, 0x0,
                    "VNC protocol version on client", HFILL }
                },
                { &hf_vnc_num_security_types,
                  { "Number of security types", "vnc.num_security_types",
                    FT_UINT8, BASE_DEC, NULL, 0x0,
                    "Number of security (authentication) types supported by the server", HFILL }
                },
                { &hf_vnc_security_type,
                  { "Security type", "vnc.security_type",
                    FT_UINT8, BASE_DEC, VALS(vnc_security_types_vs), 0x0,
                    "Security types offered by the server (VNC versions => 3.007", HFILL }
                },
                { &hf_vnc_server_security_type,
                  { "Security type", "vnc.server_security_type",
                    FT_UINT32, BASE_DEC, VALS(vnc_security_types_vs), 0x0,
                    "Security type mandated by the server", HFILL }
                },
                { &hf_vnc_client_security_type,
                  { "Security type selected", "vnc.client_security_type",
                    FT_UINT8, BASE_DEC, VALS(vnc_security_types_vs), 0x0,
                    "Security type selected by the client", HFILL }
                },
                { &hf_vnc_tight_num_tunnel_types,
                  { "Number of supported tunnel types",  "vnc.num_tunnel_types",
                    FT_UINT32, BASE_DEC, NULL, 0x0,
                    "Number of tunnel types for TightVNC", HFILL }
                },
                { &hf_vnc_tight_tunnel_type,
                  { "Tunnel type", "vnc.tunnel_type",
                    FT_UINT8, BASE_DEC, NULL, 0x0,
                    "Tunnel type specific to TightVNC", HFILL }
                },
                { &hf_vnc_tight_num_auth_types,
                  { "Number of supported authentication types", "vnc.num_auth_types",
                    FT_UINT32, BASE_DEC, NULL, 0x0,
                    "Authentication types specific to TightVNC", HFILL }
                },
                { &hf_vnc_tight_auth_code,
                  { "Authentication code", "vnc.tight_auth_code",
                    FT_UINT32, BASE_DEC, VALS(vnc_security_types_vs), 0x0,
                    "Authentication code specific to TightVNC", HFILL }
                },
                { &hf_vnc_tight_server_message_type,
                  { "Server message type (TightVNC)", "vnc.tight_server_message_type",
                    FT_INT32, BASE_DEC, NULL, 0x0,
                    "Server message type specific to TightVNC", HFILL }
                },
                { &hf_vnc_tight_server_vendor,
                  { "Server vendor code", "vnc.server_vendor",
                    FT_STRING, BASE_NONE, NULL, 0x0,
                    "Server vendor code specific to TightVNC", HFILL }
                },
                { &hf_vnc_tight_signature,
                  { "Signature", "vnc.signature",
                    FT_STRING, BASE_NONE, NULL, 0x0,
                    NULL, HFILL }
                },
                { &hf_vnc_tight_server_name,
                  { "Server name", "vnc.server_name",
                    FT_STRING, BASE_NONE, NULL, 0x0,
                    "Server name specific to TightVNC", HFILL }
                },
                { &hf_vnc_tight_client_message_type,
                  { "Client message type (TightVNC)", "vnc.tight_client_message_type",
                    FT_INT32, BASE_DEC, NULL, 0x0,
                    "Client message type specific to TightVNC", HFILL }
                },
                { &hf_vnc_tight_client_vendor,
                  { "Client vendor code", "vnc.client_vendor",
                    FT_STRING, BASE_NONE, NULL, 0x0,
                    "Client vendor code specific to TightVNC", HFILL }
                },
                { &hf_vnc_tight_client_name,
                  { "Client name", "vnc.client_name",
                    FT_STRING, BASE_NONE, NULL, 0x0,
                    "Client name specific to TightVNC", HFILL }
                },
                { &hf_vnc_tight_encoding_type,
                  { "Encoding type", "vnc.encoding_type",
                    FT_INT32, BASE_DEC, VALS(encoding_types_vs), 0x0,
                    "Encoding type specific to TightVNC", HFILL }
                },
                { &hf_vnc_tight_encoding_vendor,
                  { "Encoding vendor code", "vnc.encoding_vendor",
                    FT_STRING, BASE_NONE, NULL, 0x0,
                    "Encoding vendor code specific to TightVNC", HFILL }
                },
                { &hf_vnc_tight_encoding_name,
                  { "Encoding name", "vnc.encoding_name",
                    FT_STRING, BASE_NONE, NULL, 0x0,
                    "Encoding name specific to TightVNC", HFILL }
                },
                { &hf_vnc_tight_reset_stream0,
                  { "Reset compression stream 0", "vnc.tight_reset_stream0",
                    FT_BOOLEAN, BASE_NONE, NULL, 0x0,
                    "Tight compression, reset compression stream 0", HFILL }
                },
                { &hf_vnc_tight_reset_stream1,
                  { "Reset compression stream 1", "vnc.tight_reset_stream1",
                    FT_BOOLEAN, BASE_NONE, NULL, 0x0,
                    "Tight compression, reset compression stream 1", HFILL }
                },
                { &hf_vnc_tight_reset_stream2,
                  { "Reset compression stream 2", "vnc.tight_reset_stream2",
                    FT_BOOLEAN, BASE_NONE, NULL, 0x0,
                    "Tight compression, reset compression stream 2", HFILL }
                },
                { &hf_vnc_tight_reset_stream3,
                  { "Reset compression stream 3", "vnc.tight_reset_stream3",
                    FT_BOOLEAN, BASE_NONE, NULL, 0x0,
                    "Tight compression, reset compression stream 3", HFILL }
                },
                { &hf_vnc_tight_rect_type,
                  { "Rectangle type", "vnc.tight_rect_type",
                    FT_UINT8, BASE_HEX, NULL, 0x0,
                    "Tight compression, rectangle type", HFILL }
                },
                { &hf_vnc_tight_image_len,
                  { "Image data length", "vnc.tight_image_len",
                    FT_UINT32, BASE_DEC, NULL, 0x0,
                    "Tight compression, length of image data", HFILL }
                },
                { &hf_vnc_tight_image_data,
                  { "Image data", "vnc.tight_image_data",
                    FT_BYTES, BASE_NONE, NULL, 0x0,
                    "Tight compression, image data", HFILL }
                },
                { &hf_vnc_tight_fill_color,
                  { "Fill color (RGB)", "vnc.tight_fill_color",
                    FT_BYTES, BASE_NONE, NULL, 0x0,
                    "Tight compression, fill color for solid rectangle", HFILL }
                },
                { &hf_vnc_tight_filter_flag,
                  { "Explicit filter flag", "vnc.tight_filter_flag",
                    FT_BOOLEAN, BASE_NONE, NULL, 0x0,
                    "Tight compression, explicit filter flag", HFILL }
                },
                { &hf_vnc_tight_filter_id,
                  { "Filter ID", "vnc.tight_filter_id",
                    FT_UINT8, BASE_DEC, VALS(tight_filter_ids_vs), 0x0,
                    "Tight compression, filter ID", HFILL }
                },
                { &hf_vnc_tight_palette_num_colors,
                  { "Number of colors in palette", "vnc.tight_palette_num_colors",
                    FT_UINT8, BASE_DEC, NULL, 0x0,
                    "Tight compression, number of colors in rectangle's palette", HFILL }
                },
                { &hf_vnc_tight_palette_data,
                  { "Palette data", "vnc.tight_palette_data",
                    FT_BYTES, BASE_NONE, NULL, 0x0,
                    "Tight compression, palette data for a rectangle", HFILL }
                },
                { &hf_vnc_auth_challenge,
                  { "Authentication challenge", "vnc.auth_challenge",
                    FT_BYTES, BASE_NONE, NULL, 0x0,
                    "Random authentication challenge from server to client", HFILL }
                },
                { &hf_vnc_auth_response,
                  { "Authentication response", "vnc.auth_response",
                    FT_BYTES, BASE_NONE, NULL, 0x0,
                    "Client's encrypted response to the server's authentication challenge", HFILL }
                },
                { &hf_vnc_auth_result,
                  { "Authentication result", "vnc.auth_result",
                    FT_BOOLEAN, 32, TFS(&auth_result_tfs), 0x1,
                    NULL, HFILL }
                },
                { &hf_vnc_auth_error_length,
                  { "Length of authentication error", "vnc.auth_error_len",
                    FT_UINT32, BASE_DEC, NULL, 0x0,
                    "Authentication error length (present only if the authentication result is fail", HFILL }
                },
                { &hf_vnc_auth_error,
                  { "Authentication error", "vnc.auth_error",
                    FT_STRING, BASE_NONE, NULL, 0x0,
                    "Authentication error (present only if the authentication result is fail", HFILL }
                },
                { &hf_vnc_ard_auth_generator,
                  { "Generator", "vnc.ard_auth_generator",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                        "Generator for Diffie-Hellman key exchange", HFILL }
                },
                { &hf_vnc_ard_auth_key_len,
                  { "Key length", "vnc.ard_auth_key_len",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                        "Diffie-Hellman key length", HFILL }
                },
                { &hf_vnc_ard_auth_modulus,
                  { "Prime modulus", "vnc.ard_auth_modulus",
                    FT_BYTES, BASE_NONE, NULL, 0x0,
                        "Prime modulus for Diffie-Hellman key exchange", HFILL }
                },
                { &hf_vnc_ard_auth_server_key,
                  { "Server public key", "vnc.ard_auth_server_key",
                    FT_BYTES, BASE_NONE, NULL, 0x0,
                        "Server's public Diffie-Hellman key", HFILL }
                },
                { &hf_vnc_ard_auth_credentials,
                  { "Encrypted credentials", "vnc.ard_auth_credentials",
                    FT_BYTES, BASE_NONE, NULL, 0x0,
                        "Encrypted client username and password", HFILL }
                },
                { &hf_vnc_ard_auth_client_key,
                  { "Client public key", "vnc.ard_auth_client_key",
                    FT_BYTES, BASE_NONE, NULL, 0x0,
                        "Client's public Diffie-Hellman key", HFILL }
                },
                { &hf_vnc_share_desktop_flag,
                  { "Share desktop flag", "vnc.share_desktop_flag",
                    FT_BOOLEAN, BASE_NONE, NULL, 0x0,
                    "Client's desire to share the server's desktop with other clients", HFILL }
                },
                { &hf_vnc_width,
                  { "Framebuffer width", "vnc.width",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Width of the framebuffer (screen) in pixels", HFILL }
                },
                { &hf_vnc_height,
                  { "Framebuffer height", "vnc.height",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Height of the framebuffer (screen) in pixels", HFILL }
                },
                { &hf_vnc_server_bits_per_pixel,
                  { "Bits per pixel", "vnc.server_bits_per_pixel",
                    FT_UINT8, BASE_DEC, NULL, 0x0,
                    "Number of bits used by server for each pixel value on the wire from the server", HFILL }
                },
                { &hf_vnc_server_depth,
                  { "Depth", "vnc.server_depth",
                    FT_UINT8, BASE_DEC, NULL, 0x0,
                    "Number of useful bits in the pixel value on server", HFILL }
                },
                { &hf_vnc_server_big_endian_flag,
                  { "Big endian flag", "vnc.server_big_endian_flag",
                    FT_BOOLEAN, BASE_NONE, NULL, 0x0,
                    "True if multi-byte pixels are interpreted as big endian by server", HFILL }
                },
                { &hf_vnc_server_true_color_flag,
                  { "True color flag", "vnc.server_true_color_flag",
                    FT_BOOLEAN, BASE_NONE, NULL, 0x0,
                    "If true, then the next six items specify how to extract the red, green and blue intensities from the pixel value on the server.", HFILL }
                },
                { &hf_vnc_server_red_max,
                  { "Red maximum", "vnc.server_red_max",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Maximum red value on server as n: 2^n - 1", HFILL }
                },
                { &hf_vnc_server_green_max,
                  { "Green maximum", "vnc.server_green_max",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Maximum green value on server as n: 2^n - 1", HFILL }
                },
                { &hf_vnc_server_blue_max,
                  { "Blue maximum", "vnc.server_blue_max",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Maximum blue value on server as n: 2^n - 1", HFILL }
                },
                { &hf_vnc_server_red_shift,
                  { "Red shift", "vnc.server_red_shift",
                    FT_UINT8, BASE_DEC, NULL, 0x0,
                    "Number of shifts needed to get the red value in a pixel to the least significant bit on the server", HFILL }
                },
                { &hf_vnc_server_green_shift,
                  { "Green shift", "vnc.server_green_shift",
                    FT_UINT8, BASE_DEC, NULL, 0x0,
                    "Number of shifts needed to get the green value in a pixel to the least significant bit on the server", HFILL }
                },
                { &hf_vnc_server_blue_shift,
                  { "Blue shift", "vnc.server_blue_shift",
                    FT_UINT8, BASE_DEC, NULL, 0x0,
                    "Number of shifts needed to get the blue value in a pixel to the least significant bit on the server", HFILL }
                },
                { &hf_vnc_desktop_name_len,
                  { "Desktop name length", "vnc.desktop_name_len",
                    FT_UINT32, BASE_DEC, NULL, 0x0,
                    "Length of desktop name in bytes", HFILL }
                },
                { &hf_vnc_desktop_screen_num,
                  { "Number of screens", "vnc.screen_num",
                    FT_UINT8, BASE_DEC, NULL, 0x0,
                    NULL, HFILL }
                },
                { &hf_vnc_desktop_screen_id,
                  { "Screen ID", "vnc.screen_id",
                    FT_UINT32, BASE_DEC, NULL, 0x0,
                    "ID of screen", HFILL }
                },
                { &hf_vnc_desktop_screen_x,
                  { "Screen X position", "vnc.screen_x",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "X coordinate of screen", HFILL }
                },
                { &hf_vnc_desktop_screen_y,
                  { "Screen Y position", "vnc.screen_y",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Y coordinate of screen", HFILL }
                },
                { &hf_vnc_desktop_screen_width,
                  { "Screen width", "vnc.screen_width",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Width of screen", HFILL }
                },
                { &hf_vnc_desktop_screen_height,
                  { "Screen height", "vnc.screen_height",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Height of screen", HFILL }
                },
                { &hf_vnc_desktop_screen_flags,
                  { "Screen flags", "vnc.screen_flags",
                    FT_UINT32, BASE_DEC, NULL, 0x0,
                    "Flags of screen", HFILL }
                },
                { &hf_vnc_desktop_name,
                  { "Desktop name", "vnc.desktop_name",
                    FT_STRING, BASE_NONE, NULL, 0x0,
                    "Name of the VNC desktop on the server", HFILL }
                },
                { &hf_vnc_num_server_message_types,
                  { "Server message types", "vnc.num_server_message_types",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Unknown", HFILL } /* XXX - Needs description */
                },
                { &hf_vnc_num_client_message_types,
                  { "Client message types", "vnc.num_client_message_types",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Unknown", HFILL } /* XXX - Needs description */
                },
                { &hf_vnc_num_encoding_types,
                  { "Encoding types", "vnc.num_encoding_types",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Unknown", HFILL } /* XXX - Needs description */
                },
                { &hf_vnc_client_message_type,
                  { "Client Message Type", "vnc.client_message_type",
                    FT_UINT8, BASE_DEC, VALS(vnc_client_message_types_vs), 0x0,
                    "Message type from client", HFILL }
                },
                { &hf_vnc_client_bits_per_pixel,
                  { "Bits per pixel", "vnc.client_bits_per_pixel",
                    FT_UINT8, BASE_DEC, NULL, 0x0,
                    "Number of bits used by server for each pixel value on the wire from the client", HFILL }
                },
                { &hf_vnc_client_depth,
                  { "Depth", "vnc.client_depth",
                    FT_UINT8, BASE_DEC, NULL, 0x0,
                    "Number of useful bits in the pixel value on client", HFILL }
                },
                { &hf_vnc_client_big_endian_flag,
                  { "Big endian flag", "vnc.client_big_endian_flag",
                    FT_BOOLEAN, BASE_NONE, NULL, 0x0,
                    "True if multi-byte pixels are interpreted as big endian by client", HFILL }
                },
                { &hf_vnc_client_true_color_flag,
                  { "True color flag", "vnc.client_true_color_flag",
                    FT_BOOLEAN, BASE_NONE, NULL, 0x0,
                    "If true, then the next six items specify how to extract the red, green and blue intensities from the pixel value on the client.", HFILL }
                },
                { &hf_vnc_client_red_max,
                  { "Red maximum", "vnc.client_red_max",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Maximum red value on client as n: 2^n - 1", HFILL }
                },
                { &hf_vnc_client_green_max,
                  { "Green maximum", "vnc.client_green_max",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Maximum green value on client as n: 2^n - 1", HFILL }
                },
                { &hf_vnc_client_blue_max,
                  { "Blue maximum", "vnc.client_blue_max",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Maximum blue value on client as n: 2^n - 1", HFILL }
                },
                { &hf_vnc_client_red_shift,
                  { "Red shift", "vnc.client_red_shift",
                    FT_UINT8, BASE_DEC, NULL, 0x0,
                    "Number of shifts needed to get the red value in a pixel to the least significant bit on the client", HFILL }
                },
                { &hf_vnc_client_green_shift,
                  { "Green shift", "vnc.client_green_shift",
                    FT_UINT8, BASE_DEC, NULL, 0x0,
                    "Number of shifts needed to get the green value in a pixel to the least significant bit on the client", HFILL }
                },
                { &hf_vnc_client_blue_shift,
                  { "Blue shift", "vnc.client_blue_shift",
                    FT_UINT8, BASE_DEC, NULL, 0x0,
                    "Number of shifts needed to get the blue value in a pixel to the least significant bit on the client", HFILL }
                },

                /* Client Key Event */
                { &hf_vnc_key_down,
                  { "Key down", "vnc.key_down",
                    FT_BOOLEAN, BASE_NONE, TFS(&tfs_yes_no), 0x0,
                    "Specifies whether the key is being pressed or not", HFILL }
                },
                { &hf_vnc_key,
                  { "Key", "vnc.key",
                    FT_UINT32, BASE_HEX | BASE_EXT_STRING, &x11_keysym_vals_source_ext, 0x0, /* keysym_vals_source_exr is from packet-x11.c */
                    "Key being pressed/depressed", HFILL }
                },

                /* Client Pointer Event */
                { &hf_vnc_button_1_pos,
                  { "Mouse button #1 position", "vnc.button_1_pos",
                    FT_BOOLEAN, 8, TFS(&button_mask_tfs), 0x1,
                    "Whether mouse button #1 is being pressed or not", HFILL }
                },
                { &hf_vnc_button_2_pos,
                  { "Mouse button #2 position", "vnc.button_2_pos",
                    FT_BOOLEAN, 8, TFS(&button_mask_tfs), 0x2,
                    "Whether mouse button #2 is being pressed or not", HFILL }
                },
                { &hf_vnc_button_3_pos,
                  { "Mouse button #3 position", "vnc.button_3_pos",
                    FT_BOOLEAN, 8, TFS(&button_mask_tfs), 0x4,
                    "Whether mouse button #3 is being pressed or not", HFILL }
                },
                { &hf_vnc_button_4_pos,
                  { "Mouse button #4 position", "vnc.button_4_pos",
                    FT_BOOLEAN, 8, TFS(&button_mask_tfs), 0x8,
                    "Whether mouse button #4 is being pressed or not", HFILL }
                },
                { &hf_vnc_button_5_pos,
                  { "Mouse button #5 position", "vnc.button_5_pos",
                    FT_BOOLEAN, 8, TFS(&button_mask_tfs), 0x10,
                    "Whether mouse button #5 is being pressed or not", HFILL }
                },
                { &hf_vnc_button_6_pos,
                  { "Mouse button #6 position", "vnc.button_6_pos",
                    FT_BOOLEAN, 8, TFS(&button_mask_tfs), 0x20,
                    "Whether mouse button #6 is being pressed or not", HFILL }
                },
                { &hf_vnc_button_7_pos,
                  { "Mouse button #7 position", "vnc.button_7_pos",
                    FT_BOOLEAN, 8, TFS(&button_mask_tfs), 0x40,
                    "Whether mouse button #7 is being pressed or not", HFILL }
                },
                { &hf_vnc_button_8_pos,
                  { "Mouse button #8 position", "vnc.button_8_pos",
                    FT_BOOLEAN, 8, TFS(&button_mask_tfs), 0x80,
                    "Whether mouse button #8 is being pressed or not", HFILL }
                },
                { &hf_vnc_pointer_x_pos,
                  { "X position", "vnc.pointer_x_pos",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Position of mouse cursor on the x-axis", HFILL }
                },
                { &hf_vnc_pointer_y_pos,
                  { "Y position", "vnc.pointer_y_pos",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Position of mouse cursor on the y-axis", HFILL }
                },
                { &hf_vnc_encoding_num,
                  { "Number of encodings", "vnc.client_set_encodings_num",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Number of encodings used to send pixel data from server to client", HFILL }
                },
                { &hf_vnc_client_set_encodings_encoding_type,
                  { "Encoding type", "vnc.client_set_encodings_encoding_type",
                    FT_INT32, BASE_DEC, VALS(encoding_types_vs), 0x0,
                    "Type of encoding used to send pixel data from server to client", HFILL }
                },

                /* Client Framebuffer Update Request */
                { &hf_vnc_update_req_incremental,
                  { "Incremental update", "vnc.update_req_incremental",
                    FT_BOOLEAN, BASE_NONE, NULL, 0x0,
                    "Specifies if the client wants an incremental update instead of a full one", HFILL }
                },
                { &hf_vnc_update_req_x_pos,
                  { "X position", "vnc.update_req_x_pos",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "X position of framebuffer (screen) update requested", HFILL }
                },
                { &hf_vnc_update_req_y_pos,
                  { "Y position", "vnc.update_req_y_pos",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Y position of framebuffer (screen) update request", HFILL }
                },
                { &hf_vnc_update_req_width,
                  { "Width", "vnc.update_req_width",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Width of framebuffer (screen) update request", HFILL }
                },
                { &hf_vnc_update_req_height,
                  { "Height", "vnc.update_req_height",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Height of framebuffer (screen) update request", HFILL }
                },
                { &hf_vnc_client_cut_text_len,
                  { "Length", "vnc.client_cut_text_len",
                    FT_UINT32, BASE_DEC, NULL, 0x0,
                    "Length of client's copy/cut text (clipboard) string in bytes", HFILL }
                },
                { &hf_vnc_client_cut_text,
                  { "Text", "vnc.client_cut_text",
                    FT_STRING, BASE_NONE, NULL, 0x0,
                    "Text string in the client's copy/cut text (clipboard)", HFILL }
                },


                /********** Server Message Types **********/
                { &hf_vnc_server_message_type,
                  { "Server Message Type", "vnc.server_message_type",
                    FT_UINT8, BASE_DEC, VALS(vnc_server_message_types_vs), 0x0,
                    "Message type from server", HFILL }
                },

                { &hf_vnc_rectangle_num,
                  { "Number of rectangles", "vnc.fb_update_num_rects",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Number of rectangles of this server framebuffer update", HFILL }
                },

                { &hf_vnc_fb_update_x_pos,
                  { "X position", "vnc.fb_update_x_pos",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "X position of this server framebuffer update", HFILL }
                },

                { &hf_vnc_fb_update_y_pos,
                  { "Y position", "vnc.fb_update_y_pos",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Y position of this server framebuffer update", HFILL }
                },

                { &hf_vnc_fb_update_width,
                  { "Width", "vnc.fb_update_width",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Width of this server framebuffer update", HFILL }
                },

                { &hf_vnc_fb_update_height,
                  { "Height", "vnc.fb_update_height",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Height of this server framebuffer update", HFILL }
                },

                { &hf_vnc_fb_update_encoding_type,
                  { "Encoding type", "vnc.fb_update_encoding_type",
                    FT_INT32, BASE_DEC, VALS(encoding_types_vs), 0x0,
                    "Encoding type of this server framebuffer update", HFILL }
                },

                /* Cursor encoding */
                { &hf_vnc_cursor_x_fore_back,
                  { "X Cursor foreground RGB / background RGB", "vnc.cursor_x_fore_back",
                    FT_BYTES, BASE_NONE, NULL, 0x0,
                    "RGB values for the X cursor's foreground and background", HFILL }
                },

                { &hf_vnc_cursor_encoding_pixels,
                  { "Cursor encoding pixels", "vnc.cursor_encoding_pixels",
                    FT_BYTES, BASE_NONE, NULL, 0x0,
                    "Cursor encoding pixel data", HFILL }
                },

                { &hf_vnc_cursor_encoding_bitmask,
                  { "Cursor encoding bitmask", "vnc.cursor_encoding_bitmask",
                    FT_BYTES, BASE_NONE, NULL, 0x0,
                    "Cursor encoding pixel bitmask", HFILL }
                },

                /* Raw Encoding */
                { &hf_vnc_raw_pixel_data,
                  { "Pixel data", "vnc.raw_pixel_data",
                    FT_BYTES, BASE_NONE, NULL, 0x0,
                    "Raw pixel data.", HFILL }
                },

                /* CopyRect Encoding*/
                { &hf_vnc_copyrect_src_x_pos,
                  { "Source x position", "vnc.copyrect_src_x_pos",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "X position of the rectangle to copy from", HFILL }
                },

                { &hf_vnc_copyrect_src_y_pos,
                  { "Source y position", "vnc.copyrect_src_y_pos",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Y position of the rectangle to copy from", HFILL }
                },

                /* RRE Encoding */
                { &hf_vnc_rre_num_subrects,
                  { "Number of subrectangles", "vnc.rre_num_subrects",
                    FT_UINT32, BASE_DEC, NULL, 0x0,
                    "Number of subrectangles contained in this encoding type", HFILL }
                },

                { &hf_vnc_rre_bg_pixel,
                  { "Background pixel value", "vnc.rre_bg_pixel",
                    FT_BYTES, BASE_NONE, NULL, 0x0,
                    NULL, HFILL }
                },

                { &hf_vnc_rre_subrect_pixel,
                  { "Pixel value", "vnc.rre_subrect_pixel",
                    FT_BYTES, BASE_NONE, NULL, 0x0,
                    "Subrectangle pixel value", HFILL }
                },

                { &hf_vnc_rre_subrect_x_pos,
                  { "X position", "vnc.rre_subrect_x_pos",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Position of this subrectangle on the x axis", HFILL }
                },

                { &hf_vnc_rre_subrect_y_pos,
                  { "Y position", "vnc.rre_subrect_y_pos",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Position of this subrectangle on the y axis", HFILL }
                },

                { &hf_vnc_rre_subrect_width,
                  { "Width", "vnc.rre_subrect_width",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Width of this subrectangle", HFILL }
                },

                { &hf_vnc_rre_subrect_height,
                  { "Height", "vnc.rre_subrect_height",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Height of this subrectangle", HFILL }
                },


                /* Hextile Encoding */
                { &hf_vnc_hextile_subencoding_mask,
                  { "Subencoding type", "vnc.hextile_subencoding",
                    FT_UINT8, BASE_DEC, NULL, 0x0,
                    "Hextile subencoding type.", HFILL }
                },

                { &hf_vnc_hextile_raw,
                  { "Raw", "vnc.hextile_raw",
                    FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x1,
                    "Raw subencoding is used in this tile", HFILL }
                },

                { &hf_vnc_hextile_raw_value,
                  { "Raw pixel values", "vnc.hextile_raw_value",
                    FT_BYTES, BASE_NONE, NULL, 0x0,
                    "Raw subencoding pixel values", HFILL }
                },

                { &hf_vnc_hextile_bg,
                  { "Background Specified", "vnc.hextile_bg",
                    FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x2,
                    "Background Specified subencoding is used in this tile", HFILL }
                },

                { &hf_vnc_hextile_bg_value,
                  { "Background pixel value", "vnc.hextile_bg_value",
                    FT_BYTES, BASE_NONE, NULL, 0x0,
                    "Background color for this tile", HFILL }
                },

                { &hf_vnc_hextile_fg,
                  { "Foreground Specified", "vnc.hextile_fg",
                    FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x4,
                    "Foreground Specified subencoding is used in this tile", HFILL }
                },

                { &hf_vnc_hextile_fg_value,
                  { "Foreground pixel value", "vnc.hextile_fg_value",
                    FT_BYTES, BASE_NONE, NULL, 0x0,
                    "Foreground color for this tile", HFILL }
                },

                { &hf_vnc_hextile_anysubrects,
                  { "Any Subrects", "vnc.hextile_anysubrects",
                    FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x8,
                    "Any subrects subencoding is used in this tile", HFILL }
                },

                { &hf_vnc_hextile_num_subrects,
                  { "Number of subrectangles", "vnc.hextile_num_subrects",
                    FT_UINT8, BASE_DEC, NULL, 0x0,
                    "Number of subrectangles that follow", HFILL }
                },

                { &hf_vnc_hextile_subrectscolored,
                  { "Subrects Colored", "vnc.hextile_subrectscolored",
                    FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x10,
                    "Subrects colored subencoding is used in this tile", HFILL }
                },

                { &hf_vnc_hextile_subrect_pixel_value,
                  { "Pixel value", "vnc.hextile_subrect_pixel_value",
                    FT_BYTES, BASE_NONE, NULL, 0x0,
                    "Pixel value of this subrectangle", HFILL }
                },

                { &hf_vnc_hextile_subrect_x_pos,
                  { "X position", "vnc.hextile_subrect_x_pos",
                    FT_UINT8, BASE_DEC, NULL, 0xF0, /* Top 4 bits */
                    "X position of this subrectangle", HFILL }
                },

                { &hf_vnc_hextile_subrect_y_pos,
                  { "Y position", "vnc.hextile_subrect_y_pos",
                    FT_UINT8, BASE_DEC, NULL, 0xF, /* Bottom 4 bits */
                    "Y position of this subrectangle", HFILL }
                },

                { &hf_vnc_hextile_subrect_width,
                  { "Width", "vnc.hextile_subrect_width",
                    FT_UINT8, BASE_DEC, NULL, 0xF0, /* Top 4 bits */
                    "Subrectangle width minus one", HFILL }
                },

                { &hf_vnc_hextile_subrect_height,
                  { "Height", "vnc.hextile_subrect_height",
                    FT_UINT8, BASE_DEC, NULL, 0xF, /* Bottom 4 bits */
                    "Subrectangle height minus one", HFILL }
                },


                /* ZRLE Encoding */
                { &hf_vnc_zrle_len,
                  { "ZRLE compressed length", "vnc.zrle_len",
                    FT_UINT32, BASE_DEC, NULL, 0x0,
                    "Length of compressed ZRLE data that follows", HFILL }
                },

                { &hf_vnc_zrle_subencoding,
                  { "Subencoding type", "vnc.zrle_subencoding",
                    FT_UINT8, BASE_DEC, NULL, 0x0,
                    "Subencoding type byte", HFILL }
                },

                { &hf_vnc_zrle_rle,
                  { "RLE", "vnc.zrle_rle",
                    FT_UINT8, BASE_DEC, VALS(yes_no_vs), 0x80, /* Upper bit */
                    "Specifies that data is run-length encoded", HFILL }
                },

                { &hf_vnc_zrle_palette_size,
                  { "Palette size", "vnc.zrle_palette_size",
                    FT_UINT8, BASE_DEC, NULL, 0x7F, /* Lower 7 bits */
                    NULL, HFILL }
                },

                { &hf_vnc_zrle_data,
                  { "ZRLE compressed data", "vnc.zrle_data",
                    FT_BYTES, BASE_NONE, NULL, 0x0,
                    "Compressed ZRLE data.  Compiling with zlib support will uncompress and dissect this data", HFILL }
                },

                { &hf_vnc_zrle_raw,
                  { "Pixel values", "vnc.zrle_raw",
                    FT_BYTES, BASE_NONE, NULL, 0x0,
                    "Raw pixel values for this tile", HFILL }
                },

                { &hf_vnc_zrle_palette,
                  { "Palette", "vnc.zrle_palette",
                    FT_BYTES, BASE_NONE, NULL, 0x0,
                    "Palette pixel values", HFILL }
                },

                /* Server Set Colormap Entries */
                { &hf_vnc_colormap_first_color,
                  { "First color", "vnc.colormap_first_color",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "First color that should be mapped to given RGB intensities", HFILL }
                },

                { &hf_vnc_color_groups,
                  { "Color groups", "vnc.color_groups",
                    FT_NONE, BASE_NONE, NULL, 0x0,
                    NULL, HFILL }
                },

                { &hf_vnc_colormap_num_colors,
                  { "Number of color groups", "vnc.colormap_groups",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Number of red/green/blue color groups", HFILL }
                },
                { &hf_vnc_colormap_red,
                  { "Red", "vnc.colormap_red",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Red intensity", HFILL }
                },
                { &hf_vnc_colormap_green,
                  { "Green", "vnc.colormap_green",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Green intensity", HFILL }
                },
                { &hf_vnc_colormap_blue,
                  { "Blue", "vnc.colormap_blue",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Blue intensity", HFILL }
                },

                /* Server Cut Text */
                { &hf_vnc_server_cut_text_len,
                  { "Length", "vnc.server_cut_text_len",
                    FT_UINT32, BASE_DEC, NULL, 0x0,
                    "Length of server's copy/cut text (clipboard) string in bytes", HFILL }
                },
                { &hf_vnc_server_cut_text,
                  { "Text", "vnc.server_cut_text",
                    FT_STRING, BASE_NONE, NULL, 0x0,
                    "Text string in the server's copy/cut text (clipboard)", HFILL }
                },

                /* LibVNCServer additions */
                { &hf_vnc_supported_messages_client2server,
                  { "Client2server", "vnc.supported_messages_client2server",
                    FT_BYTES, BASE_NONE, NULL, 0x0,
                    "Supported client to server messages (bit flags)", HFILL }
                },
                { &hf_vnc_supported_messages_server2client,
                  { "Server2client", "vnc.supported_messages_server2client",
                    FT_BYTES, BASE_NONE, NULL, 0x0,
                    "Supported server to client messages (bit flags)", HFILL }
                },
                { &hf_vnc_num_supported_encodings,
                  { "Number of supported encodings", "vnc.num_supported_encodings",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    NULL, HFILL }
                },
                { &hf_vnc_supported_encodings,
                  { "Encoding", "vnc.supported_encodings",
                    FT_UINT32, BASE_HEX, NULL, 0x0,
                    "Supported encoding", HFILL }
                },
                { &hf_vnc_server_identity,
                  { "Server Identity", "vnc.server_identity",
                    FT_STRING, BASE_NONE, NULL, 0x0,
                    "Server identity string", HFILL }
                },

                /* MirrorLink */
                { &hf_vnc_mirrorlink_type,
                  { "Type", "vnc.mirrorlink_type",
                    FT_UINT8, BASE_DEC, VALS(vnc_mirrorlink_types_vs), 0x0,
                    "MirrorLink extension message type", HFILL }
                },
                { &hf_vnc_mirrorlink_length,
                  { "Length", "vnc.mirrorlink_length",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Payload length", HFILL }
                },
                { &hf_vnc_mirrorlink_version_major,
                  { "Major Version", "vnc.mirrorlink_version_major",
                    FT_UINT8, BASE_DEC, NULL, 0x0,
                    "MirrorLink major version", HFILL }
                },
                { &hf_vnc_mirrorlink_version_minor,
                  { "Minor Version", "vnc.mirrorlink_version_minor",
                    FT_UINT8, BASE_DEC, NULL, 0x0,
                    "MirrorLink minor version", HFILL }
                },
                { &hf_vnc_mirrorlink_framebuffer_configuration,
                  { "Configuration",
                    "vnc.mirrorlink_framebuffer_configuration",
                    FT_UINT16, BASE_HEX, NULL, 0x0,
                    "Framebuffer configuration", HFILL }
                },
                { &hf_vnc_mirrorlink_pixel_width,
                  { "Pixel Width", "vnc.mirrorlink_pixel_width",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Display width [pixel]", HFILL }
                },
                { &hf_vnc_mirrorlink_pixel_height,
                  { "Pixel Height", "vnc.mirrorlink_pixel_height",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Display height [pixel]", HFILL }
                },
                { &hf_vnc_mirrorlink_pixel_format,
                  { "Pixel Format", "vnc.mirrorlink_pixel_format",
                    FT_UINT16, BASE_HEX, NULL, 0x0,
                    "Pixel format support", HFILL }
                },
                { &hf_vnc_mirrorlink_display_width,
                  { "Display Width", "vnc.mirrorlink_display_width",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Display width [mm]", HFILL }
                },
                { &hf_vnc_mirrorlink_display_height,
                  { "Display Height", "vnc.mirrorlink_display_height",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Display height [mm]", HFILL }
                },
                { &hf_vnc_mirrorlink_display_distance,
                  { "Display Distance", "vnc.mirrorlink_display_distance",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Display distance [mm]", HFILL }
                },
                { &hf_vnc_mirrorlink_keyboard_language,
                  { "Keyboard Language", "vnc.mirrorlink_keyboard_language",
                    FT_STRING, BASE_NONE, NULL, 0x0,
                    "Keyboard layout - Language code (according ISO 639-1)",
                    HFILL }
                },
                { &hf_vnc_mirrorlink_keyboard_country,
                  { "Keyboard Country", "vnc.mirrorlink_keyboard_country",
                    FT_STRING, BASE_NONE, NULL, 0x0,
                    "Keyboard layout - Country code (according ISO 3166-1 alpha-2)",
                    HFILL }
                },
                { &hf_vnc_mirrorlink_ui_language,
                  { "UI Language", "vnc.mirrorlink_ui_language",
                    FT_STRING, BASE_NONE, NULL, 0x0,
                    "UI language - Language code (according ISO 639-1)", HFILL }
                },
                { &hf_vnc_mirrorlink_ui_country,
                  { "UI Country", "vnc.mirrorlink_ui_country",
                    FT_STRING, BASE_NONE, NULL, 0x0,
                    "UI language - Country code (according ISO 3166-1 alpha 2)",
                    HFILL }
                },
                { &hf_vnc_mirrorlink_knob_keys,
                  { "Knob Keys", "vnc.mirrorlink_knob_keys",
                    FT_UINT32, BASE_HEX, NULL, 0x0,
                    "Supported knob keys", HFILL }
                },
                { &hf_vnc_mirrorlink_device_keys,
                  { "Device Keys", "vnc.mirrorlink_device_keys",
                    FT_UINT32, BASE_HEX, NULL, 0x0,
                    "Supported device keys", HFILL }
                },
                { &hf_vnc_mirrorlink_multimedia_keys,
                  { "Multimedia Keys", "vnc.mirrorlink_multimedia_keys",
                    FT_UINT32, BASE_HEX, NULL, 0x0,
                    "Supported multimedia keys", HFILL }
                },
                { &hf_vnc_mirrorlink_key_related,
                  { "Keyboard", "vnc.mirrorlink_key_related",
                    FT_UINT32, BASE_HEX, NULL, 0x0,
                    "Keyboard related", HFILL }
                },
                { &hf_vnc_mirrorlink_pointer_related,
                  { "Pointer", "vnc.mirrorlink_pointer_related",
                    FT_UINT32, BASE_HEX, NULL, 0x0,
                    "Pointer related", HFILL }
                },
                { &hf_vnc_mirrorlink_key_symbol_value_client,
                  { "Client KeySymValue",
                    "vnc.mirrorlink_key_symbol_value_client",
                    FT_UINT32, BASE_HEX, NULL, 0x0,
                    "Client key symbol value", HFILL }
                },
                { &hf_vnc_mirrorlink_key_symbol_value_server,
                  { "Server KeySymValue",
                    "vnc.mirrorlink_key_symbol_value_server",
                    FT_UINT32, BASE_HEX, NULL, 0x0,
                    "Server key symbol value", HFILL }
                },
                { &hf_vnc_mirrorlink_key_configuration,
                  { "Configuration", "vnc.mirrorlink_key_configuration",
                    FT_UINT8, BASE_HEX, NULL, 0x0,
                    "Key event listing configuration", HFILL }
                },
                { &hf_vnc_mirrorlink_key_num_events,
                  { "Number of Key Events", "vnc.mirrorlink_key_num_events",
                    FT_UINT8, BASE_DEC, NULL, 0x0,
                    "Number of key events in list", HFILL }
                },
                { &hf_vnc_mirrorlink_key_event_counter,
                  { "Key Event Counter", "vnc.mirrorlink_key_event_counter",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Key event listing counter", HFILL }
                },
                { &hf_vnc_mirrorlink_key_symbol_value,
                  { "KeySymValue",
                    "vnc.mirrorlink_key_symbol_value",
                    FT_UINT32, BASE_HEX, NULL, 0x0,
                    "Key symbol value", HFILL }
                },
                { &hf_vnc_mirrorlink_key_request_configuration,
                  { "Configuration", "vnc.mirrorlink_key_request_configuration",
                    FT_UINT32, BASE_HEX, NULL, 0x0,
                    "Key event listing request configuration", HFILL }
                },
                { &hf_vnc_mirrorlink_keyboard_configuration,
                  { "Configuration", "vnc.mirrorlink_keyboard_configuration",
                    FT_UINT32, BASE_HEX, NULL, 0x0,
                    "Virtual keyboard configuration", HFILL }
                },
                { &hf_vnc_mirrorlink_cursor_x,
                  { "Cursor X", "vnc.mirrorlink_cursor_x",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Cursor - X position", HFILL }
                },
                { &hf_vnc_mirrorlink_cursor_y,
                  { "Cursor Y", "vnc.mirrorlink_cursor_y",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Cursor - Y position", HFILL }
                },
                { &hf_vnc_mirrorlink_text_x,
                  { "Text X", "vnc.mirrorlink_text_x",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Text input area - X position", HFILL }
                },
                { &hf_vnc_mirrorlink_text_y,
                  { "Text Y", "vnc.mirrorlink_text_y",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Text input area - Y position", HFILL }
                },
                { &hf_vnc_mirrorlink_text_width,
                  { "Text Width", "vnc.mirrorlink_text_width",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Text input area - Width", HFILL }
                },
                { &hf_vnc_mirrorlink_text_height,
                  { "Text Height", "vnc.mirrorlink_text_height",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Text input area - Height", HFILL }
                },
                { &hf_vnc_mirrorlink_keyboard_request_configuration,
                  { "Configuration",
                    "vnc.mirrorlink_keyboard_request_configuration",
                    FT_UINT32, BASE_HEX, NULL, 0x0,
                    "Virtual keyboard request configuration", HFILL }
                },
                { &hf_vnc_mirrorlink_device_status,
                  { "Device Status", "vnc.mirrorlink_device_status",
                    FT_UINT32, BASE_HEX, NULL, 0x0,
                    "Status of Device Features", HFILL }
                },
                { &hf_vnc_mirrorlink_app_id,
                  { "App Id", "vnc.mirrorlink_app_id",
                    FT_UINT32, BASE_HEX, NULL, 0x0,
                    "Unique application id", HFILL }
                },
                { &hf_vnc_mirrorlink_fb_block_x,
                  { "Frambuffer X", "vnc.mirrorlink_fb_block_x",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Frambuffer blocking - X position", HFILL }
                },
                { &hf_vnc_mirrorlink_fb_block_y,
                  { "Frambuffer Y", "vnc.mirrorlink_fb_block_y",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Frambuffer blocking - Y position", HFILL }
                },
                { &hf_vnc_mirrorlink_fb_block_width,
                  { "Frambuffer Width", "vnc.mirrorlink_fb_block_width",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Frambuffer blocking - Width", HFILL }
                },
                { &hf_vnc_mirrorlink_fb_block_height,
                  { "Frambuffer Height", "vnc.mirrorlink_fb_block_height",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Frambuffer blocking - Height", HFILL }
                },
                { &hf_vnc_mirrorlink_fb_block_reason,
                  { "Reason", "vnc.mirrorlink_fb_block_reason",
                    FT_UINT16, BASE_HEX, NULL, 0x0,
                    "Reason for blocking", HFILL }
                },
                { &hf_vnc_mirrorlink_audio_block_reason,
                  { "Reason", "vnc.mirrorlink_audio_block_reason",
                    FT_UINT16, BASE_HEX, NULL, 0x0,
                    "Reason for blocking", HFILL }
                },
                { &hf_vnc_mirrorlink_touch_num_events,
                  { "Number of Touch Events", "vnc.mirrorlink_touch_num_events",
                    FT_UINT8, BASE_DEC, NULL, 0x0,
                    "Number of touch events in list", HFILL }
                },
                { &hf_vnc_mirrorlink_touch_x,
                  { "Touch X", "vnc.mirrorlink_touch_x",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Touch event - X position", HFILL }
                },
                { &hf_vnc_mirrorlink_touch_y,
                  { "Touch Y", "vnc.mirrorlink_touch_y",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Touch event - Y position", HFILL }
                },
                { &hf_vnc_mirrorlink_touch_id,
                  { "Touch Id", "vnc.mirrorlink_touch_id",
                    FT_UINT8, BASE_DEC, NULL, 0x0,
                    "Touch event - identifier", HFILL }
                },
                { &hf_vnc_mirrorlink_touch_pressure,
                  { "Touch Pressure", "vnc.mirrorlink_touch_pressure",
                    FT_UINT8, BASE_DEC, NULL, 0x0,
                    "Touch event - pressure value", HFILL }
                },
                { &hf_vnc_mirrorlink_text,
                  { "Text", "vnc.mirrorlink_text",
                    FT_STRING, BASE_NONE, NULL, 0x0,
                    "Textual information", HFILL }
                },
                { &hf_vnc_mirrorlink_text_length,
                  { "Length", "vnc.mirrorlink_text_length",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Length of textual information", HFILL }
                },
                { &hf_vnc_mirrorlink_text_max_length,
                  { "Max Length", "vnc.mirrorlink_text_max_length",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Maximum length of textual information", HFILL }
                },
                { &hf_vnc_mirrorlink_unknown,
                  { "Unknown", "vnc.mirrorlink_unknown",
                    FT_BYTES, BASE_NONE, NULL, 0x0,
                    "Unknown data", HFILL }
                },

                /* Fence */
                { &hf_vnc_fence_flags,
                  {"Fence flags", "vnc.fence_flags", FT_UINT32, BASE_HEX,
                   NULL, 0, NULL, HFILL}},

                { &hf_vnc_fence_request,
                  { "Fence_request", "vnc.fence_request",
                    FT_BOOLEAN, 32, NULL, VNC_FENCE_REQUEST,
                    NULL, HFILL }
                },
                { &hf_vnc_fence_sync_next,
                  { "Fence_sync_next", "vnc.fence_sync_next",
                    FT_BOOLEAN, 32, NULL, VNC_FENCE_SYNC_NEXT,
                    NULL, HFILL }
                },
                { &hf_vnc_fence_block_after,
                  { "Fence_block_after", "vnc.fence_block_after",
                    FT_BOOLEAN, 32, NULL, VNC_FENCE_BLOCK_AFTER,
                    NULL, HFILL }
                },
                { &hf_vnc_fence_block_before,
                  { "Fence block_before", "vnc.fence_block_before",
                    FT_BOOLEAN, 32, NULL, VNC_FENCE_BLOCK_BEFORE,
                    NULL, HFILL }
                },
                { &hf_vnc_fence_payload_length,
                  { "Fence payload length", "vnc.fence_payload_length",
                    FT_UINT8, BASE_DEC, NULL, 0x0,
                    NULL, HFILL }
                },
                { &hf_vnc_fence_payload,
                  { "Fence payload", "vnc.fence_payload",
                    FT_BYTES, BASE_NONE, NULL, 0x0,
                    NULL, HFILL }
                },

                /* Context Information */
                { &hf_vnc_context_information_app_id,
                  { "App Id", "vnc.context_information_app_id",
                    FT_UINT32, BASE_HEX, NULL, 0x0,
                    "Unique application id", HFILL }
                },
                { &hf_vnc_context_information_app_trust_level,
                  { "App Trust Level",
                    "vnc.context_information_app_trust_level",
                    FT_UINT16, BASE_HEX, NULL, 0x0,
                    "Trust Level for Application Category", HFILL }
                },
                { &hf_vnc_context_information_content_trust_level,
                  { "Content Trust Level",
                    "vnc.context_information_content_trust_level",
                    FT_UINT16, BASE_HEX, NULL, 0x0,
                    "Trust Level for Content Category", HFILL }
                },
                { &hf_vnc_context_information_app_category,
                  { "App Category", "vnc.context_information_app_category",
                    FT_UINT32, BASE_HEX, NULL, 0x0,
                    "Application Category", HFILL }
                },
                { &hf_vnc_context_information_content_category,
                  { "Content Category",
                    "vnc.context_information_content_category",
                    FT_UINT32, BASE_HEX, NULL, 0x0,
                    "Visual content category", HFILL }
                },
                { &hf_vnc_context_information_content_rules,
                  { "Content Rules", "vnc.context_information_content_rules",
                    FT_UINT32, BASE_HEX, NULL, 0x0,
                    "Visual content rules", HFILL }
                },

                /* Scan Line based Run-Length Encoding */
                { &hf_vnc_slrle_run_num,
                  { "Number of Runs", "vnc.slrle_run_num",
                    FT_UINT16, BASE_DEC, NULL, 0x0,
                    "Number of Runs within Line", HFILL }
                },
                { &hf_vnc_slrle_run_data,
                  { "Raw RLE data", "vnc.slrle_run_data",
                    FT_BYTES, BASE_NONE, NULL, 0x0,
                    "Raw Run-Length encoded data within Line", HFILL }
                },

                /* H.264 Encoding */
                { &hf_vnc_h264_slice_type,
                  { "Slice Type", "vnc.h264_slice_type",
                    FT_UINT32, BASE_DEC, VALS(vnc_h264_slice_types_vs), 0x0,
                    "Frame slice type", HFILL }
                },
                { &hf_vnc_h264_nbytes,
                  { "Number of Bytes", "vnc.h264_nbytes",
                    FT_UINT32, BASE_DEC, NULL, 0x0,
                    "Number of bytes within frame", HFILL }
                },
                { &hf_vnc_h264_width,
                  { "Width", "vnc.h264_width",
                    FT_UINT32, BASE_DEC, NULL, 0x0,
                    "Frame Width", HFILL }
                },
                { &hf_vnc_h264_height,
                  { "Height", "vnc.h264_height",
                    FT_UINT32, BASE_DEC, NULL, 0x0,
                    "Frame Height", HFILL }
                },
                { &hf_vnc_h264_data,
                  { "Data", "vnc.h264_data",
                    FT_BYTES, BASE_NONE, NULL, 0x0,
                    "Frame H.264 data", HFILL }
                },

        };

        /* Setup protocol subtree arrays */
        static gint *ett[] = {
                &ett_vnc,
                &ett_vnc_client_message_type,
                &ett_vnc_server_message_type,
                &ett_vnc_rect,
                &ett_vnc_encoding_type,
                &ett_vnc_rre_subrect,
                &ett_vnc_hextile_subencoding_mask,
                &ett_vnc_hextile_num_subrects,
                &ett_vnc_hextile_subrect,
                &ett_vnc_hextile_tile,
                &ett_vnc_zrle_subencoding,
                &ett_vnc_colormap_num_groups,
                &ett_vnc_desktop_screen,
                &ett_vnc_colormap_color_group,
                &ett_vnc_key_events,
                &ett_vnc_touch_events,
                &ett_vnc_slrle_subline,
                &ett_vnc_fence_flags
        };

        static ei_register_info ei[] = {
                { &ei_vnc_possible_gtk_vnc_bug, { "vnc.possible_gtk_vnc_bug", PI_MALFORMED, PI_ERROR, "NULL found in greeting. client -> server greeting must be 12 bytes (possible gtk-vnc bug)", EXPFILL }},
                { &ei_vnc_auth_code_mismatch, { "vnc.auth_code_mismatch", PI_PROTOCOL, PI_WARN, "Authentication code does not match vendor or signature", EXPFILL }},
                { &ei_vnc_unknown_tight_vnc_auth, { "vnc.unknown_tight_vnc_auth", PI_PROTOCOL, PI_ERROR, "Unknown TIGHT VNC authentication", EXPFILL }},
                { &ei_vnc_too_many_rectangles, { "vnc.too_many_rectangles", PI_MALFORMED, PI_ERROR, "Too many rectangles, aborting dissection", EXPFILL }},
                { &ei_vnc_too_many_sub_rectangles, { "vnc.too_many_sub_rectangles", PI_MALFORMED, PI_ERROR, "Too many sub-rectangles, aborting dissection", EXPFILL }},
                { &ei_vnc_invalid_encoding, { "vnc.invalid_encoding", PI_MALFORMED, PI_ERROR, "Invalid encoding", EXPFILL }},
                { &ei_vnc_too_many_colors, { "vnc.too_many_colors", PI_MALFORMED, PI_ERROR, "Too many colors, aborting dissection", EXPFILL }},
                { &ei_vnc_too_many_cut_text, { "vnc.too_many_cut_text", PI_MALFORMED, PI_ERROR, "Too much cut text, aborting dissection", EXPFILL }},
                { &ei_vnc_zrle_failed, { "vnc.zrle_failed", PI_UNDECODED, PI_ERROR, "Decompression of ZRLE data failed", EXPFILL }},
                { &ei_vnc_unknown_tight, { "vnc.unknown_tight_packet", PI_UNDECODED, PI_WARN, "Unknown packet (TightVNC)", EXPFILL }},
                { &ei_vnc_reassemble, { "vnc.reassemble", PI_REASSEMBLE, PI_CHAT, "See further on for dissection of the complete (reassembled) PDU", EXPFILL }},
        };

        /* Register the protocol name and description */
        proto_vnc = proto_register_protocol("Virtual Network Computing",
                                            "VNC", "vnc");

        /* Required function calls to register the header fields and subtrees */
        proto_register_field_array(proto_vnc, hf, array_length(hf));
        proto_register_subtree_array(ett, array_length(ett));
        expert_vnc = expert_register_protocol(proto_vnc);
        expert_register_field_array(expert_vnc, ei, array_length(ei));

        /* Register our preferences module */
        vnc_module = prefs_register_protocol(proto_vnc, proto_reg_handoff_vnc);

        prefs_register_bool_preference(vnc_module, "desegment",
                                       "Reassemble VNC messages spanning multiple TCP segments.",
                                       "Whether the VNC dissector should reassemble messages spanning "
                                       "multiple TCP segments.  To use this option, you must also enable "
                                       "\"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
                                       &vnc_preference_desegment);

        prefs_register_uint_preference(vnc_module, "alternate_port", "Alternate TCP port",
                                       "Decode this port's traffic as VNC in addition to the default ports (5500, 5501, 5900, 5901)",
                                       10, &vnc_preference_alternate_port);

}

void
proto_reg_handoff_vnc(void)
{
        static gboolean inited = FALSE;

        /* This is a behind the scenes variable that is not changed by the user.
         * This stores last setting of the vnc_preference_alternate_port.  Used to keep
         * track of when the user has changed the setting so that we can delete
         * and re-register with the new port number. */
        static guint vnc_preference_alternate_port_last = 0;

        if(!inited) {
                vnc_handle = create_dissector_handle(dissect_vnc, proto_vnc);

                dissector_add_uint("tcp.port", 5500, vnc_handle);
                dissector_add_uint("tcp.port", 5501, vnc_handle);
                dissector_add_uint("tcp.port", 5900, vnc_handle);
                dissector_add_uint("tcp.port", 5901, vnc_handle);

                heur_dissector_add("tcp", test_vnc_protocol, "VNC over TCP", "vnc_tcp", proto_vnc, HEURISTIC_ENABLE);
                /* We don't register a port for the VNC HTTP server because
                 * that simply provides a java program for download via the
                 * HTTP protocol.  The java program then connects to a standard
                 * VNC port. */

                inited = TRUE;
        } else {  /* only after preferences have been read/changed */
                if(vnc_preference_alternate_port != vnc_preference_alternate_port_last &&
                   vnc_preference_alternate_port != 5500 &&
                   vnc_preference_alternate_port != 5501 &&
                   vnc_preference_alternate_port != 5900 &&
                   vnc_preference_alternate_port != 5901) {
                        if (vnc_preference_alternate_port_last != 0) {
                                dissector_delete_uint("tcp.port",
                                                 vnc_preference_alternate_port_last,
                                                 vnc_handle);
                        }
                        /* Save this setting to see if has changed later */
                        vnc_preference_alternate_port_last =
                                vnc_preference_alternate_port;

                        /* Register the new port setting */
                        if (vnc_preference_alternate_port != 0) {
                                dissector_add_uint("tcp.port",
                                              vnc_preference_alternate_port,
                                              vnc_handle);
                        }
                }
        }
}

/*
 * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
 *
 * Local variables:
 * c-basic-offset: 8
 * tab-width: 8
 * indent-tabs-mode: t
 * End:
 *
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
 * :indentSize=8:tabSize=8:noTabs=false:
 */