2011-09-29 18:10:14 +00:00
/* packet-spice.c
* Routines for Spice protocol dissection
* Copyright 2011 , Yaniv Kaul < ykaul @ redhat . com >
*
* $ Id $
*
* Wireshark - Network traffic analyzer
* By Gerald Combs < gerald @ wireshark . org >
* Copyright 1998 Gerald Combs
*
* This program is free software ; you can spiceistribute 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 .
*
* This code is based on the protocol specification :
* http : //www.spice-space.org/docs/spice_protocol.pdf
* and the source - git : //cgit.freedesktop.org/spice/spice-protocol
*/
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
# include <glib.h>
# include <epan/conversation.h>
# include <epan/emem.h>
# include <epan/packet.h>
# include <epan/prefs.h>
# define SPICE_MAGIC 0x52454451 /* = "REDQ" */
# define SPICE_VERSION_MAJOR_1 1
# define SPICE_VERSION_MINOR_0 0
# define SPICE_VERSION_MAJOR_UNSTABLE 0xfffe
# define SPICE_VERSION_MINOR_UNSTABLE 0xffff
# define SPICE_TICKET_PUBKEY_BYTES 162
# define SPICE_ALIGN(a, size) (((a) + ((size) - 1)) & ~((size) - 1))
typedef enum {
SPICE_LINK_CLIENT ,
SPICE_LINK_SERVER ,
SPICE_TICKET_CLIENT ,
SPICE_TICKET_SERVER ,
SPICE_DATA
} spice_session_state_e ;
static dissector_handle_t spice_handle ;
# define SPICE_CHANNEL_NONE 0
# define SPICE_CHANNEL_MAIN 1
# define SPICE_CHANNEL_DISPLAY 2
# define SPICE_CHANNEL_INPUTS 3
# define SPICE_CHANNEL_CURSOR 4
# define SPICE_CHANNEL_PLAYBACK 5
# define SPICE_CHANNEL_RECORD 6
# define SPICE_CHANNEL_TUNNEL 7
# define SPICE_CHANNEL_SMARTCARD 8
static const value_string channel_types_vs [ ] = {
{ SPICE_CHANNEL_NONE , " Invalid " } ,
{ SPICE_CHANNEL_MAIN , " Main " } ,
{ SPICE_CHANNEL_DISPLAY , " Display " } ,
{ SPICE_CHANNEL_INPUTS , " Inputs " } ,
{ SPICE_CHANNEL_CURSOR , " Cursor " } ,
{ SPICE_CHANNEL_PLAYBACK , " Playback " } ,
{ SPICE_CHANNEL_RECORD , " Record " } ,
{ SPICE_CHANNEL_TUNNEL , " Tunnel " } ,
{ SPICE_CHANNEL_SMARTCARD , " Smart Card " } ,
{ 0 , NULL }
} ;
/* common server messages */
# define SPICE_MIGRATE 1
# define SPICE_MIGRATE_DATA 2
# define SPICE_SET_ACK 3
# define SPICE_PING 4
# define SPICE_WAIT_FOR_CHANNELS 5
# define SPICE_DISCONNECTING 6
# define SPICE_NOTIFY 7
/* common client messages */
# define SPICEC_ACK_SYNC 1
# define SPICEC_ACK 2
# define SPICEC_PONG 3
# define SPICEC_MIGRATE_FLUSH_MARK 4
# define SPICEC_MIGRATE_DATA 5
# define SPICEC_DISCONNECTING 6
# define SPICE_FIRST_AVAIL_MESSAGE 101
static const value_string common_server_message_types [ ] = {
{ SPICE_MIGRATE , " Server MIGRATE " } ,
{ SPICE_MIGRATE_DATA , " Server MIGRATE_DATA " } ,
{ SPICE_SET_ACK , " Server SET_ACK " } ,
{ SPICE_PING , " Server PING " } ,
{ SPICE_WAIT_FOR_CHANNELS , " Server WAIT_FOR_CHANNELS " } ,
{ SPICE_DISCONNECTING , " Server DISCONNECTING " } ,
{ SPICE_NOTIFY , " Server NOTIFY " } ,
{ 0 , NULL }
} ;
static const value_string common_client_message_types [ ] = {
{ SPICEC_ACK_SYNC , " Client ACK_SYNC " } ,
{ SPICEC_ACK , " Client ACK " } ,
{ SPICEC_PONG , " Client PONG " } ,
{ SPICEC_MIGRATE_FLUSH_MARK , " Client MIGRATE_FLUSH_MARK " } ,
{ SPICEC_MIGRATE_DATA , " Client MIGRATE_DATA " } ,
{ SPICEC_DISCONNECTING , " Client DISCONNECTING " } ,
{ 0 , NULL }
} ;
# define sizeof_SpiceLinkHeader 16
# define sizeof_SpiceDataHeader 18
/* playback server channel messages */
# define SPICE_PLAYBACK_DATA 101
# define SPICE_PLAYBACK_MODE 102
# define SPICE_PLAYBACK_START 103
# define SPICE_PLAYBACK_STOP 104
static const value_string playback_server_message_types [ ] = {
{ SPICE_PLAYBACK_DATA , " Server PLAYBACK_DATA " } ,
{ SPICE_PLAYBACK_MODE , " Server PLAYBACK_MODE " } ,
{ SPICE_PLAYBACK_START , " Server PLAYBACK_START " } ,
{ SPICE_PLAYBACK_STOP , " Server PLAYBACK_STOP " } ,
{ 0 , NULL }
} ;
static const value_string playback_mode_vals [ ] = {
{ 0 , " INVALID " } ,
{ 1 , " RAW " } ,
{ 2 , " CELT_0_5_1 " } ,
{ 0 , NULL }
} ;
# define SPICE_PLAYBACK_CAP_CELT_0_5_1 1
static const value_string playback_caps [ ] = {
{ SPICE_PLAYBACK_CAP_CELT_0_5_1 , " PLAYBACK_CAP_CELT_0_5_1 " } ,
{ 0 , NULL }
} ;
/* main channel */
/* main channel server messages */
# define SPICE_MAIN_MIGRATE_BEGIN 101
# define SPICE_MAIN_MIGRATE_CANCEL 102
# define SPICE_MAIN_INIT 103
# define SPICE_MAIN_CHANNELS_LIST 104
# define SPICE_MAIN_MOUSE_MODE 105
# define SPICE_MAIN_MULTI_MEDIA_TIME 106
# define SPICE_MAIN_AGENT_CONNECTED 107
# define SPICE_MAIN_AGENT_DISCONNECTED 108
# define SPICE_MAIN_AGENT_DATA 109
# define SPICE_MAIN_AGENT_TOKEN 110
static const value_string main_server_message_types [ ] = {
{ SPICE_MAIN_MIGRATE_BEGIN , " Server MIGRATE_BEGIN " } ,
{ SPICE_MAIN_MIGRATE_CANCEL , " Server MIGRATE_CANCEL " } ,
{ SPICE_MAIN_INIT , " Server INIT " } ,
{ SPICE_MAIN_CHANNELS_LIST , " Server CHANNELS_LIST " } ,
{ SPICE_MAIN_MOUSE_MODE , " Server MOUSE_MODE " } ,
{ SPICE_MAIN_MULTI_MEDIA_TIME , " Server MULTI_MEDIA_TIME " } ,
{ SPICE_MAIN_AGENT_CONNECTED , " Server AGENT_CONNECTED " } ,
{ SPICE_MAIN_AGENT_DISCONNECTED , " Server AGENT_DISCONNECTED " } ,
{ SPICE_MAIN_AGENT_DATA , " Server AGENT_DATA " } ,
{ SPICE_MAIN_AGENT_TOKEN , " Server AGENT_TOKEN " } ,
{ 0 , NULL }
} ;
/* main channel client messages */
# define SPICEC_MAIN_RESERVED 101
# define SPICEC_MAIN_MIGRATE_READY 102
# define SPICEC_MAIN_MIGRATE_ERROR 103
# define SPICEC_MAIN_ATTACH_CHANNELS 104
# define SPICEC_MAIN_MOUSE_MODE_REQUEST 105
# define SPICEC_MAIN_AGENT_START 106
# define SPICEC_MAIN_AGENT_DATA 107
# define SPICEC_MAIN_AGENT_TOKEN 108
static const value_string main_client_message_types [ ] = {
{ SPICEC_MAIN_RESERVED , " Client RESERVED " } ,
{ SPICEC_MAIN_MIGRATE_READY , " Client MIGRATE_READY " } ,
{ SPICEC_MAIN_MIGRATE_ERROR , " Client MIGRATE_ERROR " } ,
{ SPICEC_MAIN_ATTACH_CHANNELS , " Client ATTACH_CHANNELS " } ,
{ SPICEC_MAIN_MOUSE_MODE_REQUEST , " Client MOUSE_MODE_REQUEST " } ,
{ SPICEC_MAIN_AGENT_START , " Client AGENT_START " } ,
{ SPICEC_MAIN_AGENT_DATA , " Client AGENT_DATA " } ,
{ SPICEC_MAIN_AGENT_TOKEN , " Client AGENT_TOKEN " } ,
{ 0 , NULL }
} ;
# define VD_AGENT_MOUSE_STATE 1
# define VD_AGENT_MONITORS_CONFIG 2
# define VD_AGENT_REPLY 3
# define VD_AGENT_CLIPBOARD 4
# define VD_AGENT_DISPLAY_CONFIG 5
# define VD_AGENT_ANNOUNCE_CAPABILITIES 6
# define VD_AGENT_CLIPBOARD_GRAB 7
# define VD_AGENT_CLIPBOARD_REQUEST 8
# define VD_AGENT_CLIPBOARD_RELEASE 9
static const value_string agent_message_type [ ] = {
{ VD_AGENT_MOUSE_STATE , " VD_AGENT_MOUSE_STATE " } ,
{ VD_AGENT_MONITORS_CONFIG , " VD_AGENT_MONITORS_CONFIG " } ,
{ VD_AGENT_REPLY , " VD_AGENT_REPLY " } ,
{ VD_AGENT_CLIPBOARD , " VD_AGENT_CLIPBOARD " } ,
{ VD_AGENT_DISPLAY_CONFIG , " VD_AGENT_DISPLAY_CONFIG " } ,
{ VD_AGENT_ANNOUNCE_CAPABILITIES , " VD_AGENT_ANNOUNCE_CAPABILITIES " } ,
{ VD_AGENT_CLIPBOARD_GRAB , " VD_AGENT_CLIPBOARD_GRAB " } ,
{ VD_AGENT_CLIPBOARD_REQUEST , " VD_AGENT_CLIPBOARD_REQUEST " } ,
{ VD_AGENT_CLIPBOARD_RELEASE , " VD_AGENT_CLIPBOARD_RELEASE " } ,
{ 0 , NULL }
} ;
# define VD_AGENT_CLIPBOARD_NONE 0
# define VD_AGENT_CLIPBOARD_UTF8_TEXT 1
# define VD_AGENT_CLIPBOARD_IMAGE_PNG 2
# define VD_AGENT_CLIPBOARD_IMAGE_BMP 3
# define VD_AGENT_CLIPBOARD_IMAGE_TIFF 4
# define VD_AGENT_CLIPBOARD_IMAGE_JPG 5
static const value_string agent_clipboard_type [ ] = {
{ VD_AGENT_CLIPBOARD_NONE , " NONE " } ,
{ VD_AGENT_CLIPBOARD_UTF8_TEXT , " UTF8_TEXT " } ,
{ VD_AGENT_CLIPBOARD_IMAGE_PNG , " IMAGE_PNG " } ,
{ VD_AGENT_CLIPBOARD_IMAGE_PNG , " IMAGE_BMP " } ,
{ VD_AGENT_CLIPBOARD_IMAGE_PNG , " IMAGE_TIFF " } ,
{ VD_AGENT_CLIPBOARD_IMAGE_PNG , " IMAGE_JPG " } ,
{ 0 , NULL }
} ;
/* record channel */
/* record channel server messages */
# define SPICE_RECORD_START 101
# define SPICE_RECORD_STOP 102
static const value_string record_server_message_types [ ] = {
{ SPICE_RECORD_START , " Server RECORD_START " } ,
{ SPICE_RECORD_STOP , " Server RECORD_STOP " } ,
{ 0 , NULL }
} ;
/* record channel client messages */
# define SPICEC_RECORD_DATA 101
# define SPICEC_RECORD_MODE 102
# define SPICEC_RECORD_START_MARK 103
static const value_string record_client_message_types [ ] = {
{ SPICEC_RECORD_DATA , " Client RECORD_DATA " } ,
{ SPICEC_RECORD_MODE , " Client RECORD_MODE " } ,
{ SPICEC_RECORD_START_MARK , " Client RECORD_START_MARK " } ,
{ 0 , NULL }
} ;
/* record channel capabilities - same as playback */
static const value_string record_caps [ ] = {
{ SPICE_PLAYBACK_CAP_CELT_0_5_1 , " PLAYBACK_CAP_CELT_0_5_1 " } ,
{ 0 , NULL }
} ;
/* display channel */
/* display channel server messages */
# define SPICE_DISPLAY_MODE 101
# define SPICE_DISPLAY_MARK 102
# define SPICE_DISPLAY_RESET 103
# define SPICE_DISPLAY_COPY_BITS 104
# define SPICE_DISPLAY_INVAL_LIST 105
# define SPICE_DISPLAY_INVAL_ALL_PIXMAPS 106
# define SPICE_DISPLAY_INVAL_PALETTE 107
# define SPICE_DISPLAY_INVAL_ALL_PALETTES 108
# define SPICE_DISPLAY_STREAM_CREATE 122
# define SPICE_DISPLAY_STREAM_DATA 123
# define SPICE_DISPLAY_STREAM_CLIP 124
# define SPICE_DISPLAY_STREAM_DESTROY 125
# define SPICE_DISPLAY_STREAM_DESTROY_ALL 126
# define SPICE_DISPLAY_DRAW_FILL 302
# define SPICE_DISPLAY_DRAW_OPAQUE 303
# define SPICE_DISPLAY_DRAW_COPY 304
# define SPICE_DISPLAY_DRAW_BLEND 305
# define SPICE_DISPLAY_DRAW_BLACKNESS 306
# define SPICE_DISPLAY_DRAW_WHITENESS 307
# define SPICE_DISPLAY_DRAW_INVERS 308
# define SPICE_DISPLAY_DRAW_ROP3 309
# define SPICE_DISPLAY_DRAW_STROKE 310
# define SPICE_DISPLAY_DRAW_TEXT 311
# define SPICE_DISPLAY_DRAW_TRANSPARENT 312
# define SPICE_DISPLAY_DRAW_ALPHA_BLEND 313
# define SPICE_DISPLAY_DRAW_SURFACE_CREATE 314
# define SPICE_DISPLAY_DRAW_SURFACE_DESTROY 315
static const value_string display_server_message_types [ ] = {
{ SPICE_DISPLAY_MODE , " MODE " } ,
{ SPICE_DISPLAY_MARK , " MARK " } ,
{ SPICE_DISPLAY_RESET , " RESET " } ,
{ SPICE_DISPLAY_COPY_BITS , " COPY_BITS " } ,
{ SPICE_DISPLAY_INVAL_LIST , " INVAL_LIST " } ,
{ SPICE_DISPLAY_INVAL_ALL_PIXMAPS , " INVAL_ALL_PIXMAPS " } ,
{ SPICE_DISPLAY_INVAL_PALETTE , " INVAL_PALETTE " } ,
{ SPICE_DISPLAY_INVAL_ALL_PALETTES , " INVAL_ALL_PALETTES " } ,
{ SPICE_DISPLAY_STREAM_CREATE , " STREAM_CREATE " } ,
{ SPICE_DISPLAY_STREAM_DATA , " STREAM_DATA " } ,
{ SPICE_DISPLAY_STREAM_CLIP , " STREAM_CLIP " } ,
{ SPICE_DISPLAY_STREAM_DESTROY , " STREAM_DESTROY " } ,
{ SPICE_DISPLAY_STREAM_DESTROY_ALL , " STREAM_DESTROY_ALL " } ,
{ SPICE_DISPLAY_DRAW_FILL , " DRAW_FILL " } ,
{ SPICE_DISPLAY_DRAW_OPAQUE , " DRAW_OPAQUE " } ,
{ SPICE_DISPLAY_DRAW_COPY , " DRAW_COPY " } ,
{ SPICE_DISPLAY_DRAW_BLEND , " DRAW_BLEND " } ,
{ SPICE_DISPLAY_DRAW_BLACKNESS , " DRAW_BLACKNESS " } ,
{ SPICE_DISPLAY_DRAW_WHITENESS , " DRAW_WHITENESS " } ,
{ SPICE_DISPLAY_DRAW_INVERS , " DRAW_INVERS " } ,
{ SPICE_DISPLAY_DRAW_ROP3 , " DRAW_ROP3 " } ,
{ SPICE_DISPLAY_DRAW_STROKE , " DRAW_STROKE " } ,
{ SPICE_DISPLAY_DRAW_TEXT , " DRAW_TEXT " } ,
{ SPICE_DISPLAY_DRAW_TRANSPARENT , " DRAW_TRANSPARENT " } ,
{ SPICE_DISPLAY_DRAW_ALPHA_BLEND , " DRAW_ALPHA_BLEND " } ,
{ SPICE_DISPLAY_DRAW_SURFACE_CREATE , " DRAW_SURFACE_CREATE " } ,
{ SPICE_DISPLAY_DRAW_SURFACE_DESTROY , " DRAW_SURFACE_DESTROY " } ,
{ 0 , NULL }
} ;
# define TOP_DOWN 1
static const value_string stream_flags [ ] = {
{ 0 , " None " } ,
{ TOP_DOWN , " TOP DOWN " } ,
{ 0 , NULL }
} ;
# define MJPEG 1
static const value_string stream_codec_types [ ] = {
{ MJPEG , " MJPEG " } ,
{ 0 , NULL }
} ;
/* display channel client messages */
# define SPICEC_DISPLAY_INIT 101
static const value_string display_client_message_types [ ] = {
{ SPICEC_DISPLAY_INIT , " Client DISPLAY INIT " } ,
{ 0 , NULL }
} ;
# define sizeof_RedcDisplayInit 14
/* cursor channel */
/* cursor channel server messages */
# define SPICE_CURSOR_INIT 101
# define SPICE_CURSOR_RESET 102
# define SPICE_CURSOR_SET 103
# define SPICE_CURSOR_MOVE 104
# define SPICE_CURSOR_HIDE 105
# define SPICE_CURSOR_TRAIL 106
# define SPICE_CURSOR_INVAL_ONE 107
# define SPICE_CURSOR_INVAL_ALL 108
static const value_string cursor_visible_vs [ ] = {
{ 1 , " Visible " } ,
{ 0 , " Invisible " } ,
{ 0 , NULL }
} ;
static const value_string cursor_server_message_types [ ] = {
{ SPICE_CURSOR_INIT , " Server CURSOR_INIT " } ,
{ SPICE_CURSOR_RESET , " Server CURSOR_RESET " } ,
{ SPICE_CURSOR_SET , " Server CURSOR_SET " } ,
{ SPICE_CURSOR_MOVE , " Server CURSOR_MOVE " } ,
{ SPICE_CURSOR_HIDE , " Server CURSOR_HIDE " } ,
{ SPICE_CURSOR_TRAIL , " Server CURSOR_TRAIL " } ,
{ SPICE_CURSOR_INVAL_ONE , " Server CURSOR_INVAL_ONE " } ,
{ SPICE_CURSOR_INVAL_ALL , " Server CURSOR_INVAL_ALL " } ,
{ 0 , NULL }
} ;
/* cursor channel client messages */
static const value_string cursor_client_message_types [ ] = {
{ 0 , NULL }
} ;
# define SPICE_CURSOR_FLAGS_NONE 1
# define SPICE_CURSOR_FLAGS_CACHE_ME 2
# define SPICE_CURSOR_FLAGS_FROM_CACHE 4
static const value_string cursor_flags_vs [ ] = {
{ SPICE_CURSOR_FLAGS_NONE , " NONE " } ,
{ SPICE_CURSOR_FLAGS_CACHE_ME , " CACHE_ME " } ,
{ SPICE_CURSOR_FLAGS_FROM_CACHE , " FROM_CACHE " } ,
{ 0 , NULL }
} ;
# define SPICE_CURSOR_TYPE_ALPHA 0
# define SPICE_CURSOR_TYPE_MONO 1
# define SPICE_CURSOR_TYPE_COLOR4 2
# define SPICE_CURSOR_TYPE_COLOR8 3
# define SPICE_CURSOR_TYPE_COLOR16 4
# define SPICE_CURSOR_TYPE_COLOR24 5
# define SPICE_CURSOR_TYPE_COLOR32 6
static const value_string cursor_type_vs [ ] = {
{ SPICE_CURSOR_TYPE_ALPHA , " CURSOR_TYPE_ALPHA " } ,
{ SPICE_CURSOR_TYPE_MONO , " CURSOR_TYPE_MONO " } ,
{ SPICE_CURSOR_TYPE_COLOR4 , " CURSOR_TYPE_COLOR4 " } ,
{ SPICE_CURSOR_TYPE_COLOR8 , " CURSOR_TYPE_COLOR8 " } ,
{ SPICE_CURSOR_TYPE_COLOR16 , " CURSOR_TYPE_COLOR16 " } ,
{ SPICE_CURSOR_TYPE_COLOR24 , " CURSOR_TYPE_COLOR24 " } ,
{ SPICE_CURSOR_TYPE_COLOR32 , " CURSOR_TYPE_COLOR32 " } ,
{ 0 , NULL }
} ;
typedef struct {
guint64 unique ;
guint8 type ;
guint16 width ;
guint16 height ;
guint16 hot_spot_x ;
guint16 hot_spot_y ;
} CursorHeader ;
# define sizeof_CursorHeader 17
# define SPICE_MOUSE_MODE_SERVER 1
# define SPICE_MOUSE_MODE_CLIENT 2
static const value_string spice_mouse_modes_vs [ ] = {
{ SPICE_MOUSE_MODE_SERVER , " Server mouse " } ,
{ SPICE_MOUSE_MODE_CLIENT , " Client mouse " } ,
{ 0 , NULL }
} ;
static const value_string spice_agent_vs [ ] = {
{ 0 , " Disconnected " } ,
{ 1 , " Connected " } ,
{ 0 , NULL }
} ;
# define SPICE_NOTIFY_SEVERITY_INFO 0
# define SPICE_NOTIFY_SEVERITY_WARN 1
# define SPICE_NOTIFY_SEVERITY_ERROR 2
static const value_string spice_severity_vs [ ] = {
{ SPICE_NOTIFY_SEVERITY_INFO , " Info " } ,
{ SPICE_NOTIFY_SEVERITY_WARN , " Warning " } ,
{ SPICE_NOTIFY_SEVERITY_ERROR , " Error " } ,
{ 0 , NULL }
} ;
# define SPICE_NOTIFY_VISIBILITY_LOW 0
# define SPICE_NOTIFY_VISIBILITY_MEDIUM 1
# define SPICE_NOTIFY_VISIBILITY_HIGH 2
static const value_string spice_visibility_vs [ ] = {
{ SPICE_NOTIFY_VISIBILITY_LOW , " Low visibility " } ,
{ SPICE_NOTIFY_VISIBILITY_MEDIUM , " Medium visibility " } ,
{ SPICE_NOTIFY_VISIBILITY_HIGH , " High visibility " } ,
{ 0 , NULL }
} ;
static const value_string spice_error_codes_vs [ ] = {
{ 0 , " OK " } ,
{ 1 , " ERROR " } ,
{ 2 , " INVALID_MAGIC " } ,
{ 3 , " INVALID_DATA " } ,
{ 4 , " VERSION_MISMATCH " } ,
{ 5 , " NEED_SECURED " } ,
{ 6 , " NEED_UNSECURED " } ,
{ 7 , " PERMISSION_DENIED " } ,
{ 8 , " BAD_CONNECTION_ID " } ,
{ 9 , " CHANNEL_NOT_AVAILABLE " } ,
{ 0 , NULL }
} ;
static const value_string spice_common_caps_vs [ ] = {
{ 0 , " SPICE_COMMON_CAP_PROTOCOL_AUTH_SELECTION " } ,
{ 1 , " SPICE_COMMON_CAP_AUTH_SPICE " } ,
{ 2 , " SPICE_COMMON_CAP_AUTH_SASL " } ,
{ 0 , NULL }
} ;
/* Inputs channel */
# define SPICEC_INPUTS_KEY_DOWN 101
# define SPICEC_INPUTS_KEY_UP 102
# define SPICEC_INPUTS_KEY_MODIFIERS 103
# define SPICEC_INPUTS_MOUSE_MOTION 111
# define SPICEC_INPUTS_MOUSE_POSITION 112
# define SPICEC_INPUTS_MOUSE_PRESS 113
# define SPICEC_INPUTS_MOUSE_RELEASE 114
# define SPICE_INPUTS_INIT 101
# define SPICE_INPUTS_KEY_MODIFIERS 102
# define SPICE_INPUTS_MOUSE_MOTION_ACK 111
static const value_string inputs_client_message_types [ ] = {
{ SPICEC_INPUTS_KEY_DOWN , " Client KEY_DOWN " } ,
{ SPICEC_INPUTS_KEY_UP , " Client INPUTS_KEY_UP " } ,
{ SPICEC_INPUTS_KEY_MODIFIERS , " Client KEY_MODIFIERS " } ,
{ SPICEC_INPUTS_MOUSE_MOTION , " Client MOUSE_MOTION " } ,
{ SPICEC_INPUTS_MOUSE_POSITION , " Client MOUSE_POSITION " } ,
{ SPICEC_INPUTS_MOUSE_PRESS , " Client MOUSE_PRESS " } ,
{ SPICEC_INPUTS_MOUSE_RELEASE , " Client MOUSE_RELEASE " } ,
{ 0 , NULL }
} ;
static const value_string inputs_server_message_types [ ] = {
{ SPICE_INPUTS_INIT , " Server INPUTS_INIT " } ,
{ SPICE_INPUTS_KEY_MODIFIERS , " Server KEY_MODIFIERS " } ,
{ SPICE_INPUTS_MOUSE_MOTION_ACK , " Server MOUSE_MOTION_ACK " } ,
{ 0 , NULL }
} ;
# define SPICE_SCROLL_LOCK_MODIFIER 1
# define SPICE_NUM_LOCK_MODIFIER 2
# define SPICE_CAPS_LOCK_MODIFIER 4
static const value_string input_modifiers_types [ ] = {
{ 0 , " None " } ,
{ SPICE_SCROLL_LOCK_MODIFIER , " Scroll lock " } ,
{ SPICE_NUM_LOCK_MODIFIER , " Num lock " } ,
{ SPICE_CAPS_LOCK_MODIFIER , " CAPS lock " } ,
{ 0 , NULL }
} ;
/* This structure will be tied to each conversation. */
typedef struct {
guint32 connection_id ;
guint32 num_channel_caps ;
guint32 destport ;
spice_session_state_e next_state ;
guint8 channel_type ;
guint8 channel_id ;
} spice_conversation_t ;
typedef struct {
spice_session_state_e state ;
} spice_packet_t ;
typedef struct {
gint32 left ;
gint32 top ;
gint32 right ;
gint32 bottom ;
} SpiceRect ;
# define sizeof_SpiceRect 16
# define CLIP_TYPE_NONE 0
# define CLIP_TYPE_RECTS 1
static const value_string clip_types_vs [ ] = {
{ CLIP_TYPE_NONE , " NONE " } ,
{ CLIP_TYPE_RECTS , " RECTS " } ,
{ 0 , NULL }
} ;
typedef struct {
guint8 type ;
} Clip ;
# define sizeof_Clip 1 /* This is correct only if the type is none. If it is RECTS, this is followed by: */
typedef struct {
guint32 num_rects ; /* this is followed by RECT rects[num_rects] */
} ClipRects ;
typedef struct {
guint32 surface_id ;
SpiceRect bounding_box ;
Clip clip ;
} DisplayBase ;
# define sizeof_DisplayBase 21 /* size without a rect list in the Clip */
typedef struct {
gint32 x ;
gint32 y ;
} POINT ;
# define sizeof_POINT 8
typedef struct {
gint16 x ;
gint16 y ;
} POINT16 ;
# define sizeof_POINT16 4
# define SPICE_BRUSH_TYPE_NONE 0
# define SPICE_BRUSH_TYPE_SOLID 1
# define SPICE_BRUSH_TYPE_PATTERN 2
static const value_string brush_types_vs [ ] = {
{ SPICE_BRUSH_TYPE_NONE , " NONE " } ,
{ SPICE_BRUSH_TYPE_SOLID , " SOLID " } ,
{ SPICE_BRUSH_TYPE_PATTERN , " PATTERN " } ,
{ 0 , NULL }
} ;
typedef struct {
guint64 image ;
POINT position ;
} Pattern ;
# define sizeof_Pattern 16
typedef struct {
guint8 type ;
union {
guint32 color ;
Pattern patten ;
} ;
} Brush ;
# define sizeof_Brush 17 /* for pattern */
static const value_string Mask_flags_vs [ ] = {
{ 0 , " MASK_FLAG_INVERS " } ,
{ 0 , NULL }
} ;
typedef struct {
guint8 flags ;
POINT position ;
guint32 bitmap ;
} Mask ;
# define sizeof_Mask 13
static const value_string scale_mode_vs [ ] = {
{ 0 , " IMAGE_SCALE_INTERPOLATE " } ,
{ 1 , " IMAGE_SCALE_NEAREST " } ,
{ 0 , NULL }
} ;
typedef struct {
guint64 id ;
guint8 type ;
guint8 flag ;
guint32 width ;
guint32 height ;
} ImageDescriptor ;
# define sizeof_ImageDescriptor 18
# define IMAGE_TYPE_BITMAP 0
# define IMAGE_TYPE_QUIC 1
# define IMAGE_TYPE_RESERVED 2
# define IMAGE_TYPE_LZ_PLT 100
# define IMAGE_TYPE_LZ_RGB 101
# define IMAGE_TYPE_GLZ_RGB 102
# define IMAGE_TYPE_FROM_CACHE 103
# define IMAGE_TYPE_SURFACE 104
# define IMAGE_TYPE_JPEG 105
# define IMAGE_TYPE_FROM_CACHE_LOSSLESS 106
# define IMAGE_TYPE_ZLIB_GLZ_RGB 107
# define IMAGE_TYPE_JPEG_ALPHA 108
static const value_string image_type_vs [ ] = {
{ IMAGE_TYPE_BITMAP , " BITMAP " } ,
{ IMAGE_TYPE_QUIC , " QUIC " } ,
{ IMAGE_TYPE_LZ_PLT , " LZ_PLT " } ,
{ IMAGE_TYPE_LZ_RGB , " LZ_RGB " } ,
{ IMAGE_TYPE_GLZ_RGB , " GLZ_RGB " } ,
{ IMAGE_TYPE_FROM_CACHE , " FROM_CACHE " } ,
{ IMAGE_TYPE_SURFACE , " SURFACE " } ,
{ IMAGE_TYPE_JPEG , " JPEG " } ,
{ IMAGE_TYPE_FROM_CACHE_LOSSLESS , " FROM_CACHE_LOSSLESS " } ,
{ IMAGE_TYPE_ZLIB_GLZ_RGB , " ZLIB_GLZ_RGB " } ,
{ IMAGE_TYPE_JPEG_ALPHA , " JPEG_ALPHA " } ,
{ 0 , NULL }
} ;
/* FIXME - those flags should be bit-wise, I guess! */
# define IMAGE_FLAGS_CACHE_ME (1 << 0)
# define IMAGE_FLAGS_HIGH_BITS_SET (1 << 1)
# define IMAGE_FLAGS_REPLACE_ME (1 << 2)
static const value_string image_flags_vs [ ] = {
{ 0 , " None " } ,
{ IMAGE_FLAGS_CACHE_ME , " CACHE_ME " } ,
{ IMAGE_FLAGS_HIGH_BITS_SET , " HIGH_BITS_SET " } ,
{ IMAGE_FLAGS_REPLACE_ME , " REPLACE_ME " } ,
{ 0 , NULL }
} ;
static const value_string rop_descriptor_vs [ ] = {
{ ( 1 < < 0 ) , " SPICE_ROPD_INVERS_SRC " } ,
{ ( 1 < < 1 ) , " SPICE_ROPD_INVERS_BRUSH " } ,
{ ( 1 < < 2 ) , " SPICE_ROPD_INVERS_DEST " } ,
{ ( 1 < < 3 ) , " SPICE_ROPD_OP_PUT " } ,
{ ( 1 < < 4 ) , " SPICE_ROPD_OP_OR " } ,
{ ( 1 < < 5 ) , " SPICE_ROPD_OP_AND " } ,
{ ( 1 < < 6 ) , " SPICE_ROPD_OP_XOR " } ,
{ ( 1 < < 7 ) , " SPICE_ROPD_OP_BLACKNESS " } ,
{ ( 1 < < 8 ) , " SPICE_ROPD_OP_WHITENESS " } ,
{ ( 1 < < 9 ) , " SPICE_ROPD_OP_INVERS " } ,
{ ( 1 < < 10 ) , " SPICE_ROPD_INVERS_RES " } ,
{ 0 , NULL }
} ;
# define QUIC_IMAGE_TYPE_INVALID 0
# define QUIC_IMAGE_TYPE_GRAY 1
# define QUIC_IMAGE_TYPE_RGB16 2
# define QUIC_IMAGE_TYPE_RGB24 3
# define QUIC_IMAGE_TYPE_RGB32 4
# define QUIC_IMAGE_TYPE_RGBA 5
static const value_string quic_type_vs [ ] = {
{ QUIC_IMAGE_TYPE_INVALID , " INVALID " } ,
{ QUIC_IMAGE_TYPE_GRAY , " GRAY " } ,
{ QUIC_IMAGE_TYPE_RGB16 , " RGB16 " } ,
{ QUIC_IMAGE_TYPE_RGB24 , " RGB24 " } ,
{ QUIC_IMAGE_TYPE_RGB32 , " RGB32 " } ,
{ QUIC_IMAGE_TYPE_RGBA , " RGBA " } ,
{ 0 , NULL }
} ;
# define LZ_IMAGE_TYPE_INVALID 0
# define LZ_IMAGE_TYPE_PLT1_LE 1
# define LZ_IMAGE_TYPE_PLT1_BE 2 /* PLT stands for palette */
# define LZ_IMAGE_TYPE_PLT4_LE 3
# define LZ_IMAGE_TYPE_PLT4_BE 4
# define LZ_IMAGE_TYPE_PLT8 5
# define LZ_IMAGE_TYPE_RGB16 6
# define LZ_IMAGE_TYPE_RGB24 7
# define LZ_IMAGE_TYPE_RGB32 8
# define LZ_IMAGE_TYPE_RGBA 9
# define LZ_IMAGE_TYPE_XXXA 10
static const value_string LzImage_type_vs [ ] = {
{ LZ_IMAGE_TYPE_INVALID , " INVALID " } ,
{ LZ_IMAGE_TYPE_PLT1_LE , " PLT1_LE " } ,
{ LZ_IMAGE_TYPE_PLT1_BE , " PLT1_BE " } ,
{ LZ_IMAGE_TYPE_PLT4_LE , " PLT4_LE " } ,
{ LZ_IMAGE_TYPE_PLT4_BE , " PLT4_BE " } ,
{ LZ_IMAGE_TYPE_PLT8 , " PLT8 " } ,
{ LZ_IMAGE_TYPE_RGB16 , " RGB16 " } ,
{ LZ_IMAGE_TYPE_RGB24 , " RGB24 " } ,
{ LZ_IMAGE_TYPE_RGB32 , " RGB32 " } ,
{ LZ_IMAGE_TYPE_RGBA , " RGBA " } ,
{ LZ_IMAGE_TYPE_XXXA , " RGB JPEG, Alpha LZ " } ,
{ 0 , NULL }
} ;
# define PIXMAP_FORMAT_1BIT_LE 1
# define PIXMAP_FORMAT_1BIT_BE 2
# define PIXMAP_FORMAT_4BIT_LE 3
# define PIXMAP_FORMAT_4BIT_BE 4
# define PIXMAP_FORMAT_8BIT 5
# define PIXMAP_FORMAT_16BIT 6
# define PIXMAP_FORMAT_24BIT 7
# define PIXMAP_FORMAT_32BIT 8
# define PIXMAP_FORMAT_RGBA 9
static const value_string Pixmap_types_vs [ ] = {
{ PIXMAP_FORMAT_1BIT_LE , " 1BIT_LE " } ,
{ PIXMAP_FORMAT_1BIT_BE , " 1BIT_BE " } ,
{ PIXMAP_FORMAT_4BIT_LE , " 4BIT_LE " } ,
{ PIXMAP_FORMAT_4BIT_BE , " 4BIT_BE " } ,
{ PIXMAP_FORMAT_8BIT , " 8BIT " } ,
{ PIXMAP_FORMAT_16BIT , " 16BIT " } ,
{ PIXMAP_FORMAT_24BIT , " 24BIT " } ,
{ PIXMAP_FORMAT_32BIT , " 32BIT " } ,
{ PIXMAP_FORMAT_RGBA , " RGBA " } ,
{ 0 , NULL }
} ;
/* desegmentation of spice protocol */
static gboolean spice_desegment = TRUE ;
static gint ett_spice = - 1 ;
static gint ett_link_client = - 1 ;
static gint ett_link_server = - 1 ;
static gint ett_link_caps = - 1 ;
static gint ett_data = - 1 ;
static gint ett_message = - 1 ;
static gint ett_ticket_client = - 1 ;
static gint ett_ticket_server = - 1 ;
static gint ett_playback = - 1 ;
static gint ett_display_client = - 1 ;
static gint ett_display_server = - 1 ;
static gint ett_common_server_message = - 1 ;
static gint ett_common_client_message = - 1 ;
static gint ett_point = - 1 ;
static gint ett_point16 = - 1 ;
static gint ett_cursor = - 1 ;
static gint ett_spice_main = - 1 ;
static gint ett_rect = - 1 ;
static gint ett_DisplayBase = - 1 ;
static gint ett_Clip = - 1 ;
static gint ett_Mask = - 1 ;
static gint ett_imagedesc = - 1 ;
static gint ett_imageQuic = - 1 ;
static gint ett_GLZ_RGB = - 1 ;
static gint ett_LZ_RGB = - 1 ;
static gint ett_LZ_PLT = - 1 ;
static gint ett_ZLIB_GLZ = - 1 ;
static gint ett_Uncomp_tree = - 1 ;
static gint ett_LZ_JPEG = - 1 ;
static gint ett_JPEG = - 1 ;
static gint ett_cursor_header = - 1 ;
static gint ett_RedCursor = - 1 ;
static gint ett_pattern = - 1 ;
static gint ett_brush = - 1 ;
static gint ett_Pixmap = - 1 ;
static gint ett_inputs_client = - 1 ;
static gint ett_rectlist = - 1 ;
static gint ett_inputs_server = - 1 ;
static gint ett_record_client = - 1 ;
static gint ett_main_client = - 1 ;
static gint ett_spice_agent = - 1 ;
static int proto_spice = - 1 ;
static int hf_spice_magic = - 1 ;
static int hf_major_version = - 1 ;
static int hf_minor_version = - 1 ;
static int hf_message_size = - 1 ;
static int hf_conn_id = - 1 ;
static int hf_channel_type = - 1 ;
static int hf_channel_id = - 1 ;
static int hf_num_common_caps = - 1 ;
static int hf_num_channel_caps = - 1 ;
static int hf_caps_offset = - 1 ;
static int hf_error_code = - 1 ;
static int hf_data = - 1 ;
static int hf_serial = - 1 ;
static int hf_data_size = - 1 ;
static int hf_data_sublist = - 1 ;
static int hf_link_client = - 1 ;
static int hf_link_server = - 1 ;
static int hf_ticket_client = - 1 ;
static int hf_ticket_server = - 1 ;
static int hf_main_cap = - 1 ;
static int hf_display_cap = - 1 ;
static int hf_inputs_cap = - 1 ;
static int hf_cursor_cap = - 1 ;
static int hf_record_cap = - 1 ;
static int hf_common_cap = - 1 ;
static int hf_playback_cap = - 1 ;
static int hf_playback_record_mode_timstamp = - 1 ;
static int hf_playback_record_mode = - 1 ;
static int hf_red_set_ack_generation = - 1 ;
static int hf_red_set_ack_window = - 1 ;
static int hf_Clip_type = - 1 ;
static int hf_Mask_flag = - 1 ;
static int hf_Mask_bitmap = - 1 ;
static int hf_display_rop_descriptor = - 1 ;
static int hf_display_scale_mode = - 1 ;
static int hf_display_stream_id = - 1 ;
static int hf_display_stream_width = - 1 ;
static int hf_display_stream_height = - 1 ;
static int hf_display_stream_src_width = - 1 ;
static int hf_display_stream_src_height = - 1 ;
static int hf_display_stream_data_size = - 1 ;
static int hf_display_stream_codec_type = - 1 ;
static int hf_display_stream_flags = - 1 ;
static int hf_red_ping_id = - 1 ;
static int hf_red_timestamp = - 1 ;
static int hf_spice_display_mode_width = - 1 ;
static int hf_spice_display_mode_height = - 1 ;
static int hf_spice_display_mode_depth = - 1 ;
static int hf_image_desc_id = - 1 ;
static int hf_image_desc_type = - 1 ;
static int hf_image_desc_flags = - 1 ;
static int hf_image_desc_width = - 1 ;
static int hf_image_desc_height = - 1 ;
static int hf_quic_width = - 1 ;
static int hf_quic_height = - 1 ;
static int hf_quic_major_version = - 1 ;
static int hf_quic_minor_version = - 1 ;
static int hf_quic_type = - 1 ;
static int hf_GLZ_RGB_width = - 1 ;
static int hf_GLZ_RGB_height = - 1 ;
static int hf_LZ_major_version = - 1 ;
static int hf_LZ_minor_version = - 1 ;
static int hf_GLZ_RGB_type = - 1 ;
static int hf_GLZ_RGB_stride = - 1 ;
static int hf_GLZ_RGB_dict_id = - 1 ;
static int hf_cursor_trail_len = - 1 ;
static int hf_cursor_trail_freq = - 1 ;
static int hf_cursor_trail_visible = - 1 ;
static int hf_cursor_unique = - 1 ;
static int hf_cursor_type = - 1 ;
static int hf_cursor_width = - 1 ;
static int hf_cursor_height = - 1 ;
static int hf_cursor_hotspot_x = - 1 ;
static int hf_cursor_hotspot_y = - 1 ;
static int hf_cursor_flags = - 1 ;
static int hf_cursor_id = - 1 ;
static int hf_spice_display_init_cache_id = - 1 ;
static int hf_spice_display_init_cache_size = - 1 ;
static int hf_spice_display_init_glz_dict_id = - 1 ;
static int hf_spice_display_init_dict_window_size = - 1 ;
static int hf_brush_type = - 1 ;
static int hf_brush_rgb = - 1 ;
static int hf_pixmap_width = - 1 ;
static int hf_pixmap_height = - 1 ;
static int hf_pixmap_stride = - 1 ;
static int hf_pixmap_address = - 1 ;
static int hf_pixmap_format = - 1 ;
static int hf_pixmap_flags = - 1 ;
static int hf_keyboard_bits = - 1 ;
static int hf_rectlist_size = - 1 ;
static int hf_session_id = - 1 ;
static int hf_display_channels_hint = - 1 ;
static int hf_supported_mouse_modes = - 1 ;
static int hf_current_mouse_mode = - 1 ;
static int hf_agent_connected = - 1 ;
static int hf_agent_tokens = - 1 ;
static int hf_agent_protocol = - 1 ;
static int hf_agent_type = - 1 ;
static int hf_agent_opaque = - 1 ;
static int hf_agent_size = - 1 ;
static int hf_agent_token = - 1 ;
static int hf_agent_clipboard_selection = - 1 ;
static int hf_agent_clipboard_type = - 1 ;
static int hf_multi_media_time = - 1 ;
static int hf_ram_hint = - 1 ;
static int hf_button_state = - 1 ;
static int hf_mouse_display_id = - 1 ;
static int hf_display_text_fore_mode = - 1 ;
static int hf_display_text_back_mode = - 1 ;
static int hf_display_surface_id = - 1 ;
static int hf_display_surface_width = - 1 ;
static int hf_display_surface_height = - 1 ;
static int hf_display_surface_format = - 1 ;
static int hf_display_surface_flags = - 1 ;
static int hf_main_client_agent_tokens = - 1 ;
static int hf_tranparent_src_color = - 1 ;
static int hf_tranparent_true_color = - 1 ;
static dissector_handle_t jpeg_handle ;
static guint32
dissect_ID ( tvbuff_t * tvb , proto_tree * tree , const guint32 offset )
{
const guint32 id = tvb_get_letohl ( tvb , offset ) ;
proto_tree_add_text ( tree , tvb , offset , 4 , " ID: %u (0x%x) " , id , id ) ;
return id ;
}
/* returns the pixmap size in bytes */
static guint32
dissect_Pixmap ( tvbuff_t * tvb , proto_tree * tree , guint32 offset )
{
proto_item * ti = NULL ;
proto_tree * Pixmap_tree ;
guint32 PixmapSize ;
2011-09-29 18:26:57 +00:00
guint32 strides , height /**, pallete_ptr**/ ;
2011-09-29 18:10:14 +00:00
ti = proto_tree_add_text ( tree , tvb , offset , 0 , " Pixmap " ) ; /* size is fixed later */
Pixmap_tree = proto_item_add_subtree ( ti , ett_Pixmap ) ;
proto_tree_add_item ( Pixmap_tree , hf_pixmap_format , tvb , offset , 1 , ENC_LITTLE_ENDIAN ) ;
offset + = 1 ;
proto_tree_add_item ( Pixmap_tree , hf_pixmap_flags , tvb , offset , 1 , ENC_LITTLE_ENDIAN ) ;
offset + = 1 ;
proto_tree_add_item ( Pixmap_tree , hf_pixmap_width , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
height = tvb_get_letohl ( tvb , offset ) ;
proto_tree_add_item ( Pixmap_tree , hf_pixmap_height , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
strides = tvb_get_letohl ( tvb , offset ) ;
proto_tree_add_item ( Pixmap_tree , hf_pixmap_stride , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
2011-09-29 18:26:57 +00:00
/** pallete_ptr = tvb_get_letohl(tvb, offset); **/
2011-09-29 18:10:14 +00:00
proto_tree_add_item ( Pixmap_tree , hf_pixmap_address , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
PixmapSize = height * strides ;
proto_item_set_len ( ti , 18 + PixmapSize ) ;
proto_tree_add_text ( Pixmap_tree , tvb , offset , PixmapSize , " Pixmap pixels (%d bytes) " , PixmapSize ) ;
/*TODO: complete pixmap dissection */
return PixmapSize + 18 ;
}
/* returns the type of cursor */
static guint8
dissect_CursorHeader ( tvbuff_t * tvb , proto_tree * tree , guint32 offset , guint16 * width , guint16 * height )
{
proto_item * ti = NULL ;
proto_tree * CursorHeader_tree ;
const guint8 type = tvb_get_guint8 ( tvb , offset + 8 ) ;
* width = tvb_get_letohs ( tvb , offset + 8 + 1 ) ;
* height = tvb_get_letohs ( tvb , offset + 8 + 1 + 2 ) ;
if ( tree ) {
ti = proto_tree_add_text ( tree , tvb , offset , sizeof_CursorHeader , " Cursor Header " ) ;
CursorHeader_tree = proto_item_add_subtree ( ti , ett_cursor_header ) ;
proto_tree_add_item ( CursorHeader_tree , hf_cursor_unique , tvb , offset , 8 , ENC_LITTLE_ENDIAN ) ;
offset + = 8 ;
proto_tree_add_item ( CursorHeader_tree , hf_cursor_type , tvb , offset , 1 , ENC_LITTLE_ENDIAN ) ;
offset + = 1 ;
proto_tree_add_item ( CursorHeader_tree , hf_cursor_width , tvb , offset , 2 , ENC_LITTLE_ENDIAN ) ;
offset + = 2 ;
proto_tree_add_item ( CursorHeader_tree , hf_cursor_height , tvb , offset , 2 , ENC_LITTLE_ENDIAN ) ;
offset + = 2 ;
proto_tree_add_item ( CursorHeader_tree , hf_cursor_hotspot_x , tvb , offset , 2 , ENC_LITTLE_ENDIAN ) ;
offset + = 2 ;
proto_tree_add_item ( CursorHeader_tree , hf_cursor_hotspot_y , tvb , offset , 2 , ENC_LITTLE_ENDIAN ) ;
}
return type ;
}
/* returns the size of RedCursor */
static guint32
dissect_RedCursor ( tvbuff_t * tvb , proto_tree * tree , guint32 offset )
{
proto_item * ti = NULL ;
proto_tree * RedCursor_tree ;
guint8 type ;
guint16 height , width ;
guint32 init_offset = offset , data_size = 0 ;
const guint16 flags = tvb_get_letohs ( tvb , offset ) ;
ti = proto_tree_add_text ( tree , tvb , offset , 2 , " RedCursor " ) ; /* FIXME - fix size if flag is not NONE */
RedCursor_tree = proto_item_add_subtree ( ti , ett_RedCursor ) ;
proto_tree_add_item ( RedCursor_tree , hf_cursor_flags , tvb , offset , 2 , ENC_LITTLE_ENDIAN ) ;
if ( flags = = SPICE_CURSOR_FLAGS_NONE ) {
return 2 ;
}
offset + = 2 ;
type = dissect_CursorHeader ( tvb , RedCursor_tree , offset , & width , & height ) ;
offset + = sizeof_CursorHeader ;
if ( ( width = = 0 | | height = = 0 ) | | flags = = SPICE_CURSOR_FLAGS_FROM_CACHE ) {
proto_item_set_len ( ti , offset - init_offset ) ;
return ( offset - init_offset ) ;
}
switch ( type ) {
case SPICE_CURSOR_TYPE_ALPHA :
data_size = ( width < < 2 ) * height ;
break ;
case SPICE_CURSOR_TYPE_MONO :
data_size = ( SPICE_ALIGN ( width , 8 ) > > 2 ) * height ;
break ;
/* TODO: fix all size calculations for below cursor types, using SPICE_ALIGN */
case SPICE_CURSOR_TYPE_COLOR4 :
case SPICE_CURSOR_TYPE_COLOR8 :
case SPICE_CURSOR_TYPE_COLOR16 :
case SPICE_CURSOR_TYPE_COLOR24 :
case SPICE_CURSOR_TYPE_COLOR32 :
break ;
default :
data_size = 0 ;
break ;
}
if ( data_size ! = 0 ) {
proto_tree_add_text ( RedCursor_tree , tvb , offset , data_size , " Cursor data (%u bytes) " , data_size ) ;
} else {
proto_tree_add_text ( RedCursor_tree , tvb , offset , - 1 , " Cursor data " ) ;
}
2011-09-29 20:03:51 +00:00
offset + = data_size ;
2011-09-29 18:10:14 +00:00
return ( offset - init_offset ) ;
}
/* returns the image type, needed for later */
static guint8
dissect_ImageDescriptor ( tvbuff_t * tvb , proto_tree * tree , guint32 offset )
{
proto_item * ti = NULL ;
proto_tree * ImageDescriptor_tree ;
const guint8 type = tvb_get_guint8 ( tvb , offset + 8 ) ;
if ( tree ) {
ti = proto_tree_add_text ( tree , tvb , offset , sizeof_ImageDescriptor , " Image Descriptor " ) ;
ImageDescriptor_tree = proto_item_add_subtree ( ti , ett_imagedesc ) ;
proto_tree_add_item ( ImageDescriptor_tree , hf_image_desc_id , tvb , offset , 8 , ENC_LITTLE_ENDIAN ) ;
offset + = 8 ;
proto_tree_add_item ( ImageDescriptor_tree , hf_image_desc_type , tvb , offset , 1 , ENC_LITTLE_ENDIAN ) ;
offset + = 1 ;
proto_tree_add_item ( ImageDescriptor_tree , hf_image_desc_flags , tvb , offset , 1 , ENC_LITTLE_ENDIAN ) ;
offset + = 1 ;
proto_tree_add_item ( ImageDescriptor_tree , hf_image_desc_width , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( ImageDescriptor_tree , hf_image_desc_height , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
}
return type ;
}
static guint32
dissect_ImageQuic ( tvbuff_t * tvb , proto_tree * tree , guint32 offset )
{
proto_item * ti = NULL ;
proto_tree * ImageQuic_tree ;
const guint32 QuicSize = tvb_get_letohl ( tvb , offset ) ;
if ( tree ) {
ti = proto_tree_add_text ( tree , tvb , offset , QuicSize + 4 , " QUIC Image " ) ;
ImageQuic_tree = proto_item_add_subtree ( ti , ett_imageQuic ) ;
proto_tree_add_text ( ImageQuic_tree , tvb , offset , 4 , " QUIC image size: %u bytes " , QuicSize ) ;
offset + = 4 ;
proto_tree_add_text ( ImageQuic_tree , tvb , offset , 4 , " QUIC magic (QUIC) " ) ;
offset + = 4 ;
proto_tree_add_item ( ImageQuic_tree , hf_quic_major_version , tvb , offset , 2 , ENC_LITTLE_ENDIAN ) ;
offset + = 2 ;
proto_tree_add_item ( ImageQuic_tree , hf_quic_minor_version , tvb , offset , 2 , ENC_LITTLE_ENDIAN ) ;
offset + = 2 ;
proto_tree_add_item ( ImageQuic_tree , hf_quic_type , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( ImageQuic_tree , hf_quic_width , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( ImageQuic_tree , hf_quic_height , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_text ( ImageQuic_tree , tvb , offset , QuicSize - 20 , " QUIC compressed image data (%u bytes) " , QuicSize ) ;
}
return QuicSize + 4 ;
}
static void
dissect_ImageLZ_common ( tvbuff_t * tvb , proto_tree * tree , const guint32 offset )
{
proto_tree_add_text ( tree , tvb , offset , 4 , " LZ magic ( \" ZL \" ) " ) ;
proto_tree_add_item ( tree , hf_LZ_major_version , tvb , offset + 4 , 2 , ENC_BIG_ENDIAN ) ;
proto_tree_add_item ( tree , hf_LZ_minor_version , tvb , offset + 6 , 2 , ENC_BIG_ENDIAN ) ;
}
static guint32
dissect_ImageLZ_JPEG ( tvbuff_t * tvb , proto_tree * tree , const guint32 offset )
{
proto_item * ti = NULL ;
proto_tree * LZ_JPEG_tree ;
const guint32 LZ_JPEGSize = tvb_get_letohl ( tvb , offset ) ;
if ( tree ) {
ti = proto_tree_add_text ( tree , tvb , offset , LZ_JPEGSize + 4 , " LZ_JPEG Image " ) ;
LZ_JPEG_tree = proto_item_add_subtree ( ti , ett_LZ_JPEG ) ;
proto_tree_add_text ( LZ_JPEG_tree , tvb , offset , 4 , " LZ JPEG image size: %u bytes " , LZ_JPEGSize ) ;
dissect_ImageLZ_common ( tvb , LZ_JPEG_tree , offset + 4 ) ;
}
return offset + 12 ;
}
static guint32
dissect_ImageGLZ_RGB ( tvbuff_t * tvb , proto_tree * tree , guint32 offset , const guint32 size , gboolean zlib_compressed )
{
proto_item * ti = NULL ;
proto_tree * GLZ_RGB_tree ;
guint32 GLZ_RGBSize ;
if ( size = = 0 ) { /* if no size was passed to us, need to fetch it. Otherwise, we already have it from the callee */
GLZ_RGBSize = tvb_get_letohl ( tvb , offset ) ;
ti = proto_tree_add_text ( tree , tvb , offset , GLZ_RGBSize + 4 , " GLZ_RGB Image " ) ;
GLZ_RGB_tree = proto_item_add_subtree ( ti , ett_GLZ_RGB ) ;
proto_tree_add_text ( GLZ_RGB_tree , tvb , offset , 4 , " GLZ RGB image size: %u bytes " , GLZ_RGBSize ) ;
offset + = 4 ;
} else {
GLZ_RGBSize = size ;
ti = proto_tree_add_text ( tree , tvb , offset , GLZ_RGBSize , " GLZ_RGB Image " ) ;
GLZ_RGB_tree = proto_item_add_subtree ( ti , ett_GLZ_RGB ) ;
}
if ( tree ) {
dissect_ImageLZ_common ( tvb , GLZ_RGB_tree , offset ) ;
offset + = 8 ;
/* FIXME: this disregards the possible encoding within the type, so currently the type is sometimes wrong. From server/glz_encoder.c:
if ( top_down ) {
encode ( encoder , ( type & LZ_IMAGE_TYPE_MASK ) | ( 1 < < LZ_IMAGE_TYPE_LOG ) ) ;
} else {
encode ( encoder , ( type & LZ_IMAGE_TYPE_MASK ) ) ;
}
*/
proto_tree_add_item ( GLZ_RGB_tree , hf_GLZ_RGB_type , tvb , offset , 2 , ENC_LITTLE_ENDIAN ) ;
offset + = 2 ;
if ( ! zlib_compressed )
offset + = 2 ; /* TODO: in ZLIB_GLZ_RGB, there are two extra bytes here! Understand why, but for the time being, hop over them. */
proto_tree_add_item ( GLZ_RGB_tree , hf_GLZ_RGB_width , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( GLZ_RGB_tree , hf_GLZ_RGB_height , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( GLZ_RGB_tree , hf_GLZ_RGB_stride , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( GLZ_RGB_tree , hf_GLZ_RGB_dict_id , tvb , offset , 8 , ENC_LITTLE_ENDIAN ) ;
offset + = 8 ;
proto_tree_add_text ( GLZ_RGB_tree , tvb , offset , GLZ_RGBSize - 30 , " GLZ_RGB compressed image data (%u bytes) " , GLZ_RGBSize - 30 ) ;
}
return GLZ_RGBSize + 4 ;
}
static guint32
dissect_ImageLZ_RGB ( tvbuff_t * tvb , proto_tree * tree , guint32 offset )
{
proto_item * ti = NULL ;
proto_tree * LZ_RGB_tree ;
const guint32 LZ_RGBSize = tvb_get_letohl ( tvb , offset ) ;
if ( tree ) {
ti = proto_tree_add_text ( tree , tvb , offset , LZ_RGBSize + 4 , " LZ_RGB Image " ) ;
LZ_RGB_tree = proto_item_add_subtree ( ti , ett_LZ_RGB ) ;
proto_tree_add_text ( LZ_RGB_tree , tvb , offset , 4 , " LZ RGB image size: %u bytes " , LZ_RGBSize ) ;
offset + = 4 ;
dissect_ImageLZ_common ( tvb , LZ_RGB_tree , offset ) ;
offset + = 8 ;
/* FIXME: this disregards the possible encoding within the type, so currently the type is sometimes wrong. From server/glz_encoder.c:
if ( top_down ) {
encode ( encoder , ( type & LZ_IMAGE_TYPE_MASK ) | ( 1 < < LZ_IMAGE_TYPE_LOG ) ) ;
} else {
encode ( encoder , ( type & LZ_IMAGE_TYPE_MASK ) ) ;
}
*/
offset + = 2 ; /* TODO: not sure we need this! Understand what this is */
2011-10-06 03:35:44 +00:00
proto_tree_add_item ( LZ_RGB_tree , hf_GLZ_RGB_type , tvb , offset , 2 , ENC_BIG_ENDIAN ) ;
2011-09-29 18:10:14 +00:00
offset + = 2 ;
/*FIXME: sometimes width is incorrect (bitmask?) */
2011-10-06 03:35:44 +00:00
proto_tree_add_item ( LZ_RGB_tree , hf_GLZ_RGB_width , tvb , offset , 4 , ENC_BIG_ENDIAN ) ;
2011-09-29 18:10:14 +00:00
offset + = 4 ;
2011-10-06 03:35:44 +00:00
proto_tree_add_item ( LZ_RGB_tree , hf_GLZ_RGB_height , tvb , offset , 4 , ENC_BIG_ENDIAN ) ;
2011-09-29 18:10:14 +00:00
offset + = 4 ;
2011-10-06 03:35:44 +00:00
proto_tree_add_item ( LZ_RGB_tree , hf_GLZ_RGB_stride , tvb , offset , 4 , ENC_BIG_ENDIAN ) ;
2011-09-29 18:10:14 +00:00
offset + = 4 ;
2011-10-06 03:35:44 +00:00
proto_tree_add_item ( LZ_RGB_tree , hf_GLZ_RGB_dict_id , tvb , offset , 8 , ENC_LITTLE_ENDIAN ) ;
2011-09-29 18:10:14 +00:00
offset + = 8 ;
/*TODO: show win_head_image_dist - not sure what it stands for though
* proto_tree_add_item ( LZ_RGB_tree , hf_GLZ_RGB_win_head_image_dist , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
* offset + = 4 ;
* */
proto_tree_add_text ( LZ_RGB_tree , tvb , offset , LZ_RGBSize - 30 , " LZ_RGB compressed image data (%u bytes) " , LZ_RGBSize ) ;
}
return LZ_RGBSize + 4 ;
}
static guint32
dissect_ImageLZ_PLT ( tvbuff_t * tvb , proto_tree * tree , guint32 offset )
{
proto_item * ti = NULL ;
proto_tree * LZ_PLT_tree ;
guint32 LZ_PLTSize , somesize ;
/* TODO: const guint8 flags = tvb_get_guint8(tvb, offset); dissect and present */
LZ_PLTSize = tvb_get_letohl ( tvb , offset + 1 ) ;
ti = proto_tree_add_text ( tree , tvb , offset , LZ_PLTSize + 5 , " LZ_PLT Image " ) ;
LZ_PLT_tree = proto_item_add_subtree ( ti , ett_LZ_PLT ) ;
proto_tree_add_text ( LZ_PLT_tree , tvb , offset , 1 , " Flag " ) ;
offset + = 1 ;
proto_tree_add_text ( LZ_PLT_tree , tvb , offset , 4 , " LZ PLT image size: %u bytes " , LZ_PLTSize ) ;
offset + = 4 ;
somesize = tvb_get_letohl ( tvb , offset ) ;
proto_tree_add_text ( LZ_PLT_tree , tvb , offset , 4 , " Some other size: %u " , somesize ) ;
offset + = 4 ; /* FIXME - pal pallete value? offset? */
dissect_ImageLZ_common ( tvb , LZ_PLT_tree , offset ) ;
offset + = 8 ;
/* TODO - complete dissection */
return 1 + 4 + 4 + 8 + 8 + LZ_PLTSize ;
}
static guint32
dissect_ImageJPEG_Alpha ( tvbuff_t * tvb , proto_tree * tree , packet_info * pinfo , guint32 offset )
{
2011-09-29 20:03:51 +00:00
proto_item * ti = NULL ;
proto_tree * JPEG_tree ;
tvbuff_t * jpeg_tvb ;
2011-09-29 18:10:14 +00:00
guint32 JPEG_Size , Data_Size ;
/*TODO: const guint8 flags = tvb_get_guint8(tvb, offset); dissect and present */
offset + = 1 ;
JPEG_Size = tvb_get_letohl ( tvb , offset ) ;
offset + = 4 ;
Data_Size = tvb_get_letohl ( tvb , offset ) ;
offset + = 4 ;
2011-09-29 20:03:51 +00:00
ti = proto_tree_add_text ( tree , tvb , offset - 9 , Data_Size + 5 , " RGB JPEG Image, Alpha channel (%u bytes) " , Data_Size ) ;
JPEG_tree = proto_item_add_subtree ( ti , ett_JPEG ) ;
2011-09-29 18:10:14 +00:00
2011-09-29 20:03:51 +00:00
jpeg_tvb = tvb_new_subset ( tvb , offset , JPEG_Size , JPEG_Size ) ;
call_dissector ( jpeg_handle , jpeg_tvb , pinfo , JPEG_tree ) ;
2011-09-29 18:10:14 +00:00
offset + = JPEG_Size ;
offset + = dissect_ImageLZ_JPEG ( tvb , JPEG_tree , offset - 4 ) ;
2011-09-29 20:03:51 +00:00
return Data_Size + 1 ;
2011-09-29 18:10:14 +00:00
}
static guint32
dissect_ImageJPEG ( tvbuff_t * tvb , proto_tree * tree , packet_info * pinfo , const guint32 offset )
{
proto_item * ti = NULL ;
proto_tree * JPEG_tree ;
tvbuff_t * jpeg_tvb ;
const guint32 JPEG_Size = tvb_get_letohl ( tvb , offset ) ;
ti = proto_tree_add_text ( tree , tvb , offset , JPEG_Size + 4 , " JPEG Image (%u bytes) " , JPEG_Size ) ;
JPEG_tree = proto_item_add_subtree ( ti , ett_JPEG ) ;
jpeg_tvb = tvb_new_subset ( tvb , offset + 4 , JPEG_Size , JPEG_Size ) ;
call_dissector ( jpeg_handle , jpeg_tvb , pinfo , JPEG_tree ) ;
return JPEG_Size + 4 ;
}
2011-09-29 19:57:52 +00:00
# ifdef HAVE_LIBZ
static void
dissect_ImageZLIB_GLZ_stream ( tvbuff_t * tvb , proto_tree * ZLIB_GLZ_tree , packet_info * pinfo ,
guint32 offset , guint32 ZLIB_GLZSize , guint32 ZLIB_uncompSize )
{
proto_item * ti ;
proto_tree * Uncomp_tree ;
tvbuff_t * uncompressed_tvb ;
ti = proto_tree_add_text ( ZLIB_GLZ_tree , tvb , offset , ZLIB_GLZSize , " ZLIB stream (%u bytes) " , ZLIB_GLZSize ) ;
uncompressed_tvb = tvb_uncompress ( tvb , offset , ZLIB_GLZSize ) ;
if ( uncompressed_tvb ! = NULL ) {
add_new_data_source ( pinfo , uncompressed_tvb , " Uncompressed GLZ stream " ) ;
Uncomp_tree = proto_item_add_subtree ( ti , ett_Uncomp_tree ) ;
dissect_ImageGLZ_RGB ( uncompressed_tvb , Uncomp_tree , 0 , ZLIB_uncompSize , TRUE ) ;
} else {
proto_tree_add_text ( ZLIB_GLZ_tree , tvb , offset , - 1 , " Error: Unable to decompress content " ) ;
}
}
# else
static void
dissect_ImageZLIB_GLZ_stream ( tvbuff_t * tvb , proto_tree * ZLIB_GLZ_tree , packet_info * pinfo _U_ ,
guint32 offset , guint32 ZLIB_GLZSize , guint32 ZLIB_uncompSize _U_ )
{
proto_tree_add_text ( ZLIB_GLZ_tree , tvb , offset , ZLIB_GLZSize , " ZLIB stream (%u bytes) " , ZLIB_GLZSize ) ;
}
# endif
2011-09-29 18:10:14 +00:00
static guint32
dissect_ImageZLIB_GLZ ( tvbuff_t * tvb , proto_tree * tree , packet_info * pinfo , guint32 offset )
{
proto_item * ti = NULL ;
2011-09-29 19:57:52 +00:00
proto_tree * ZLIB_GLZ_tree ;
2011-09-29 18:10:14 +00:00
guint32 ZLIB_GLZSize , ZLIB_uncompSize ;
ZLIB_uncompSize = tvb_get_letohl ( tvb , offset ) ;
ZLIB_GLZSize = tvb_get_letohl ( tvb , offset + 4 ) ; /* compressed size */
if ( tree ) {
ti = proto_tree_add_text ( tree , tvb , offset , ZLIB_GLZSize + 8 , " ZLIB over GLZ Image " ) ;
ZLIB_GLZ_tree = proto_item_add_subtree ( ti , ett_ZLIB_GLZ ) ;
proto_tree_add_text ( ZLIB_GLZ_tree , tvb , offset , 4 , " ZLIB stream uncompressed size: %u bytes " , ZLIB_uncompSize ) ;
offset + = 4 ;
proto_tree_add_text ( ZLIB_GLZ_tree , tvb , offset , 4 , " ZLIB stream compressed size: %u bytes " , ZLIB_GLZSize ) ;
offset + = 4 ;
2011-09-29 19:57:52 +00:00
dissect_ImageZLIB_GLZ_stream ( tvb , ZLIB_GLZ_tree , pinfo , offset , ZLIB_GLZSize , ZLIB_uncompSize ) ;
2011-09-29 18:10:14 +00:00
}
return ZLIB_GLZSize + 8 ;
}
/* returns the size of an image, not offset */
static guint32
dissect_Image ( tvbuff_t * tvb , proto_tree * tree , packet_info * pinfo , guint32 offset )
{
guint32 ImageSize = 0 ;
const guint8 type = dissect_ImageDescriptor ( tvb , tree , offset ) ; ;
offset + = sizeof_ImageDescriptor ;
switch ( type ) {
case IMAGE_TYPE_QUIC :
ImageSize = dissect_ImageQuic ( tvb , tree , offset ) ;
break ;
case IMAGE_TYPE_GLZ_RGB :
ImageSize = dissect_ImageGLZ_RGB ( tvb , tree , offset , 0 , FALSE ) ;
break ;
case IMAGE_TYPE_LZ_RGB :
ImageSize = dissect_ImageLZ_RGB ( tvb , tree , offset ) ;
break ;
case IMAGE_TYPE_BITMAP :
ImageSize = dissect_Pixmap ( tvb , tree , offset ) ;
break ;
case IMAGE_TYPE_FROM_CACHE :
proto_tree_add_text ( tree , tvb , offset , 0 , " Image from Cache " ) ;
break ;
case IMAGE_TYPE_FROM_CACHE_LOSSLESS :
proto_tree_add_text ( tree , tvb , offset , 0 , " Image from Cache - lossless " ) ;
break ;
case IMAGE_TYPE_ZLIB_GLZ_RGB :
ImageSize = dissect_ImageZLIB_GLZ ( tvb , tree , pinfo , offset ) ;
break ;
case IMAGE_TYPE_JPEG :
ImageSize = dissect_ImageJPEG ( tvb , tree , pinfo , offset ) ;
break ;
case IMAGE_TYPE_JPEG_ALPHA :
ImageSize = dissect_ImageJPEG_Alpha ( tvb , tree , pinfo , offset ) ;
break ;
case IMAGE_TYPE_LZ_PLT :
ImageSize = dissect_ImageLZ_PLT ( tvb , tree , offset ) ;
break ;
case IMAGE_TYPE_SURFACE :
ImageSize = 4 ; /* surface ID */
proto_tree_add_text ( tree , tvb , offset , ImageSize , " Surface ID: %u " , tvb_get_letohl ( tvb , offset ) ) ;
break ;
default :
proto_tree_add_text ( tree , tvb , offset , 0 , " Unknown image type - cannot dissect " ) ;
}
return sizeof_ImageDescriptor + ImageSize ;
}
static SpiceRect
dissect_SpiceRect ( tvbuff_t * tvb , proto_tree * tree , const guint32 offset , const gint32 id )
{
proto_item * ti = NULL ;
proto_tree * rect_tree ;
SpiceRect rect ;
rect . left = tvb_get_letohl ( tvb , offset ) ;
rect . top = tvb_get_letohl ( tvb , offset + 4 ) ;
rect . right = tvb_get_letohl ( tvb , offset + 8 ) ;
rect . bottom = tvb_get_letohl ( tvb , offset + 12 ) ;
if ( tree ) {
if ( id ! = - 1 ) {
ti = proto_tree_add_text ( tree , tvb , offset , sizeof_SpiceRect , " RECT %u: (%u-%u, %u-%u) " , id , rect . left , rect . top , rect . right , rect . bottom ) ;
} else { /* single rectangle */
ti = proto_tree_add_text ( tree , tvb , offset , sizeof_SpiceRect , " RECT: (%u-%u, %u-%u) " , rect . left , rect . top , rect . right , rect . bottom ) ;
}
rect_tree = proto_item_add_subtree ( ti , ett_rect ) ;
proto_tree_add_text ( rect_tree , tvb , offset , 4 , " left: %u " , rect . left ) ;
proto_tree_add_text ( rect_tree , tvb , offset + 4 , 4 , " top: %u " , rect . top ) ;
proto_tree_add_text ( rect_tree , tvb , offset + 8 , 4 , " right: %u " , rect . right ) ;
proto_tree_add_text ( rect_tree , tvb , offset + 12 , 4 , " bottom: %u " , rect . bottom ) ;
}
return rect ;
}
static guint32
rect_is_empty ( const SpiceRect r )
{
return r . top = = r . bottom | | r . left = = r . right ;
}
static guint32
dissect_RectList ( tvbuff_t * tvb , proto_tree * tree , guint32 offset )
{
proto_item * ti = NULL ;
proto_tree * rectlist_tree ;
guint32 i ;
const guint32 rectlist_size = tvb_get_letohl ( tvb , offset ) ;
if ( tree ) {
ti = proto_tree_add_text ( tree , tvb , offset , 4 + ( rectlist_size * sizeof_SpiceRect ) , " RectList (%d rects) " , rectlist_size ) ;
rectlist_tree = proto_item_add_subtree ( ti , ett_rectlist ) ;
proto_tree_add_item ( rectlist_tree , hf_rectlist_size , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
for ( i = 0 ; i ! = rectlist_size ; i + + ) {
dissect_SpiceRect ( tvb , rectlist_tree , offset , i ) ;
offset + = sizeof_SpiceRect ;
}
}
return ( 4 + ( rectlist_size * sizeof_SpiceRect ) ) ;
}
/* returns clip type */
static guint8
dissect_Clip ( tvbuff_t * tvb , proto_tree * tree , const guint32 offset )
{
proto_item * ti = NULL ;
proto_tree * Clip_tree ;
const guint8 type = tvb_get_guint8 ( tvb , offset ) ;
if ( tree ) {
ti = proto_tree_add_text ( tree , tvb , offset , 1 , " SpiceClip " ) ;
Clip_tree = proto_item_add_subtree ( ti , ett_Clip ) ;
proto_tree_add_item ( Clip_tree , hf_Clip_type , tvb , offset , sizeof_Clip , ENC_LITTLE_ENDIAN ) ;
}
return type ;
}
static POINT
dissect_POINT ( tvbuff_t * tvb , proto_tree * tree , const guint32 offset )
{
proto_item * ti = NULL ;
proto_tree * point_tree ;
POINT point ;
point . x = tvb_get_letohl ( tvb , offset ) ;
point . y = tvb_get_letohl ( tvb , offset + 4 ) ;
if ( tree ) {
ti = proto_tree_add_text ( tree , tvb , offset , sizeof_POINT , " POINT (%u, %u) " , point . x , point . y ) ;
point_tree = proto_item_add_subtree ( ti , ett_point ) ;
proto_tree_add_text ( point_tree , tvb , offset , 4 , " x: %u " , point . x ) ;
proto_tree_add_text ( point_tree , tvb , offset + 4 , 4 , " y: %u " , point . y ) ;
}
return point ;
}
static POINT16
dissect_POINT16 ( tvbuff_t * tvb , proto_tree * tree , const guint32 offset )
{
proto_item * ti = NULL ;
proto_tree * point16_tree ;
POINT16 point16 ;
point16 . x = tvb_get_letohs ( tvb , offset ) ;
point16 . y = tvb_get_letohs ( tvb , offset + 2 ) ;
if ( tree ) {
ti = proto_tree_add_text ( tree , tvb , offset , sizeof_POINT16 , " POINT16 (%u, %u) " , point16 . x , point16 . y ) ;
point16_tree = proto_item_add_subtree ( ti , ett_point16 ) ;
proto_tree_add_text ( point16_tree , tvb , offset , 2 , " x: %u " , point16 . x ) ;
proto_tree_add_text ( point16_tree , tvb , offset + 2 , 2 , " y: %u " , point16 . y ) ;
}
return point16 ;
}
static guint32
dissect_Mask ( tvbuff_t * tvb , proto_tree * tree , guint32 offset )
{
proto_item * ti = NULL ;
proto_tree * Mask_tree ;
guint32 bitmap ;
ti = proto_tree_add_text ( tree , tvb , offset , sizeof_Mask , " Mask " ) ;
Mask_tree = proto_item_add_subtree ( ti , ett_Mask ) ;
proto_tree_add_item ( Mask_tree , hf_Mask_flag , tvb , offset , 1 , ENC_LITTLE_ENDIAN ) ;
offset + = 1 ;
dissect_POINT ( tvb , Mask_tree , offset ) ;
offset + = sizeof_POINT ;
bitmap = tvb_get_letohl ( tvb , offset ) ;
proto_tree_add_item ( Mask_tree , hf_Mask_bitmap , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
if ( bitmap ! = 0 ) {
proto_item_set_len ( ti , sizeof_Mask + sizeof_ImageDescriptor ) ;
dissect_ImageDescriptor ( tvb , Mask_tree , offset ) ;
return sizeof_Mask + sizeof_ImageDescriptor ;
}
return sizeof_Mask ;
}
/* returns brush size */
static guint32
dissect_Brush ( tvbuff_t * tvb , proto_tree * tree , guint32 offset )
{
proto_item * ti = NULL ;
proto_tree * brush_tree ;
const guint8 type = tvb_get_guint8 ( tvb , offset ) ;
switch ( type ) {
case SPICE_BRUSH_TYPE_SOLID :
ti = proto_tree_add_text ( tree , tvb , offset , 5 , " Brush - SOLID " ) ;
brush_tree = proto_item_add_subtree ( ti , ett_brush ) ;
proto_tree_add_item ( brush_tree , hf_brush_type , tvb , offset , 1 , ENC_LITTLE_ENDIAN ) ;
offset + = 1 ;
proto_tree_add_item ( brush_tree , hf_brush_rgb , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
return 5 ;
break ;
case SPICE_BRUSH_TYPE_PATTERN :
ti = proto_tree_add_text ( tree , tvb , offset , 17 , " Brush - PATTERN " ) ;
brush_tree = proto_item_add_subtree ( ti , ett_brush ) ;
proto_tree_add_item ( brush_tree , hf_brush_type , tvb , offset , 1 , ENC_LITTLE_ENDIAN ) ;
offset + = 1 ;
/* FIXME: this is supposed to be the offset to the image to be used as the pattern. For now the hack is that callers check if the returned size was not 5 (therefore SOLID, it's a pattern and later on dissect the image. That's bad. Really. */
dissect_ID ( tvb , brush_tree , offset ) ;
offset + = 4 ;
dissect_POINT ( tvb , brush_tree , offset ) ;
offset + = 8 ;
return ( 1 + 4 + 8 ) ;
break ;
case SPICE_BRUSH_TYPE_NONE :
proto_tree_add_text ( tree , tvb , offset , 1 , " Brush - NONE " ) ;
return 1 ;
break ;
default :
proto_tree_add_text ( tree , tvb , offset , 0 , " Invalid Brush type " ) ;
return 0 ;
break ;
}
return 0 ;
}
static guint32
dissect_DisplayBase ( tvbuff_t * tvb , proto_tree * tree , guint32 offset )
{
proto_item * ti = NULL ;
proto_tree * DisplayBase_tree ;
SpiceRect rect ;
guint8 clip_type ;
guint32 clip_size = 0 ;
ti = proto_tree_add_text ( tree , tvb , offset , sizeof_DisplayBase , " SpiceMsgDisplayBase " ) ;
DisplayBase_tree = proto_item_add_subtree ( ti , ett_DisplayBase ) ;
proto_tree_add_item ( DisplayBase_tree , hf_display_surface_id , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
rect = dissect_SpiceRect ( tvb , DisplayBase_tree , offset , - 1 ) ;
proto_item_append_text ( ti , " - SpiceRect box (%u-%u, %u-%u) " , rect . left , rect . top , rect . right , rect . bottom ) ;
offset + = sizeof_SpiceRect ;
clip_type = dissect_Clip ( tvb , DisplayBase_tree , offset ) ;
offset + = sizeof_Clip ;
if ( clip_type = = CLIP_TYPE_RECTS ) {
clip_size = dissect_RectList ( tvb , DisplayBase_tree , offset ) ;
offset + = clip_size ;
proto_item_set_len ( ti , sizeof_DisplayBase + clip_size ) ;
return sizeof_DisplayBase + clip_size ;
}
return sizeof_DisplayBase ;
}
static const gchar * get_message_type_string ( const guint16 message_type , const spice_conversation_t * spice_info , const gboolean client_message )
{
if ( message_type < SPICE_FIRST_AVAIL_MESSAGE ) { /* this is a common message */
if ( client_message ) {
return val_to_str_const ( message_type , common_client_message_types , " Unknown client message " ) ;
} else {
return val_to_str_const ( message_type , common_server_message_types , " Unknown server message " ) ;
}
}
switch ( spice_info - > channel_type ) {
case SPICE_CHANNEL_PLAYBACK :
return val_to_str_const ( message_type , playback_server_message_types , " Unknown playback channel server message " ) ;
break ;
case SPICE_CHANNEL_RECORD :
if ( client_message ) {
return val_to_str_const ( message_type , record_client_message_types , " Unknown record channel client message " ) ;
} else {
return val_to_str_const ( message_type , record_server_message_types , " Unknown record channel server message " ) ;
}
break ;
case SPICE_CHANNEL_MAIN :
if ( client_message ) {
return val_to_str_const ( message_type , main_client_message_types , " Unknown main channel client message " ) ;
} else {
return val_to_str_const ( message_type , main_server_message_types , " Unknown main channel server message " ) ;
}
break ;
case SPICE_CHANNEL_CURSOR :
if ( client_message ) {
return val_to_str_const ( message_type , cursor_client_message_types , " Unknown cursor channel client message " ) ;
} else {
return val_to_str_const ( message_type , cursor_server_message_types , " Unknown cursor channel server message " ) ;
}
break ;
case SPICE_CHANNEL_DISPLAY :
if ( client_message ) {
return val_to_str_const ( message_type , display_client_message_types , " Unknown display channel client message " ) ;
} else {
return val_to_str_const ( message_type , display_server_message_types , " Unknown display channel server message " ) ;
}
break ;
case SPICE_CHANNEL_INPUTS :
if ( client_message ) {
return val_to_str_const ( message_type , inputs_client_message_types , " Unknown inputs channel client message " ) ;
} else {
return val_to_str_const ( message_type , inputs_server_message_types , " Unknown inputs channel server message " ) ;
}
break ;
default :
break ;
}
return " Unknown message " ;
}
static void
dissect_spice_data_header ( tvbuff_t * tvb , proto_tree * tree , const spice_conversation_t * spice_info , const gboolean client_message , const guint16 message_type , guint32 * sublist_size , guint32 offset )
{
* sublist_size = tvb_get_letohl ( tvb , offset + 14 ) ;
if ( tree ) {
proto_tree_add_item ( tree , hf_serial , tvb , offset , 8 , ENC_LITTLE_ENDIAN ) ;
offset + = 8 ;
proto_tree_add_text ( tree , tvb , offset , 2 , " Message type: %s (%d) " , get_message_type_string ( message_type , spice_info , client_message ) , message_type ) ;
offset + = 2 ;
proto_tree_add_item ( tree , hf_data_size , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( tree , hf_data_sublist , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
}
}
static guint32
dissect_spice_common_client_messages ( tvbuff_t * tvb , proto_tree * tree , const guint16 message_type , guint32 offset )
{
proto_item * ti = NULL ;
proto_tree * client_message_tree ;
switch ( message_type ) {
case SPICEC_ACK_SYNC :
ti = proto_tree_add_text ( tree , tvb , offset , 4 , " Client ACK_SYNC message " ) ;
client_message_tree = proto_item_add_subtree ( ti , ett_common_client_message ) ;
proto_tree_add_item ( client_message_tree , hf_red_set_ack_generation , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
break ;
case SPICEC_ACK :
ti = proto_tree_add_text ( tree , tvb , offset , 0 , " Client ACK message " ) ;
client_message_tree = proto_item_add_subtree ( ti , ett_common_client_message ) ;
break ;
case SPICEC_PONG :
ti = proto_tree_add_text ( tree , tvb , offset , 12 , " Client PONG message " ) ;
client_message_tree = proto_item_add_subtree ( ti , ett_common_client_message ) ;
proto_tree_add_item ( client_message_tree , hf_red_ping_id , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( client_message_tree , hf_red_timestamp , tvb , offset , 8 , ENC_LITTLE_ENDIAN ) ;
offset + = 8 ;
break ;
/*
case SPICEC_MIGRATE_FLUSH_MARK :
case SPICEC_MIGRATE_DATA :
case SPICEC_DISCONNECTING :
*/
default :
proto_tree_add_text ( tree , tvb , offset , 0 , " Unknown common client message - cannot dissect " ) ;
break ;
}
return offset ;
}
static guint32
dissect_spice_common_server_messages ( tvbuff_t * tvb , proto_tree * tree , const guint16 message_type , guint32 offset , const guint32 total_message_size )
{
proto_item * ti = NULL ;
proto_tree * server_message_tree ;
guint32 message_len , severity , visibility ;
switch ( message_type ) {
/*
case SPICE_MIGRATE :
case SPICE_MIGRATE_DATA :
case SPICE_WAIT_FOR_CHANNELS :
case SPICE_DISCONNECTING :
*/
case SPICE_SET_ACK :
ti = proto_tree_add_text ( tree , tvb , offset , 8 , " Server SET_ACK message " ) ;
server_message_tree = proto_item_add_subtree ( ti , ett_common_server_message ) ;
proto_tree_add_item ( server_message_tree , hf_red_set_ack_generation , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( server_message_tree , hf_red_set_ack_window , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
break ;
case SPICE_PING :
ti = proto_tree_add_text ( tree , tvb , offset , 12 , " Server PING message " ) ;
server_message_tree = proto_item_add_subtree ( ti , ett_common_server_message ) ;
proto_tree_add_item ( server_message_tree , hf_red_ping_id , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( server_message_tree , hf_red_timestamp , tvb , offset , 8 , ENC_LITTLE_ENDIAN ) ;
offset + = 8 ;
if ( total_message_size > 12 ) {
proto_tree_add_text ( server_message_tree , tvb , offset , total_message_size - 12 , " PING DATA (%d bytes) " , total_message_size - 12 ) ;
offset + = ( total_message_size - 12 ) ;
}
break ;
case SPICE_NOTIFY :
ti = proto_tree_add_text ( tree , tvb , offset , 12 , " Server NOTIFY message " ) ;
server_message_tree = proto_item_add_subtree ( ti , ett_common_server_message ) ;
proto_tree_add_item ( server_message_tree , hf_red_timestamp , tvb , offset , 8 , ENC_LITTLE_ENDIAN ) ;
offset + = 8 ;
/* TODO: properly dissect severity and visibility flags, using hf_ and proto_tree_add_item */
severity = tvb_get_letohl ( tvb , offset ) ;
proto_tree_add_text ( server_message_tree , tvb , offset , 4 , " Severity: %s (%d) " , val_to_str_const ( severity , spice_severity_vs , " unknown severity " ) , severity ) ;
offset + = 4 ;
visibility = tvb_get_letohl ( tvb , offset ) ;
proto_tree_add_text ( server_message_tree , tvb , offset , 4 , " Visibility: %s (%d) " , val_to_str_const ( visibility , spice_visibility_vs , " unknown visibility " ) , visibility ) ;
offset + = 4 ;
/*TODO: based on severity, dissect the error code */
proto_tree_add_text ( server_message_tree , tvb , offset , 4 , " error/warning/info code: %d " , tvb_get_letohl ( tvb , offset ) ) ;
offset + = 4 ;
message_len = tvb_get_letohl ( tvb , offset ) ;
proto_tree_add_text ( server_message_tree , tvb , offset , 4 , " message length: %d " , message_len ) ;
offset + = 4 ;
/*TODO use proto_tree_add_string and get the stringz using tvb_get_ephemeral_stringz() */
proto_tree_add_text ( server_message_tree , tvb , offset , message_len + 1 , " Message content " ) ;
offset + = ( message_len + 1 ) ;
break ;
default :
proto_tree_add_text ( tree , tvb , offset , 0 , " Unknown common server message - cannot dissect " ) ;
break ;
}
return offset ;
}
static guint32
dissect_spice_record_client ( tvbuff_t * tvb , proto_tree * tree , const guint16 message_type , guint32 offset )
{
proto_item * ti = NULL ;
proto_tree * record_tree ;
switch ( message_type ) {
case SPICEC_RECORD_MODE :
ti = proto_tree_add_text ( tree , tvb , offset , 8 , " Client RECORD_MODE message " ) ; /* size is incorrect, fixed later */
record_tree = proto_item_add_subtree ( ti , ett_record_client ) ;
proto_tree_add_item ( record_tree , hf_playback_record_mode_timstamp , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( record_tree , hf_playback_record_mode , tvb , offset , 2 , ENC_LITTLE_ENDIAN ) ;
offset + = 2 ;
/* TODO - mode dependant, there may be more data here */
break ;
default :
proto_tree_add_text ( tree , tvb , offset , 0 , " Unknown record client message - cannot dissect " ) ;
break ;
}
return offset ;
}
static guint32
dissect_spice_display_client ( tvbuff_t * tvb , proto_tree * tree , const guint16 message_type , guint32 offset )
{
proto_item * ti = NULL ;
proto_tree * display_tree ;
switch ( message_type ) {
case SPICEC_DISPLAY_INIT :
ti = proto_tree_add_text ( tree , tvb , offset , sizeof_RedcDisplayInit , " Client INIT message " ) ;
display_tree = proto_item_add_subtree ( ti , ett_display_client ) ;
proto_tree_add_item ( display_tree , hf_spice_display_init_cache_id , tvb , offset , 1 , ENC_LITTLE_ENDIAN ) ;
offset + = 1 ;
proto_tree_add_item ( display_tree , hf_spice_display_init_cache_size , tvb , offset , 8 , ENC_LITTLE_ENDIAN ) ;
offset + = 8 ;
proto_tree_add_item ( display_tree , hf_spice_display_init_glz_dict_id , tvb , offset , 1 , ENC_LITTLE_ENDIAN ) ;
offset + = 1 ;
proto_tree_add_item ( display_tree , hf_spice_display_init_dict_window_size , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
break ;
default :
proto_tree_add_text ( tree , tvb , offset , 0 , " Unknown display client message - cannot dissect " ) ;
break ;
}
return offset ;
}
static guint32
dissect_spice_display_server ( tvbuff_t * tvb , proto_tree * tree , packet_info * pinfo , const guint16 message_type , guint32 offset )
{
guint32 data_size , displayBaseLen ;
guint8 clip_type ;
guint16 glyphs ;
SpiceRect r ;
tvbuff_t * jpeg_tvb ;
switch ( message_type ) {
case SPICE_DISPLAY_MODE :
proto_tree_add_item ( tree , hf_spice_display_mode_width , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( tree , hf_spice_display_mode_height , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( tree , hf_spice_display_mode_depth , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
break ;
case SPICE_DISPLAY_MARK :
proto_tree_add_text ( tree , tvb , offset , 0 , " DISPLAY_MARK message " ) ;
break ;
case SPICE_DISPLAY_RESET :
proto_tree_add_text ( tree , tvb , offset , 0 , " DISPLAY_RESET message " ) ;
break ;
case SPICE_DISPLAY_DRAW_ALPHA_BLEND :
displayBaseLen = dissect_DisplayBase ( tvb , tree , offset ) ;
offset + = displayBaseLen ;
/* TODO: Flag 1 byte, Alpha 1 byte dissection*/
offset + = 2 ;
dissect_ID ( tvb , tree , offset ) ;
offset + = 4 ;
dissect_SpiceRect ( tvb , tree , offset , - 1 ) ;
offset + = sizeof_SpiceRect ;
data_size = dissect_Image ( tvb , tree , pinfo , offset ) ;
offset + = data_size ;
break ;
case SPICE_DISPLAY_DRAW_BLACKNESS :
displayBaseLen = dissect_DisplayBase ( tvb , tree , offset ) ;
offset + = displayBaseLen ;
offset + = dissect_Mask ( tvb , tree , offset ) ;
break ;
case SPICE_DISPLAY_COPY_BITS :
displayBaseLen = dissect_DisplayBase ( tvb , tree , offset ) ;
offset + = displayBaseLen ;
dissect_POINT ( tvb , tree , offset ) ;
offset + = sizeof_POINT ;
break ;
case SPICE_DISPLAY_DRAW_WHITENESS :
displayBaseLen = dissect_DisplayBase ( tvb , tree , offset ) ;
offset + = displayBaseLen ;
offset + = dissect_Mask ( tvb , tree , offset ) ;
break ;
case SPICE_DISPLAY_DRAW_INVERS :
displayBaseLen = dissect_DisplayBase ( tvb , tree , offset ) ;
offset + = displayBaseLen ;
offset + = dissect_Mask ( tvb , tree , offset ) ;
break ;
case SPICE_DISPLAY_DRAW_FILL :
displayBaseLen = dissect_DisplayBase ( tvb , tree , offset ) ;
offset + = displayBaseLen ;
data_size = dissect_Brush ( tvb , tree , offset ) ;
offset + = data_size ;
proto_tree_add_item ( tree , hf_display_rop_descriptor , tvb , offset , 2 , ENC_LITTLE_ENDIAN ) ;
offset + = 2 ;
offset + = dissect_Mask ( tvb , tree , offset ) ;
if ( data_size ! = 5 ) { /* if it's not a SOLID brush, it's a PATTERN, dissect its image descriptior */
offset + = dissect_Image ( tvb , tree , pinfo , offset ) ;
}
break ;
case SPICE_DISPLAY_DRAW_TRANSPARENT :
displayBaseLen = dissect_DisplayBase ( tvb , tree , offset ) ;
offset + = displayBaseLen ;
dissect_ID ( tvb , tree , offset ) ;
offset + = 4 ;
/* source area */
dissect_SpiceRect ( tvb , tree , offset , - 1 ) ;
offset + = sizeof_SpiceRect ;
proto_tree_add_item ( tree , hf_tranparent_src_color , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( tree , hf_tranparent_true_color , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
data_size = dissect_Image ( tvb , tree , pinfo , offset ) ;
offset + = data_size ;
break ;
case SPICE_DISPLAY_DRAW_BLEND :
case SPICE_DISPLAY_DRAW_COPY :
displayBaseLen = dissect_DisplayBase ( tvb , tree , offset ) ;
offset + = displayBaseLen ;
/* SpiceImage *src_bitmap */
dissect_ID ( tvb , tree , offset ) ;
offset + = 4 ;
/* source area */
dissect_SpiceRect ( tvb , tree , offset , - 1 ) ;
offset + = sizeof_SpiceRect ;
proto_tree_add_item ( tree , hf_display_rop_descriptor , tvb , offset , 2 , ENC_LITTLE_ENDIAN ) ;
offset + = 2 ;
proto_tree_add_item ( tree , hf_display_scale_mode , tvb , offset , 1 , ENC_LITTLE_ENDIAN ) ;
offset + = 1 ;
offset + = dissect_Mask ( tvb , tree , offset ) ;
data_size = dissect_Image ( tvb , tree , pinfo , offset ) ;
offset + = data_size ;
break ;
case SPICE_DISPLAY_INVAL_ALL_PALETTES :
proto_tree_add_text ( tree , tvb , offset , 0 , " DISPLAY_INVAL_ALL_PALETTES message " ) ;
break ;
case SPICE_DISPLAY_DRAW_TEXT :
displayBaseLen = dissect_DisplayBase ( tvb , tree , offset ) ;
offset + = displayBaseLen ;
dissect_ID ( tvb , tree , offset ) ;
offset + = 4 ;
r = dissect_SpiceRect ( tvb , tree , offset , - 1 ) ;
offset + = sizeof_SpiceRect ;
if ( ! rect_is_empty ( r ) ) {
data_size = dissect_Brush ( tvb , tree , offset ) ;
offset + = data_size ;
}
proto_tree_add_item ( tree , hf_display_text_fore_mode , tvb , offset , 2 , ENC_LITTLE_ENDIAN ) ;
offset + = 2 ;
proto_tree_add_item ( tree , hf_display_text_back_mode , tvb , offset , 2 , ENC_LITTLE_ENDIAN ) ;
offset + = 2 ;
glyphs = tvb_get_letohs ( tvb , offset ) ;
proto_tree_add_text ( tree , tvb , offset , 2 , " Number of glyphs: %u " , glyphs ) ;
offset + = 2 ;
proto_tree_add_text ( tree , tvb , offset , 2 , " Glyph flags " ) ;
/*TODO finish dissecting glyph list */
break ;
case SPICE_DISPLAY_DRAW_STROKE :
displayBaseLen = dissect_DisplayBase ( tvb , tree , offset ) ;
offset + = displayBaseLen ;
/*TODO: complete and correct dissection */
break ;
case SPICE_DISPLAY_STREAM_CLIP :
proto_tree_add_item ( tree , hf_display_stream_id , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
clip_type = dissect_Clip ( tvb , tree , offset ) ;
offset + = sizeof_Clip ;
if ( clip_type = = CLIP_TYPE_RECTS ) {
offset + = dissect_RectList ( tvb , tree , offset ) ;
}
break ;
case SPICE_DISPLAY_STREAM_CREATE :
proto_tree_add_item ( tree , hf_display_surface_id , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( tree , hf_display_stream_id , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( tree , hf_display_stream_flags , tvb , offset , 1 , ENC_LITTLE_ENDIAN ) ;
offset + = 1 ;
proto_tree_add_item ( tree , hf_display_stream_codec_type , tvb , offset , 1 , ENC_LITTLE_ENDIAN ) ;
offset + = 1 ;
proto_tree_add_text ( tree , tvb , offset , 8 , " stamp " ) ;
offset + = 8 ;
proto_tree_add_item ( tree , hf_display_stream_width , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( tree , hf_display_stream_height , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( tree , hf_display_stream_src_width , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( tree , hf_display_stream_src_height , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
dissect_SpiceRect ( tvb , tree , offset , - 1 ) ;
offset + = sizeof_SpiceRect ;
clip_type = dissect_Clip ( tvb , tree , offset ) ;
offset + = sizeof_Clip ;
if ( clip_type = = CLIP_TYPE_RECTS ) {
offset + = dissect_RectList ( tvb , tree , offset ) ;
}
break ;
case SPICE_DISPLAY_STREAM_DATA :
data_size = tvb_get_letohl ( tvb , offset + 8 ) ;
proto_tree_add_item ( tree , hf_display_stream_id , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( tree , hf_multi_media_time , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( tree , hf_display_stream_data_size , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_text ( tree , tvb , offset , data_size , " Stream data " ) ;
jpeg_tvb = tvb_new_subset ( tvb , offset , data_size , data_size ) ;
call_dissector ( jpeg_handle , jpeg_tvb , pinfo , tree ) ;
offset + = data_size ;
break ;
case SPICE_DISPLAY_STREAM_DESTROY :
proto_tree_add_item ( tree , hf_display_stream_id , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
break ;
case SPICE_DISPLAY_STREAM_DESTROY_ALL :
proto_tree_add_text ( tree , tvb , offset , 0 , " DISPLAY_STREAM_DESTROY_ALL message " ) ;
break ;
case SPICE_DISPLAY_DRAW_SURFACE_CREATE :
proto_tree_add_item ( tree , hf_display_surface_id , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( tree , hf_display_surface_width , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( tree , hf_display_surface_height , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( tree , hf_display_surface_format , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( tree , hf_display_surface_flags , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
break ;
case SPICE_DISPLAY_DRAW_SURFACE_DESTROY :
proto_tree_add_item ( tree , hf_display_surface_id , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
break ;
default :
proto_tree_add_text ( tree , tvb , offset , 0 , " Unknown display server message - cannot dissect " ) ;
break ;
}
return offset ;
}
static guint32
dissect_spice_playback_server ( tvbuff_t * tvb , proto_tree * tree , const guint16 message_type , guint32 offset )
{
switch ( message_type ) {
case SPICE_PLAYBACK_DATA :
proto_tree_add_item ( tree , hf_playback_record_mode_timstamp , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
/* TODO - mode dependent, there may be more data here */
break ;
break ;
case SPICE_PLAYBACK_MODE :
proto_tree_add_item ( tree , hf_playback_record_mode_timstamp , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( tree , hf_playback_record_mode , tvb , offset , 2 , ENC_LITTLE_ENDIAN ) ;
offset + = 2 ;
/* TODO - mode dependent, there may be more data here */
break ;
case SPICE_PLAYBACK_START :
/*TODO: no. channels (UINT32), format (UINT16), frequency (UINT32), time (UINT32)*/
offset + = 14 ;
break ;
case SPICE_PLAYBACK_STOP :
proto_tree_add_text ( tree , tvb , offset , 0 , " PLAYBACK_STOP message " ) ;
break ;
default :
proto_tree_add_text ( tree , tvb , offset , 0 , " Unknown playback server message - cannot dissect " ) ;
break ;
}
return offset ;
}
static guint32
dissect_spice_cursor_server ( tvbuff_t * tvb , proto_tree * tree , const guint16 message_type , guint32 offset )
{
guint32 RedCursorSize ;
switch ( message_type ) {
case SPICE_CURSOR_INIT :
dissect_POINT16 ( tvb , tree , offset ) ;
offset + = sizeof_POINT16 ;
proto_tree_add_item ( tree , hf_cursor_trail_len , tvb , offset , 2 , ENC_LITTLE_ENDIAN ) ;
offset + = 2 ;
proto_tree_add_item ( tree , hf_cursor_trail_freq , tvb , offset , 2 , ENC_LITTLE_ENDIAN ) ;
offset + = 2 ;
proto_tree_add_item ( tree , hf_cursor_trail_visible , tvb , offset , 1 , ENC_LITTLE_ENDIAN ) ;
offset + = 1 ;
RedCursorSize = dissect_RedCursor ( tvb , tree , offset ) ;
offset + = RedCursorSize ;
break ;
case SPICE_CURSOR_RESET :
proto_tree_add_text ( tree , tvb , offset , 0 , " CURSOR_RESET message " ) ;
break ;
case SPICE_CURSOR_SET :
dissect_POINT16 ( tvb , tree , offset ) ;
offset + = sizeof_POINT16 ;
offset + = 1 ; /*TODO flags */
RedCursorSize = dissect_RedCursor ( tvb , tree , offset ) ;
offset + = RedCursorSize ;
break ;
case SPICE_CURSOR_MOVE :
dissect_POINT16 ( tvb , tree , offset ) ;
offset + = sizeof_POINT16 ;
break ;
case SPICE_CURSOR_HIDE :
proto_tree_add_text ( tree , tvb , offset , 0 , " CURSOR_HIDE message " ) ;
break ;
case SPICE_CURSOR_TRAIL :
proto_tree_add_item ( tree , hf_cursor_trail_len , tvb , offset , 2 , ENC_LITTLE_ENDIAN ) ;
offset + = 2 ;
proto_tree_add_item ( tree , hf_cursor_trail_freq , tvb , offset , 2 , ENC_LITTLE_ENDIAN ) ;
offset + = 2 ;
break ;
case SPICE_CURSOR_INVAL_ONE :
proto_tree_add_item ( tree , hf_cursor_id , tvb , offset , 8 , ENC_LITTLE_ENDIAN ) ;
offset + = 8 ;
break ;
case SPICE_CURSOR_INVAL_ALL :
proto_tree_add_text ( tree , tvb , offset , 0 , " CURSOR_INVAL_ALL message " ) ;
break ;
default :
proto_tree_add_text ( tree , tvb , offset , 0 , " Unknown cursor server message - cannot dissect " ) ;
break ;
}
return offset ;
}
static guint32
dissect_spice_record_server ( tvbuff_t * tvb , proto_tree * tree , const guint16 message_type , const guint32 offset )
{
switch ( message_type ) {
case SPICE_RECORD_STOP :
proto_tree_add_text ( tree , tvb , offset , 0 , " RECORD_STOP message " ) ;
break ;
default :
proto_tree_add_text ( tree , tvb , offset , 0 , " Unknown record server message - cannot dissect " ) ;
break ;
}
return offset ;
}
static guint32
dissect_spice_agent_message ( tvbuff_t * tvb , proto_tree * tree , const guint32 message_type , guint32 message_len , guint32 offset )
{
proto_item * ti = NULL ;
proto_tree * agent_tree ;
switch ( message_type ) {
case VD_AGENT_MOUSE_STATE :
ti = proto_tree_add_text ( tree , tvb , offset , 4 , " VD_AGENT_MOUSE_STATE message " ) ;
agent_tree = proto_item_add_subtree ( ti , ett_spice_agent ) ;
offset + = 4 ;
break ;
case VD_AGENT_MONITORS_CONFIG :
ti = proto_tree_add_text ( tree , tvb , offset , 4 , " VD_AGENT_MONITORS_CONFIG message " ) ;
agent_tree = proto_item_add_subtree ( ti , ett_spice_agent ) ;
offset + = 4 ;
break ;
case VD_AGENT_REPLY :
ti = proto_tree_add_text ( tree , tvb , offset , 8 , " VD_AGENT_REPLY message " ) ;
agent_tree = proto_item_add_subtree ( ti , ett_spice_agent ) ;
/* TODO: complete dissection */
offset + = 8 ;
break ;
case VD_AGENT_CLIPBOARD :
ti = proto_tree_add_text ( tree , tvb , offset , message_len , " VD_AGENT_CLIPBOARD message " ) ;
agent_tree = proto_item_add_subtree ( ti , ett_spice_agent ) ;
/*TODO: display string */
offset + = message_len ;
break ;
case VD_AGENT_DISPLAY_CONFIG :
ti = proto_tree_add_text ( tree , tvb , offset , 4 , " VD_AGENT_DISPLAY_CONFIG message " ) ;
agent_tree = proto_item_add_subtree ( ti , ett_spice_agent ) ;
offset + = 4 ;
break ;
case VD_AGENT_ANNOUNCE_CAPABILITIES :
ti = proto_tree_add_text ( tree , tvb , offset , 8 , " VD_AGENT_ANNOUNCE_CAPABILITIES message " ) ;
agent_tree = proto_item_add_subtree ( ti , ett_spice_agent ) ;
/* TODO: complete dissection */
offset + = 8 ;
break ;
case VD_AGENT_CLIPBOARD_GRAB :
ti = proto_tree_add_text ( tree , tvb , offset , 4 , " VD_AGENT_CLIPBOARD_GRAB message " ) ;
agent_tree = proto_item_add_subtree ( ti , ett_spice_agent ) ;
proto_tree_add_item ( agent_tree , hf_agent_clipboard_selection , tvb , offset , 1 , ENC_LITTLE_ENDIAN ) ;
offset + = 1 ;
proto_tree_add_text ( agent_tree , tvb , offset , 3 , " reserverd " ) ;
offset + = 3 ;
break ;
case VD_AGENT_CLIPBOARD_REQUEST :
ti = proto_tree_add_text ( tree , tvb , offset , 8 , " VD_AGENT_CLIPBOARD_REQUEST message " ) ;
agent_tree = proto_item_add_subtree ( ti , ett_spice_agent ) ;
proto_tree_add_item ( agent_tree , hf_agent_clipboard_selection , tvb , offset , 1 , ENC_LITTLE_ENDIAN ) ;
offset + = 1 ;
proto_tree_add_text ( agent_tree , tvb , offset , 3 , " reserverd " ) ;
offset + = 3 ;
proto_tree_add_item ( agent_tree , hf_agent_clipboard_type , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
break ;
case VD_AGENT_CLIPBOARD_RELEASE :
ti = proto_tree_add_text ( tree , tvb , offset , 0 , " VD_AGENT_CLIPBOARD_RELEASE message " ) ;
agent_tree = proto_item_add_subtree ( ti , ett_spice_agent ) ;
break ;
default :
proto_tree_add_text ( tree , tvb , offset , 0 , " Unknown agent message - cannot dissect " ) ;
break ;
}
return offset ;
}
static guint32
dissect_spice_main_server ( tvbuff_t * tvb , proto_tree * tree , const guint16 message_type , guint32 offset )
{
guint32 num_channels , i , agent_msg_type , agent_msg_len ;
guint8 channel_type ;
switch ( message_type ) {
case SPICE_MAIN_INIT :
proto_tree_add_item ( tree , hf_session_id , tvb , offset , 4 , ENC_BIG_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( tree , hf_display_channels_hint , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( tree , hf_supported_mouse_modes , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( tree , hf_current_mouse_mode , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( tree , hf_agent_connected , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( tree , hf_agent_tokens , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( tree , hf_multi_media_time , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( tree , hf_ram_hint , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
break ;
case SPICE_MAIN_CHANNELS_LIST :
num_channels = tvb_get_letohl ( tvb , offset ) ;
proto_tree_add_text ( tree , tvb , offset , 4 , " Number of channels: %u " , num_channels ) ;
offset + = 4 ;
for ( i = 1 ; i < = num_channels ; i + + ) {
channel_type = tvb_get_guint8 ( tvb , offset ) ;
proto_tree_add_text ( tree , tvb , offset , 1 , " Type: %s [%d] " , val_to_str_const ( channel_type , channel_types_vs , " Unknown " ) , channel_type ) ;
offset + = 1 ;
proto_tree_add_text ( tree , tvb , offset , 1 , " \t ID: %d " , tvb_get_guint8 ( tvb , offset ) ) ;
offset + = 1 ;
}
break ;
case SPICE_MAIN_MULTI_MEDIA_TIME :
proto_tree_add_item ( tree , hf_multi_media_time , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
break ;
case SPICE_MAIN_MOUSE_MODE :
proto_tree_add_text ( tree , tvb , offset , 4 , " MOUSE_MODE message " ) ;
/* TODO:
mouse_mode supported_modes ;
mouse_mode current_mode ;
*/
offset + = 4 ;
break ;
case SPICE_MAIN_AGENT_DATA :
proto_tree_add_item ( tree , hf_agent_protocol , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
proto_tree_add_item ( tree , hf_agent_type , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
agent_msg_type = tvb_get_letohl ( tvb , offset ) ;
offset + = 4 ;
proto_tree_add_item ( tree , hf_agent_opaque , tvb , offset , 8 , ENC_LITTLE_ENDIAN ) ;
offset + = 8 ;
proto_tree_add_item ( tree , hf_agent_size , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
agent_msg_len = tvb_get_letohl ( tvb , offset ) ;
offset + = 4 ;
offset = dissect_spice_agent_message ( tvb , tree , agent_msg_type , agent_msg_len , offset ) ;
break ;
case SPICE_MAIN_AGENT_TOKEN :
proto_tree_add_item ( tree , hf_agent_token , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
break ;
default :
proto_tree_add_text ( tree , tvb , offset , 0 , " Unknown main server message - cannot dissect " ) ;
break ;
}
return offset ;
}
static guint32
dissect_spice_main_client ( tvbuff_t * tvb , proto_tree * tree , const guint16 message_type , guint32 offset )
{
proto_item * ti = NULL ;
proto_tree * main_tree ;
switch ( message_type ) {
case SPICEC_MAIN_MOUSE_MODE_REQUEST :
ti = proto_tree_add_text ( tree , tvb , offset , 4 , " Client MOUSE_MODE_REQUEST message " ) ;
main_tree = proto_item_add_subtree ( ti , ett_main_client ) ;
/* TODO: mouse_mode , 2 bytes. */
offset + = 2 ;
break ;
case SPICEC_MAIN_ATTACH_CHANNELS :
ti = proto_tree_add_text ( tree , tvb , offset , 4 , " Client MAIN_ATTACH_CHANNEL message " ) ;
main_tree = proto_item_add_subtree ( ti , ett_main_client ) ;
break ;
case SPICEC_MAIN_AGENT_START :
ti = proto_tree_add_text ( tree , tvb , offset , 4 , " Client AGENT_START message " ) ;
main_tree = proto_item_add_subtree ( ti , ett_main_client ) ;
proto_tree_add_item ( main_tree , hf_main_client_agent_tokens , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
break ;
default :
proto_tree_add_text ( tree , tvb , offset , 0 , " Unknown main client message - cannot dissect " ) ;
break ;
}
return offset ;
}
static guint32
dissect_spice_inputs_client ( tvbuff_t * tvb , proto_tree * tree , const guint16 message_type , guint32 offset )
{
proto_item * ti = NULL ;
proto_tree * inputs_tree ;
switch ( message_type ) {
case SPICEC_INPUTS_KEY_DOWN : /*TODO: complete dissection*/
ti = proto_tree_add_text ( tree , tvb , offset , 4 , " Client KEY_DOWN message " ) ;
inputs_tree = proto_item_add_subtree ( ti , ett_inputs_client ) ;
offset + = 4 ;
break ;
case SPICEC_INPUTS_KEY_UP :
ti = proto_tree_add_text ( tree , tvb , offset , 4 , " Client KEY_UP message " ) ;
inputs_tree = proto_item_add_subtree ( ti , ett_inputs_client ) ;
offset + = 4 ;
break ;
case SPICEC_INPUTS_KEY_MODIFIERS :
ti = proto_tree_add_text ( tree , tvb , offset , 2 , " Client KEY_MODIFIERS message " ) ;
inputs_tree = proto_item_add_subtree ( ti , ett_inputs_client ) ;
proto_tree_add_item ( inputs_tree , hf_keyboard_bits , tvb , offset , 2 , ENC_LITTLE_ENDIAN ) ;
offset + = 2 ;
break ;
case SPICEC_INPUTS_MOUSE_POSITION :
ti = proto_tree_add_text ( tree , tvb , offset , sizeof_POINT + 3 , " Client MOUSE_POSITION message " ) ;
inputs_tree = proto_item_add_subtree ( ti , ett_inputs_client ) ;
dissect_POINT ( tvb , inputs_tree , offset ) ;
offset + = sizeof_POINT ;
proto_tree_add_item ( inputs_tree , hf_button_state , tvb , offset , 2 , ENC_LITTLE_ENDIAN ) ;
offset + = 2 ;
proto_tree_add_item ( inputs_tree , hf_mouse_display_id , tvb , offset , 1 , ENC_LITTLE_ENDIAN ) ;
offset + = 1 ;
break ;
case SPICEC_INPUTS_MOUSE_MOTION :
ti = proto_tree_add_text ( tree , tvb , offset , sizeof_POINT + 4 , " Client MOUSE_MOTION message " ) ;
inputs_tree = proto_item_add_subtree ( ti , ett_inputs_client ) ;
dissect_POINT ( tvb , inputs_tree , offset ) ;
offset + = sizeof_POINT ;
proto_tree_add_item ( inputs_tree , hf_button_state , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
break ;
case SPICEC_INPUTS_MOUSE_PRESS :
ti = proto_tree_add_text ( tree , tvb , offset , 3 , " Client MOUSE_PRESS message " ) ;
inputs_tree = proto_item_add_subtree ( ti , ett_inputs_client ) ;
proto_tree_add_item ( inputs_tree , hf_button_state , tvb , offset , 2 , ENC_LITTLE_ENDIAN ) ;
offset + = 2 ;
proto_tree_add_item ( inputs_tree , hf_mouse_display_id , tvb , offset , 1 , ENC_LITTLE_ENDIAN ) ;
offset + = 1 ;
break ;
case SPICEC_INPUTS_MOUSE_RELEASE :
ti = proto_tree_add_text ( tree , tvb , offset , 3 , " Client MOUSE_RELEASE message " ) ;
inputs_tree = proto_item_add_subtree ( ti , ett_inputs_client ) ;
proto_tree_add_item ( inputs_tree , hf_button_state , tvb , offset , 2 , ENC_LITTLE_ENDIAN ) ;
offset + = 2 ;
proto_tree_add_item ( inputs_tree , hf_mouse_display_id , tvb , offset , 1 , ENC_LITTLE_ENDIAN ) ;
offset + = 1 ;
break ;
default :
proto_tree_add_text ( tree , tvb , offset , 0 , " Unknown inputs client message - cannot dissect " ) ;
break ;
}
return offset ;
}
static guint32
dissect_spice_inputs_server ( tvbuff_t * tvb , proto_tree * tree , const guint16 message_type , guint32 offset )
{
switch ( message_type ) {
case SPICE_INPUTS_INIT :
proto_tree_add_item ( tree , hf_keyboard_bits , tvb , offset , 2 , ENC_LITTLE_ENDIAN ) ;
offset + = 2 ;
break ;
case SPICE_INPUTS_KEY_MODIFIERS :
proto_tree_add_item ( tree , hf_keyboard_bits , tvb , offset , 2 , ENC_LITTLE_ENDIAN ) ;
offset + = 2 ;
break ;
case SPICE_INPUTS_MOUSE_MOTION_ACK :
proto_tree_add_text ( tree , tvb , offset , 0 , " Server INPUTS_MOUSE_MOTION_ACK message " ) ;
break ;
default :
proto_tree_add_text ( tree , tvb , offset , 0 , " Unknown inputs server message - cannot dissect " ) ;
break ;
}
return offset ;
}
static guint32
dissect_spice_data_server_pdu ( tvbuff_t * tvb , proto_tree * tree , packet_info * pinfo , spice_conversation_t * spice_info , guint32 offset , const guint32 total_message_size )
{
proto_item * ti = NULL , * msg_ti = NULL ;
proto_tree * data_header_tree , * message_tree ;
guint16 message_type ;
guint32 message_size , sublist_size , old_offset ;
message_type = tvb_get_letohs ( tvb , offset + 8 ) ;
message_size = tvb_get_letohl ( tvb , offset + 10 ) ;
msg_ti = proto_tree_add_text ( tree , tvb , offset , 0 , " %s (%d bytes) " , get_message_type_string ( message_type , spice_info , FALSE ) , message_size + sizeof_SpiceDataHeader ) ;
message_tree = proto_item_add_subtree ( msg_ti , ett_message ) ;
2011-10-04 22:44:31 +00:00
ti = proto_tree_add_item ( message_tree , hf_data , tvb , 0 , sizeof_SpiceDataHeader , ENC_NA ) ;
2011-09-29 18:10:14 +00:00
data_header_tree = proto_item_add_subtree ( ti , ett_data ) ;
dissect_spice_data_header ( tvb , data_header_tree , spice_info , FALSE , message_type , & sublist_size , offset ) ;
proto_item_set_len ( msg_ti , message_size + sizeof_SpiceDataHeader ) ;
offset + = sizeof_SpiceDataHeader ;
old_offset = offset ;
col_append_fstr ( pinfo - > cinfo , COL_INFO , " %s " , get_message_type_string ( message_type , spice_info , FALSE ) ) ;
if ( message_type < SPICE_FIRST_AVAIL_MESSAGE ) { /* this is a common message */
offset = dissect_spice_common_server_messages ( tvb , message_tree , message_type , offset , total_message_size - sizeof_SpiceDataHeader ) ;
return offset ;
}
switch ( spice_info - > channel_type ) {
case SPICE_CHANNEL_PLAYBACK :
offset = dissect_spice_playback_server ( tvb , message_tree , message_type , offset ) ;
break ;
case SPICE_CHANNEL_RECORD :
offset = dissect_spice_record_server ( tvb , message_tree , message_type , offset ) ;
break ;
case SPICE_CHANNEL_MAIN :
offset = dissect_spice_main_server ( tvb , message_tree , message_type , offset ) ;
break ;
case SPICE_CHANNEL_CURSOR :
offset = dissect_spice_cursor_server ( tvb , message_tree , message_type , offset ) ;
break ;
case SPICE_CHANNEL_DISPLAY :
offset = dissect_spice_display_server ( tvb , message_tree , pinfo , message_type , offset ) ;
break ;
case SPICE_CHANNEL_INPUTS :
offset = dissect_spice_inputs_server ( tvb , message_tree , message_type , offset ) ;
break ;
case SPICE_CHANNEL_TUNNEL :
/* TODO: Not implemented yet */
case SPICE_CHANNEL_SMARTCARD :
/* TODO: Not implemented yet */
default :
proto_tree_add_text ( message_tree , tvb , offset , 0 , " Unknown server PDU - cannot dissect " ) ;
}
if ( ( offset - old_offset ) ! = message_size ) {
/* This means we have not dissected the complete message. Would be nice to fix */
/* DBG3("dissect_spice_data_server_pdu() - FIXME:message %u in packet %d was not fully dissected (offset 0x%x).\r\n", message_type, pinfo->fd->num, offset); */
offset = old_offset + message_size ;
}
return offset ;
}
static guint32
dissect_spice_data_client_pdu ( tvbuff_t * tvb , proto_tree * tree , packet_info * pinfo , spice_conversation_t * spice_info , guint32 offset )
{
proto_item * ti = NULL ;
proto_tree * data_header_tree ;
guint16 message_type ;
2011-09-29 18:26:57 +00:00
guint32 /** message_size,**/ sublist_size ;
2011-09-29 18:10:14 +00:00
2011-10-04 22:44:31 +00:00
ti = proto_tree_add_item ( tree , hf_data , tvb , 0 , sizeof_SpiceDataHeader , ENC_NA ) ;
2011-09-29 18:10:14 +00:00
data_header_tree = proto_item_add_subtree ( ti , ett_data ) ;
message_type = tvb_get_letohs ( tvb , offset + 8 ) ;
2011-09-29 18:26:57 +00:00
/** message_size = tvb_get_letohl(tvb, offset + 10); **/
2011-09-29 18:10:14 +00:00
dissect_spice_data_header ( tvb , data_header_tree , spice_info , TRUE , message_type , & sublist_size , offset ) ;
col_append_fstr ( pinfo - > cinfo , COL_INFO , " %s " , get_message_type_string ( message_type , spice_info , TRUE ) ) ;
offset + = sizeof_SpiceDataHeader ;
/* TODO: deal with sub-messages list first. As implementation does not uses sub-messsages list yet, it cannot be implemented in the dissector yet.*/
if ( message_type < SPICE_FIRST_AVAIL_MESSAGE ) { /* this is a common message */
return dissect_spice_common_client_messages ( tvb , tree , message_type , offset ) ;
}
switch ( spice_info - > channel_type ) {
case SPICE_CHANNEL_PLAYBACK :
break ;
case SPICE_CHANNEL_RECORD :
offset = dissect_spice_record_client ( tvb , tree , message_type , offset ) ;
break ;
case SPICE_CHANNEL_MAIN :
offset = dissect_spice_main_client ( tvb , tree , message_type , offset ) ;
break ;
case SPICE_CHANNEL_DISPLAY :
offset = dissect_spice_display_client ( tvb , tree , message_type , offset ) ;
break ;
case SPICE_CHANNEL_INPUTS :
offset = dissect_spice_inputs_client ( tvb , tree , message_type , offset ) ;
break ;
default :
proto_tree_add_text ( tree , tvb , offset , 0 , " Unknown client PDU - cannot dissect " ) ;
break ;
}
return offset ;
}
static void
dissect_spice_link_common_header ( tvbuff_t * tvb , proto_tree * tree )
{
if ( tree ) {
/* dissect common header */
2011-09-29 20:03:51 +00:00
proto_tree_add_item ( tree , hf_spice_magic , tvb , 0 , 4 , ENC_ASCII | ENC_LITTLE_ENDIAN ) ;
2011-09-29 18:10:14 +00:00
proto_tree_add_item ( tree , hf_major_version , tvb , 4 , 4 , ENC_LITTLE_ENDIAN ) ;
proto_tree_add_item ( tree , hf_minor_version , tvb , 8 , 4 , ENC_LITTLE_ENDIAN ) ;
proto_tree_add_item ( tree , hf_message_size , tvb , 12 , 4 , ENC_LITTLE_ENDIAN ) ;
}
}
static void
dissect_spice_common_capabilities ( tvbuff_t * tvb , proto_tree * tree , guint32 offset , const int caps_len )
{
/* TODO: save common and per-channel capabilities in spice_info ? */
int i ;
for ( i = 0 ; i ! = caps_len ; i + + ) {
proto_tree_add_item ( tree , hf_common_cap , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
offset + = 4 ;
}
}
static void
dissect_spice_link_capabilities ( tvbuff_t * tvb , proto_tree * tree , guint32 offset , const int caps_len , const spice_conversation_t * spice_info )
{
/* TODO: save common and per-channel capabilities in spice_info ? */
int i ;
for ( i = 0 ; i ! = caps_len ; i + + ) {
switch ( spice_info - > channel_type ) {
case SPICE_CHANNEL_PLAYBACK :
proto_tree_add_item ( tree , hf_playback_cap , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
break ;
case SPICE_CHANNEL_MAIN :
proto_tree_add_item ( tree , hf_main_cap , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
break ;
case SPICE_CHANNEL_DISPLAY :
proto_tree_add_item ( tree , hf_display_cap , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
break ;
case SPICE_CHANNEL_INPUTS :
proto_tree_add_item ( tree , hf_inputs_cap , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
break ;
case SPICE_CHANNEL_CURSOR :
proto_tree_add_item ( tree , hf_cursor_cap , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
break ;
case SPICE_CHANNEL_RECORD :
proto_tree_add_item ( tree , hf_record_cap , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
break ;
default :
proto_tree_add_text ( tree , tvb , offset , 0 , " Unknown channel - cannot dissect " ) ;
break ;
}
offset + = 4 ;
}
}
static void
dissect_spice_link_client_pdu ( tvbuff_t * tvb , proto_tree * tree , spice_conversation_t * spice_info )
{
guint32 offset ;
guint32 common_caps_len , channel_caps_len ;
proto_item * ti = NULL ;
proto_tree * link_header_tree = NULL ;
proto_tree * caps_tree = NULL ;
if ( tree ) {
2011-10-04 22:44:31 +00:00
ti = proto_tree_add_item ( tree , hf_link_client , tvb , 0 , sizeof_SpiceLinkHeader , ENC_NA ) ;
2011-09-29 18:10:14 +00:00
link_header_tree = proto_item_add_subtree ( ti , ett_link_client ) ;
dissect_spice_link_common_header ( tvb , link_header_tree ) ;
}
offset = sizeof_SpiceLinkHeader ;
if ( tree ) {
proto_tree_add_item ( tree , hf_conn_id , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
proto_tree_add_item ( tree , hf_channel_type , tvb , offset + 4 , 1 , ENC_LITTLE_ENDIAN ) ;
proto_tree_add_item ( tree , hf_channel_id , tvb , offset + 5 , 1 , ENC_LITTLE_ENDIAN ) ;
proto_tree_add_item ( tree , hf_num_common_caps , tvb , offset + 6 , 4 , ENC_LITTLE_ENDIAN ) ;
proto_tree_add_item ( tree , hf_num_channel_caps , tvb , offset + 10 , 4 , ENC_LITTLE_ENDIAN ) ;
proto_tree_add_item ( tree , hf_caps_offset , tvb , offset + 14 , 4 , ENC_LITTLE_ENDIAN ) ;
}
common_caps_len = tvb_get_letohl ( tvb , offset + 6 ) ;
channel_caps_len = tvb_get_letohl ( tvb , offset + 10 ) ;
if ( spice_info - > channel_type = = SPICE_CHANNEL_NONE ) {
spice_info - > channel_type = tvb_get_guint8 ( tvb , offset + 4 ) ;
}
offset + = sizeof_SpiceDataHeader ;
if ( common_caps_len > 0 ) {
ti = proto_tree_add_text ( tree , tvb , offset , common_caps_len * 4 , " Common Capabilities (%d bytes) " , common_caps_len * 4 ) ; /* caps_len multiplied by 4 as length is in UINT32 units */
caps_tree = proto_item_add_subtree ( ti , ett_link_caps ) ;
/* TODO: dissect common capabilities */
offset + = ( common_caps_len * 4 ) ;
}
if ( channel_caps_len > 0 ) {
ti = proto_tree_add_text ( tree , tvb , offset , channel_caps_len * 4 , " Channel Capabilities (%d bytes) " , channel_caps_len * 4 ) ; /* caps_len multiplied by 4 as length is in UINT32 units */
caps_tree = proto_item_add_subtree ( ti , ett_link_caps ) ;
dissect_spice_link_capabilities ( tvb , caps_tree , offset , channel_caps_len , spice_info ) ;
}
}
static void
dissect_spice_link_server_pdu ( tvbuff_t * tvb , proto_tree * tree , spice_conversation_t * spice_info )
{
guint32 offset ;
guint32 common_caps_len , channel_caps_len ;
proto_item * ti = NULL ;
proto_tree * link_tree = NULL ;
proto_tree * caps_tree = NULL ;
if ( tree ) {
2011-10-04 22:44:31 +00:00
ti = proto_tree_add_item ( tree , hf_link_server , tvb , 0 , sizeof_SpiceLinkHeader , ENC_NA ) ;
2011-09-29 18:10:14 +00:00
link_tree = proto_item_add_subtree ( ti , ett_link_server ) ;
dissect_spice_link_common_header ( tvb , link_tree ) ;
}
offset = sizeof_SpiceLinkHeader ;
if ( tree ) {
proto_tree_add_item ( tree , hf_error_code , tvb , offset , 4 , ENC_LITTLE_ENDIAN ) ;
proto_tree_add_text ( tree , tvb , offset + 4 , SPICE_TICKET_PUBKEY_BYTES , " X.509 SubjectPublicKeyInfo (ASN.1) " ) ;
proto_tree_add_item ( tree , hf_num_common_caps , tvb , offset + 4 + SPICE_TICKET_PUBKEY_BYTES , 4 , ENC_LITTLE_ENDIAN ) ;
proto_tree_add_item ( tree , hf_num_channel_caps , tvb , offset + 8 + SPICE_TICKET_PUBKEY_BYTES , 4 , ENC_LITTLE_ENDIAN ) ;
proto_tree_add_item ( tree , hf_caps_offset , tvb , offset + 12 + SPICE_TICKET_PUBKEY_BYTES , 4 , ENC_LITTLE_ENDIAN ) ;
}
common_caps_len = tvb_get_letohl ( tvb , offset + 4 + SPICE_TICKET_PUBKEY_BYTES ) ;
channel_caps_len = tvb_get_letohl ( tvb , offset + 8 + SPICE_TICKET_PUBKEY_BYTES ) ;
offset + = sizeof_SpiceLinkHeader + SPICE_TICKET_PUBKEY_BYTES ;
if ( common_caps_len > 0 ) {
ti = proto_tree_add_text ( tree , tvb , offset , common_caps_len * 4 , " Common Capabilities (%d bytes) " , common_caps_len * 4 ) ; /* caps_len multiplied by 4 as length is in UINT32 units */
caps_tree = proto_item_add_subtree ( ti , ett_link_caps ) ;
dissect_spice_common_capabilities ( tvb , caps_tree , offset , common_caps_len ) ;
offset + = ( common_caps_len * 4 ) ;
}
if ( channel_caps_len > 0 ) {
ti = proto_tree_add_text ( tree , tvb , offset , channel_caps_len * 4 , " Channel Capabilities (%d bytes) " , channel_caps_len * 4 ) ; /* caps_len multiplied by 4 as length is in UINT32 units */
caps_tree = proto_item_add_subtree ( ti , ett_link_caps ) ;
dissect_spice_link_capabilities ( tvb , caps_tree , offset , channel_caps_len , spice_info ) ;
}
}
static int
dissect_spice ( tvbuff_t * tvb , packet_info * pinfo , proto_tree * tree )
{
conversation_t * conversation ;
spice_conversation_t * spice_info ;
spice_packet_t * per_packet_info ;
guint32 avail ;
guint32 pdu_len = 0 ;
guint32 offset ;
proto_item * ti = NULL ;
proto_tree * spice_tree = NULL ;
proto_tree * spice_data_tree = NULL ;
gboolean first_record_in_frame ;
conversation = find_or_create_conversation ( pinfo ) ;
spice_info = ( spice_conversation_t * ) conversation_get_proto_data ( conversation , proto_spice ) ;
if ( ! spice_info ) {
spice_info = se_alloc0 ( sizeof ( spice_conversation_t ) ) ;
spice_info - > destport = pinfo - > destport ;
spice_info - > channel_type = SPICE_CHANNEL_NONE ;
spice_info - > next_state = SPICE_LINK_CLIENT ;
conversation_add_proto_data ( conversation , proto_spice , spice_info ) ;
}
per_packet_info = p_get_proto_data ( pinfo - > fd , proto_spice ) ;
if ( ! per_packet_info ) {
per_packet_info = se_alloc ( sizeof ( spice_packet_t ) ) ;
per_packet_info - > state = spice_info - > next_state ;
p_add_proto_data ( pinfo - > fd , proto_spice , per_packet_info ) ;
}
col_clear ( pinfo - > cinfo , COL_INFO ) ;
first_record_in_frame = TRUE ;
switch ( per_packet_info - > state ) {
case SPICE_LINK_CLIENT :
avail = tvb_reported_length ( tvb ) ;
if ( avail < sizeof_SpiceLinkHeader ) { /* the header is at least sizeof_SpiceLinkHeader (16) bytes long */
pinfo - > desegment_offset = 0 ;
pinfo - > desegment_len = sizeof_SpiceLinkHeader - avail ;
return avail ;
}
pdu_len = tvb_get_letohl ( tvb , 12 ) + sizeof_SpiceLinkHeader ;
if ( avail < pdu_len & & spice_desegment ) { /* Did not get all the PDU - request the full length of the PDU */
pinfo - > desegment_offset = 0 ;
pinfo - > desegment_len = pdu_len - avail ;
return avail ;
}
col_set_str ( pinfo - > cinfo , COL_INFO , " Client link message " ) ;
if ( tree ) {
ti = proto_tree_add_item ( tree , proto_spice , tvb , 0 , - 1 , ENC_NA ) ;
spice_tree = proto_item_add_subtree ( ti , ett_spice ) ;
}
dissect_spice_link_client_pdu ( tvb , spice_tree , spice_info ) ;
col_add_fstr ( pinfo - > cinfo , COL_PROTOCOL , " Spice %s " , val_to_str_const ( spice_info - > channel_type , channel_types_vs , " Unknown " ) ) ;
spice_info - > next_state = SPICE_LINK_SERVER ;
return pdu_len ;
break ;
case SPICE_LINK_SERVER :
avail = tvb_reported_length ( tvb ) ;
if ( avail < sizeof_SpiceLinkHeader ) {
pinfo - > desegment_offset = 0 ;
pinfo - > desegment_len = sizeof_SpiceLinkHeader - avail ;
return avail ;
}
pdu_len = tvb_get_letohl ( tvb , 12 ) + sizeof_SpiceLinkHeader ;
if ( avail < pdu_len ) {
pinfo - > desegment_offset = 0 ;
pinfo - > desegment_len = pdu_len - avail ;
return avail ;
}
col_set_str ( pinfo - > cinfo , COL_INFO , " Server link message " ) ;
col_add_fstr ( pinfo - > cinfo , COL_PROTOCOL , " Spice %s " , val_to_str_const ( spice_info - > channel_type , channel_types_vs , " Unknown " ) ) ;
if ( tree ) {
ti = proto_tree_add_item ( tree , proto_spice , tvb , 0 , - 1 , ENC_NA ) ;
spice_tree = proto_item_add_subtree ( ti , ett_spice ) ;
}
dissect_spice_link_server_pdu ( tvb , spice_tree , spice_info ) ;
spice_info - > next_state = SPICE_TICKET_CLIENT ;
return pdu_len ;
break ;
case SPICE_DATA :
offset = 0 ;
while ( offset < tvb_reported_length ( tvb ) ) {
avail = tvb_length_remaining ( tvb , offset ) ;
if ( avail < sizeof_SpiceDataHeader ) { /* didn't get enough to dissect even the header */
pinfo - > desegment_offset = offset ;
pinfo - > desegment_len = sizeof_SpiceDataHeader - avail ;
return offset ;
}
pdu_len = tvb_get_letohl ( tvb , offset + 14 ) ; /* this is actually the sub-message list size */
if ( pdu_len = = 0 ) { /* if there are no sub-messages, get the usual message body size. Note that we do not dissect properly yet sub-messages - but they are not used in the protcol either */
pdu_len = tvb_get_letohl ( tvb , offset + 10 ) ;
} else {
pdu_len = tvb_get_letohl ( tvb , offset + 10 ) ;
}
pdu_len + = sizeof_SpiceDataHeader ; /* +sizeof_SpiceDataHeader since you need to exclude the SPICE data header, which is sizeof_SpiceDataHeader (18) bytes long) */
if ( avail < pdu_len ) { /* didn't get the complete PDU, returning */
pinfo - > desegment_offset = offset ;
pinfo - > desegment_len = pdu_len - avail ;
return offset ;
}
col_add_fstr ( pinfo - > cinfo , COL_PROTOCOL , " Spice %s " , val_to_str_const ( spice_info - > channel_type , channel_types_vs , " Unknown " ) ) ;
if ( ! first_record_in_frame ) { /* if it's not the first dissected PDU, we want in COL_INFO to have: "PDU_type_A, PDU_typeB, PDU_typeC, etc. */
col_append_str ( pinfo - > cinfo , COL_INFO , " , " ) ;
}
if ( tree & & spice_data_tree = = NULL ) {
ti = proto_tree_add_item ( tree , proto_spice , tvb , offset , pdu_len , ENC_NA ) ;
spice_data_tree = proto_item_add_subtree ( ti , ett_data ) ;
}
if ( spice_info - > destport = = pinfo - > destport ) { /* client to server traffic */
offset = dissect_spice_data_client_pdu ( tvb , spice_data_tree , pinfo , spice_info , offset ) ;
avail = tvb_length_remaining ( tvb , offset ) ;
} else { /* server to client traffic */
offset = dissect_spice_data_server_pdu ( tvb , spice_data_tree , pinfo , spice_info , offset , pdu_len ) ;
avail = tvb_length_remaining ( tvb , offset ) ;
}
first_record_in_frame = FALSE ;
}
return offset ;
break ;
case SPICE_TICKET_CLIENT :
avail = tvb_reported_length ( tvb ) ;
pdu_len = 128 ;
if ( avail < pdu_len ) {
pinfo - > desegment_offset = 0 ;
pinfo - > desegment_len = pdu_len - avail ;
return avail ;
}
col_set_str ( pinfo - > cinfo , COL_INFO , " Client ticket " ) ;
col_add_fstr ( pinfo - > cinfo , COL_PROTOCOL , " Spice %s " , val_to_str_const ( spice_info - > channel_type , channel_types_vs , " Unknown " ) ) ;
if ( tree ) {
ti = proto_tree_add_item ( tree , proto_spice , tvb , 0 , - 1 , ENC_NA ) ;
spice_tree = proto_item_add_subtree ( ti , ett_ticket_client ) ;
2011-10-04 22:44:31 +00:00
proto_tree_add_item ( spice_tree , hf_ticket_client , tvb , 0 , - 1 , ENC_NA ) ;
2011-09-29 18:10:14 +00:00
}
spice_info - > next_state = SPICE_TICKET_SERVER ;
return 128 ;
break ;
case SPICE_TICKET_SERVER :
avail = tvb_reported_length ( tvb ) ;
pdu_len = 4 ;
if ( avail < pdu_len ) {
pinfo - > desegment_offset = 0 ;
pinfo - > desegment_len = pdu_len - avail ;
return avail ;
}
col_set_str ( pinfo - > cinfo , COL_INFO , " Server ticket " ) ;
col_add_fstr ( pinfo - > cinfo , COL_PROTOCOL , " Spice %s " , val_to_str_const ( spice_info - > channel_type , channel_types_vs , " Unknown " ) ) ;
if ( tree ) {
ti = proto_tree_add_item ( tree , proto_spice , tvb , 0 , - 1 , ENC_NA ) ;
spice_tree = proto_item_add_subtree ( ti , ett_ticket_server ) ;
proto_tree_add_item ( spice_tree , hf_ticket_server , tvb , 0 , 4 , ENC_LITTLE_ENDIAN ) ;
}
spice_info - > next_state = SPICE_DATA ;
return pdu_len ;
break ;
default :
break ;
}
return 0 ;
}
static gboolean
test_spice_protocol ( tvbuff_t * tvb , packet_info * pinfo , proto_tree * tree )
{
conversation_t * conversation ;
if ( tvb_reported_length ( tvb ) > = 4 & & tvb_get_ntohl ( tvb , 0 ) = = SPICE_MAGIC ) {
conversation = conversation_new ( pinfo - > fd - > num , & pinfo - > src , & pinfo - > dst ,
pinfo - > ptype , pinfo - > srcport ,
pinfo - > destport , 0 ) ;
conversation_set_dissector ( conversation , spice_handle ) ;
dissect_spice ( tvb , pinfo , tree ) ;
return TRUE ;
}
return FALSE ;
}
/* Register the protocol with Wireshark */
void
proto_register_spice ( void )
{
/* Setup list of header fields */
static hf_register_info hf [ ] = {
{ & hf_link_client ,
{ " Link client header " , " spice.link_client " ,
FT_NONE , BASE_NONE , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_link_server ,
{ " LINK server header " , " spice.link_server " ,
FT_NONE , BASE_NONE , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_spice_magic ,
{ " SPICE MAGIC " , " spice.magic " ,
FT_STRING , BASE_NONE , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_major_version ,
{ " Protocol major version " , " spice.major_version " ,
FT_INT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_minor_version ,
{ " Protocol minor version " , " spice.minor_version " ,
FT_INT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_message_size ,
{ " Message size " , " spice.message_size " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_conn_id ,
{ " Session ID " , " spice.conn_id " ,
FT_UINT32 , BASE_HEX , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_channel_type ,
{ " Channel type " , " spice.channel_type " ,
FT_UINT8 , BASE_DEC , VALS ( channel_types_vs ) , 0x0 ,
NULL , HFILL }
} ,
{ & hf_channel_id ,
{ " Channel ID " , " spice.channel_id " ,
FT_UINT8 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_num_common_caps ,
{ " Number of common capabilities " , " spice.num_common_caps " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_num_channel_caps ,
{ " Number of channel capabilities " , " spice.num_channel_caps " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_caps_offset ,
{ " Capabilities offset (bytes) " , " spice.caps_offset " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_error_code ,
{ " spice ERROR " , " spice.error_code " ,
FT_UINT32 , BASE_DEC , VALS ( spice_error_codes_vs ) , 0x0 ,
NULL , HFILL }
} ,
{ & hf_serial ,
{ " Message serial number " , " spice.serial " ,
FT_UINT64 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_data ,
{ " Message header " , " spice.message_header " ,
FT_NONE , BASE_NONE , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_data_size ,
{ " Message body size (bytes) " , " spice.message_size " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_data_sublist ,
{ " Sub-list offset (bytes) " , " spice.message_sublist " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_ticket_client ,
{ " Ticket - client " , " spice.ticket_client " ,
FT_NONE , BASE_NONE , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_ticket_server ,
{ " Link result " , " spice.ticket_server " ,
FT_UINT32 , BASE_DEC , VALS ( spice_error_codes_vs ) , 0x0 ,
NULL , HFILL }
} ,
{ & hf_common_cap ,
{ " Common channels capabilitity " , " spice.common_cap " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_playback_cap ,
{ " Playback channel capability " , " spice.playback_cap " ,
FT_UINT32 , BASE_DEC , VALS ( playback_caps ) , 0x0 ,
NULL , HFILL }
} ,
{ & hf_record_cap ,
{ " Record channel capability " , " spice.record_cap " ,
FT_UINT32 , BASE_DEC , VALS ( record_caps ) , 0x0 ,
NULL , HFILL }
} ,
{ & hf_cursor_cap ,
{ " Cursor channel capability " , " spice.cursor_cap " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_inputs_cap ,
{ " Inputs channel capability " , " spice.inputs_cap " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_main_cap ,
{ " Main channel capability " , " spice.main_cap " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_display_cap ,
{ " Display channelcapability " , " spice.display_cap " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_playback_record_mode_timstamp ,
{ " Timestamp " , " spice.timestamp " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_playback_record_mode ,
{ " Mode " , " spice.mode " ,
FT_UINT16 , BASE_DEC , VALS ( playback_mode_vals ) , 0x0 ,
NULL , HFILL }
} ,
{ & hf_red_set_ack_generation ,
{ " Set ACK generation " , " spice.red_set_ack_generation " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_red_set_ack_window ,
{ " Set ACK window (messages) " , " spice.red_set_ack_window " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_Clip_type ,
{ " Clip type " , " spice.clip_type " ,
FT_UINT8 , BASE_DEC , VALS ( clip_types_vs ) , 0x0 ,
NULL , HFILL }
} ,
{ & hf_Mask_flag ,
{ " Mask flag " , " spice.mask_flag " ,
FT_UINT8 , BASE_DEC , VALS ( Mask_flags_vs ) , 0x0 ,
NULL , HFILL }
} ,
{ & hf_Mask_bitmap ,
{ " Bitmap address " , " spice.mask_bitmap " ,
FT_UINT32 , BASE_HEX_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_display_rop_descriptor ,
{ " ROP descriptor " , " spice.display_rop_descriptor " ,
FT_UINT16 , BASE_HEX , VALS ( rop_descriptor_vs ) , 0x0 ,
NULL , HFILL }
} ,
{ & hf_display_scale_mode ,
{ " Scale mode " , " spice.scale_mode " ,
FT_UINT8 , BASE_DEC , VALS ( scale_mode_vs ) , 0x0 ,
NULL , HFILL }
} ,
{ & hf_red_ping_id ,
{ " Ping ID " , " spice.ping_id " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_red_timestamp ,
{ " timestamp " , " spice.timestamp " ,
FT_UINT64 , BASE_HEX , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_spice_display_mode_width ,
{ " Display Width " , " spice.display_width " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_spice_display_mode_height ,
{ " Display Height " , " spice.display_height " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_spice_display_mode_depth ,
{ " Color depth " , " spice.display_depth " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_image_desc_id ,
{ " Image ID " , " spice.image_id " ,
FT_UINT64 , BASE_HEX , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_image_desc_type ,
{ " Image type " , " spice.image_type " ,
FT_UINT8 , BASE_DEC , VALS ( image_type_vs ) , 0x0 ,
NULL , HFILL }
} ,
{ & hf_image_desc_flags ,
{ " Flags " , " spice.image_flags " ,
FT_UINT8 , BASE_HEX , VALS ( image_flags_vs ) , 0x0 ,
NULL , HFILL }
} ,
{ & hf_image_desc_width ,
{ " Width " , " spice.image_width " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_image_desc_height ,
{ " Height " , " spice.image_height " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_quic_width ,
{ " Width " , " spice.quic_width " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_quic_type ,
{ " QUIC image type " , " spice.quic_type " ,
FT_UINT32 , BASE_DEC , VALS ( quic_type_vs ) , 0x0 ,
NULL , HFILL }
} ,
{ & hf_quic_height ,
{ " Height " , " spice.quic_height " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_quic_major_version ,
{ " QUIC major version " , " spice.quic_major_version " ,
FT_UINT16 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_quic_minor_version ,
{ " QUIC minor version " , " spice.quic_minor_version " ,
FT_UINT16 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_GLZ_RGB_width ,
{ " Width " , " spice.GLZ_RGB_width " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_GLZ_RGB_height ,
{ " Height " , " spice.GLZ_RGB_height " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_GLZ_RGB_type ,
{ " (G)LZ_RGB image type " , " spice.GLZ_RGB_type " ,
FT_UINT16 , BASE_DEC , VALS ( LzImage_type_vs ) , 0xf ,
NULL , HFILL }
} ,
{ & hf_LZ_major_version ,
{ " LZ major version " , " spice.LZ_major_version " ,
FT_UINT16 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_LZ_minor_version ,
{ " LZ minor version " , " spice.LZ_minor_version " ,
FT_UINT16 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_GLZ_RGB_stride ,
{ " Stride " , " spice.GLZ_RGB_stride " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_GLZ_RGB_dict_id ,
{ " (G)LZ RGB Dictionary ID " , " spice.GLZ_RGB_dict_id " ,
FT_UINT64 , BASE_HEX , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_cursor_trail_len ,
{ " Cursor trail length " , " spice.cursor_trail_len " ,
FT_UINT16 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_cursor_trail_freq ,
{ " Cursor trail frequency " , " spice.cursor_trail_freq " ,
FT_UINT16 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_cursor_trail_visible ,
{ " Cursor trail visiblity " , " spice.cursor_trail_visible " ,
FT_UINT8 , BASE_DEC , VALS ( cursor_visible_vs ) , 0x0 ,
NULL , HFILL }
} ,
{ & hf_cursor_unique ,
{ " Cursor unique ID " , " spice._cursor_unique " ,
FT_UINT64 , BASE_HEX , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_cursor_type ,
{ " Cursor type " , " spice.cursor_type " ,
FT_UINT8 , BASE_HEX , VALS ( cursor_type_vs ) , 0x0 ,
NULL , HFILL }
} ,
{ & hf_cursor_width ,
{ " Cursor width " , " spice.cursor_width " ,
FT_UINT16 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_cursor_height ,
{ " Cursor height " , " spice.cursor_height " ,
FT_UINT16 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_cursor_hotspot_x ,
{ " Cursor hotspot X " , " spice.cursor_hotspot_x " ,
FT_UINT16 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_cursor_hotspot_y ,
{ " Cursor hotspot Y " , " spice.cursor_hotspot_y " ,
FT_UINT16 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_cursor_flags , /*FIXME - those are flags */
{ " Cursor flags " , " spice.cursor_flags " ,
FT_UINT16 , BASE_HEX , VALS ( cursor_flags_vs ) , 0x0 ,
NULL , HFILL }
} ,
{ & hf_cursor_id ,
{ " Cursor ID " , " spice.cursor_id " ,
FT_UINT64 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_spice_display_init_cache_id ,
{ " Cache ID " , " spice.display_init_cache_id " ,
FT_UINT8 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_spice_display_init_cache_size ,
{ " Cache size (pixels) " , " spice.display_init_cache_size " ,
FT_UINT64 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_spice_display_init_glz_dict_id ,
{ " GLZ Dictionary ID " , " spice.display_init_glz_dict_id " ,
FT_UINT8 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_spice_display_init_dict_window_size ,
{ " Dictionary window size " , " spice.display_init_dict_window_size " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_brush_type ,
{ " Brush type " , " spice.brush_type " ,
FT_UINT8 , BASE_DEC , VALS ( brush_types_vs ) , 0x0 ,
NULL , HFILL }
} ,
{ & hf_brush_rgb ,
{ " Brush color " , " spice.brush_rgb " ,
FT_UINT32 , BASE_HEX , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_pixmap_width ,
{ " Pixmap width " , " spice.pixmap_width " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_pixmap_height ,
{ " Pixmap height " , " spice.pixmap_height " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_pixmap_stride ,
{ " Pixmap stride " , " spice.pixmap_stride " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_pixmap_address ,
{ " Pixmap palettte pointer " , " spice.pixmap_palette_address " ,
FT_UINT32 , BASE_HEX_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_pixmap_format ,
{ " Pixmap format " , " spice.pixmap_format " ,
FT_UINT8 , BASE_DEC , VALS ( Pixmap_types_vs ) , 0x0 ,
NULL , HFILL }
} ,
{ & hf_pixmap_flags ,
{ " Pixmap flags " , " spice.pixmap_flags " ,
FT_UINT8 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_keyboard_bits , /* FIXME - flags */
{ " Keyboard led bits " , " spice.keyboard_bits " ,
FT_UINT16 , BASE_HEX , VALS ( input_modifiers_types ) , 0x0 ,
NULL , HFILL }
} ,
{ & hf_rectlist_size ,
{ " RectList size " , " spice.rectlist_size " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_session_id ,
{ " Session ID " , " spice.main_session_id " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_display_channels_hint ,
{ " Number of display channels " , " spice.display_channels_hint " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_supported_mouse_modes , /* FIXME: bitmask */
{ " Supported mouse modes " , " spice.supported_mouse_modes " ,
FT_UINT32 , BASE_HEX , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_current_mouse_mode ,
{ " Current mouse mode " , " spice.current_mouse_mode " ,
FT_UINT32 , BASE_DEC , VALS ( spice_mouse_modes_vs ) , 0x0 ,
NULL , HFILL }
} ,
{ & hf_agent_connected ,
{ " Agent " , " spice.agent " ,
FT_UINT32 , BASE_DEC , VALS ( spice_agent_vs ) , 0x0 ,
NULL , HFILL }
} ,
{ & hf_agent_tokens ,
{ " Agent tokens " , " spice.agent_tokens " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_multi_media_time ,
{ " Current server multimedia time " , " spice.multimedia_time " ,
FT_UINT32 , BASE_HEX , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_ram_hint ,
{ " RAM hint " , " spice.ram_hint " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_button_state , /*FIXME - bitmask */
{ " Mouse button state " , " spice.button_state " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_mouse_display_id ,
{ " Mouse display ID " , " spice.mouse_display_id " ,
FT_UINT8 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_display_text_fore_mode ,
{ " Text foreground mode " , " spice.draw_text_fore_mode " ,
FT_UINT16 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_display_text_back_mode ,
{ " Text background mode " , " spice.draw_text_back_mode " ,
FT_UINT16 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_display_stream_id ,
{ " Stream ID " , " spice.display_stream_id " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_display_stream_flags ,
{ " Stream flags " , " spice.display_stream_flags " ,
FT_UINT8 , BASE_DEC , VALS ( stream_flags ) , 0x0 ,
NULL , HFILL }
} ,
{ & hf_display_stream_codec_type ,
{ " Stream codec type " , " spice.display_stream_codec_type " ,
FT_UINT32 , BASE_DEC , VALS ( stream_codec_types ) , 0x0 ,
NULL , HFILL }
} ,
{ & hf_display_stream_data_size ,
{ " Stream data size " , " spice.display_stream_data_size " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_display_stream_width ,
{ " Stream width " , " spice.stream_width " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_display_stream_height ,
{ " Stream height " , " spice.stream_height " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_display_stream_src_width ,
{ " Stream source width " , " spice.stream_src_width " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_display_stream_src_height ,
{ " Stream source height " , " spice.stream_src_height " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_display_surface_id ,
{ " Surface ID " , " spice.surface_id " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_display_surface_width ,
{ " Surface width " , " spice.surface_width " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_display_surface_height ,
{ " Surface height " , " spice.surface_height " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_display_surface_format ,
{ " Surface format " , " spice.surface_format " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_display_surface_flags ,
{ " Surface flags " , " spice.surface_flags " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_tranparent_src_color ,
{ " Transparent source color " , " spice.display_transparent_src_color " ,
FT_UINT32 , BASE_HEX , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_tranparent_true_color ,
{ " Transparent true color " , " spice.display_transparent_true_color " ,
FT_UINT32 , BASE_HEX , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_main_client_agent_tokens ,
{ " Agent Tokes " , " spice.main_agent_tokens " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_agent_protocol ,
{ " Agent Protocol version " , " spice.main_agent_protocol " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_agent_type ,
{ " Agent Type " , " spice.main_agent_type " ,
FT_UINT32 , BASE_DEC , VALS ( agent_message_type ) , 0x0 ,
NULL , HFILL }
} ,
{ & hf_agent_opaque ,
{ " Agent Opaque " , " spice.main_agent_opaque " ,
FT_UINT64 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_agent_size ,
{ " Agent message size " , " spice.main_agent_size " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_agent_token ,
{ " Agent token " , " spice.main_agent_token " ,
FT_UINT32 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_agent_clipboard_selection ,
{ " Agent clipboard selection " , " spice.main_agent_clipboard_selection " ,
FT_UINT8 , BASE_DEC , NULL , 0x0 ,
NULL , HFILL }
} ,
{ & hf_agent_clipboard_type ,
{ " Agent clipboard type " , " spice.main_agent_clipboard_type " ,
FT_UINT32 , BASE_DEC , VALS ( agent_clipboard_type ) , 0x0 ,
NULL , HFILL }
} ,
} ;
/* Setup protocol subtree arrays */
static gint * ett [ ] = {
& ett_spice ,
& ett_link_client ,
& ett_link_server ,
& ett_link_caps ,
& ett_ticket_client ,
& ett_ticket_server ,
& ett_data ,
& ett_message ,
& ett_playback ,
& ett_common_server_message ,
& ett_common_client_message ,
& ett_display_client ,
& ett_display_server ,
& ett_point ,
& ett_point16 ,
& ett_rect ,
& ett_DisplayBase ,
& ett_Clip ,
& ett_Mask ,
& ett_imagedesc ,
& ett_imageQuic ,
& ett_GLZ_RGB ,
& ett_LZ_RGB ,
& ett_ZLIB_GLZ ,
& ett_Uncomp_tree ,
& ett_LZ_JPEG ,
& ett_LZ_PLT ,
& ett_JPEG ,
& ett_cursor_header ,
& ett_RedCursor ,
& ett_cursor ,
& ett_spice_main ,
& ett_brush ,
& ett_pattern ,
& ett_Pixmap ,
& ett_inputs_client ,
& ett_rectlist ,
& ett_inputs_server ,
& ett_record_client ,
& ett_main_client ,
& ett_spice_agent
} ;
/* Register the protocol name and description */
proto_spice = proto_register_protocol ( " Spice protocol " ,
" Spice " , " spice " ) ;
/* Requispice function calls to register the header fields and subtrees */
proto_register_field_array ( proto_spice , hf , array_length ( hf ) ) ;
proto_register_subtree_array ( ett , array_length ( ett ) ) ;
}
void
proto_reg_handoff_spice ( void )
{
spice_handle = new_create_dissector_handle ( dissect_spice , proto_spice ) ;
dissector_add_handle ( " tcp.port " , spice_handle ) ; /* for "decode as" */
heur_dissector_add ( " tcp " , test_spice_protocol , proto_spice ) ;
jpeg_handle = find_dissector ( " image-jfif " ) ;
}