/* packet-vnc.c * Routines for VNC dissection (Virtual Network Computing) * Copyright 2005, Ulf Lamping * Copyright 2006, Stephen Fisher * * $Id$ * * Wireshark - Network traffic analyzer * By Gerald Combs * 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. */ /* Dissection of the VNC (Virtual Network Computing) network traffic. * * Several VNC implementations available, see: * http://www.realvnc.com/ * http://www.tightvnc.com/ * http://ultravnc.sourceforge.net/ * ... * * 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 source code */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include #include #include #include "packet-x11-keysym.h" /* This contains the X11 value_string "keysym_vals_source" that VNC uses */ static const value_string security_types_vs[] = { { 0, "Invalid" }, { 1, "None" }, { 2, "VNC" }, { 5, "RA2" }, { 6, "RA2ne" }, { 16, "Tight" }, { 17, "Ultra" }, { 18, "TLS" }, { 0, NULL } }; static const value_string auth_result_vs[] = { { 0, "OK" }, { 1, "Failed" }, { 0, NULL } }; static const value_string yes_no_vs[] = { { 0, "No" }, { 1, "Yes" }, { 0, NULL } }; static const value_string client_message_types_vs[] = { { 0, "Set Pixel Format" }, { 2, "Set Encodings" }, { 3, "Framebuffer Update Request" }, { 4, "Key Event" }, { 5, "Pointer Event" }, { 6, "Cut Text" }, { 0, NULL } }; static const value_string server_message_types_vs[] = { { 0, "Framebuffer Update" }, { 1, "Set Colormap Entries" }, { 2, "Ring Bell" }, { 3, "Cut Text" }, { 0, NULL } }; static const value_string button_mask_vs[] = { { 0, "Not pressed" }, { 1, "Pressed" }, { 0, NULL } }; static const value_string encoding_types_vs[] = { { -239, "Cursor (pseudo)" }, { -223, "DesktopSize (pseudo)" }, { 0, "Raw" }, { 1, "CopyRect" }, { 2, "RRE" }, { 4, "CoRRE" }, { 5, "Hextile" }, { 16, "ZRLE" }, { 0, NULL } }; void proto_reg_handoff_vnc(void); /* Variables for our preferences */ static guint vnc_preference_alternate_port = 0; /* An alternate port besides the default (5500, 5501, 5900, 5901) */ /* 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; /* 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_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; /********** 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_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 */ /* Server Set Colormap Entries */ 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; /********** 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_colormap_num_groups = -1; static gint ett_vnc_colormap_color_group = -1; static dissector_handle_t vnc_handle; /* Initialize the structure that will be tied to each conversation. */ typedef struct { /* Packet numbers for the first 9 packets of each conversation. */ guint32 first_packet_number, second_packet_number, third_packet_number, forth_packet_number, fifth_packet_number, sixth_packet_number, seventh_packet_number, eighth_packet_number, ninth_packet_number; gdouble server_proto_ver, client_proto_ver; } vnc_hash_entry_t; /* Code to dissect the packets */ static void dissect_vnc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { guint8 num_security_types, message_type; guint16 number_of_encodings = 0; /* Part of: Client Set Encodings */ guint16 number_of_colors; /* Part of: Server Set Colormap Entries */ guint32 text_len; /* Part of: Client Cut Text & Server Cut Text */ guint32 auth_result, desktop_name_len; guint offset = 0, counter; /* Set up structures needed to add the protocol subtree and manage it */ proto_item *ti=NULL; proto_tree *vnc_tree=NULL, *vnc_client_message_type_tree, *vnc_server_message_type_tree, *vnc_colormap_num_groups, *vnc_colormap_color_group; conversation_t *conversation; vnc_hash_entry_t *hash_info; conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0); if(!conversation) { /* Conversation does not exist yet - create it */ conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0); } /* Retrieve information from conversation * or add it if it isn't there yet */ hash_info = conversation_get_proto_data(conversation, proto_vnc); if(!hash_info) { hash_info = se_alloc(sizeof(vnc_hash_entry_t)); /* We must be on the first packet now */ hash_info->first_packet_number = pinfo->fd->num; /* The rest of these values will be set as we come across each packet */ hash_info->second_packet_number = 0; hash_info->third_packet_number = 0; hash_info->forth_packet_number = 0; hash_info->fifth_packet_number = 0; hash_info->sixth_packet_number = 0; hash_info->seventh_packet_number = 0; hash_info->eighth_packet_number = 0; hash_info->ninth_packet_number = 0; hash_info->server_proto_ver = 0.0; hash_info->client_proto_ver = 0.0; conversation_add_proto_data(conversation, proto_vnc, hash_info); } /* Store the number of the first nine packets of this conversation as we reach them for the first time. * These are the packets that contain connection setup data and do not have a message type identifier. * The packets after the ninth one do contain message type identifiers. */ if(!hash_info->second_packet_number && pinfo->fd->num > hash_info->first_packet_number) { /* We're on the second packet of the conversation */ hash_info->second_packet_number = pinfo->fd->num; } else if(hash_info->second_packet_number && !hash_info->third_packet_number && pinfo->fd->num > hash_info->second_packet_number) { /* We're on the third packet of the conversation */ hash_info->third_packet_number = pinfo->fd->num; } else if(hash_info->third_packet_number && !hash_info->forth_packet_number && pinfo->fd->num > hash_info->third_packet_number) { /* We're on the forth packet of the conversation */ hash_info->forth_packet_number = pinfo->fd->num; } else if(hash_info->forth_packet_number && !hash_info->fifth_packet_number && pinfo->fd->num > hash_info->forth_packet_number) { /* We're on the fifth packet of the conversation */ hash_info->fifth_packet_number = pinfo->fd->num; } else if(hash_info->fifth_packet_number && !hash_info->sixth_packet_number && pinfo->fd->num > hash_info->fifth_packet_number) { /* We're on the sixth packet of the conversation */ hash_info->sixth_packet_number = pinfo->fd->num; } else if(hash_info->sixth_packet_number && !hash_info->seventh_packet_number && pinfo->fd->num > hash_info->sixth_packet_number) { /* We're on the seventh packet of the conversation */ hash_info->seventh_packet_number = pinfo->fd->num; } else if(hash_info->seventh_packet_number && !hash_info->eighth_packet_number && pinfo->fd->num > hash_info->seventh_packet_number) { /* We're on the eighth packet of the conversation */ hash_info->eighth_packet_number = pinfo->fd->num; } else if(hash_info->eighth_packet_number && !hash_info->ninth_packet_number && pinfo->fd->num > hash_info->eighth_packet_number) { /* We're on the ninth packet of the conversation */ hash_info->ninth_packet_number = pinfo->fd->num; } /* Make entries in Protocol column and Info column on summary display */ if (check_col(pinfo->cinfo, COL_PROTOCOL)) col_set_str(pinfo->cinfo, COL_PROTOCOL, "VNC"); /* First, clear the info column */ if(check_col(pinfo->cinfo, COL_INFO)) col_clear(pinfo->cinfo, COL_INFO); /* create display subtree for the protocol */ if(tree) { ti = proto_tree_add_item(tree, proto_vnc, tvb, 0, -1, FALSE); vnc_tree = proto_item_add_subtree(ti, ett_vnc); } offset = 0; /* Start at the beginning of the VNC protocol data */ /* Server Protocol Version */ if(pinfo->fd->num == hash_info->first_packet_number) { proto_tree_add_item(vnc_tree, hf_vnc_server_proto_ver, tvb, 4, 7, FALSE); hash_info->server_proto_ver = g_strtod(tvb_get_ephemeral_string(tvb, 4, 7), NULL); if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Server protocol version: %s", tvb_format_text(tvb, 4, 7)); } /* Second Packet = Client Protocol Version */ else if(pinfo->fd->num == hash_info->second_packet_number) { proto_tree_add_item(vnc_tree, hf_vnc_client_proto_ver, tvb, 4, 7, FALSE); hash_info->client_proto_ver = g_strtod(tvb_get_ephemeral_string(tvb, 4, 7), NULL); if (check_col(pinfo->cinfo, COL_INFO)) col_add_fstr(pinfo->cinfo, COL_INFO, "Client protocol version: %s", tvb_format_text(tvb, 4, 7)); } /* Security Types Supported */ else if(pinfo->fd->num == hash_info->third_packet_number) { if (check_col(pinfo->cinfo, COL_INFO)) 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 version it supports. */ if(hash_info->client_proto_ver >= 3.007) { proto_tree_add_item(vnc_tree, hf_vnc_num_security_types, tvb, offset, 1, FALSE); num_security_types = tvb_get_guint8(tvb, offset); for(offset = 1; offset <= num_security_types; offset++) { proto_tree_add_item(vnc_tree, hf_vnc_security_type, tvb, offset, 1, FALSE); } } else { /* Version < 3.007: The server decides the authentication time for us to use */ proto_tree_add_item(vnc_tree, hf_vnc_server_security_type, tvb, offset, 4, FALSE); } } /* Authentication type selected by client */ /* This field is skipped by versions < 3.007 so the packet number is off on each if statement below */ else if(hash_info->client_proto_ver >= 3.007 && pinfo->fd->num == hash_info->forth_packet_number) { if (check_col(pinfo->cinfo, COL_INFO)) col_set_str(pinfo->cinfo, COL_INFO, "Authentication type selected by client"); proto_tree_add_item(vnc_tree, hf_vnc_client_security_type, tvb, offset, 1, FALSE); } /* Authentication challenge from server */ else if((hash_info->client_proto_ver >= 3.007 && pinfo->fd->num == hash_info->fifth_packet_number) || (hash_info->client_proto_ver < 3.007 && pinfo->fd->num == hash_info->forth_packet_number)) { if (check_col(pinfo->cinfo, COL_INFO)) col_set_str(pinfo->cinfo, COL_INFO, "Authentication challenge from server"); proto_tree_add_item(vnc_tree, hf_vnc_auth_challenge, tvb, offset, 16, FALSE); } /* Authentication response from client */ else if((hash_info->client_proto_ver >= 3.007 && pinfo->fd->num == hash_info->sixth_packet_number) || (hash_info->client_proto_ver < 3.007 && pinfo->fd->num == hash_info->fifth_packet_number)) { if (check_col(pinfo->cinfo, COL_INFO)) col_set_str(pinfo->cinfo, COL_INFO, "Authentication response from client"); proto_tree_add_item(vnc_tree, hf_vnc_auth_response, tvb, offset, 16, FALSE); } /* Authentication result */ else if((hash_info->client_proto_ver >= 3.007 && pinfo->fd->num == hash_info->seventh_packet_number) || (hash_info->client_proto_ver < 3.007 && pinfo->fd->num == hash_info->sixth_packet_number)) { if (check_col(pinfo->cinfo, COL_INFO)) col_set_str(pinfo->cinfo, COL_INFO, "Authentication result"); proto_tree_add_item(vnc_tree, hf_vnc_auth_result, tvb, offset, 4, FALSE); auth_result = tvb_get_ntohl(tvb, offset); /* 32-bit big endian accessor */ offset += 4; if(hash_info->client_proto_ver >= 3.007 && auth_result == 1) { /* 1 = failed */ text_len = tvb_get_ntohl(tvb, offset); proto_tree_add_text(vnc_tree, tvb, offset, 4, "Length of authentication error: %d", text_len); offset += 4; proto_tree_add_item(vnc_tree, hf_vnc_auth_error, tvb, offset, text_len, FALSE); offset += text_len; } } /* Share desktop */ else if((hash_info->client_proto_ver >= 3.007 && pinfo->fd->num == hash_info->eighth_packet_number) || (hash_info->client_proto_ver < 3.007 && pinfo->fd->num == hash_info->seventh_packet_number)) { if (check_col(pinfo->cinfo, COL_INFO)) col_set_str(pinfo->cinfo, COL_INFO, "Share desktop flag"); proto_tree_add_item(vnc_tree, hf_vnc_share_desktop_flag, tvb, offset, 1, FALSE); } /* Various parameters for the frame buffer (screen) from the server */ else if((hash_info->client_proto_ver >= 3.007 && pinfo->fd->num == hash_info->ninth_packet_number) || (hash_info->client_proto_ver < 3.007 && pinfo->fd->num == hash_info->eighth_packet_number)) { if (check_col(pinfo->cinfo, COL_INFO)) col_set_str(pinfo->cinfo, COL_INFO, "Server framebuffer parameters"); proto_tree_add_item(vnc_tree, hf_vnc_width, tvb, offset, 2, FALSE); offset += 2; proto_tree_add_item(vnc_tree, hf_vnc_height, tvb, offset, 2, FALSE); offset += 2; proto_tree_add_item(vnc_tree, hf_vnc_server_bits_per_pixel, tvb, offset, 1, FALSE); offset += 1; proto_tree_add_item(vnc_tree, hf_vnc_server_depth, tvb, offset, 1, FALSE); offset += 1; proto_tree_add_item(vnc_tree, hf_vnc_server_big_endian_flag, tvb, offset, 1, FALSE); offset += 1; proto_tree_add_item(vnc_tree, hf_vnc_server_true_color_flag, tvb, offset, 1, FALSE); offset += 1; proto_tree_add_item(vnc_tree, hf_vnc_server_red_max, tvb, offset, 2, FALSE); offset += 2; proto_tree_add_item(vnc_tree, hf_vnc_server_green_max, tvb, offset, 2, FALSE); offset += 2; proto_tree_add_item(vnc_tree, hf_vnc_server_blue_max, tvb, offset, 2, FALSE); offset += 2; proto_tree_add_item(vnc_tree, hf_vnc_server_red_shift, tvb, offset, 1, FALSE); offset += 1; proto_tree_add_item(vnc_tree, hf_vnc_server_green_shift, tvb, offset, 1, FALSE); offset += 1; proto_tree_add_item(vnc_tree, hf_vnc_server_blue_shift, tvb, offset, 1, FALSE); offset += 1; proto_tree_add_item(vnc_tree, hf_vnc_padding, tvb, offset, 3, FALSE); offset += 3; /* Skip over 3 bytes of padding */ if(tvb_length_remaining(tvb, offset) > 0) { /* Sometimes the desktop name & length is skipped */ proto_tree_add_item(vnc_tree, hf_vnc_desktop_name_len, tvb, offset, 4, FALSE); desktop_name_len = tvb_get_ntohl(tvb, offset); /* 32-bit big endian accessor */ offset += 4; proto_tree_add_item(vnc_tree, hf_vnc_desktop_name, tvb, offset, desktop_name_len, FALSE); } } /* All packets beyond #9 */ else { if(pinfo->destport == 5500 || pinfo->destport == 5501 || pinfo->destport == 5900 || pinfo->destport == 5901 || pinfo->destport == vnc_preference_alternate_port) { /* From client to server */ message_type = tvb_get_guint8(tvb, offset); switch(message_type) { case 0 : /* Client Set Pixel Format */ if (check_col(pinfo->cinfo, COL_INFO)) col_set_str(pinfo->cinfo, COL_INFO, "Client set pixel format"); ti = proto_tree_add_item(vnc_tree, hf_vnc_client_message_type, tvb, offset, 1, FALSE); vnc_client_message_type_tree = proto_item_add_subtree(ti, ett_vnc_client_message_type); offset += 1; proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_padding, tvb, offset, 3, FALSE); offset += 3; /* Skip over 3 bytes of padding */ proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_client_bits_per_pixel, tvb, offset, 1, FALSE); offset += 1; proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_client_depth, tvb, offset, 1, FALSE); offset += 1; proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_client_big_endian_flag, tvb, offset, 1, FALSE); offset += 1; proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_client_true_color_flag, tvb, offset, 1, FALSE); offset += 1; proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_client_red_max, tvb, offset, 2, FALSE); offset += 2; proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_client_green_max, tvb, offset, 2, FALSE); offset += 2; proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_client_blue_max, tvb, offset, 2, FALSE); offset += 2; proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_client_red_shift, tvb, offset, 1, FALSE); offset += 1; proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_client_green_shift, tvb, offset, 1, FALSE); offset += 1; proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_client_blue_shift, tvb, offset, 1, FALSE); offset += 1; proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_padding, tvb, offset, 3, FALSE); offset += 3; /* Skip over 3 bytes of padding */ break; case 2 : /* Client Set Encodings */ if (check_col(pinfo->cinfo, COL_INFO)) col_set_str(pinfo->cinfo, COL_INFO, "Client set encodings"); ti = proto_tree_add_item(vnc_tree, hf_vnc_client_message_type, tvb, offset, 1, FALSE); vnc_client_message_type_tree = proto_item_add_subtree(ti, ett_vnc_client_message_type); offset += 1; proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_padding, tvb, offset, 1, FALSE); offset += 1; /* Skip over 1 byte of padding */ number_of_encodings = tvb_get_ntohs(tvb, offset); proto_tree_add_text(vnc_client_message_type_tree, tvb, offset, 2, "Number of encodings: %d", number_of_encodings); offset += 2; for(counter = 1; counter <= number_of_encodings; counter++) { proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_client_set_encodings_encoding_type, tvb, offset, 4, FALSE); offset += 4; } break; case 3 : /* Client Framebuffer Update Request */ if (check_col(pinfo->cinfo, COL_INFO)) col_set_str(pinfo->cinfo, COL_INFO, "Client framebuffer update request"); ti = proto_tree_add_item(vnc_tree, hf_vnc_client_message_type, tvb, offset, 1, FALSE); vnc_client_message_type_tree = proto_item_add_subtree(ti, ett_vnc_client_message_type); offset += 1; proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_update_req_incremental, tvb, offset, 1, FALSE); offset += 1; proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_update_req_x_pos, tvb, offset, 2, FALSE); offset += 2; proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_update_req_y_pos, tvb, offset, 2, FALSE); offset += 2; proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_update_req_width, tvb, offset, 2, FALSE); offset += 2; proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_update_req_height, tvb, offset, 2, FALSE); offset += 2; break; case 4 : /* Client Key Event */ if (check_col(pinfo->cinfo, COL_INFO)) col_set_str(pinfo->cinfo, COL_INFO, "Client key event"); ti = proto_tree_add_item(vnc_tree, hf_vnc_client_message_type, tvb, offset, 1, FALSE); vnc_client_message_type_tree = proto_item_add_subtree(ti, ett_vnc_client_message_type); offset += 1; proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_key_down, tvb, offset, 1, FALSE); offset += 1; proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_padding, tvb, offset, 2, FALSE); offset += 2; /* Skip over 2 bytes of padding */ proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_key, tvb, offset, 4, FALSE); offset += 4; break; case 5: /* Client Pointer Event */ if (check_col(pinfo->cinfo, COL_INFO)) col_set_str(pinfo->cinfo, COL_INFO, "Client pointer event"); ti = proto_tree_add_item(vnc_tree, hf_vnc_client_message_type, tvb, offset, 1, FALSE); vnc_client_message_type_tree = proto_item_add_subtree(ti, ett_vnc_client_message_type); offset += 1; proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_button_1_pos, tvb, offset, 1, FALSE); proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_button_2_pos, tvb, offset, 1, FALSE); proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_button_3_pos, tvb, offset, 1, FALSE); proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_button_4_pos, tvb, offset, 1, FALSE); proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_button_5_pos, tvb, offset, 1, FALSE); proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_button_6_pos, tvb, offset, 1, FALSE); proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_button_7_pos, tvb, offset, 1, FALSE); proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_button_8_pos, tvb, offset, 1, FALSE); offset += 1; proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_pointer_x_pos, tvb, offset, 2, FALSE); offset += 2; proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_pointer_y_pos, tvb, offset, 2, FALSE); offset += 2; break; case 6 : /* Client Cut Text */ if (check_col(pinfo->cinfo, COL_INFO)) col_set_str(pinfo->cinfo, COL_INFO, "Client cut text"); ti = proto_tree_add_item(vnc_tree, hf_vnc_client_message_type, tvb, offset, 1, FALSE); vnc_client_message_type_tree = proto_item_add_subtree(ti, ett_vnc_client_message_type); offset += 1; proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_padding, tvb, offset, 3, FALSE); offset += 3; /* Skip over 3 bytes of padding */ text_len = tvb_get_ntohl(tvb, offset); /* 32-bit big endian accessor */ proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_client_cut_text_len, tvb, offset, 4, FALSE); offset += 4; proto_tree_add_item(vnc_client_message_type_tree, hf_vnc_client_cut_text, tvb, offset, text_len, FALSE); offset += text_len; break; default : if (check_col(pinfo->cinfo, COL_INFO)) col_append_str(pinfo->cinfo, COL_INFO, "Unknown client message type"); proto_tree_add_text(vnc_tree, tvb, 0, -1, "Unknown client message type"); break; } } else { /* Packet is going from server to client */ message_type = tvb_get_guint8(tvb, offset); switch(message_type) { case 0 : /* Server Framebuffer Update */ /* XXX - This message type is not fully dissected yet */ if (check_col(pinfo->cinfo, COL_INFO)) col_set_str(pinfo->cinfo, COL_INFO, "Server framebuffer update"); ti = proto_tree_add_item(vnc_tree, hf_vnc_server_message_type, tvb, offset, 1, FALSE); vnc_server_message_type_tree = proto_item_add_subtree(ti, ett_vnc_server_message_type); offset += 1; proto_tree_add_text(vnc_server_message_type_tree, tvb, offset, -1, "Data"); break; case 1 : /* Server Set Colormap Entries */ if (check_col(pinfo->cinfo, COL_INFO)) col_set_str(pinfo->cinfo, COL_INFO, "Server set colormap entries"); number_of_colors = tvb_get_ntohs(tvb, 4); if(vnc_preference_desegment && pinfo->can_desegment && tvb_length_remaining(tvb, offset) < (number_of_colors * 6) + 6) { pinfo->desegment_offset = 0; pinfo->desegment_len = (number_of_colors * 6) + 6 - tvb_length_remaining(tvb, offset); return; } ti = proto_tree_add_item(vnc_tree, hf_vnc_server_message_type, tvb, offset, 1, FALSE); vnc_server_message_type_tree = proto_item_add_subtree(ti, ett_vnc_server_message_type); offset += 1; proto_tree_add_item(vnc_server_message_type_tree, hf_vnc_padding, tvb, offset, 1, FALSE); offset += 1; /* Skip over 1 byte of padding */ proto_tree_add_item(vnc_server_message_type_tree, hf_vnc_colormap_first_color, tvb, offset, 2, FALSE); offset += 2; ti = proto_tree_add_item(vnc_server_message_type_tree, hf_vnc_colormap_num_colors, tvb, offset, 2, FALSE); vnc_colormap_num_groups = proto_item_add_subtree(ti, ett_vnc_colormap_num_groups); offset += 2; for(counter = 1; counter <= number_of_colors; counter++) { ti = proto_tree_add_text(vnc_colormap_num_groups, tvb, offset, 6, "Color group #%d", counter); vnc_colormap_color_group = proto_item_add_subtree(ti, ett_vnc_colormap_color_group); proto_tree_add_item(vnc_colormap_color_group, hf_vnc_colormap_red, tvb, offset, 2, FALSE); offset += 2; proto_tree_add_item(vnc_colormap_color_group, hf_vnc_colormap_green, tvb, offset, 2, FALSE); offset += 2; proto_tree_add_item(vnc_colormap_color_group, hf_vnc_colormap_blue, tvb, offset, 2, FALSE); offset += 2; } break; case 2 : /* Server Ring Bell (on client) */ if (check_col(pinfo->cinfo, COL_INFO)) col_set_str(pinfo->cinfo, COL_INFO, "Server ring bell on client"); ti = proto_tree_add_item(vnc_tree, hf_vnc_server_message_type, tvb, offset, 1, FALSE); vnc_server_message_type_tree = proto_item_add_subtree(ti, ett_vnc_server_message_type); offset += 1; /* This message type has no payload... */ break; case 3 : /* Server Cut Text */ if (check_col(pinfo->cinfo, COL_INFO)) col_set_str(pinfo->cinfo, COL_INFO, "Server cut text"); ti = proto_tree_add_item(vnc_tree, hf_vnc_server_message_type, tvb, offset, 1, FALSE); vnc_server_message_type_tree = proto_item_add_subtree(ti, ett_vnc_server_message_type); offset += 1; proto_tree_add_item(vnc_server_message_type_tree, hf_vnc_padding, tvb, offset, 3, FALSE); offset += 3; /* Skip over 3 bytes of padding */ text_len = tvb_get_ntohl(tvb, offset); /* 32-bit big endian accessor */ proto_tree_add_item(vnc_server_message_type_tree, hf_vnc_server_cut_text_len, tvb, offset, 4, FALSE); offset += 4; if(vnc_preference_desegment && pinfo->can_desegment && tvb_length_remaining(tvb, offset) < text_len) { pinfo->desegment_offset = 0; pinfo->desegment_len = text_len - tvb_length_remaining(tvb, offset); return; } proto_tree_add_item(vnc_server_message_type_tree, hf_vnc_server_cut_text, tvb, offset, text_len, FALSE); offset += text_len; break; default : if (check_col(pinfo->cinfo, COL_INFO)) col_append_str(pinfo->cinfo, COL_INFO, "Server framebuffer update (continuation)"); proto_tree_add_text(vnc_tree, tvb, 0, -1, "Server framebuffer update data (continuation)"); break; } } } } /* Register the protocol with Wireshark */ void proto_register_vnc(void) { module_t *vnc_module; /* To handle our preferences */ /* 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(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(security_types_vs), 0x0, "Security type mandated by the server (VNC versions < 3.007)", HFILL } }, { &hf_vnc_client_security_type, { "Security type selected", "vnc.security_type", FT_UINT8, BASE_DEC, VALS(security_types_vs), 0x0, "Security type selected by the client", HFILL } }, { &hf_vnc_auth_challenge, { "Authentication challenge", "vnc.auth_challenge", FT_STRING, BASE_NONE, NULL, 0x0, "Random authentication challenge from server to client", HFILL } }, { &hf_vnc_auth_response, { "Authentication response", "vnc.auth_response", FT_STRING, 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_UINT32, BASE_DEC, VALS(auth_result_vs), 0x0, "Authentication result", 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_share_desktop_flag, { "Share desktop flag", "vnc.share_desktop_flag", FT_UINT8, BASE_DEC, VALS(yes_no_vs), 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.width", 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_name, { "Desktop name", "vnc.desktop_name", FT_STRING, BASE_NONE, NULL, 0x0, "Name of the VNC desktop on the server", HFILL } }, { &hf_vnc_client_message_type, { "Client Message Type", "vnc.client_message_type", FT_UINT8, BASE_DEC, VALS(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_UINT8, BASE_DEC, VALS(yes_no_vs), 0x0, "Specifies whether the key is being pressed or not", HFILL } }, { &hf_vnc_key, { "Key", "vnc.key", FT_UINT32, BASE_HEX, VALS(keysym_vals_source), 0x0, /* keysym_vals_source is from packet-x11-keysym.h */ "Key being pressed/depressed", HFILL } }, /* Client Pointer Event */ { &hf_vnc_button_1_pos, { "Mouse button #1 position", "vnc.button_1_pos", FT_UINT8, BASE_DEC, VALS(&button_mask_vs), 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_UINT8, BASE_DEC, VALS(&button_mask_vs), 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_UINT8, BASE_DEC, VALS(&button_mask_vs), 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_UINT8, BASE_DEC, VALS(&button_mask_vs), 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_UINT8, BASE_DEC, VALS(&button_mask_vs), 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_UINT8, BASE_DEC, VALS(&button_mask_vs), 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_UINT8, BASE_DEC, VALS(&button_mask_vs), 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_UINT8, BASE_DEC, VALS(&button_mask_vs), 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_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_DEC, 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(server_message_types_vs), 0x0, "Message type from server", 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_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 } }, }; /* Setup protocol subtree arrays */ static gint *ett[] = { &ett_vnc, &ett_vnc_client_message_type, &ett_vnc_server_message_type, &ett_vnc_colormap_num_groups, &ett_vnc_colormap_color_group }; /* 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 used */ proto_register_field_array(proto_vnc, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); /* 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.", "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; if(!inited) { vnc_handle = create_dissector_handle(dissect_vnc, proto_vnc); dissector_add("tcp.port", 5500, vnc_handle); /* First screen on listening vnc viewer */ dissector_add("tcp.port", 5501, vnc_handle); /* Second screen on listening vnc viewer */ dissector_add("tcp.port", 5900, vnc_handle); /* First screen on server */ dissector_add("tcp.port", 5901, vnc_handle); /* Second screen on server */ /* We don't register a port for the VNC HTTP server because that simply provides a java program * for download via the normal HTTP protocol. The java program then connects to a standard VNC port (above). */ inited = TRUE; } if(vnc_preference_alternate_port != 5500 && vnc_preference_alternate_port != 5501 && vnc_preference_alternate_port != 5900 && vnc_preference_alternate_port != 5901 && vnc_preference_alternate_port != 0) { dissector_delete("tcp.port", vnc_preference_alternate_port_last, vnc_handle); vnc_preference_alternate_port_last = vnc_preference_alternate_port; /* Save this setting to see if has changed later */ dissector_add("tcp.port", vnc_preference_alternate_port, vnc_handle); /* Register the new port setting */ } }