From d28d5e97f08097fec6b3367ba87960810a742649 Mon Sep 17 00:00:00 2001
From: Michal Suchanek <msuchanek@suse.de>
Date: Mon, 23 Jan 2017 14:53:53 +0100
Subject: [PATCH] Move mouse-specific handling out of virtio_port_read_complete

Move some mouse-specific code from the start of
virtio_port_read_complete to a separate helper
as is the case with other message types.

Signed-off-by: Michal Suchanek <msuchanek@suse.de>
Acked-by: Christophe Fergeau <cfergeau@redhat.com>
---
 src/vdagentd.c | 43 ++++++++++++++++++++++++-------------------
 1 file changed, 24 insertions(+), 19 deletions(-)

--- spice-vdagent-0.16.0.orig/src/vdagentd.c
+++ spice-vdagent-0.16.0/src/vdagentd.c
@@ -78,6 +78,34 @@ static int client_connected = 0;
 static int max_clipboard = -1;
 
 /* utility functions */
+static void virtio_msg_uint32_to_le(uint8_t *_msg, uint32_t size, uint32_t offset)
+{
+    uint32_t i, *msg = (uint32_t *)(_msg + offset);
+
+    /* offset - size % 4 should be 0 - extra bytes are ignored */
+    for (i = 0; i < (size - offset) / 4; i++)
+        msg[i] = GUINT32_TO_LE(msg[i]);
+}
+
+static void virtio_msg_uint32_from_le(uint8_t *_msg, uint32_t size, uint32_t offset)
+{
+    uint32_t i, *msg = (uint32_t *)(_msg + offset);
+
+    /* offset - size % 4 should be 0 - extra bytes are ignored */
+    for (i = 0; i < (size - offset) / 4; i++)
+        msg[i] = GUINT32_FROM_LE(msg[i]);
+}
+
+static void virtio_msg_uint16_from_le(uint8_t *_msg, uint32_t size, uint32_t offset)
+{
+    uint32_t i;
+    uint16_t *msg = (uint16_t *)(_msg + offset);
+
+    /* offset - size % 2 should be 0 - extra bytes are ignored */
+    for (i = 0; i < (size - offset) / 2; i++)
+        msg[i] = GUINT16_FROM_LE(msg[i]);
+}
+
 /* vdagentd <-> spice-client communication handling */
 static void send_capabilities(struct vdagent_virtio_port *vport,
     uint32_t request)
@@ -102,6 +130,7 @@ static void send_capabilities(struct vda
     VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_GUEST_LINEEND_LF);
     VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MAX_CLIPBOARD);
     VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_AUDIO_VOLUME_SYNC);
+    virtio_msg_uint32_to_le((uint8_t *)caps, size, 0);
 
     vdagent_virtio_port_write(vport, VDP_CLIENT_PORT,
                               VD_AGENT_ANNOUNCE_CAPABILITIES, 0,
@@ -118,6 +147,29 @@ static void do_client_disconnect(void)
     }
 }
 
+void do_client_mouse(struct vdagentd_uinput **uinputp, VDAgentMouseState *mouse)
+{
+    vdagentd_uinput_do_mouse(uinputp, mouse);
+    if (!*uinputp) {
+        /* Try to re-open the tablet */
+        struct agent_data *agent_data =
+            udscs_get_user_data(active_session_conn);
+        if (agent_data)
+            *uinputp = vdagentd_uinput_create(uinput_device,
+                                              agent_data->width,
+                                              agent_data->height,
+                                              agent_data->screen_info,
+                                              agent_data->screen_count,
+                                              debug > 1,
+                                              uinput_fake);
+        if (!*uinputp) {
+            syslog(LOG_CRIT, "Fatal uinput error");
+            retval = 1;
+            quit = 1;
+        }
+    }
+}
+
 static void do_client_monitors(struct vdagent_virtio_port *vport, int port_nr,
     VDAgentMessage *message_header, VDAgentMonitorsConfig *new_monitors)
 {
@@ -151,8 +203,8 @@ static void do_client_monitors(struct vd
                     (uint8_t *)mon_config, size);
 
     /* Acknowledge reception of monitors config to spice server / client */
-    reply.type  = VD_AGENT_MONITORS_CONFIG;
-    reply.error = VD_AGENT_SUCCESS;
+    reply.type  = GUINT32_TO_LE(VD_AGENT_MONITORS_CONFIG);
+    reply.error = GUINT32_TO_LE(VD_AGENT_SUCCESS);
     vdagent_virtio_port_write(vport, port_nr, VD_AGENT_REPLY, 0,
                               (uint8_t *)&reply, sizeof(reply));
 }
@@ -253,8 +305,8 @@ static void cancel_file_xfer(struct vdag
                              const char *msg, uint32_t id)
 {
     VDAgentFileXferStatusMessage status = {
-        .id = id,
-        .result = VD_AGENT_FILE_XFER_STATUS_CANCELLED,
+        .id = GUINT32_TO_LE(id),
+        .result = GUINT32_TO_LE(VD_AGENT_FILE_XFER_STATUS_CANCELLED),
     };
     syslog(LOG_WARNING, msg, id);
     if (vport)
@@ -296,6 +348,8 @@ static void do_client_file_xfer(struct v
         id = d->id;
         break;
     }
+    default:
+        g_return_if_reached(); /* quiet uninitialized variable warning */
     }
 
     conn = g_hash_table_lookup(active_xfers, GUINT_TO_POINTER(id));
@@ -307,52 +361,156 @@ static void do_client_file_xfer(struct v
     udscs_write(conn, msg_type, 0, 0, data, message_header->size);
 }
 
-int virtio_port_read_complete(
-        struct vdagent_virtio_port *vport,
-        int port_nr,
-        VDAgentMessage *message_header,
+static gsize vdagent_message_min_size[] =
+{
+    -1, /* Does not exist */
+    sizeof(VDAgentMouseState), /* VD_AGENT_MOUSE_STATE */
+    sizeof(VDAgentMonitorsConfig), /* VD_AGENT_MONITORS_CONFIG */
+    sizeof(VDAgentReply), /* VD_AGENT_REPLY */
+    sizeof(VDAgentClipboard), /* VD_AGENT_CLIPBOARD */
+    sizeof(VDAgentDisplayConfig), /* VD_AGENT_DISPLAY_CONFIG */
+    sizeof(VDAgentAnnounceCapabilities), /* VD_AGENT_ANNOUNCE_CAPABILITIES */
+    sizeof(VDAgentClipboardGrab), /* VD_AGENT_CLIPBOARD_GRAB */
+    sizeof(VDAgentClipboardRequest), /* VD_AGENT_CLIPBOARD_REQUEST */
+    sizeof(VDAgentClipboardRelease), /* VD_AGENT_CLIPBOARD_RELEASE */
+    sizeof(VDAgentFileXferStartMessage), /* VD_AGENT_FILE_XFER_START */
+    sizeof(VDAgentFileXferStatusMessage), /* VD_AGENT_FILE_XFER_STATUS */
+    sizeof(VDAgentFileXferDataMessage), /* VD_AGENT_FILE_XFER_DATA */
+    0, /* VD_AGENT_CLIENT_DISCONNECTED */
+    sizeof(VDAgentMaxClipboard), /* VD_AGENT_MAX_CLIPBOARD */
+    sizeof(VDAgentAudioVolumeSync), /* VD_AGENT_AUDIO_VOLUME_SYNC */
+};
+
+static void vdagent_message_clipboard_from_le(VDAgentMessage *message_header,
+        uint8_t *data)
+{
+    gsize min_size = vdagent_message_min_size[message_header->type];
+    uint32_t *data_type = (uint32_t *) data;
+
+    if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
+                                VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
+        min_size += 4;
+        data_type++;
+    }
+
+    switch (message_header->type) {
+    case VD_AGENT_CLIPBOARD_REQUEST:
+    case VD_AGENT_CLIPBOARD:
+        *data_type = GUINT32_FROM_LE(*data_type);
+        break;
+    case VD_AGENT_CLIPBOARD_GRAB:
+        virtio_msg_uint32_from_le(data, message_header->size, min_size);
+        break;
+    case VD_AGENT_CLIPBOARD_RELEASE:
+        break;
+    default:
+        g_warn_if_reached();
+    }
+}
+
+static void vdagent_message_file_xfer_from_le(VDAgentMessage *message_header,
         uint8_t *data)
 {
+    uint32_t *id = (uint32_t *)data;
+    *id = GUINT32_FROM_LE(*id);
+    id++; /* status */
+
+    switch (message_header->type) {
+    case VD_AGENT_FILE_XFER_DATA: {
+       VDAgentFileXferDataMessage *msg = (VDAgentFileXferDataMessage *)data;
+       msg->size = GUINT64_FROM_LE(msg->size);
+       break;
+    }
+    case VD_AGENT_FILE_XFER_STATUS:
+       *id = GUINT32_FROM_LE(*id); /* status */
+       break;
+    }
+}
+
+static gboolean vdagent_message_check_size(const VDAgentMessage *message_header)
+{
     uint32_t min_size = 0;
 
     if (message_header->protocol != VD_AGENT_PROTOCOL) {
         syslog(LOG_ERR, "message with wrong protocol version ignoring");
-        return 0;
+        return FALSE;
+    }
+
+    if (!message_header->type ||
+        message_header->type >= G_N_ELEMENTS(vdagent_message_min_size)) {
+        syslog(LOG_WARNING, "unknown message type %d, ignoring",
+               message_header->type);
+        return FALSE;
+    }
+
+    min_size = vdagent_message_min_size[message_header->type];
+    if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
+                                VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
+        switch (message_header->type) {
+        case VD_AGENT_CLIPBOARD_GRAB:
+        case VD_AGENT_CLIPBOARD_REQUEST:
+        case VD_AGENT_CLIPBOARD:
+        case VD_AGENT_CLIPBOARD_RELEASE:
+          min_size += 4;
+        }
     }
 
     switch (message_header->type) {
+    case VD_AGENT_MONITORS_CONFIG:
+    case VD_AGENT_FILE_XFER_START:
+    case VD_AGENT_FILE_XFER_DATA:
+    case VD_AGENT_CLIPBOARD:
+    case VD_AGENT_CLIPBOARD_GRAB:
+    case VD_AGENT_AUDIO_VOLUME_SYNC:
+    case VD_AGENT_ANNOUNCE_CAPABILITIES:
+        if (message_header->size < min_size) {
+            syslog(LOG_ERR, "read: invalid message size: %u for message type: %u",
+                   message_header->size, message_header->type);
+            return FALSE;
+        }
+        break;
     case VD_AGENT_MOUSE_STATE:
-        if (message_header->size != sizeof(VDAgentMouseState))
-            goto size_error;
-        vdagentd_uinput_do_mouse(&uinput, (VDAgentMouseState *)data);
-        if (!uinput) {
-            /* Try to re-open the tablet */
-            struct agent_data *agent_data =
-                udscs_get_user_data(active_session_conn);
-            if (agent_data)
-                uinput = vdagentd_uinput_create(uinput_device,
-                                                agent_data->width,
-                                                agent_data->height,
-                                                agent_data->screen_info,
-                                                agent_data->screen_count,
-                                                debug > 1,
-                                                uinput_fake);
-            if (!uinput) {
-                syslog(LOG_CRIT, "Fatal uinput error");
-                retval = 1;
-                quit = 1;
-            }
+    case VD_AGENT_FILE_XFER_STATUS:
+    case VD_AGENT_DISPLAY_CONFIG:
+    case VD_AGENT_REPLY:
+    case VD_AGENT_CLIPBOARD_REQUEST:
+    case VD_AGENT_CLIPBOARD_RELEASE:
+    case VD_AGENT_MAX_CLIPBOARD:
+    case VD_AGENT_CLIENT_DISCONNECTED:
+        if (message_header->size != min_size) {
+            syslog(LOG_ERR, "read: invalid message size: %u for message type: %u",
+                   message_header->size, message_header->type);
+            return FALSE;
         }
         break;
+    default:
+        g_warn_if_reached();
+        return FALSE;
+    }
+    return TRUE;
+}
+
+int virtio_port_read_complete(
+        struct vdagent_virtio_port *vport,
+        int port_nr,
+        VDAgentMessage *message_header,
+        uint8_t *data)
+{
+    if (!vdagent_message_check_size(message_header))
+        return 0;
+
+    switch (message_header->type) {
+    case VD_AGENT_MOUSE_STATE:
+        virtio_msg_uint32_from_le(data, message_header->size, 0);
+        do_client_mouse(&uinput, (VDAgentMouseState *)data);
+        break;
     case VD_AGENT_MONITORS_CONFIG:
-        if (message_header->size < sizeof(VDAgentMonitorsConfig))
-            goto size_error;
+        virtio_msg_uint32_from_le(data, message_header->size, 0);
         do_client_monitors(vport, port_nr, message_header,
                     (VDAgentMonitorsConfig *)data);
         break;
     case VD_AGENT_ANNOUNCE_CAPABILITIES:
-        if (message_header->size < sizeof(VDAgentAnnounceCapabilities))
-            goto size_error;
+        virtio_msg_uint32_from_le(data, message_header->size, 0);
         do_client_capabilities(vport, message_header,
                         (VDAgentAnnounceCapabilities *)data);
         break;
@@ -360,61 +518,41 @@ int virtio_port_read_complete(
     case VD_AGENT_CLIPBOARD_REQUEST:
     case VD_AGENT_CLIPBOARD:
     case VD_AGENT_CLIPBOARD_RELEASE:
-        switch (message_header->type) {
-        case VD_AGENT_CLIPBOARD_GRAB:
-            min_size = sizeof(VDAgentClipboardGrab); break;
-        case VD_AGENT_CLIPBOARD_REQUEST:
-            min_size = sizeof(VDAgentClipboardRequest); break;
-        case VD_AGENT_CLIPBOARD:
-            min_size = sizeof(VDAgentClipboard); break;
-        }
-        if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
-                                    VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
-            min_size += 4;
-        }
-        if (message_header->size < min_size) {
-            goto size_error;
-        }
+        vdagent_message_clipboard_from_le(message_header, data);
         do_client_clipboard(vport, message_header, data);
         break;
     case VD_AGENT_FILE_XFER_START:
     case VD_AGENT_FILE_XFER_STATUS:
     case VD_AGENT_FILE_XFER_DATA:
+        vdagent_message_file_xfer_from_le(message_header, data);
         do_client_file_xfer(vport, message_header, data);
         break;
     case VD_AGENT_CLIENT_DISCONNECTED:
         vdagent_virtio_port_reset(vport, VDP_CLIENT_PORT);
         do_client_disconnect();
         break;
-    case VD_AGENT_MAX_CLIPBOARD:
-        if (message_header->size != sizeof(VDAgentMaxClipboard))
-            goto size_error;
-        VDAgentMaxClipboard *msg = (VDAgentMaxClipboard *)data;
-        syslog(LOG_DEBUG, "Set max clipboard: %d", msg->max);
-        max_clipboard = msg->max;
+    case VD_AGENT_MAX_CLIPBOARD: {
+        max_clipboard = GUINT32_FROM_LE(((VDAgentMaxClipboard *)data)->max);
+        syslog(LOG_DEBUG, "Set max clipboard: %d", max_clipboard);
         break;
-    case VD_AGENT_AUDIO_VOLUME_SYNC:
-        if (message_header->size < sizeof(VDAgentAudioVolumeSync))
-            goto size_error;
+    }
+    case VD_AGENT_AUDIO_VOLUME_SYNC: {
+        VDAgentAudioVolumeSync *vdata = (VDAgentAudioVolumeSync *)data;
+        virtio_msg_uint16_from_le((uint8_t *)vdata, message_header->size,
+            offsetof(VDAgentAudioVolumeSync, volume));
 
-        do_client_volume_sync(vport, port_nr, message_header,
-                (VDAgentAudioVolumeSync *)data);
+        do_client_volume_sync(vport, port_nr, message_header, vdata);
         break;
+    }
     default:
-        syslog(LOG_WARNING, "unknown message type %d, ignoring",
-               message_header->type);
+        g_warn_if_reached();
     }
 
     return 0;
-
-size_error:
-    syslog(LOG_ERR, "read: invalid message size: %u for message type: %u",
-           message_header->size, message_header->type);
-    return 0;
 }
 
 static void virtio_write_clipboard(uint8_t selection, uint32_t msg_type,
-    uint32_t data_type, const uint8_t *data, uint32_t data_size)
+    uint32_t data_type, uint8_t *data, uint32_t data_size)
 {
     uint32_t size = data_size;
 
@@ -435,15 +573,18 @@ static void virtio_write_clipboard(uint8
         vdagent_virtio_port_write_append(virtio_port, sel, 4);
     }
     if (data_type != -1) {
+        data_type = GUINT32_TO_LE(data_type);
         vdagent_virtio_port_write_append(virtio_port, (uint8_t*)&data_type, 4);
     }
 
+    if (msg_type == VD_AGENT_CLIPBOARD_GRAB)
+        virtio_msg_uint32_to_le(data, data_size, 0);
     vdagent_virtio_port_write_append(virtio_port, data, data_size);
 }
 
 /* vdagentd <-> vdagent communication handling */
 int do_agent_clipboard(struct udscs_connection *conn,
-        struct udscs_message_header *header, const uint8_t *data)
+        struct udscs_message_header *header, uint8_t *data)
 {
     uint8_t selection = header->arg1;
     uint32_t msg_type = 0, data_type = -1, size = header->size;
@@ -746,8 +887,8 @@ void agent_read_complete(struct udscs_co
         break;
     case VDAGENTD_FILE_XFER_STATUS:{
         VDAgentFileXferStatusMessage status;
-        status.id = header->arg1;
-        status.result = header->arg2;
+        status.id = GUINT32_TO_LE(header->arg1);
+        status.result = GUINT32_TO_LE(header->arg2);
         vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
                                   VD_AGENT_FILE_XFER_STATUS, 0,
                                   (uint8_t *)&status, sizeof(status));
--- spice-vdagent-0.16.0.orig/src/vdagent-virtio-port.c
+++ spice-vdagent-0.16.0/src/vdagent-virtio-port.c
@@ -28,6 +28,7 @@
 #include <sys/select.h>
 #include <sys/socket.h>
 #include <sys/un.h>
+#include <glib.h>
 
 #include "vdagent-virtio-port.h"
 
@@ -217,16 +218,16 @@ int vdagent_virtio_port_write_start(
         return -1;
     }
 
-    chunk_header.port = port_nr;
-    chunk_header.size = sizeof(message_header) + data_size;
+    chunk_header.port = GUINT32_TO_LE(port_nr);
+    chunk_header.size = GUINT32_TO_LE(sizeof(message_header) + data_size);
     memcpy(new_wbuf->buf + new_wbuf->write_pos, &chunk_header,
            sizeof(chunk_header));
     new_wbuf->write_pos += sizeof(chunk_header);
-    
-    message_header.protocol = VD_AGENT_PROTOCOL;
-    message_header.type = message_type;
-    message_header.opaque = message_opaque;
-    message_header.size = data_size;
+
+    message_header.protocol = GUINT32_TO_LE(VD_AGENT_PROTOCOL);
+    message_header.type = GUINT32_TO_LE(message_type);
+    message_header.opaque = GUINT64_TO_LE(message_opaque);
+    message_header.size = GUINT32_TO_LE(data_size);
     memcpy(new_wbuf->buf + new_wbuf->write_pos, &message_header,
            sizeof(message_header));
     new_wbuf->write_pos += sizeof(message_header);
@@ -310,13 +311,20 @@ static void vdagent_virtio_port_do_chunk
         memcpy((uint8_t *)&port->message_header + port->message_header_read,
                vport->chunk_data, read);
         port->message_header_read += read;
-        if (port->message_header_read == sizeof(port->message_header) &&
-                port->message_header.size) {
-            port->message_data = malloc(port->message_header.size);
-            if (!port->message_data) {
-                syslog(LOG_ERR, "out of memory, disconnecting virtio");
-                vdagent_virtio_port_destroy(vportp);
-                return;
+        if (port->message_header_read == sizeof(port->message_header)) {
+
+            port->message_header.protocol = GUINT32_FROM_LE(port->message_header.protocol);
+            port->message_header.type = GUINT32_FROM_LE(port->message_header.type);
+            port->message_header.opaque = GUINT64_FROM_LE(port->message_header.opaque);
+            port->message_header.size = GUINT32_FROM_LE(port->message_header.size);
+
+            if (port->message_header.size) {
+                port->message_data = malloc(port->message_header.size);
+                if (!port->message_data) {
+                    syslog(LOG_ERR, "out of memory, disconnecting virtio");
+                    vdagent_virtio_port_destroy(vportp);
+                    return;
+                }
             }
         }
         pos = read;
@@ -421,6 +429,8 @@ static void vdagent_virtio_port_do_read(
     if (vport->chunk_header_read < sizeof(vport->chunk_header)) {
         vport->chunk_header_read += n;
         if (vport->chunk_header_read == sizeof(vport->chunk_header)) {
+            vport->chunk_header.size = GUINT32_FROM_LE(vport->chunk_header.size);
+            vport->chunk_header.port = GUINT32_FROM_LE(vport->chunk_header.port);
             if (vport->chunk_header.size > VD_AGENT_MAX_DATA_SIZE) {
                 syslog(LOG_ERR, "chunk size %u too large",
                        vport->chunk_header.size);
