Ethereal-dev: [Ethereal-dev] Need help with desegmenting VNC traffic over TCP.

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: Brad Hards <bhards@xxxxxxxxxxxxxx>
Date: Mon, 06 Oct 2003 22:15:19 +1000
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

OK, I'm confused again.
How do I desegment a message like the attached one?

My dissector (also attached) handles the packets up to packet 21 OK, but then 
I'm in trouble, because the packet only has four bytes, and they aren't 
enough to tell how many bytes are still to come. 

The only way I'm seeing at this stage is to use some kind of state attached to 
the conversation, but I'm not seeing exactly what that would entail.

I'm stuck right now - I'm happy to:
1. take advice on how to fix it
2. work with someone on fixing it
3. hand the whole thing over to someone capable of fixing it.

I'm out of my depth here - please be kind...

Brad
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.7 (GNU/Linux)

iD8DBQE/gVzXW6pHgIdAuOMRAgulAKCSXwcujwcSlvgH+MQ4G8nLNHqEtACgm1NE
BDM5KBf0s2uewnQuzSPQW3s=
=RDWm
-----END PGP SIGNATURE-----

Attachment: rfb-short.cap
Description: Binary data

/* packet-rfb.c
 * Routines for Remote Frame Buffer dissection
 * [ rough, protocol version 3.3 only ]
 * Copyright 2003, Brad Hards <bradh@xxxxxxxxxxxxx>
 *
 * $Id: packet-rsync.c,v 1.6 2003/07/08 04:06:26 tpot Exp $
 *
 * Ethereal - Network traffic analyser
 * By Gerald Combs <gerald@xxxxxxxxxxxx>
 * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>

#include <string.h>
#include <time.h>
#include <glib.h>

#include <epan/packet.h>
#include <epan/strutil.h>
#include <epan/conversation.h>

#include "prefs.h"


/* what states make sense here ? */
typedef enum _rfb_state {
  RFB_SERV_VER = 0,
  RFB_CLIENT_VER = 1,
  RFB_SERV_SECURITY = 2,
  RFB_CLIENT_SECURITY = 3,
  RFB_SECURITY_RESULT = 5,
  RFB_CLIENT_INIT = 6,
  RFB_SERVER_INIT = 7,
  RFB_DATA = 99
} rfb_state_t;

/* this is a guide to the current conversation state */
struct rfb_conversation_data {
    rfb_state_t   state;
    guint8        serv_ver; /* 3 for v3.3, 7 for v3.7 */
    guint8        client_ver; /* 3 for v3.3, 7 for v3.7 */
    guint32       auth_type; /* authentication type */
};

struct rfb_frame_data {
    rfb_state_t 	state;
};

static int proto_rfb = -1;

static int hf_rfb_proto_ver_server = -1;
static int hf_rfb_proto_ver_client = -1;
static int hf_rfb_security_server = -1;
static int hf_rfb_security_challenge = -1;
static int hf_rfb_security_response = -1;
static int hf_rfb_security_result = -1;
static int hf_rfb_client_init = -1;
static int hf_rfb_server_init = -1;
static int hf_rfb_server_init_framebufferwidth = -1;
static int hf_rfb_server_init_framebufferheight = -1;
static int hf_rfb_server_init_name_length = -1;
static int hf_rfb_server_init_name_string = -1;
static int hf_rfb_pixelformat = -1;
static int hf_rfb_pixelformat_bpp = -1;
static int hf_rfb_pixelformat_depth = -1;
static int hf_rfb_pixelformat_bigendian = -1;
static int hf_rfb_pixelformat_truecolour = -1;
static int hf_rfb_pixelformat_redmax = -1;
static int hf_rfb_pixelformat_greenmax = -1;
static int hf_rfb_pixelformat_bluemax = -1;
static int hf_rfb_pixelformat_redshift = -1;
static int hf_rfb_pixelformat_greenshift = -1;
static int hf_rfb_pixelformat_blueshift = -1;
static int hf_rfb_clientdata_messagetype = -1;
static int hf_rfb_clientdata_setencodings_number = -1;
static int hf_rfb_clientdata_setencodings_encodingtype = -1;
static int hf_rfb_clientdata_framebufferupdaterequest_incremental = -1;
static int hf_rfb_clientdata_framebufferupdaterequest_xposition = -1;
static int hf_rfb_clientdata_framebufferupdaterequest_yposition = -1;
static int hf_rfb_clientdata_framebufferupdaterequest_width = -1;
static int hf_rfb_clientdata_framebufferupdaterequest_height = -1;
static int hf_rfb_clientdata_pointerevent_buttonmask = -1;
static int hf_rfb_clientdata_pointerevent_xposition = -1;
static int hf_rfb_clientdata_pointerevent_yposition = -1;
static int hf_rfb_serverdata_messagetype = -1;
static int hf_rfb_serverdata_framebufferupdate_numrectangles = -1;
static int hf_rfb_serverdata_framebufferupdate_xposition = -1;
static int hf_rfb_serverdata_framebufferupdate_yposition = -1;
static int hf_rfb_serverdata_framebufferupdate_width = -1;
static int hf_rfb_serverdata_framebufferupdate_height = -1;
static int hf_rfb_serverdata_framebufferupdate_encodingtype = -1;

static const value_string rfb_security_type[] = {
	{ 0x00000000, "Connection failed" },
	{ 0x00000001, "No authentication" },
	{ 0x00000002, "VNC authentication" },
	{ 0, NULL }
};

static const value_string rfb_security_result[] = {
	{ 0x00000000, "Authentication OK" },
	{ 0x00000001, "Authentication failed" },
	{ 0x00000002, "Too many" },
	{ 0, NULL }
};

static const value_string rfb_client_messagetype[] = {
	{ 0x00, "SetPixelFormat" },
	{ 0x01, "FixColourMapEntries - no longer exists" },
	{ 0x02, "SetEncodings" },
	{ 0x03, "FramebufferUpdateRequest" },
	{ 0x04, "KeyEvent" },
	{ 0x05, "PointerEvent" },
	{ 0x06, "ClientCutText" },
	{ 0, NULL }
};

static const value_string rfb_server_messagetype[] = {
	{ 0x00, "FramebufferUpdate" },
	{ 0x01, "SetColourMapEntries" },
	{ 0x02, "Bell" },
	{ 0x03, "ServerCutText" },
	{ 0, NULL }
};

static const value_string rfb_encoding_type[] = {
	{ 0x00000000, "Raw" },
	{ 0x00000001, "CopyRect" },
	{ 0x00000002, "RRE" },
	{ 0x00000004, "CoRRE" },
	{ 0x00000005, "Hextile" },
	{ 0x00000006, "zlib" },
	{ 0x00000007, "tight" },
	{ 0x00000008, "zlibhex" },
	{ 0x00000010, "ZRLE" },
	{ 0xffffff00, "tight option" },
	{ 0xffffff01, "tight option" },
	{ 0xffffff02, "tight option" },
	{ 0xffffff03, "tight option" },
	{ 0xffffff04, "tight option" },
	{ 0xffffff05, "tight option" },
	{ 0xffffff06, "tight option" },
	{ 0xffffff07, "tight option" },
	{ 0xffffff08, "tight option" },
	{ 0xffffff09, "tight option" },
	{ 0xffffff0a, "tight option" },
	{ 0xffffff0b, "tight option" },
	{ 0xffffff0c, "tight option" },
	{ 0xffffff0d, "tight option" },
	{ 0xffffff0e, "tight option" },
	{ 0xffffff0f, "tight option" },
	{ 0xffffff10, "tight option" },
	{ 0xffffff12, "tight option" },
	{ 0xffffff13, "tight option" },
	{ 0xffffff14, "tight option" },
	{ 0xffffff15, "tight option" },
	{ 0xffffff16, "tight option" },
	{ 0xffffff17, "tight option" },
	{ 0xffffff18, "tight option" },
	{ 0xffffff19, "tight option" },
	{ 0xffffff1a, "tight option" },
	{ 0xffffff1b, "tight option" },
	{ 0xffffff1c, "tight option" },
	{ 0xffffff1d, "tight option" },
	{ 0xffffff1e, "tight option" },
	{ 0xffffff1f, "tight option" },
	{ 0xffffff20, "tight option" },
	/* there are some more in here, not seen though */
	{ 0xffffff11, "Cursor pseudo-encoding" },
	{ 0xffffff21, "DesktopSize pseudo-encoding" },
	{ 0, NULL }
};

static gint ett_rfb = -1;
static gint ett_rfb_serv_init = -1;

dissector_handle_t rfb_handle;

static dissector_handle_t data_handle;

static gboolean rfb_desegment = TRUE;

#define TCP_PORT_RFB	5900

static unsigned int glb_rfb_tcp_port = TCP_PORT_RFB;

static void
dissect_framebuffer_update(tvbuff_t *tvb, int offset, proto_tree *rfb_tree, packet_info *pinfo)
{
    guint16                             num_rectangles;
    int                                 yalv;

    offset +=2; /* skip padding */
    proto_tree_add_item(rfb_tree, hf_rfb_serverdata_framebufferupdate_numrectangles, tvb, offset, 2, FALSE);
    num_rectangles = tvb_get_ntohs(tvb, offset);
    offset +=2; 
    for (yalv = 0; yalv < num_rectangles; yalv++) {
      /* need re-assembly by here - we need at least at least the next 12 bytes to 
	 be able to figure out the total length of data in this rectangle */
	proto_tree_add_item(rfb_tree, hf_rfb_serverdata_framebufferupdate_xposition, tvb, offset, 2, FALSE);
	offset +=2; 
	proto_tree_add_item(rfb_tree, hf_rfb_serverdata_framebufferupdate_yposition, tvb, offset, 2, FALSE);
	offset +=2; 
	proto_tree_add_item(rfb_tree, hf_rfb_serverdata_framebufferupdate_width, tvb, offset, 2, FALSE);
	offset +=2;
	proto_tree_add_item(rfb_tree, hf_rfb_serverdata_framebufferupdate_height, tvb, offset, 2, FALSE);
	offset +=2;
	proto_tree_add_item(rfb_tree, hf_rfb_serverdata_framebufferupdate_encodingtype, tvb, offset, 4, FALSE);
	offset +=4;
	switch (tvb_get_ntohl(tvb, offset)) {
	case 0: /* Raw encoding */
	    if (check_col(pinfo->cinfo, COL_INFO)) {
		col_append_fstr(pinfo->cinfo, COL_INFO, " Raw encoding");
	    }		
	    break;
	case 1: /* CopyRect encoding */
	    if (check_col(pinfo->cinfo, COL_INFO)) {
		col_append_fstr(pinfo->cinfo, COL_INFO, " CopyRect encoding");
	    }		
	    break;
	case 2: /* RRE encoding */
	    if (check_col(pinfo->cinfo, COL_INFO)) {
		col_append_fstr(pinfo->cinfo, COL_INFO, " RRE encoding");
	    }		
	    break;
	case 4: /* CoRRE encoding */
	    if (check_col(pinfo->cinfo, COL_INFO)) {
		col_append_fstr(pinfo->cinfo, COL_INFO, " CoRRE encoding");
	    }		
	    break;
	case 5: /* Hextile encoding */
	    if (check_col(pinfo->cinfo, COL_INFO)) {
		col_append_fstr(pinfo->cinfo, COL_INFO, " Hextile encoding");
	    }		
	    break;
	case 16: /* ZRLE encoding */
	    if (check_col(pinfo->cinfo, COL_INFO)) {
		col_append_fstr(pinfo->cinfo, COL_INFO, " ZRLE encoding");
	    }		
	    break;
	default:
	    if (check_col(pinfo->cinfo, COL_INFO)) {
		col_append_fstr(pinfo->cinfo, COL_INFO, " unknown encoding");
	    }		
	    break;
	}
    }
}

static void
dissect_pixelformat(tvbuff_t *tvb, int offset, proto_tree *tree)
{
    proto_item         *tf;
    proto_tree         *pixelformat_tree;

    tf = proto_tree_add_item(tree, hf_rfb_pixelformat, tvb, offset, 16, TRUE);
    pixelformat_tree = proto_item_add_subtree(tf, ett_rfb_serv_init);

    proto_tree_add_item(pixelformat_tree, hf_rfb_pixelformat_bpp, tvb, offset, 1, FALSE);
    offset += 1;
    proto_tree_add_item(pixelformat_tree, hf_rfb_pixelformat_depth, tvb, offset, 1, FALSE);
    offset += 1;
    proto_tree_add_item(pixelformat_tree, hf_rfb_pixelformat_bigendian, tvb, offset, 1, FALSE);
    offset += 1;
    proto_tree_add_item(pixelformat_tree, hf_rfb_pixelformat_truecolour, tvb, offset, 1, FALSE);
    offset += 1;
    proto_tree_add_item(pixelformat_tree, hf_rfb_pixelformat_redmax, tvb, offset, 2, FALSE);
    offset += 2;
    proto_tree_add_item(pixelformat_tree, hf_rfb_pixelformat_greenmax, tvb, offset, 2, FALSE);
    offset += 2;
    proto_tree_add_item(pixelformat_tree, hf_rfb_pixelformat_bluemax, tvb, offset, 2, FALSE);
    offset += 2;
    proto_tree_add_item(pixelformat_tree, hf_rfb_pixelformat_redshift, tvb, offset, 1, FALSE);
    offset += 1;
    proto_tree_add_item(pixelformat_tree, hf_rfb_pixelformat_greenshift, tvb, offset, 1, FALSE);
    offset += 1;
    proto_tree_add_item(pixelformat_tree, hf_rfb_pixelformat_blueshift, tvb, offset, 1, FALSE);
    offset += 1;
}

/* Packet dissection routine called by tcp (& udp) when port 5900 detected */
static void
dissect_rfb_encap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
    conversation_t			*conversation;
    struct rfb_conversation_data	*conversation_data;
    struct rfb_frame_data		*frame_data;
    proto_item				*ti, *tf;
    proto_tree				*rfb_tree, *rfb_servinit_tree;
    int					offset = 0;
    gchar				version[13];
    guint32                             name_length;
    guint16                             num_encodings;
    int                                 yalv;

    if (check_col(pinfo->cinfo, COL_PROTOCOL))
        col_set_str(pinfo->cinfo, COL_PROTOCOL, "RFB");

    if (check_col(pinfo->cinfo, COL_INFO))
        col_clear(pinfo->cinfo, COL_INFO);

    conversation = find_conversation(&pinfo->src, &pinfo->dst, pinfo->ptype,
				     pinfo->srcport, pinfo->destport, 0);
    if (conversation == NULL) {
	conversation = conversation_new(&pinfo->src, &pinfo->dst,
					pinfo->ptype, pinfo->srcport,
					pinfo->destport, 0);
    }

    conversation_data = conversation_get_proto_data(conversation, proto_rfb);

    if (conversation_data == NULL) {
	conversation_data = g_malloc(sizeof(struct rfb_conversation_data));
	conversation_data->state = RFB_SERV_VER;
	conversation_data->serv_ver = 0; /* if we ever find 0, something is wrong */
	conversation_data->client_ver = 0; /* if we ever find 0, something is wroong */
	conversation_add_proto_data(conversation, proto_rfb, conversation_data);
    }

    conversation_set_dissector(conversation, rfb_handle);
   
    ti = proto_tree_add_item(tree, proto_rfb, tvb, 0, -1, FALSE);

    rfb_tree = proto_item_add_subtree(ti, ett_rfb);

    frame_data = p_get_proto_data(pinfo->fd, proto_rfb);
    if (!frame_data) {
	/* then we haven't seen this frame before */
	frame_data = g_malloc(sizeof(struct rfb_frame_data));
	frame_data->state = conversation_data->state;
	p_add_proto_data(pinfo->fd, proto_rfb, frame_data);
    }

    switch (frame_data->state) {
    case RFB_SERV_VER:
	proto_tree_add_item(rfb_tree, hf_rfb_proto_ver_server, tvb, offset, -1, TRUE);
	/* We drop off one byte, to ensure that the \n doesn't mess up the display */
	tvb_get_nstringz0(tvb, offset, sizeof(version)-1, version);
	/* The 11'th byte changes between RFB 3.3 and RFB 3.7. */
	conversation_data->serv_ver = tvb_get_guint8(tvb, 10) - 0x30;

        if (check_col(pinfo->cinfo, COL_INFO)) {
            col_append_fstr(pinfo->cinfo, COL_INFO, "Server Version %s", version);
	}

	conversation_data->state = RFB_CLIENT_VER;
        conversation_add_proto_data(conversation, proto_rfb, conversation_data);

	break;
    case RFB_CLIENT_VER:
	proto_tree_add_item(rfb_tree, hf_rfb_proto_ver_client, tvb, offset, -1, TRUE);
	/* We drop off one byte, to ensure that the \n doesn't mess up the display */
	tvb_get_nstringz0(tvb, offset, sizeof(version)-1, version);
	/* The 11'th byte changes between RFB 3.3 and RFB 3.7. */
	conversation_data->client_ver = tvb_get_guint8(tvb, 10) - 0x30;

        if (check_col(pinfo->cinfo, COL_INFO)) {
            col_append_fstr(pinfo->cinfo, COL_INFO, "Client Version %s", version);
	}

	conversation_data->state = RFB_SERV_SECURITY;
        conversation_add_proto_data(conversation, proto_rfb, conversation_data);

	break;

    case RFB_SERV_SECURITY:
	if ((3 == conversation_data->serv_ver) || (3 == conversation_data->client_ver)) {
	    /* then we're using RFB version 3.3 */
	    proto_tree_add_item(rfb_tree, hf_rfb_security_server, tvb, offset, 4, FALSE);
	    conversation_data->auth_type = tvb_get_ntohl(tvb, 0);
	    if (0x02 == conversation_data->auth_type) {
		/* we're doing VNC challenge-response */
		if (check_col(pinfo->cinfo, COL_INFO)) {
		    col_append_fstr(pinfo->cinfo, COL_INFO, "VNC Challenge");
		}
		offset += 4;
		proto_tree_add_item(rfb_tree, hf_rfb_security_challenge, tvb, offset, 16, TRUE);
		conversation_data->state = RFB_CLIENT_SECURITY;
	    } else {
		/* we're doing no authentication */
		if (check_col(pinfo->cinfo, COL_INFO)) {
		    col_append_fstr(pinfo->cinfo, COL_INFO, "No authentication");
		}
		conversation_data->state = RFB_CLIENT_INIT;
	    }
	} else if ((7 == conversation_data->serv_ver) || (7 == conversation_data->client_ver)) {
	    /* we're using RFB version 3.7 */
	    /* TODO */
	} /* else its broken, or the dissector is outdated */

        conversation_add_proto_data(conversation, proto_rfb, conversation_data);

	break;

    case RFB_CLIENT_SECURITY:
	/* we're using RFB with VNC Authentication */
	proto_tree_add_item(rfb_tree, hf_rfb_security_response, tvb, offset, 16, TRUE);
	if (check_col(pinfo->cinfo, COL_INFO)) {
	    col_append_fstr(pinfo->cinfo, COL_INFO, "VNC Response");
	}
	conversation_data->state = RFB_SECURITY_RESULT;
        conversation_add_proto_data(conversation, proto_rfb, conversation_data);

	break;

   case RFB_SECURITY_RESULT:
	/* we're using RFB with VNC Authentication */
	proto_tree_add_item(rfb_tree, hf_rfb_security_result, tvb, offset, 4, FALSE);
	if (check_col(pinfo->cinfo, COL_INFO)) {
	    col_append_fstr(pinfo->cinfo, COL_INFO, "VNC Result");
	}
	conversation_data->state = RFB_CLIENT_INIT;
        conversation_add_proto_data(conversation, proto_rfb, conversation_data);

	break;

    case RFB_CLIENT_INIT:
	proto_tree_add_item(rfb_tree, hf_rfb_client_init, tvb, offset, 1, FALSE);
	if (check_col(pinfo->cinfo, COL_INFO)) {
	    col_append_fstr(pinfo->cinfo, COL_INFO, "Client initialisation");
	}
	if (tvb_get_guint8(tvb, 0)) {
	    if (check_col(pinfo->cinfo, COL_INFO)) {
		col_append_fstr(pinfo->cinfo, COL_INFO, " (Share desktop)");
	    }
	} else {
	    if (check_col(pinfo->cinfo, COL_INFO)) {
		col_append_fstr(pinfo->cinfo, COL_INFO, " (Exclusive access)");
	    }
	}
	conversation_data->state = RFB_SERVER_INIT;
        conversation_add_proto_data(conversation, proto_rfb, conversation_data);
	break;

    case RFB_SERVER_INIT:
	tf = proto_tree_add_item(rfb_tree, hf_rfb_server_init, tvb, offset, -1, TRUE);
	rfb_servinit_tree = proto_item_add_subtree(tf, ett_rfb_serv_init);
	proto_tree_add_item(rfb_servinit_tree, hf_rfb_server_init_framebufferwidth, tvb, offset, 2, FALSE);
	offset += 2;
	proto_tree_add_item(rfb_servinit_tree, hf_rfb_server_init_framebufferheight, tvb, offset, 2, FALSE);
	offset += 2;
	dissect_pixelformat(tvb, offset, rfb_servinit_tree);
	offset += 16;
	proto_tree_add_item(rfb_servinit_tree, hf_rfb_server_init_name_length, tvb, offset, 4, FALSE);
	name_length = tvb_get_ntohl(tvb, offset);
	offset +=4;
	proto_tree_add_item(rfb_servinit_tree, hf_rfb_server_init_name_string, tvb, offset, name_length, FALSE);
	if (check_col(pinfo->cinfo, COL_INFO)) {
	    col_append_fstr(pinfo->cinfo, COL_INFO, "Server initialisation");
	}

	conversation_data->state = RFB_DATA;
        conversation_add_proto_data(conversation, proto_rfb, conversation_data);
	break;

    case RFB_DATA:
	/* this means we've negotiated, and are ready to transfer framebuffer info */
	if (glb_rfb_tcp_port == pinfo->destport) {
	    /* its a Client to Server message */
	    if (check_col(pinfo->cinfo, COL_INFO)) {
		col_append_fstr(pinfo->cinfo, COL_INFO, "Client->Server Data");
	    }
	    proto_tree_add_item(rfb_tree, hf_rfb_clientdata_messagetype, tvb, offset, 1, TRUE);
	    switch (tvb_get_guint8(tvb, 0)) {

	    case 0: /* SetPixelFormat - 6.2.1 */
		offset += 4;
		dissect_pixelformat(tvb, offset, rfb_tree);
		if (check_col(pinfo->cinfo, COL_INFO)) {
		    col_append_fstr(pinfo->cinfo, COL_INFO, " (SetPixelFormat)");
		}
		break;
		
	    case 2: /* SetEncodings - 6.2.3 */
		offset +=2;
		proto_tree_add_item(rfb_tree, hf_rfb_clientdata_setencodings_number, tvb, offset, 2, FALSE);
		num_encodings = tvb_get_ntohs(tvb, offset);
		offset +=2;
		for (yalv = 0; yalv < num_encodings; yalv++) {
		    proto_tree_add_item(rfb_tree, hf_rfb_clientdata_setencodings_encodingtype, tvb, offset, 4, FALSE);
		    offset +=4;
		}
		if (check_col(pinfo->cinfo, COL_INFO)) {
		    col_append_fstr(pinfo->cinfo, COL_INFO, " (SetEncodings)");
		}
		break;

	    case 3: /* FramebufferUpdateRequest - 6.2.4 */
		offset +=1;
		proto_tree_add_item(rfb_tree, hf_rfb_clientdata_framebufferupdaterequest_incremental, tvb, offset, 1, FALSE);
		offset +=1;
		proto_tree_add_item(rfb_tree, hf_rfb_clientdata_framebufferupdaterequest_xposition, tvb, offset, 2, FALSE);
		offset +=2;
		proto_tree_add_item(rfb_tree, hf_rfb_clientdata_framebufferupdaterequest_yposition, tvb, offset, 2, FALSE);
		offset +=2;
		proto_tree_add_item(rfb_tree, hf_rfb_clientdata_framebufferupdaterequest_width, tvb, offset, 2, FALSE);
		offset +=2;
		proto_tree_add_item(rfb_tree, hf_rfb_clientdata_framebufferupdaterequest_height, tvb, offset, 2, FALSE);
		if (check_col(pinfo->cinfo, COL_INFO)) {
		    col_append_fstr(pinfo->cinfo, COL_INFO, " (FramebufferUpdateRequest)");
		}
		break;

	    case 4: /* KeyEvent - 6.2.5 */
		if (check_col(pinfo->cinfo, COL_INFO)) {
		    col_append_fstr(pinfo->cinfo, COL_INFO, " (KeyEvent - FIXME)");
		}
		break;

	    case 5: /* PointerEvent - 6.2.6 */
		offset +=1;
		proto_tree_add_item(rfb_tree, hf_rfb_clientdata_pointerevent_buttonmask, tvb, offset, 1, FALSE);
		offset +=1;
		proto_tree_add_item(rfb_tree, hf_rfb_clientdata_pointerevent_xposition, tvb, offset, 2, FALSE);
		offset +=2;
		proto_tree_add_item(rfb_tree, hf_rfb_clientdata_pointerevent_yposition, tvb, offset, 2, FALSE);
		offset +=2;
		if (check_col(pinfo->cinfo, COL_INFO)) {
		    col_append_fstr(pinfo->cinfo, COL_INFO, " (PointerEvent)");
		}
		break;

	    case 6: /* ClientCutText - 6.2.7 */
		if (check_col(pinfo->cinfo, COL_INFO)) {
		    col_append_fstr(pinfo->cinfo, COL_INFO, " (ClientCutText - FIXME)");
		}		
		break;
	    }
	} else {
	    if (check_col(pinfo->cinfo, COL_INFO)) {
		col_append_fstr(pinfo->cinfo, COL_INFO, "Server->Client Data");
	    }
	    proto_tree_add_item(rfb_tree, hf_rfb_serverdata_messagetype, tvb, offset, 1, TRUE);
	    switch (tvb_get_guint8(tvb, 0)) {
	    case 0: /* FramebufferUpdate - 6.3.1 */
		if (check_col(pinfo->cinfo, COL_INFO)) {
		    col_append_fstr(pinfo->cinfo, COL_INFO, " (FramebufferUpdate - FIXME)");
		}		
		dissect_framebuffer_update(tvb, offset, rfb_tree, pinfo);
		break;
	    case 1: /* SetColourMapEntries - 6.3.2 */
		if (check_col(pinfo->cinfo, COL_INFO)) {
		    col_append_fstr(pinfo->cinfo, COL_INFO, " (SetColourMapEntries - FIXME)");
		}		
		break;
	    case 2: /* Bell - 6.3.3 */
		if (check_col(pinfo->cinfo, COL_INFO)) {
		    col_append_fstr(pinfo->cinfo, COL_INFO, " (Bell)");
		}		
		break;
	    case 3: /* ServerCutText - 6.3.4 */
		if (check_col(pinfo->cinfo, COL_INFO)) {
		    col_append_fstr(pinfo->cinfo, COL_INFO, " (ServerCutText - FIXME)");
		}		
		break;
	    }
	}
	break;
    }

}

/* Packet dissection routine called by tcp (& udp) when port 5900 detected */
static void
dissect_rfb(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
  dissect_rfb_encap(tvb, pinfo, tree);
}

/* Register protocol with Ethereal. */
void
proto_register_rfb(void)
{
    static hf_register_info hf[] = {
	{&hf_rfb_proto_ver_server,
	 {"Server Protocol Version", "rfb.protocol_ver.server",
	  FT_STRING, BASE_NONE, NULL, 0x0, 
	  "This field contains the latest version of the protocol supported by the server", HFILL }
	},
	{&hf_rfb_proto_ver_client,
	 {"Client Protocol Version", "rfb.protocol_ver.client",
	  FT_STRING, BASE_NONE, NULL, 0x0,
	  "This field contains the latest version of the protocol supported by the client", HFILL }
	},
	{&hf_rfb_security_server,
	 {"V3.3 security type", "rfb.security.type_server",
	  FT_UINT32, BASE_HEX, VALS(rfb_security_type), 0x0,
	  "This field contains the security type support by the server in version 3.3", HFILL }
	},
	{&hf_rfb_security_challenge,
	 {"VNC Authenication Challenge", "rfb.security.vnc_challenge",
	  FT_BYTES, BASE_HEX, NULL, 0x0,
	  "This field contains the VNC authentication challenge sent by the server", HFILL }
	},
	{&hf_rfb_security_response,
	 {"VNC Authenication Response", "rfb.security.vnc_response",
	  FT_BYTES, BASE_HEX, NULL, 0x0,
	  "This field contains the VNC authentication response sent by the client", HFILL }
	},
	{&hf_rfb_security_result,
	 {"VNC Authenication Result", "rfb.security.vnc_result",
	  FT_UINT32, BASE_HEX, VALS(rfb_security_result), 0x0,
	  "This field contains the VNC authentication result sent by the server", HFILL }
	},
	{&hf_rfb_client_init,
	 {"Client Initialisation", "rfb.client_init",
	  FT_UINT8, BASE_HEX, NULL, 0x0,
	  "This field indicates whether the server should share the desktop, or disconnect other clients", HFILL }
	},
	{&hf_rfb_server_init,
	 {"Server Initialisation", "rfb.server_init",
	  FT_NONE, BASE_DEC, NULL, 0x0,
	  "This is the Server Initialisation", HFILL }
	},
	{&hf_rfb_server_init_framebufferwidth,
	 {"Framebuffer width", "rfb.server_init.framebuffer_width",
	  FT_UINT16, BASE_DEC, NULL, 0x0,
	  "This field specifies the width of the server's framebuffer", HFILL }
	},
	{&hf_rfb_server_init_framebufferheight,
	 {"Framebuffer height", "rfb.server_init.framebuffer_height",
	  FT_UINT16, BASE_DEC, NULL, 0x0,
	  "This field specifies the height of the server's framebuffer", HFILL }
	},
	{&hf_rfb_server_init_name_length,
	 {"Name length", "rfb.server_init.name_length",
	  FT_UINT32, BASE_DEC, NULL, 0x0,
	  "This field specifies the length of the name string", HFILL }
	},
	{&hf_rfb_server_init_name_string,
	 {"Name string", "rfb.server_init.name_string",
	  FT_STRING, BASE_NONE, NULL, 0x0,
	  "This field contains the framebuffer's name string", HFILL }
	},
	{&hf_rfb_pixelformat,
	 {"Pixel format", "rfb.pixelformat",
	  FT_NONE, BASE_DEC, NULL, 0x0,
	  "This field specifies the pixel format of the server's framebuffer", HFILL }
	},
	{&hf_rfb_pixelformat_bpp,
	 {"Bits per pixel", "rfb.pixelformat.bpp",
	  FT_UINT8, BASE_DEC, NULL, 0x0,
	  "This field specifies the bits per pixel of the server's framebuffer", HFILL }
	},
	{&hf_rfb_pixelformat_depth,
	 {"Depth", "rfb.pixelformat.depth",
	  FT_UINT8, BASE_DEC, NULL, 0x0,
	  "This field specifies the depth of the server's framebuffer", HFILL }
	},
	{&hf_rfb_pixelformat_bigendian,
	 {"Big endian", "rfb.pixelformat.bigendian",
	  FT_UINT8, BASE_DEC, NULL, 0x0,
	  "This field is true if multibyte pixels are bigendian format", HFILL }
	},
	{&hf_rfb_pixelformat_truecolour,
	 {"True Colour", "rfb.pixelformat.truecolour",
	  FT_UINT8, BASE_DEC, NULL, 0x0,
	  "This field is true if rest of the pixel format information applies", HFILL }
	},
	{&hf_rfb_pixelformat_redmax,
	 {"Maximum Red", "rfb.pixelformat.redmax",
	  FT_UINT16, BASE_DEC, NULL, 0x0,
	  "This field specifies the maximum value for red", HFILL }
	},
	{&hf_rfb_pixelformat_greenmax,
	 {"Maximum Green", "rfb.pixelformat.greenmax",
	  FT_UINT16, BASE_DEC, NULL, 0x0,
	  "This field specifies the maximum value for green", HFILL }
	},
	{&hf_rfb_pixelformat_bluemax,
	 {"Maximum Blue", "rfb.pixelformat.bluemax",
	  FT_UINT16, BASE_DEC, NULL, 0x0,
	  "This field specifies the maximum value for blue", HFILL }
	},
	{&hf_rfb_pixelformat_redshift,
	 {"Red Shift", "rfb.pixelformat.redshift",
	  FT_UINT8, BASE_DEC, NULL, 0x0,
	  "This field specifies the shift required for red", HFILL }
	},
	{&hf_rfb_pixelformat_greenshift,
	 {"Green Shift", "rfb.pixelformat.greenshift",
	  FT_UINT8, BASE_DEC, NULL, 0x0,
	  "This field specifies the shift required for green", HFILL }
	},
	{&hf_rfb_pixelformat_blueshift,
	 {"Blue Shift", "rfb.pixelformat.blueshift",
	  FT_UINT8, BASE_DEC, NULL, 0x0,
	  "This field specifies the shift required for blue", HFILL }
	},
	{&hf_rfb_clientdata_messagetype,
	 {"Client Message Type", "rfb.clientdata.messagetype",
	  FT_UINT8, BASE_HEX, VALS(rfb_client_messagetype), 0x0,
	  "This field specifies the client-to-server message type", HFILL }
	},
	{&hf_rfb_clientdata_setencodings_number,
	 {"Client Message SetEncodings - Number", "rfb.clientdata.setencodings.number",
	  FT_UINT16, BASE_DEC, NULL, 0x0,
	  "This field specifies the number of encodings in the SetEncodings message", HFILL }
	},
	{&hf_rfb_clientdata_setencodings_encodingtype,
	 {"Client Message SetEncodings - Encoding type", "rfb.clientdata.setencodings.encodingtype",
	  FT_UINT32, BASE_HEX, VALS(rfb_encoding_type), 0x0,
	  "This field specifies an encoding type in the SetEncodings message", HFILL }
	},
	{&hf_rfb_clientdata_framebufferupdaterequest_incremental,
	 {"Client Message FrameBuffer Update Request Incremental flag", "rfb.clientdata.framebufferupdaterequest.incremental",
	  FT_UINT16, BASE_DEC, NULL, 0x0,
	  "This field specifies whether the area being requested can be incrementally updated", HFILL }
	},
	{&hf_rfb_clientdata_framebufferupdaterequest_xposition,
	 {"Client Message FrameBuffer Update Request X-position", "rfb.clientdata.framebufferupdaterequest.xposition",
	  FT_UINT16, BASE_DEC, NULL, 0x0,
	  "This field specifies the X position for the area being requested", HFILL }
	},
	{&hf_rfb_clientdata_framebufferupdaterequest_yposition,
	 {"Client Message FrameBuffer Update Request Y-position", "rfb.clientdata.framebufferupdaterequest.yposition",
	  FT_UINT16, BASE_DEC, NULL, 0x0,
	  "This field specifies the Y position for the area being requested", HFILL }
	},
	{&hf_rfb_clientdata_framebufferupdaterequest_width,
	 {"Client Message FrameBuffer Update Request Width", "rfb.clientdata.framebufferupdaterequest.width",
	  FT_UINT16, BASE_DEC, NULL, 0x0,
	  "This field specifies the width of the area being requested", HFILL }
	},
	{&hf_rfb_clientdata_framebufferupdaterequest_height,
	 {"Client Message FrameBuffer Update Request Height", "rfb.clientdata.framebufferupdaterequest.height",
	  FT_UINT16, BASE_DEC, NULL, 0x0,
	  "This field specifies the height of the area being requested", HFILL }
	},
	{&hf_rfb_clientdata_pointerevent_buttonmask,
	 {"Client Message Pointer Event Buttonmask", "rfb.clientdata.pointerevent.buttonmask",
	  FT_UINT8, BASE_HEX, NULL, 0x0,
	  "This field specifies the mouse button status sent from client to server", HFILL }
	},
	{&hf_rfb_clientdata_pointerevent_xposition,
	 {"Client Message Pointer Event X-position", "rfb.clientdata.pointerevent.xposition",
	  FT_UINT16, BASE_DEC, NULL, 0x0,
	  "This field specifies the x component of the pointer's current position sent from client to server", HFILL }
	},
	{&hf_rfb_clientdata_pointerevent_yposition,
	 {"Client Message Pointer Event Y-position", "rfb.clientdata.pointerevent.yposition",
	  FT_UINT16, BASE_DEC, NULL, 0x0,
	  "This field specifies the y component of the pointer's current position sent from client to server", HFILL }
	},
	{&hf_rfb_serverdata_messagetype,
	 {"Server Message Type", "rfb.serverdata.messagetype",
	  FT_UINT8, BASE_HEX, VALS(rfb_server_messagetype), 0x0,
	  "This field specifies the server-to-client message type", HFILL }
	},
	{&hf_rfb_serverdata_framebufferupdate_numrectangles,
	 {"Server Message FrameBuffer Update Number of Rectangles", "rfb.serverdata.framebufferupdate.num_rectangles",
	  FT_UINT16, BASE_DEC, NULL, 0x0,
	  "This field specifies the number of rectangles being sent", HFILL }
	},
	{&hf_rfb_serverdata_framebufferupdate_xposition,
	 {"Server Message FrameBuffer Update X-position", "rfb.serverdata.framebufferupdate.xposition",
	  FT_UINT16, BASE_DEC, NULL, 0x0,
	  "This field specifies the X position for the area being sent", HFILL }
	},
	{&hf_rfb_serverdata_framebufferupdate_yposition,
	 {"Server Message FrameBuffer Update Y-position", "rfb.serverdata.framebufferupdate.yposition",
	  FT_UINT16, BASE_DEC, NULL, 0x0,
	  "This field specifies the Y position for the area being sent", HFILL }
	},
	{&hf_rfb_serverdata_framebufferupdate_width,
	 {"Server Message FrameBuffer Update Width", "rfb.serverdata.framebufferupdate.width",
	  FT_UINT16, BASE_DEC, NULL, 0x0,
	  "This field specifies the width of the area being sent", HFILL }
	},
	{&hf_rfb_serverdata_framebufferupdate_height,
	 {"Server Message FrameBuffer Update Height", "rfb.serverdata.framebufferupdate.height",
	  FT_UINT16, BASE_DEC, NULL, 0x0,
	  "This field specifies the height of the area being sent", HFILL }
	},
	{&hf_rfb_serverdata_framebufferupdate_encodingtype,
	 {"Server Message Framebuffer Encoding type", "rfb.serverdata.framebufferupdate.encodingtype",
	  FT_UINT32, BASE_HEX, VALS(rfb_encoding_type), 0x0,
	  "This field specifies the encoding type in the area being sent", HFILL }
	},
    };

    static gint *ett[] = {
	&ett_rfb,
	&ett_rfb_serv_init,
    };

    module_t *rfb_module;

    proto_rfb = proto_register_protocol("Remote Frame Buffer",
					   "RFB", "rfb");
    proto_register_field_array(proto_rfb, hf, array_length(hf));
    proto_register_subtree_array(ett, array_length(ett));

    rfb_module = prefs_register_protocol(proto_rfb, NULL);
    prefs_register_uint_preference(rfb_module, "tcp_port",
				   "RFB TCP Port",
				   "Set the TCP port for RFB messages",
				   10,
				   &glb_rfb_tcp_port);
    prefs_register_bool_preference(rfb_module, "desegment_rfb_over_tcp",
				   "Desegment all RFB-over-TCP messages",
				   "Whether the RFB dissector should desegment all RFB-over-TCP messages",
				   &rfb_desegment);
}


void
proto_reg_handoff_rfb(void)
{
    rfb_handle = create_dissector_handle(dissect_rfb, proto_rfb);
    dissector_add("tcp.port", glb_rfb_tcp_port, rfb_handle);

    data_handle = find_dissector("data");
}