xref: /openbmc/qemu/ui/vnc.c (revision cc7a8ea740ec74a144e866a1d24aa6b490e31923)
13e230dd2SCorentin Chary /*
23e230dd2SCorentin Chary  * QEMU VNC display driver
33e230dd2SCorentin Chary  *
43e230dd2SCorentin Chary  * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
53e230dd2SCorentin Chary  * Copyright (C) 2006 Fabrice Bellard
63e230dd2SCorentin Chary  * Copyright (C) 2009 Red Hat, Inc
73e230dd2SCorentin Chary  *
83e230dd2SCorentin Chary  * Permission is hereby granted, free of charge, to any person obtaining a copy
93e230dd2SCorentin Chary  * of this software and associated documentation files (the "Software"), to deal
103e230dd2SCorentin Chary  * in the Software without restriction, including without limitation the rights
113e230dd2SCorentin Chary  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
123e230dd2SCorentin Chary  * copies of the Software, and to permit persons to whom the Software is
133e230dd2SCorentin Chary  * furnished to do so, subject to the following conditions:
143e230dd2SCorentin Chary  *
153e230dd2SCorentin Chary  * The above copyright notice and this permission notice shall be included in
163e230dd2SCorentin Chary  * all copies or substantial portions of the Software.
173e230dd2SCorentin Chary  *
183e230dd2SCorentin Chary  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
193e230dd2SCorentin Chary  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
203e230dd2SCorentin Chary  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
213e230dd2SCorentin Chary  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
223e230dd2SCorentin Chary  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
233e230dd2SCorentin Chary  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
243e230dd2SCorentin Chary  * THE SOFTWARE.
253e230dd2SCorentin Chary  */
263e230dd2SCorentin Chary 
273e230dd2SCorentin Chary #include "vnc.h"
28bd023f95SCorentin Chary #include "vnc-jobs.h"
2940066175SGerd Hoffmann #include "trace.h"
301d0d59feSGerd Hoffmann #include "hw/qdev.h"
319c17d615SPaolo Bonzini #include "sysemu/sysemu.h"
32d49b6836SMarkus Armbruster #include "qemu/error-report.h"
331de7afc9SPaolo Bonzini #include "qemu/sockets.h"
341de7afc9SPaolo Bonzini #include "qemu/timer.h"
351de7afc9SPaolo Bonzini #include "qemu/acl.h"
364db14629SGerd Hoffmann #include "qemu/config-file.h"
37*cc7a8ea7SMarkus Armbruster #include "qapi/qmp/qerror.h"
387b1b5d19SPaolo Bonzini #include "qapi/qmp/types.h"
392b54aa87SLuiz Capitulino #include "qmp-commands.h"
401de7afc9SPaolo Bonzini #include "qemu/osdep.h"
418d447d10SGerd Hoffmann #include "ui/input.h"
42fb6ba0d5SWenchao Xia #include "qapi-event.h"
433e230dd2SCorentin Chary 
440f7b2864SGerd Hoffmann #define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT
453e230dd2SCorentin Chary #define VNC_REFRESH_INTERVAL_INC  50
460f7b2864SGerd Hoffmann #define VNC_REFRESH_INTERVAL_MAX  GUI_REFRESH_INTERVAL_IDLE
47999342a0SCorentin Chary static const struct timeval VNC_REFRESH_STATS = { 0, 500000 };
48999342a0SCorentin Chary static const struct timeval VNC_REFRESH_LOSSY = { 2, 0 };
493e230dd2SCorentin Chary 
503e230dd2SCorentin Chary #include "vnc_keysym.h"
513e230dd2SCorentin Chary #include "d3des.h"
523e230dd2SCorentin Chary 
53d616ccc5SGerd Hoffmann static QTAILQ_HEAD(, VncDisplay) vnc_displays =
54d616ccc5SGerd Hoffmann     QTAILQ_HEAD_INITIALIZER(vnc_displays);
553e230dd2SCorentin Chary 
563e230dd2SCorentin Chary static int vnc_cursor_define(VncState *vs);
577bc9318bSGerd Hoffmann static void vnc_release_modifiers(VncState *vs);
583e230dd2SCorentin Chary 
598cf36489SGerd Hoffmann static void vnc_set_share_mode(VncState *vs, VncShareMode mode)
608cf36489SGerd Hoffmann {
618cf36489SGerd Hoffmann #ifdef _VNC_DEBUG
628cf36489SGerd Hoffmann     static const char *mn[] = {
638cf36489SGerd Hoffmann         [0]                           = "undefined",
648cf36489SGerd Hoffmann         [VNC_SHARE_MODE_CONNECTING]   = "connecting",
658cf36489SGerd Hoffmann         [VNC_SHARE_MODE_SHARED]       = "shared",
668cf36489SGerd Hoffmann         [VNC_SHARE_MODE_EXCLUSIVE]    = "exclusive",
678cf36489SGerd Hoffmann         [VNC_SHARE_MODE_DISCONNECTED] = "disconnected",
688cf36489SGerd Hoffmann     };
698cf36489SGerd Hoffmann     fprintf(stderr, "%s/%d: %s -> %s\n", __func__,
708cf36489SGerd Hoffmann             vs->csock, mn[vs->share_mode], mn[mode]);
718cf36489SGerd Hoffmann #endif
728cf36489SGerd Hoffmann 
73e5f34cddSGerd Hoffmann     switch (vs->share_mode) {
74e5f34cddSGerd Hoffmann     case VNC_SHARE_MODE_CONNECTING:
75e5f34cddSGerd Hoffmann         vs->vd->num_connecting--;
76e5f34cddSGerd Hoffmann         break;
77e5f34cddSGerd Hoffmann     case VNC_SHARE_MODE_SHARED:
78e5f34cddSGerd Hoffmann         vs->vd->num_shared--;
79e5f34cddSGerd Hoffmann         break;
80e5f34cddSGerd Hoffmann     case VNC_SHARE_MODE_EXCLUSIVE:
818cf36489SGerd Hoffmann         vs->vd->num_exclusive--;
82e5f34cddSGerd Hoffmann         break;
83e5f34cddSGerd Hoffmann     default:
84e5f34cddSGerd Hoffmann         break;
858cf36489SGerd Hoffmann     }
86e5f34cddSGerd Hoffmann 
878cf36489SGerd Hoffmann     vs->share_mode = mode;
88e5f34cddSGerd Hoffmann 
89e5f34cddSGerd Hoffmann     switch (vs->share_mode) {
90e5f34cddSGerd Hoffmann     case VNC_SHARE_MODE_CONNECTING:
91e5f34cddSGerd Hoffmann         vs->vd->num_connecting++;
92e5f34cddSGerd Hoffmann         break;
93e5f34cddSGerd Hoffmann     case VNC_SHARE_MODE_SHARED:
94e5f34cddSGerd Hoffmann         vs->vd->num_shared++;
95e5f34cddSGerd Hoffmann         break;
96e5f34cddSGerd Hoffmann     case VNC_SHARE_MODE_EXCLUSIVE:
978cf36489SGerd Hoffmann         vs->vd->num_exclusive++;
98e5f34cddSGerd Hoffmann         break;
99e5f34cddSGerd Hoffmann     default:
100e5f34cddSGerd Hoffmann         break;
1018cf36489SGerd Hoffmann     }
1028cf36489SGerd Hoffmann }
1038cf36489SGerd Hoffmann 
1043e230dd2SCorentin Chary static char *addr_to_string(const char *format,
1053e230dd2SCorentin Chary                             struct sockaddr_storage *sa,
1063e230dd2SCorentin Chary                             socklen_t salen) {
1073e230dd2SCorentin Chary     char *addr;
1083e230dd2SCorentin Chary     char host[NI_MAXHOST];
1093e230dd2SCorentin Chary     char serv[NI_MAXSERV];
1103e230dd2SCorentin Chary     int err;
1113e230dd2SCorentin Chary     size_t addrlen;
1123e230dd2SCorentin Chary 
1133e230dd2SCorentin Chary     if ((err = getnameinfo((struct sockaddr *)sa, salen,
1143e230dd2SCorentin Chary                            host, sizeof(host),
1153e230dd2SCorentin Chary                            serv, sizeof(serv),
1163e230dd2SCorentin Chary                            NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
1173e230dd2SCorentin Chary         VNC_DEBUG("Cannot resolve address %d: %s\n",
1183e230dd2SCorentin Chary                   err, gai_strerror(err));
1193e230dd2SCorentin Chary         return NULL;
1203e230dd2SCorentin Chary     }
1213e230dd2SCorentin Chary 
1223e230dd2SCorentin Chary     /* Enough for the existing format + the 2 vars we're
1233e230dd2SCorentin Chary      * substituting in. */
1243e230dd2SCorentin Chary     addrlen = strlen(format) + strlen(host) + strlen(serv);
1257267c094SAnthony Liguori     addr = g_malloc(addrlen + 1);
1263e230dd2SCorentin Chary     snprintf(addr, addrlen, format, host, serv);
1273e230dd2SCorentin Chary     addr[addrlen] = '\0';
1283e230dd2SCorentin Chary 
1293e230dd2SCorentin Chary     return addr;
1303e230dd2SCorentin Chary }
1313e230dd2SCorentin Chary 
1323e230dd2SCorentin Chary 
1333e230dd2SCorentin Chary char *vnc_socket_local_addr(const char *format, int fd) {
1343e230dd2SCorentin Chary     struct sockaddr_storage sa;
1353e230dd2SCorentin Chary     socklen_t salen;
1363e230dd2SCorentin Chary 
1373e230dd2SCorentin Chary     salen = sizeof(sa);
1383e230dd2SCorentin Chary     if (getsockname(fd, (struct sockaddr*)&sa, &salen) < 0)
1393e230dd2SCorentin Chary         return NULL;
1403e230dd2SCorentin Chary 
1413e230dd2SCorentin Chary     return addr_to_string(format, &sa, salen);
1423e230dd2SCorentin Chary }
1433e230dd2SCorentin Chary 
1443e230dd2SCorentin Chary char *vnc_socket_remote_addr(const char *format, int fd) {
1453e230dd2SCorentin Chary     struct sockaddr_storage sa;
1463e230dd2SCorentin Chary     socklen_t salen;
1473e230dd2SCorentin Chary 
1483e230dd2SCorentin Chary     salen = sizeof(sa);
1493e230dd2SCorentin Chary     if (getpeername(fd, (struct sockaddr*)&sa, &salen) < 0)
1503e230dd2SCorentin Chary         return NULL;
1513e230dd2SCorentin Chary 
1523e230dd2SCorentin Chary     return addr_to_string(format, &sa, salen);
1533e230dd2SCorentin Chary }
1543e230dd2SCorentin Chary 
155fb6ba0d5SWenchao Xia static VncBasicInfo *vnc_basic_info_get(struct sockaddr_storage *sa,
1563e230dd2SCorentin Chary                                         socklen_t salen)
1573e230dd2SCorentin Chary {
158fb6ba0d5SWenchao Xia     VncBasicInfo *info;
1593e230dd2SCorentin Chary     char host[NI_MAXHOST];
1603e230dd2SCorentin Chary     char serv[NI_MAXSERV];
1613e230dd2SCorentin Chary     int err;
1623e230dd2SCorentin Chary 
1633e230dd2SCorentin Chary     if ((err = getnameinfo((struct sockaddr *)sa, salen,
1643e230dd2SCorentin Chary                            host, sizeof(host),
1653e230dd2SCorentin Chary                            serv, sizeof(serv),
1663e230dd2SCorentin Chary                            NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
1673e230dd2SCorentin Chary         VNC_DEBUG("Cannot resolve address %d: %s\n",
1683e230dd2SCorentin Chary                   err, gai_strerror(err));
169fb6ba0d5SWenchao Xia         return NULL;
1703e230dd2SCorentin Chary     }
1713e230dd2SCorentin Chary 
172fb6ba0d5SWenchao Xia     info = g_malloc0(sizeof(VncBasicInfo));
173fb6ba0d5SWenchao Xia     info->host = g_strdup(host);
174fb6ba0d5SWenchao Xia     info->service = g_strdup(serv);
175fb6ba0d5SWenchao Xia     info->family = inet_netfamily(sa->ss_family);
176fb6ba0d5SWenchao Xia     return info;
1773e230dd2SCorentin Chary }
1783e230dd2SCorentin Chary 
179fb6ba0d5SWenchao Xia static VncBasicInfo *vnc_basic_info_get_from_server_addr(int fd)
1803e230dd2SCorentin Chary {
1813e230dd2SCorentin Chary     struct sockaddr_storage sa;
1823e230dd2SCorentin Chary     socklen_t salen;
1833e230dd2SCorentin Chary 
1843e230dd2SCorentin Chary     salen = sizeof(sa);
1853e230dd2SCorentin Chary     if (getsockname(fd, (struct sockaddr*)&sa, &salen) < 0) {
186fb6ba0d5SWenchao Xia         return NULL;
1873e230dd2SCorentin Chary     }
1883e230dd2SCorentin Chary 
189fb6ba0d5SWenchao Xia     return vnc_basic_info_get(&sa, salen);
1903e230dd2SCorentin Chary }
1913e230dd2SCorentin Chary 
192fb6ba0d5SWenchao Xia static VncBasicInfo *vnc_basic_info_get_from_remote_addr(int fd)
1933e230dd2SCorentin Chary {
1943e230dd2SCorentin Chary     struct sockaddr_storage sa;
1953e230dd2SCorentin Chary     socklen_t salen;
1963e230dd2SCorentin Chary 
1973e230dd2SCorentin Chary     salen = sizeof(sa);
1983e230dd2SCorentin Chary     if (getpeername(fd, (struct sockaddr*)&sa, &salen) < 0) {
199fb6ba0d5SWenchao Xia         return NULL;
2003e230dd2SCorentin Chary     }
2013e230dd2SCorentin Chary 
202fb6ba0d5SWenchao Xia     return vnc_basic_info_get(&sa, salen);
2033e230dd2SCorentin Chary }
2043e230dd2SCorentin Chary 
2053e230dd2SCorentin Chary static const char *vnc_auth_name(VncDisplay *vd) {
2063e230dd2SCorentin Chary     switch (vd->auth) {
2073e230dd2SCorentin Chary     case VNC_AUTH_INVALID:
2083e230dd2SCorentin Chary         return "invalid";
2093e230dd2SCorentin Chary     case VNC_AUTH_NONE:
2103e230dd2SCorentin Chary         return "none";
2113e230dd2SCorentin Chary     case VNC_AUTH_VNC:
2123e230dd2SCorentin Chary         return "vnc";
2133e230dd2SCorentin Chary     case VNC_AUTH_RA2:
2143e230dd2SCorentin Chary         return "ra2";
2153e230dd2SCorentin Chary     case VNC_AUTH_RA2NE:
2163e230dd2SCorentin Chary         return "ra2ne";
2173e230dd2SCorentin Chary     case VNC_AUTH_TIGHT:
2183e230dd2SCorentin Chary         return "tight";
2193e230dd2SCorentin Chary     case VNC_AUTH_ULTRA:
2203e230dd2SCorentin Chary         return "ultra";
2213e230dd2SCorentin Chary     case VNC_AUTH_TLS:
2223e230dd2SCorentin Chary         return "tls";
2233e230dd2SCorentin Chary     case VNC_AUTH_VENCRYPT:
2243e230dd2SCorentin Chary #ifdef CONFIG_VNC_TLS
2253e230dd2SCorentin Chary         switch (vd->subauth) {
2263e230dd2SCorentin Chary         case VNC_AUTH_VENCRYPT_PLAIN:
2273e230dd2SCorentin Chary             return "vencrypt+plain";
2283e230dd2SCorentin Chary         case VNC_AUTH_VENCRYPT_TLSNONE:
2293e230dd2SCorentin Chary             return "vencrypt+tls+none";
2303e230dd2SCorentin Chary         case VNC_AUTH_VENCRYPT_TLSVNC:
2313e230dd2SCorentin Chary             return "vencrypt+tls+vnc";
2323e230dd2SCorentin Chary         case VNC_AUTH_VENCRYPT_TLSPLAIN:
2333e230dd2SCorentin Chary             return "vencrypt+tls+plain";
2343e230dd2SCorentin Chary         case VNC_AUTH_VENCRYPT_X509NONE:
2353e230dd2SCorentin Chary             return "vencrypt+x509+none";
2363e230dd2SCorentin Chary         case VNC_AUTH_VENCRYPT_X509VNC:
2373e230dd2SCorentin Chary             return "vencrypt+x509+vnc";
2383e230dd2SCorentin Chary         case VNC_AUTH_VENCRYPT_X509PLAIN:
2393e230dd2SCorentin Chary             return "vencrypt+x509+plain";
2403e230dd2SCorentin Chary         case VNC_AUTH_VENCRYPT_TLSSASL:
2413e230dd2SCorentin Chary             return "vencrypt+tls+sasl";
2423e230dd2SCorentin Chary         case VNC_AUTH_VENCRYPT_X509SASL:
2433e230dd2SCorentin Chary             return "vencrypt+x509+sasl";
2443e230dd2SCorentin Chary         default:
2453e230dd2SCorentin Chary             return "vencrypt";
2463e230dd2SCorentin Chary         }
2473e230dd2SCorentin Chary #else
2483e230dd2SCorentin Chary         return "vencrypt";
2493e230dd2SCorentin Chary #endif
2503e230dd2SCorentin Chary     case VNC_AUTH_SASL:
2513e230dd2SCorentin Chary         return "sasl";
2523e230dd2SCorentin Chary     }
2533e230dd2SCorentin Chary     return "unknown";
2543e230dd2SCorentin Chary }
2553e230dd2SCorentin Chary 
256d616ccc5SGerd Hoffmann static VncServerInfo *vnc_server_info_get(VncDisplay *vd)
2573e230dd2SCorentin Chary {
258fb6ba0d5SWenchao Xia     VncServerInfo *info;
259d616ccc5SGerd Hoffmann     VncBasicInfo *bi = vnc_basic_info_get_from_server_addr(vd->lsock);
260fb6ba0d5SWenchao Xia     if (!bi) {
261fb6ba0d5SWenchao Xia         return NULL;
2623e230dd2SCorentin Chary     }
2633e230dd2SCorentin Chary 
264fb6ba0d5SWenchao Xia     info = g_malloc(sizeof(*info));
265fb6ba0d5SWenchao Xia     info->base = bi;
266fb6ba0d5SWenchao Xia     info->has_auth = true;
267d616ccc5SGerd Hoffmann     info->auth = g_strdup(vnc_auth_name(vd));
268fb6ba0d5SWenchao Xia     return info;
2693e230dd2SCorentin Chary }
2703e230dd2SCorentin Chary 
2713e230dd2SCorentin Chary static void vnc_client_cache_auth(VncState *client)
2723e230dd2SCorentin Chary {
2733e230dd2SCorentin Chary     if (!client->info) {
2743e230dd2SCorentin Chary         return;
2753e230dd2SCorentin Chary     }
2763e230dd2SCorentin Chary 
2773e230dd2SCorentin Chary #ifdef CONFIG_VNC_TLS
2783e230dd2SCorentin Chary     if (client->tls.session &&
2793e230dd2SCorentin Chary         client->tls.dname) {
280fb6ba0d5SWenchao Xia         client->info->has_x509_dname = true;
281fb6ba0d5SWenchao Xia         client->info->x509_dname = g_strdup(client->tls.dname);
2823e230dd2SCorentin Chary     }
2833e230dd2SCorentin Chary #endif
2843e230dd2SCorentin Chary #ifdef CONFIG_VNC_SASL
2853e230dd2SCorentin Chary     if (client->sasl.conn &&
2863e230dd2SCorentin Chary         client->sasl.username) {
287fb6ba0d5SWenchao Xia         client->info->has_sasl_username = true;
288fb6ba0d5SWenchao Xia         client->info->sasl_username = g_strdup(client->sasl.username);
2893e230dd2SCorentin Chary     }
2903e230dd2SCorentin Chary #endif
2913e230dd2SCorentin Chary }
2923e230dd2SCorentin Chary 
2933e230dd2SCorentin Chary static void vnc_client_cache_addr(VncState *client)
2943e230dd2SCorentin Chary {
295fb6ba0d5SWenchao Xia     VncBasicInfo *bi = vnc_basic_info_get_from_remote_addr(client->csock);
2963e230dd2SCorentin Chary 
297fb6ba0d5SWenchao Xia     if (bi) {
298fb6ba0d5SWenchao Xia         client->info = g_malloc0(sizeof(*client->info));
299fb6ba0d5SWenchao Xia         client->info->base = bi;
300fb6ba0d5SWenchao Xia     }
3013e230dd2SCorentin Chary }
3023e230dd2SCorentin Chary 
303fb6ba0d5SWenchao Xia static void vnc_qmp_event(VncState *vs, QAPIEvent event)
3043e230dd2SCorentin Chary {
305fb6ba0d5SWenchao Xia     VncServerInfo *si;
3063e230dd2SCorentin Chary 
3073e230dd2SCorentin Chary     if (!vs->info) {
3083e230dd2SCorentin Chary         return;
3093e230dd2SCorentin Chary     }
310fb6ba0d5SWenchao Xia     g_assert(vs->info->base);
3113e230dd2SCorentin Chary 
312d616ccc5SGerd Hoffmann     si = vnc_server_info_get(vs->vd);
313fb6ba0d5SWenchao Xia     if (!si) {
3143e230dd2SCorentin Chary         return;
3153e230dd2SCorentin Chary     }
3163e230dd2SCorentin Chary 
317fb6ba0d5SWenchao Xia     switch (event) {
318fb6ba0d5SWenchao Xia     case QAPI_EVENT_VNC_CONNECTED:
319fb6ba0d5SWenchao Xia         qapi_event_send_vnc_connected(si, vs->info->base, &error_abort);
320fb6ba0d5SWenchao Xia         break;
321fb6ba0d5SWenchao Xia     case QAPI_EVENT_VNC_INITIALIZED:
322fb6ba0d5SWenchao Xia         qapi_event_send_vnc_initialized(si, vs->info, &error_abort);
323fb6ba0d5SWenchao Xia         break;
324fb6ba0d5SWenchao Xia     case QAPI_EVENT_VNC_DISCONNECTED:
325fb6ba0d5SWenchao Xia         qapi_event_send_vnc_disconnected(si, vs->info, &error_abort);
326fb6ba0d5SWenchao Xia         break;
327fb6ba0d5SWenchao Xia     default:
328fb6ba0d5SWenchao Xia         break;
329fb6ba0d5SWenchao Xia     }
3303e230dd2SCorentin Chary 
331fb6ba0d5SWenchao Xia     qapi_free_VncServerInfo(si);
3323e230dd2SCorentin Chary }
3333e230dd2SCorentin Chary 
3342b54aa87SLuiz Capitulino static VncClientInfo *qmp_query_vnc_client(const VncState *client)
3353e230dd2SCorentin Chary {
3362b54aa87SLuiz Capitulino     struct sockaddr_storage sa;
3372b54aa87SLuiz Capitulino     socklen_t salen = sizeof(sa);
3382b54aa87SLuiz Capitulino     char host[NI_MAXHOST];
3392b54aa87SLuiz Capitulino     char serv[NI_MAXSERV];
3402b54aa87SLuiz Capitulino     VncClientInfo *info;
3413e230dd2SCorentin Chary 
3422b54aa87SLuiz Capitulino     if (getpeername(client->csock, (struct sockaddr *)&sa, &salen) < 0) {
3432b54aa87SLuiz Capitulino         return NULL;
3442b54aa87SLuiz Capitulino     }
3452b54aa87SLuiz Capitulino 
3462b54aa87SLuiz Capitulino     if (getnameinfo((struct sockaddr *)&sa, salen,
3472b54aa87SLuiz Capitulino                     host, sizeof(host),
3482b54aa87SLuiz Capitulino                     serv, sizeof(serv),
3492b54aa87SLuiz Capitulino                     NI_NUMERICHOST | NI_NUMERICSERV) < 0) {
3502b54aa87SLuiz Capitulino         return NULL;
3512b54aa87SLuiz Capitulino     }
3522b54aa87SLuiz Capitulino 
3532b54aa87SLuiz Capitulino     info = g_malloc0(sizeof(*info));
354a589569fSWenchao Xia     info->base = g_malloc0(sizeof(*info->base));
355a589569fSWenchao Xia     info->base->host = g_strdup(host);
356a589569fSWenchao Xia     info->base->service = g_strdup(serv);
357a589569fSWenchao Xia     info->base->family = inet_netfamily(sa.ss_family);
3584478aa76SGerd Hoffmann #ifdef CONFIG_VNC_WS
3594478aa76SGerd Hoffmann     info->base->websocket = client->websocket;
3604478aa76SGerd Hoffmann #endif
3613e230dd2SCorentin Chary 
3623e230dd2SCorentin Chary #ifdef CONFIG_VNC_TLS
3632b54aa87SLuiz Capitulino     if (client->tls.session && client->tls.dname) {
3642b54aa87SLuiz Capitulino         info->has_x509_dname = true;
3652b54aa87SLuiz Capitulino         info->x509_dname = g_strdup(client->tls.dname);
3662b54aa87SLuiz Capitulino     }
3673e230dd2SCorentin Chary #endif
3683e230dd2SCorentin Chary #ifdef CONFIG_VNC_SASL
3692b54aa87SLuiz Capitulino     if (client->sasl.conn && client->sasl.username) {
3702b54aa87SLuiz Capitulino         info->has_sasl_username = true;
3712b54aa87SLuiz Capitulino         info->sasl_username = g_strdup(client->sasl.username);
3722b54aa87SLuiz Capitulino     }
3733e230dd2SCorentin Chary #endif
3742b54aa87SLuiz Capitulino 
3752b54aa87SLuiz Capitulino     return info;
3763e230dd2SCorentin Chary }
3773e230dd2SCorentin Chary 
378d616ccc5SGerd Hoffmann static VncDisplay *vnc_display_find(const char *id)
379d616ccc5SGerd Hoffmann {
380d616ccc5SGerd Hoffmann     VncDisplay *vd;
381d616ccc5SGerd Hoffmann 
382d616ccc5SGerd Hoffmann     if (id == NULL) {
383d616ccc5SGerd Hoffmann         return QTAILQ_FIRST(&vnc_displays);
384d616ccc5SGerd Hoffmann     }
385d616ccc5SGerd Hoffmann     QTAILQ_FOREACH(vd, &vnc_displays, next) {
386d616ccc5SGerd Hoffmann         if (strcmp(id, vd->id) == 0) {
387d616ccc5SGerd Hoffmann             return vd;
388d616ccc5SGerd Hoffmann         }
389d616ccc5SGerd Hoffmann     }
390d616ccc5SGerd Hoffmann     return NULL;
391d616ccc5SGerd Hoffmann }
392d616ccc5SGerd Hoffmann 
3932d29a436SGerd Hoffmann static VncClientInfoList *qmp_query_client_list(VncDisplay *vd)
3942d29a436SGerd Hoffmann {
3952d29a436SGerd Hoffmann     VncClientInfoList *cinfo, *prev = NULL;
3962d29a436SGerd Hoffmann     VncState *client;
3972d29a436SGerd Hoffmann 
3982d29a436SGerd Hoffmann     QTAILQ_FOREACH(client, &vd->clients, next) {
3992d29a436SGerd Hoffmann         cinfo = g_new0(VncClientInfoList, 1);
4002d29a436SGerd Hoffmann         cinfo->value = qmp_query_vnc_client(client);
4012d29a436SGerd Hoffmann         cinfo->next = prev;
4022d29a436SGerd Hoffmann         prev = cinfo;
4032d29a436SGerd Hoffmann     }
4042d29a436SGerd Hoffmann     return prev;
4052d29a436SGerd Hoffmann }
4062d29a436SGerd Hoffmann 
4072b54aa87SLuiz Capitulino VncInfo *qmp_query_vnc(Error **errp)
4083e230dd2SCorentin Chary {
4092b54aa87SLuiz Capitulino     VncInfo *info = g_malloc0(sizeof(*info));
410d616ccc5SGerd Hoffmann     VncDisplay *vd = vnc_display_find(NULL);
4113e230dd2SCorentin Chary 
412bf7aa45eSGerd Hoffmann     if (vd == NULL || !vd->enabled) {
4132b54aa87SLuiz Capitulino         info->enabled = false;
4143e230dd2SCorentin Chary     } else {
4152b54aa87SLuiz Capitulino         struct sockaddr_storage sa;
4162b54aa87SLuiz Capitulino         socklen_t salen = sizeof(sa);
4172b54aa87SLuiz Capitulino         char host[NI_MAXHOST];
4182b54aa87SLuiz Capitulino         char serv[NI_MAXSERV];
4193e230dd2SCorentin Chary 
4202b54aa87SLuiz Capitulino         info->enabled = true;
4212b54aa87SLuiz Capitulino 
4222b54aa87SLuiz Capitulino         /* for compatibility with the original command */
4232b54aa87SLuiz Capitulino         info->has_clients = true;
4242d29a436SGerd Hoffmann         info->clients = qmp_query_client_list(vd);
4253e230dd2SCorentin Chary 
426d616ccc5SGerd Hoffmann         if (vd->lsock == -1) {
427417b0b88SPaolo Bonzini             return info;
428417b0b88SPaolo Bonzini         }
429417b0b88SPaolo Bonzini 
430d616ccc5SGerd Hoffmann         if (getsockname(vd->lsock, (struct sockaddr *)&sa,
4312b54aa87SLuiz Capitulino                         &salen) == -1) {
432c6bd8c70SMarkus Armbruster             error_setg(errp, QERR_UNDEFINED_ERROR);
4332b54aa87SLuiz Capitulino             goto out_error;
4342b54aa87SLuiz Capitulino         }
4353e230dd2SCorentin Chary 
4362b54aa87SLuiz Capitulino         if (getnameinfo((struct sockaddr *)&sa, salen,
4372b54aa87SLuiz Capitulino                         host, sizeof(host),
4382b54aa87SLuiz Capitulino                         serv, sizeof(serv),
4392b54aa87SLuiz Capitulino                         NI_NUMERICHOST | NI_NUMERICSERV) < 0) {
440c6bd8c70SMarkus Armbruster             error_setg(errp, QERR_UNDEFINED_ERROR);
4412b54aa87SLuiz Capitulino             goto out_error;
4423e230dd2SCorentin Chary         }
4432b54aa87SLuiz Capitulino 
4442b54aa87SLuiz Capitulino         info->has_host = true;
4452b54aa87SLuiz Capitulino         info->host = g_strdup(host);
4462b54aa87SLuiz Capitulino 
4472b54aa87SLuiz Capitulino         info->has_service = true;
4482b54aa87SLuiz Capitulino         info->service = g_strdup(serv);
4492b54aa87SLuiz Capitulino 
4502b54aa87SLuiz Capitulino         info->has_family = true;
451a589569fSWenchao Xia         info->family = inet_netfamily(sa.ss_family);
4522b54aa87SLuiz Capitulino 
4532b54aa87SLuiz Capitulino         info->has_auth = true;
454d616ccc5SGerd Hoffmann         info->auth = g_strdup(vnc_auth_name(vd));
4553e230dd2SCorentin Chary     }
4562b54aa87SLuiz Capitulino 
4572b54aa87SLuiz Capitulino     return info;
4582b54aa87SLuiz Capitulino 
4592b54aa87SLuiz Capitulino out_error:
4602b54aa87SLuiz Capitulino     qapi_free_VncInfo(info);
4612b54aa87SLuiz Capitulino     return NULL;
4623e230dd2SCorentin Chary }
4633e230dd2SCorentin Chary 
464df887684SGerd Hoffmann static VncBasicInfoList *qmp_query_server_entry(int socket,
4654478aa76SGerd Hoffmann                                                 bool websocket,
466df887684SGerd Hoffmann                                                 VncBasicInfoList *prev)
467df887684SGerd Hoffmann {
468df887684SGerd Hoffmann     VncBasicInfoList *list;
469df887684SGerd Hoffmann     VncBasicInfo *info;
470df887684SGerd Hoffmann     struct sockaddr_storage sa;
471df887684SGerd Hoffmann     socklen_t salen = sizeof(sa);
472df887684SGerd Hoffmann     char host[NI_MAXHOST];
473df887684SGerd Hoffmann     char serv[NI_MAXSERV];
474df887684SGerd Hoffmann 
475df887684SGerd Hoffmann     if (getsockname(socket, (struct sockaddr *)&sa, &salen) < 0 ||
476df887684SGerd Hoffmann         getnameinfo((struct sockaddr *)&sa, salen,
477df887684SGerd Hoffmann                     host, sizeof(host), serv, sizeof(serv),
478df887684SGerd Hoffmann                     NI_NUMERICHOST | NI_NUMERICSERV) < 0) {
479df887684SGerd Hoffmann         return prev;
480df887684SGerd Hoffmann     }
481df887684SGerd Hoffmann 
482df887684SGerd Hoffmann     info = g_new0(VncBasicInfo, 1);
483df887684SGerd Hoffmann     info->host = g_strdup(host);
484df887684SGerd Hoffmann     info->service = g_strdup(serv);
485df887684SGerd Hoffmann     info->family = inet_netfamily(sa.ss_family);
4864478aa76SGerd Hoffmann     info->websocket = websocket;
487df887684SGerd Hoffmann 
488df887684SGerd Hoffmann     list = g_new0(VncBasicInfoList, 1);
489df887684SGerd Hoffmann     list->value = info;
490df887684SGerd Hoffmann     list->next = prev;
491df887684SGerd Hoffmann     return list;
492df887684SGerd Hoffmann }
493df887684SGerd Hoffmann 
494df887684SGerd Hoffmann static void qmp_query_auth(VncDisplay *vd, VncInfo2 *info)
495df887684SGerd Hoffmann {
496df887684SGerd Hoffmann     switch (vd->auth) {
497df887684SGerd Hoffmann     case VNC_AUTH_VNC:
498df887684SGerd Hoffmann         info->auth = VNC_PRIMARY_AUTH_VNC;
499df887684SGerd Hoffmann         break;
500df887684SGerd Hoffmann     case VNC_AUTH_RA2:
501df887684SGerd Hoffmann         info->auth = VNC_PRIMARY_AUTH_RA2;
502df887684SGerd Hoffmann         break;
503df887684SGerd Hoffmann     case VNC_AUTH_RA2NE:
504df887684SGerd Hoffmann         info->auth = VNC_PRIMARY_AUTH_RA2NE;
505df887684SGerd Hoffmann         break;
506df887684SGerd Hoffmann     case VNC_AUTH_TIGHT:
507df887684SGerd Hoffmann         info->auth = VNC_PRIMARY_AUTH_TIGHT;
508df887684SGerd Hoffmann         break;
509df887684SGerd Hoffmann     case VNC_AUTH_ULTRA:
510df887684SGerd Hoffmann         info->auth = VNC_PRIMARY_AUTH_ULTRA;
511df887684SGerd Hoffmann         break;
512df887684SGerd Hoffmann     case VNC_AUTH_TLS:
513df887684SGerd Hoffmann         info->auth = VNC_PRIMARY_AUTH_TLS;
514df887684SGerd Hoffmann         break;
515df887684SGerd Hoffmann     case VNC_AUTH_VENCRYPT:
516df887684SGerd Hoffmann         info->auth = VNC_PRIMARY_AUTH_VENCRYPT;
517df887684SGerd Hoffmann #ifdef CONFIG_VNC_TLS
518df887684SGerd Hoffmann         info->has_vencrypt = true;
519df887684SGerd Hoffmann         switch (vd->subauth) {
520df887684SGerd Hoffmann         case VNC_AUTH_VENCRYPT_PLAIN:
521df887684SGerd Hoffmann             info->vencrypt = VNC_VENCRYPT_SUB_AUTH_PLAIN;
522df887684SGerd Hoffmann             break;
523df887684SGerd Hoffmann         case VNC_AUTH_VENCRYPT_TLSNONE:
524df887684SGerd Hoffmann             info->vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_NONE;
525df887684SGerd Hoffmann             break;
526df887684SGerd Hoffmann         case VNC_AUTH_VENCRYPT_TLSVNC:
527df887684SGerd Hoffmann             info->vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_VNC;
528df887684SGerd Hoffmann             break;
529df887684SGerd Hoffmann         case VNC_AUTH_VENCRYPT_TLSPLAIN:
530df887684SGerd Hoffmann             info->vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_PLAIN;
531df887684SGerd Hoffmann             break;
532df887684SGerd Hoffmann         case VNC_AUTH_VENCRYPT_X509NONE:
533df887684SGerd Hoffmann             info->vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_NONE;
534df887684SGerd Hoffmann             break;
535df887684SGerd Hoffmann         case VNC_AUTH_VENCRYPT_X509VNC:
536df887684SGerd Hoffmann             info->vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_VNC;
537df887684SGerd Hoffmann             break;
538df887684SGerd Hoffmann         case VNC_AUTH_VENCRYPT_X509PLAIN:
539df887684SGerd Hoffmann             info->vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_PLAIN;
540df887684SGerd Hoffmann             break;
541df887684SGerd Hoffmann         case VNC_AUTH_VENCRYPT_TLSSASL:
542df887684SGerd Hoffmann             info->vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_SASL;
543df887684SGerd Hoffmann             break;
544df887684SGerd Hoffmann         case VNC_AUTH_VENCRYPT_X509SASL:
545df887684SGerd Hoffmann             info->vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_SASL;
546df887684SGerd Hoffmann             break;
547df887684SGerd Hoffmann         default:
548df887684SGerd Hoffmann             info->has_vencrypt = false;
549df887684SGerd Hoffmann             break;
550df887684SGerd Hoffmann         }
551df887684SGerd Hoffmann #endif
552df887684SGerd Hoffmann         break;
553df887684SGerd Hoffmann     case VNC_AUTH_SASL:
554df887684SGerd Hoffmann         info->auth = VNC_PRIMARY_AUTH_SASL;
555df887684SGerd Hoffmann         break;
556df887684SGerd Hoffmann     case VNC_AUTH_NONE:
557df887684SGerd Hoffmann     default:
558df887684SGerd Hoffmann         info->auth = VNC_PRIMARY_AUTH_NONE;
559df887684SGerd Hoffmann         break;
560df887684SGerd Hoffmann     }
561df887684SGerd Hoffmann }
562df887684SGerd Hoffmann 
563df887684SGerd Hoffmann VncInfo2List *qmp_query_vnc_servers(Error **errp)
564df887684SGerd Hoffmann {
565df887684SGerd Hoffmann     VncInfo2List *item, *prev = NULL;
566df887684SGerd Hoffmann     VncInfo2 *info;
567df887684SGerd Hoffmann     VncDisplay *vd;
568df887684SGerd Hoffmann     DeviceState *dev;
569df887684SGerd Hoffmann 
570df887684SGerd Hoffmann     QTAILQ_FOREACH(vd, &vnc_displays, next) {
571df887684SGerd Hoffmann         info = g_new0(VncInfo2, 1);
572df887684SGerd Hoffmann         info->id = g_strdup(vd->id);
573df887684SGerd Hoffmann         info->clients = qmp_query_client_list(vd);
574df887684SGerd Hoffmann         qmp_query_auth(vd, info);
575df887684SGerd Hoffmann         if (vd->dcl.con) {
576df887684SGerd Hoffmann             dev = DEVICE(object_property_get_link(OBJECT(vd->dcl.con),
577df887684SGerd Hoffmann                                                   "device", NULL));
578df887684SGerd Hoffmann             info->has_display = true;
579df887684SGerd Hoffmann             info->display = g_strdup(dev->id);
580df887684SGerd Hoffmann         }
581df887684SGerd Hoffmann         if (vd->lsock != -1) {
5824478aa76SGerd Hoffmann             info->server = qmp_query_server_entry(vd->lsock, false,
583df887684SGerd Hoffmann                                                   info->server);
584df887684SGerd Hoffmann         }
585df887684SGerd Hoffmann #ifdef CONFIG_VNC_WS
586df887684SGerd Hoffmann         if (vd->lwebsock != -1) {
5874478aa76SGerd Hoffmann             info->server = qmp_query_server_entry(vd->lwebsock, true,
5884478aa76SGerd Hoffmann                                                   info->server);
589df887684SGerd Hoffmann         }
590df887684SGerd Hoffmann #endif
591df887684SGerd Hoffmann 
592df887684SGerd Hoffmann         item = g_new0(VncInfo2List, 1);
593df887684SGerd Hoffmann         item->value = info;
594df887684SGerd Hoffmann         item->next = prev;
595df887684SGerd Hoffmann         prev = item;
596df887684SGerd Hoffmann     }
597df887684SGerd Hoffmann     return prev;
598df887684SGerd Hoffmann }
599df887684SGerd Hoffmann 
6003e230dd2SCorentin Chary /* TODO
6013e230dd2SCorentin Chary    1) Get the queue working for IO.
6023e230dd2SCorentin Chary    2) there is some weirdness when using the -S option (the screen is grey
6033e230dd2SCorentin Chary       and not totally invalidated
6043e230dd2SCorentin Chary    3) resolutions > 1024
6053e230dd2SCorentin Chary */
6063e230dd2SCorentin Chary 
60738ee14f4SGerd Hoffmann static int vnc_update_client(VncState *vs, int has_dirty, bool sync);
6083e230dd2SCorentin Chary static void vnc_disconnect_start(VncState *vs);
6093e230dd2SCorentin Chary 
6103e230dd2SCorentin Chary static void vnc_colordepth(VncState *vs);
6113e230dd2SCorentin Chary static void framebuffer_update_request(VncState *vs, int incremental,
6123e230dd2SCorentin Chary                                        int x_position, int y_position,
6133e230dd2SCorentin Chary                                        int w, int h);
6140f7b2864SGerd Hoffmann static void vnc_refresh(DisplayChangeListener *dcl);
6153e230dd2SCorentin Chary static int vnc_refresh_server_surface(VncDisplay *vd);
6163e230dd2SCorentin Chary 
617bea60dd7SPeter Lieven static void vnc_set_area_dirty(DECLARE_BITMAP(dirty[VNC_MAX_HEIGHT],
618bea60dd7SPeter Lieven                                VNC_MAX_WIDTH / VNC_DIRTY_PIXELS_PER_BIT),
619bea60dd7SPeter Lieven                                int width, int height,
620bea60dd7SPeter Lieven                                int x, int y, int w, int h) {
62191937225SPeter Lieven     /* this is needed this to ensure we updated all affected
62291937225SPeter Lieven      * blocks if x % VNC_DIRTY_PIXELS_PER_BIT != 0 */
623b4c85ddcSPeter Lieven     w += (x % VNC_DIRTY_PIXELS_PER_BIT);
624b4c85ddcSPeter Lieven     x -= (x % VNC_DIRTY_PIXELS_PER_BIT);
6253e230dd2SCorentin Chary 
6269f64916dSGerd Hoffmann     x = MIN(x, width);
6279f64916dSGerd Hoffmann     y = MIN(y, height);
6289f64916dSGerd Hoffmann     w = MIN(x + w, width) - x;
62991937225SPeter Lieven     h = MIN(y + h, height);
6303e230dd2SCorentin Chary 
631b4c85ddcSPeter Lieven     for (; y < h; y++) {
632bea60dd7SPeter Lieven         bitmap_set(dirty[y], x / VNC_DIRTY_PIXELS_PER_BIT,
63391937225SPeter Lieven                    DIV_ROUND_UP(w, VNC_DIRTY_PIXELS_PER_BIT));
634b4c85ddcSPeter Lieven     }
6353e230dd2SCorentin Chary }
6363e230dd2SCorentin Chary 
637bea60dd7SPeter Lieven static void vnc_dpy_update(DisplayChangeListener *dcl,
638bea60dd7SPeter Lieven                            int x, int y, int w, int h)
639bea60dd7SPeter Lieven {
640bea60dd7SPeter Lieven     VncDisplay *vd = container_of(dcl, VncDisplay, dcl);
641bea60dd7SPeter Lieven     struct VncSurface *s = &vd->guest;
642bea60dd7SPeter Lieven     int width = pixman_image_get_width(vd->server);
643bea60dd7SPeter Lieven     int height = pixman_image_get_height(vd->server);
644bea60dd7SPeter Lieven 
645bea60dd7SPeter Lieven     vnc_set_area_dirty(s->dirty, width, height, x, y, w, h);
646bea60dd7SPeter Lieven }
647bea60dd7SPeter Lieven 
6483e230dd2SCorentin Chary void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
6493e230dd2SCorentin Chary                             int32_t encoding)
6503e230dd2SCorentin Chary {
6513e230dd2SCorentin Chary     vnc_write_u16(vs, x);
6523e230dd2SCorentin Chary     vnc_write_u16(vs, y);
6533e230dd2SCorentin Chary     vnc_write_u16(vs, w);
6543e230dd2SCorentin Chary     vnc_write_u16(vs, h);
6553e230dd2SCorentin Chary 
6563e230dd2SCorentin Chary     vnc_write_s32(vs, encoding);
6573e230dd2SCorentin Chary }
6583e230dd2SCorentin Chary 
6593e230dd2SCorentin Chary void buffer_reserve(Buffer *buffer, size_t len)
6603e230dd2SCorentin Chary {
6613e230dd2SCorentin Chary     if ((buffer->capacity - buffer->offset) < len) {
6623e230dd2SCorentin Chary         buffer->capacity += (len + 1024);
6637267c094SAnthony Liguori         buffer->buffer = g_realloc(buffer->buffer, buffer->capacity);
6643e230dd2SCorentin Chary     }
6653e230dd2SCorentin Chary }
6663e230dd2SCorentin Chary 
66771a8cdecSBlue Swirl static int buffer_empty(Buffer *buffer)
6683e230dd2SCorentin Chary {
6693e230dd2SCorentin Chary     return buffer->offset == 0;
6703e230dd2SCorentin Chary }
6713e230dd2SCorentin Chary 
6727536ee4bSTim Hardeck uint8_t *buffer_end(Buffer *buffer)
6733e230dd2SCorentin Chary {
6743e230dd2SCorentin Chary     return buffer->buffer + buffer->offset;
6753e230dd2SCorentin Chary }
6763e230dd2SCorentin Chary 
6773e230dd2SCorentin Chary void buffer_reset(Buffer *buffer)
6783e230dd2SCorentin Chary {
6793e230dd2SCorentin Chary         buffer->offset = 0;
6803e230dd2SCorentin Chary }
6813e230dd2SCorentin Chary 
6823e230dd2SCorentin Chary void buffer_free(Buffer *buffer)
6833e230dd2SCorentin Chary {
6847267c094SAnthony Liguori     g_free(buffer->buffer);
6853e230dd2SCorentin Chary     buffer->offset = 0;
6863e230dd2SCorentin Chary     buffer->capacity = 0;
6873e230dd2SCorentin Chary     buffer->buffer = NULL;
6883e230dd2SCorentin Chary }
6893e230dd2SCorentin Chary 
6903e230dd2SCorentin Chary void buffer_append(Buffer *buffer, const void *data, size_t len)
6913e230dd2SCorentin Chary {
6923e230dd2SCorentin Chary     memcpy(buffer->buffer + buffer->offset, data, len);
6933e230dd2SCorentin Chary     buffer->offset += len;
6943e230dd2SCorentin Chary }
6953e230dd2SCorentin Chary 
69632ed2680STim Hardeck void buffer_advance(Buffer *buf, size_t len)
69732ed2680STim Hardeck {
69832ed2680STim Hardeck     memmove(buf->buffer, buf->buffer + len,
69932ed2680STim Hardeck             (buf->offset - len));
70032ed2680STim Hardeck     buf->offset -= len;
70132ed2680STim Hardeck }
70232ed2680STim Hardeck 
7033e230dd2SCorentin Chary static void vnc_desktop_resize(VncState *vs)
7043e230dd2SCorentin Chary {
7053e230dd2SCorentin Chary     if (vs->csock == -1 || !vnc_has_feature(vs, VNC_FEATURE_RESIZE)) {
7063e230dd2SCorentin Chary         return;
7073e230dd2SCorentin Chary     }
708bea60dd7SPeter Lieven     if (vs->client_width == pixman_image_get_width(vs->vd->server) &&
709bea60dd7SPeter Lieven         vs->client_height == pixman_image_get_height(vs->vd->server)) {
7103e230dd2SCorentin Chary         return;
7113e230dd2SCorentin Chary     }
712bea60dd7SPeter Lieven     vs->client_width = pixman_image_get_width(vs->vd->server);
713bea60dd7SPeter Lieven     vs->client_height = pixman_image_get_height(vs->vd->server);
714bd023f95SCorentin Chary     vnc_lock_output(vs);
7153e230dd2SCorentin Chary     vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
7163e230dd2SCorentin Chary     vnc_write_u8(vs, 0);
7173e230dd2SCorentin Chary     vnc_write_u16(vs, 1); /* number of rects */
7183e230dd2SCorentin Chary     vnc_framebuffer_update(vs, 0, 0, vs->client_width, vs->client_height,
7193e230dd2SCorentin Chary                            VNC_ENCODING_DESKTOPRESIZE);
720bd023f95SCorentin Chary     vnc_unlock_output(vs);
7213e230dd2SCorentin Chary     vnc_flush(vs);
7223e230dd2SCorentin Chary }
7233e230dd2SCorentin Chary 
724bd023f95SCorentin Chary static void vnc_abort_display_jobs(VncDisplay *vd)
725bd023f95SCorentin Chary {
726bd023f95SCorentin Chary     VncState *vs;
727bd023f95SCorentin Chary 
728bd023f95SCorentin Chary     QTAILQ_FOREACH(vs, &vd->clients, next) {
729bd023f95SCorentin Chary         vnc_lock_output(vs);
730bd023f95SCorentin Chary         vs->abort = true;
731bd023f95SCorentin Chary         vnc_unlock_output(vs);
732bd023f95SCorentin Chary     }
733bd023f95SCorentin Chary     QTAILQ_FOREACH(vs, &vd->clients, next) {
734bd023f95SCorentin Chary         vnc_jobs_join(vs);
735bd023f95SCorentin Chary     }
736bd023f95SCorentin Chary     QTAILQ_FOREACH(vs, &vd->clients, next) {
737bd023f95SCorentin Chary         vnc_lock_output(vs);
738bd023f95SCorentin Chary         vs->abort = false;
739bd023f95SCorentin Chary         vnc_unlock_output(vs);
740bd023f95SCorentin Chary     }
741bd023f95SCorentin Chary }
742bd023f95SCorentin Chary 
7439f64916dSGerd Hoffmann int vnc_server_fb_stride(VncDisplay *vd)
7449f64916dSGerd Hoffmann {
7459f64916dSGerd Hoffmann     return pixman_image_get_stride(vd->server);
7469f64916dSGerd Hoffmann }
7479f64916dSGerd Hoffmann 
7489f64916dSGerd Hoffmann void *vnc_server_fb_ptr(VncDisplay *vd, int x, int y)
7499f64916dSGerd Hoffmann {
7509f64916dSGerd Hoffmann     uint8_t *ptr;
7519f64916dSGerd Hoffmann 
7529f64916dSGerd Hoffmann     ptr  = (uint8_t *)pixman_image_get_data(vd->server);
7539f64916dSGerd Hoffmann     ptr += y * vnc_server_fb_stride(vd);
7549f64916dSGerd Hoffmann     ptr += x * VNC_SERVER_FB_BYTES;
7559f64916dSGerd Hoffmann     return ptr;
7569f64916dSGerd Hoffmann }
7579f64916dSGerd Hoffmann 
758c12aeb86SGerd Hoffmann static void vnc_dpy_switch(DisplayChangeListener *dcl,
759c12aeb86SGerd Hoffmann                            DisplaySurface *surface)
7603e230dd2SCorentin Chary {
76121ef45d7SGerd Hoffmann     VncDisplay *vd = container_of(dcl, VncDisplay, dcl);
7623e230dd2SCorentin Chary     VncState *vs;
763bea60dd7SPeter Lieven     int width, height;
7643e230dd2SCorentin Chary 
765bd023f95SCorentin Chary     vnc_abort_display_jobs(vd);
766bd023f95SCorentin Chary 
7673e230dd2SCorentin Chary     /* server surface */
7689f64916dSGerd Hoffmann     qemu_pixman_image_unref(vd->server);
769d39fa6d8SGerd Hoffmann     vd->ds = surface;
770bea60dd7SPeter Lieven     width = MIN(VNC_MAX_WIDTH, ROUND_UP(surface_width(vd->ds),
771bea60dd7SPeter Lieven                                         VNC_DIRTY_PIXELS_PER_BIT));
772bea60dd7SPeter Lieven     height = MIN(VNC_MAX_HEIGHT, surface_height(vd->ds));
7739f64916dSGerd Hoffmann     vd->server = pixman_image_create_bits(VNC_SERVER_FB_FORMAT,
774bea60dd7SPeter Lieven                                           width, height, NULL, 0);
7753e230dd2SCorentin Chary 
7763e230dd2SCorentin Chary     /* guest surface */
7779f64916dSGerd Hoffmann #if 0 /* FIXME */
7783e230dd2SCorentin Chary     if (ds_get_bytes_per_pixel(ds) != vd->guest.ds->pf.bytes_per_pixel)
7793e230dd2SCorentin Chary         console_color_init(ds);
7809f64916dSGerd Hoffmann #endif
7819f64916dSGerd Hoffmann     qemu_pixman_image_unref(vd->guest.fb);
782d39fa6d8SGerd Hoffmann     vd->guest.fb = pixman_image_ref(surface->image);
783d39fa6d8SGerd Hoffmann     vd->guest.format = surface->format;
784bea60dd7SPeter Lieven     memset(vd->guest.dirty, 0x00, sizeof(vd->guest.dirty));
785bea60dd7SPeter Lieven     vnc_set_area_dirty(vd->guest.dirty, width, height, 0, 0,
786bea60dd7SPeter Lieven                        width, height);
7873e230dd2SCorentin Chary 
7883e230dd2SCorentin Chary     QTAILQ_FOREACH(vs, &vd->clients, next) {
7893e230dd2SCorentin Chary         vnc_colordepth(vs);
7903e230dd2SCorentin Chary         vnc_desktop_resize(vs);
7913e230dd2SCorentin Chary         if (vs->vd->cursor) {
7923e230dd2SCorentin Chary             vnc_cursor_define(vs);
7933e230dd2SCorentin Chary         }
794bea60dd7SPeter Lieven         memset(vs->dirty, 0x00, sizeof(vs->dirty));
795bea60dd7SPeter Lieven         vnc_set_area_dirty(vs->dirty, width, height, 0, 0,
796bea60dd7SPeter Lieven                            width, height);
7973e230dd2SCorentin Chary     }
7983e230dd2SCorentin Chary }
7993e230dd2SCorentin Chary 
8003e230dd2SCorentin Chary /* fastest code */
8019f64916dSGerd Hoffmann static void vnc_write_pixels_copy(VncState *vs,
8023e230dd2SCorentin Chary                                   void *pixels, int size)
8033e230dd2SCorentin Chary {
8043e230dd2SCorentin Chary     vnc_write(vs, pixels, size);
8053e230dd2SCorentin Chary }
8063e230dd2SCorentin Chary 
8073e230dd2SCorentin Chary /* slowest but generic code. */
8083e230dd2SCorentin Chary void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v)
8093e230dd2SCorentin Chary {
8103e230dd2SCorentin Chary     uint8_t r, g, b;
8113e230dd2SCorentin Chary 
8129f64916dSGerd Hoffmann #if VNC_SERVER_FB_FORMAT == PIXMAN_FORMAT(32, PIXMAN_TYPE_ARGB, 0, 8, 8, 8)
8139f64916dSGerd Hoffmann     r = (((v & 0x00ff0000) >> 16) << vs->client_pf.rbits) >> 8;
8149f64916dSGerd Hoffmann     g = (((v & 0x0000ff00) >>  8) << vs->client_pf.gbits) >> 8;
8159f64916dSGerd Hoffmann     b = (((v & 0x000000ff) >>  0) << vs->client_pf.bbits) >> 8;
8169f64916dSGerd Hoffmann #else
8179f64916dSGerd Hoffmann # error need some bits here if you change VNC_SERVER_FB_FORMAT
8189f64916dSGerd Hoffmann #endif
8199f64916dSGerd Hoffmann     v = (r << vs->client_pf.rshift) |
8209f64916dSGerd Hoffmann         (g << vs->client_pf.gshift) |
8219f64916dSGerd Hoffmann         (b << vs->client_pf.bshift);
8229f64916dSGerd Hoffmann     switch (vs->client_pf.bytes_per_pixel) {
8233e230dd2SCorentin Chary     case 1:
8243e230dd2SCorentin Chary         buf[0] = v;
8253e230dd2SCorentin Chary         break;
8263e230dd2SCorentin Chary     case 2:
8279f64916dSGerd Hoffmann         if (vs->client_be) {
8283e230dd2SCorentin Chary             buf[0] = v >> 8;
8293e230dd2SCorentin Chary             buf[1] = v;
8303e230dd2SCorentin Chary         } else {
8313e230dd2SCorentin Chary             buf[1] = v >> 8;
8323e230dd2SCorentin Chary             buf[0] = v;
8333e230dd2SCorentin Chary         }
8343e230dd2SCorentin Chary         break;
8353e230dd2SCorentin Chary     default:
8363e230dd2SCorentin Chary     case 4:
8379f64916dSGerd Hoffmann         if (vs->client_be) {
8383e230dd2SCorentin Chary             buf[0] = v >> 24;
8393e230dd2SCorentin Chary             buf[1] = v >> 16;
8403e230dd2SCorentin Chary             buf[2] = v >> 8;
8413e230dd2SCorentin Chary             buf[3] = v;
8423e230dd2SCorentin Chary         } else {
8433e230dd2SCorentin Chary             buf[3] = v >> 24;
8443e230dd2SCorentin Chary             buf[2] = v >> 16;
8453e230dd2SCorentin Chary             buf[1] = v >> 8;
8463e230dd2SCorentin Chary             buf[0] = v;
8473e230dd2SCorentin Chary         }
8483e230dd2SCorentin Chary         break;
8493e230dd2SCorentin Chary     }
8503e230dd2SCorentin Chary }
8513e230dd2SCorentin Chary 
8529f64916dSGerd Hoffmann static void vnc_write_pixels_generic(VncState *vs,
8533e230dd2SCorentin Chary                                      void *pixels1, int size)
8543e230dd2SCorentin Chary {
8553e230dd2SCorentin Chary     uint8_t buf[4];
8563e230dd2SCorentin Chary 
8579f64916dSGerd Hoffmann     if (VNC_SERVER_FB_BYTES == 4) {
8583e230dd2SCorentin Chary         uint32_t *pixels = pixels1;
8593e230dd2SCorentin Chary         int n, i;
8603e230dd2SCorentin Chary         n = size >> 2;
8613e230dd2SCorentin Chary         for (i = 0; i < n; i++) {
8623e230dd2SCorentin Chary             vnc_convert_pixel(vs, buf, pixels[i]);
8639f64916dSGerd Hoffmann             vnc_write(vs, buf, vs->client_pf.bytes_per_pixel);
8643e230dd2SCorentin Chary         }
8653e230dd2SCorentin Chary     }
8663e230dd2SCorentin Chary }
8673e230dd2SCorentin Chary 
8683e230dd2SCorentin Chary int vnc_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
8693e230dd2SCorentin Chary {
8703e230dd2SCorentin Chary     int i;
8713e230dd2SCorentin Chary     uint8_t *row;
8723e230dd2SCorentin Chary     VncDisplay *vd = vs->vd;
8733e230dd2SCorentin Chary 
8749f64916dSGerd Hoffmann     row = vnc_server_fb_ptr(vd, x, y);
8753e230dd2SCorentin Chary     for (i = 0; i < h; i++) {
8769f64916dSGerd Hoffmann         vs->write_pixels(vs, row, w * VNC_SERVER_FB_BYTES);
8779f64916dSGerd Hoffmann         row += vnc_server_fb_stride(vd);
8783e230dd2SCorentin Chary     }
8793e230dd2SCorentin Chary     return 1;
8803e230dd2SCorentin Chary }
8813e230dd2SCorentin Chary 
882bd023f95SCorentin Chary int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
8833e230dd2SCorentin Chary {
8843e230dd2SCorentin Chary     int n = 0;
8853e230dd2SCorentin Chary 
8863e230dd2SCorentin Chary     switch(vs->vnc_encoding) {
8873e230dd2SCorentin Chary         case VNC_ENCODING_ZLIB:
8883e230dd2SCorentin Chary             n = vnc_zlib_send_framebuffer_update(vs, x, y, w, h);
8893e230dd2SCorentin Chary             break;
8903e230dd2SCorentin Chary         case VNC_ENCODING_HEXTILE:
8913e230dd2SCorentin Chary             vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_HEXTILE);
8923e230dd2SCorentin Chary             n = vnc_hextile_send_framebuffer_update(vs, x, y, w, h);
8933e230dd2SCorentin Chary             break;
8943e230dd2SCorentin Chary         case VNC_ENCODING_TIGHT:
8953e230dd2SCorentin Chary             n = vnc_tight_send_framebuffer_update(vs, x, y, w, h);
8963e230dd2SCorentin Chary             break;
897efe556adSCorentin Chary         case VNC_ENCODING_TIGHT_PNG:
898efe556adSCorentin Chary             n = vnc_tight_png_send_framebuffer_update(vs, x, y, w, h);
899efe556adSCorentin Chary             break;
900148954faSCorentin Chary         case VNC_ENCODING_ZRLE:
901148954faSCorentin Chary             n = vnc_zrle_send_framebuffer_update(vs, x, y, w, h);
902148954faSCorentin Chary             break;
903148954faSCorentin Chary         case VNC_ENCODING_ZYWRLE:
904148954faSCorentin Chary             n = vnc_zywrle_send_framebuffer_update(vs, x, y, w, h);
905148954faSCorentin Chary             break;
9063e230dd2SCorentin Chary         default:
9073e230dd2SCorentin Chary             vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_RAW);
9083e230dd2SCorentin Chary             n = vnc_raw_send_framebuffer_update(vs, x, y, w, h);
9093e230dd2SCorentin Chary             break;
9103e230dd2SCorentin Chary     }
9113e230dd2SCorentin Chary     return n;
9123e230dd2SCorentin Chary }
9133e230dd2SCorentin Chary 
9143e230dd2SCorentin Chary static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
9153e230dd2SCorentin Chary {
9163e230dd2SCorentin Chary     /* send bitblit op to the vnc client */
917bd023f95SCorentin Chary     vnc_lock_output(vs);
9183e230dd2SCorentin Chary     vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
9193e230dd2SCorentin Chary     vnc_write_u8(vs, 0);
9203e230dd2SCorentin Chary     vnc_write_u16(vs, 1); /* number of rects */
9213e230dd2SCorentin Chary     vnc_framebuffer_update(vs, dst_x, dst_y, w, h, VNC_ENCODING_COPYRECT);
9223e230dd2SCorentin Chary     vnc_write_u16(vs, src_x);
9233e230dd2SCorentin Chary     vnc_write_u16(vs, src_y);
924bd023f95SCorentin Chary     vnc_unlock_output(vs);
9253e230dd2SCorentin Chary     vnc_flush(vs);
9263e230dd2SCorentin Chary }
9273e230dd2SCorentin Chary 
9287c20b4a3SGerd Hoffmann static void vnc_dpy_copy(DisplayChangeListener *dcl,
9297c20b4a3SGerd Hoffmann                          int src_x, int src_y,
9307c20b4a3SGerd Hoffmann                          int dst_x, int dst_y, int w, int h)
9313e230dd2SCorentin Chary {
93221ef45d7SGerd Hoffmann     VncDisplay *vd = container_of(dcl, VncDisplay, dcl);
9333e230dd2SCorentin Chary     VncState *vs, *vn;
9343e230dd2SCorentin Chary     uint8_t *src_row;
9353e230dd2SCorentin Chary     uint8_t *dst_row;
9369f64916dSGerd Hoffmann     int i, x, y, pitch, inc, w_lim, s;
9373e230dd2SCorentin Chary     int cmp_bytes;
9383e230dd2SCorentin Chary 
9393e230dd2SCorentin Chary     vnc_refresh_server_surface(vd);
9403e230dd2SCorentin Chary     QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) {
9413e230dd2SCorentin Chary         if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
9423e230dd2SCorentin Chary             vs->force_update = 1;
94338ee14f4SGerd Hoffmann             vnc_update_client(vs, 1, true);
9443e230dd2SCorentin Chary             /* vs might be free()ed here */
9453e230dd2SCorentin Chary         }
9463e230dd2SCorentin Chary     }
9473e230dd2SCorentin Chary 
9483e230dd2SCorentin Chary     /* do bitblit op on the local surface too */
9499f64916dSGerd Hoffmann     pitch = vnc_server_fb_stride(vd);
9509f64916dSGerd Hoffmann     src_row = vnc_server_fb_ptr(vd, src_x, src_y);
9519f64916dSGerd Hoffmann     dst_row = vnc_server_fb_ptr(vd, dst_x, dst_y);
9523e230dd2SCorentin Chary     y = dst_y;
9533e230dd2SCorentin Chary     inc = 1;
9543e230dd2SCorentin Chary     if (dst_y > src_y) {
9553e230dd2SCorentin Chary         /* copy backwards */
9563e230dd2SCorentin Chary         src_row += pitch * (h-1);
9573e230dd2SCorentin Chary         dst_row += pitch * (h-1);
9583e230dd2SCorentin Chary         pitch = -pitch;
9593e230dd2SCorentin Chary         y = dst_y + h - 1;
9603e230dd2SCorentin Chary         inc = -1;
9613e230dd2SCorentin Chary     }
962b4c85ddcSPeter Lieven     w_lim = w - (VNC_DIRTY_PIXELS_PER_BIT - (dst_x % VNC_DIRTY_PIXELS_PER_BIT));
963b4c85ddcSPeter Lieven     if (w_lim < 0) {
9643e230dd2SCorentin Chary         w_lim = w;
965b4c85ddcSPeter Lieven     } else {
966b4c85ddcSPeter Lieven         w_lim = w - (w_lim % VNC_DIRTY_PIXELS_PER_BIT);
967b4c85ddcSPeter Lieven     }
9683e230dd2SCorentin Chary     for (i = 0; i < h; i++) {
9693e230dd2SCorentin Chary         for (x = 0; x <= w_lim;
9703e230dd2SCorentin Chary                 x += s, src_row += cmp_bytes, dst_row += cmp_bytes) {
9713e230dd2SCorentin Chary             if (x == w_lim) {
9723e230dd2SCorentin Chary                 if ((s = w - w_lim) == 0)
9733e230dd2SCorentin Chary                     break;
9743e230dd2SCorentin Chary             } else if (!x) {
975b4c85ddcSPeter Lieven                 s = (VNC_DIRTY_PIXELS_PER_BIT -
976b4c85ddcSPeter Lieven                     (dst_x % VNC_DIRTY_PIXELS_PER_BIT));
9773e230dd2SCorentin Chary                 s = MIN(s, w_lim);
9783e230dd2SCorentin Chary             } else {
979b4c85ddcSPeter Lieven                 s = VNC_DIRTY_PIXELS_PER_BIT;
9803e230dd2SCorentin Chary             }
9819f64916dSGerd Hoffmann             cmp_bytes = s * VNC_SERVER_FB_BYTES;
9823e230dd2SCorentin Chary             if (memcmp(src_row, dst_row, cmp_bytes) == 0)
9833e230dd2SCorentin Chary                 continue;
9843e230dd2SCorentin Chary             memmove(dst_row, src_row, cmp_bytes);
9853e230dd2SCorentin Chary             QTAILQ_FOREACH(vs, &vd->clients, next) {
9863e230dd2SCorentin Chary                 if (!vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
987b4c85ddcSPeter Lieven                     set_bit(((x + dst_x) / VNC_DIRTY_PIXELS_PER_BIT),
988b4c85ddcSPeter Lieven                             vs->dirty[y]);
9893e230dd2SCorentin Chary                 }
9903e230dd2SCorentin Chary             }
9913e230dd2SCorentin Chary         }
9929f64916dSGerd Hoffmann         src_row += pitch - w * VNC_SERVER_FB_BYTES;
9939f64916dSGerd Hoffmann         dst_row += pitch - w * VNC_SERVER_FB_BYTES;
9943e230dd2SCorentin Chary         y += inc;
9953e230dd2SCorentin Chary     }
9963e230dd2SCorentin Chary 
9973e230dd2SCorentin Chary     QTAILQ_FOREACH(vs, &vd->clients, next) {
9983e230dd2SCorentin Chary         if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
9993e230dd2SCorentin Chary             vnc_copy(vs, src_x, src_y, dst_x, dst_y, w, h);
10003e230dd2SCorentin Chary         }
10013e230dd2SCorentin Chary     }
10023e230dd2SCorentin Chary }
10033e230dd2SCorentin Chary 
10047c20b4a3SGerd Hoffmann static void vnc_mouse_set(DisplayChangeListener *dcl,
10057c20b4a3SGerd Hoffmann                           int x, int y, int visible)
10063e230dd2SCorentin Chary {
10073e230dd2SCorentin Chary     /* can we ask the client(s) to move the pointer ??? */
10083e230dd2SCorentin Chary }
10093e230dd2SCorentin Chary 
10103e230dd2SCorentin Chary static int vnc_cursor_define(VncState *vs)
10113e230dd2SCorentin Chary {
10123e230dd2SCorentin Chary     QEMUCursor *c = vs->vd->cursor;
10133e230dd2SCorentin Chary     int isize;
10143e230dd2SCorentin Chary 
10153e230dd2SCorentin Chary     if (vnc_has_feature(vs, VNC_FEATURE_RICH_CURSOR)) {
1016d01f9595SCorentin Chary         vnc_lock_output(vs);
10173e230dd2SCorentin Chary         vnc_write_u8(vs,  VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
10183e230dd2SCorentin Chary         vnc_write_u8(vs,  0);  /*  padding     */
10193e230dd2SCorentin Chary         vnc_write_u16(vs, 1);  /*  # of rects  */
10203e230dd2SCorentin Chary         vnc_framebuffer_update(vs, c->hot_x, c->hot_y, c->width, c->height,
10213e230dd2SCorentin Chary                                VNC_ENCODING_RICH_CURSOR);
10229f64916dSGerd Hoffmann         isize = c->width * c->height * vs->client_pf.bytes_per_pixel;
10239f64916dSGerd Hoffmann         vnc_write_pixels_generic(vs, c->data, isize);
10243e230dd2SCorentin Chary         vnc_write(vs, vs->vd->cursor_mask, vs->vd->cursor_msize);
1025d01f9595SCorentin Chary         vnc_unlock_output(vs);
10263e230dd2SCorentin Chary         return 0;
10273e230dd2SCorentin Chary     }
10283e230dd2SCorentin Chary     return -1;
10293e230dd2SCorentin Chary }
10303e230dd2SCorentin Chary 
10317c20b4a3SGerd Hoffmann static void vnc_dpy_cursor_define(DisplayChangeListener *dcl,
10327c20b4a3SGerd Hoffmann                                   QEMUCursor *c)
10333e230dd2SCorentin Chary {
1034d616ccc5SGerd Hoffmann     VncDisplay *vd = container_of(dcl, VncDisplay, dcl);
10353e230dd2SCorentin Chary     VncState *vs;
10363e230dd2SCorentin Chary 
10373e230dd2SCorentin Chary     cursor_put(vd->cursor);
10387267c094SAnthony Liguori     g_free(vd->cursor_mask);
10393e230dd2SCorentin Chary 
10403e230dd2SCorentin Chary     vd->cursor = c;
10413e230dd2SCorentin Chary     cursor_get(vd->cursor);
10423e230dd2SCorentin Chary     vd->cursor_msize = cursor_get_mono_bpl(c) * c->height;
10437267c094SAnthony Liguori     vd->cursor_mask = g_malloc0(vd->cursor_msize);
10443e230dd2SCorentin Chary     cursor_get_mono_mask(c, 0, vd->cursor_mask);
10453e230dd2SCorentin Chary 
10463e230dd2SCorentin Chary     QTAILQ_FOREACH(vs, &vd->clients, next) {
10473e230dd2SCorentin Chary         vnc_cursor_define(vs);
10483e230dd2SCorentin Chary     }
10493e230dd2SCorentin Chary }
10503e230dd2SCorentin Chary 
10514769a881SChih-Min Chao static int find_and_clear_dirty_height(VncState *vs,
10526c71a539SCorentin Chary                                        int y, int last_x, int x, int height)
10533e230dd2SCorentin Chary {
10543e230dd2SCorentin Chary     int h;
10553e230dd2SCorentin Chary 
10566c71a539SCorentin Chary     for (h = 1; h < (height - y); h++) {
1057bc2429b9SCorentin Chary         if (!test_bit(last_x, vs->dirty[y + h])) {
10583e230dd2SCorentin Chary             break;
1059bc2429b9SCorentin Chary         }
1060863d7c91SPeter Lieven         bitmap_clear(vs->dirty[y + h], last_x, x - last_x);
10613e230dd2SCorentin Chary     }
10623e230dd2SCorentin Chary 
10633e230dd2SCorentin Chary     return h;
10643e230dd2SCorentin Chary }
10653e230dd2SCorentin Chary 
106638ee14f4SGerd Hoffmann static int vnc_update_client(VncState *vs, int has_dirty, bool sync)
10673e230dd2SCorentin Chary {
106863658280SGerd Hoffmann     vs->has_dirty += has_dirty;
10693e230dd2SCorentin Chary     if (vs->need_update && vs->csock != -1) {
10703e230dd2SCorentin Chary         VncDisplay *vd = vs->vd;
1071bd023f95SCorentin Chary         VncJob *job;
10723e230dd2SCorentin Chary         int y;
10732f487a3dSPeter Lieven         int height, width;
1074bd023f95SCorentin Chary         int n = 0;
1075bd023f95SCorentin Chary 
10763e230dd2SCorentin Chary         if (vs->output.offset && !vs->audio_cap && !vs->force_update)
10773e230dd2SCorentin Chary             /* kernel send buffers are full -> drop frames to throttle */
10783e230dd2SCorentin Chary             return 0;
10793e230dd2SCorentin Chary 
108063658280SGerd Hoffmann         if (!vs->has_dirty && !vs->audio_cap && !vs->force_update)
10813e230dd2SCorentin Chary             return 0;
10823e230dd2SCorentin Chary 
10833e230dd2SCorentin Chary         /*
10843e230dd2SCorentin Chary          * Send screen updates to the vnc client using the server
10853e230dd2SCorentin Chary          * surface and server dirty map.  guest surface updates
10863e230dd2SCorentin Chary          * happening in parallel don't disturb us, the next pass will
10873e230dd2SCorentin Chary          * send them to the client.
10883e230dd2SCorentin Chary          */
1089bd023f95SCorentin Chary         job = vnc_job_new(vs);
10903e230dd2SCorentin Chary 
1091bea60dd7SPeter Lieven         height = pixman_image_get_height(vd->server);
1092bea60dd7SPeter Lieven         width = pixman_image_get_width(vd->server);
10933e230dd2SCorentin Chary 
109412b316d4SPeter Lieven         y = 0;
109512b316d4SPeter Lieven         for (;;) {
109612b316d4SPeter Lieven             int x, h;
109712b316d4SPeter Lieven             unsigned long x2;
109812b316d4SPeter Lieven             unsigned long offset = find_next_bit((unsigned long *) &vs->dirty,
109912b316d4SPeter Lieven                                                  height * VNC_DIRTY_BPL(vs),
110012b316d4SPeter Lieven                                                  y * VNC_DIRTY_BPL(vs));
110112b316d4SPeter Lieven             if (offset == height * VNC_DIRTY_BPL(vs)) {
110212b316d4SPeter Lieven                 /* no more dirty bits */
110312b316d4SPeter Lieven                 break;
11043e230dd2SCorentin Chary             }
110512b316d4SPeter Lieven             y = offset / VNC_DIRTY_BPL(vs);
110612b316d4SPeter Lieven             x = offset % VNC_DIRTY_BPL(vs);
110712b316d4SPeter Lieven             x2 = find_next_zero_bit((unsigned long *) &vs->dirty[y],
110812b316d4SPeter Lieven                                     VNC_DIRTY_BPL(vs), x);
110912b316d4SPeter Lieven             bitmap_clear(vs->dirty[y], x, x2 - x);
111012b316d4SPeter Lieven             h = find_and_clear_dirty_height(vs, y, x, x2, height);
11112f487a3dSPeter Lieven             x2 = MIN(x2, width / VNC_DIRTY_PIXELS_PER_BIT);
11122f487a3dSPeter Lieven             if (x2 > x) {
111312b316d4SPeter Lieven                 n += vnc_job_add_rect(job, x * VNC_DIRTY_PIXELS_PER_BIT, y,
111412b316d4SPeter Lieven                                       (x2 - x) * VNC_DIRTY_PIXELS_PER_BIT, h);
11153e230dd2SCorentin Chary             }
11160e7d6f60SPeter Lieven             if (!x && x2 == width / VNC_DIRTY_PIXELS_PER_BIT) {
11170e7d6f60SPeter Lieven                 y += h;
11180e7d6f60SPeter Lieven                 if (y == height) {
11190e7d6f60SPeter Lieven                     break;
11200e7d6f60SPeter Lieven                 }
11210e7d6f60SPeter Lieven             }
11222f487a3dSPeter Lieven         }
1123bd023f95SCorentin Chary 
1124bd023f95SCorentin Chary         vnc_job_push(job);
1125eb214ff8SGerd Hoffmann         if (sync) {
1126eb214ff8SGerd Hoffmann             vnc_jobs_join(vs);
1127eb214ff8SGerd Hoffmann         }
11283e230dd2SCorentin Chary         vs->force_update = 0;
112963658280SGerd Hoffmann         vs->has_dirty = 0;
1130bd023f95SCorentin Chary         return n;
11313e230dd2SCorentin Chary     }
11323e230dd2SCorentin Chary 
113338ee14f4SGerd Hoffmann     if (vs->csock == -1) {
11343e230dd2SCorentin Chary         vnc_disconnect_finish(vs);
113538ee14f4SGerd Hoffmann     } else if (sync) {
113638ee14f4SGerd Hoffmann         vnc_jobs_join(vs);
113738ee14f4SGerd Hoffmann     }
11383e230dd2SCorentin Chary 
11393e230dd2SCorentin Chary     return 0;
11403e230dd2SCorentin Chary }
11413e230dd2SCorentin Chary 
11423e230dd2SCorentin Chary /* audio */
11433e230dd2SCorentin Chary static void audio_capture_notify(void *opaque, audcnotification_e cmd)
11443e230dd2SCorentin Chary {
11453e230dd2SCorentin Chary     VncState *vs = opaque;
11463e230dd2SCorentin Chary 
11473e230dd2SCorentin Chary     switch (cmd) {
11483e230dd2SCorentin Chary     case AUD_CNOTIFY_DISABLE:
1149bd023f95SCorentin Chary         vnc_lock_output(vs);
11503e230dd2SCorentin Chary         vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
11513e230dd2SCorentin Chary         vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
11523e230dd2SCorentin Chary         vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_END);
1153bd023f95SCorentin Chary         vnc_unlock_output(vs);
11543e230dd2SCorentin Chary         vnc_flush(vs);
11553e230dd2SCorentin Chary         break;
11563e230dd2SCorentin Chary 
11573e230dd2SCorentin Chary     case AUD_CNOTIFY_ENABLE:
1158bd023f95SCorentin Chary         vnc_lock_output(vs);
11593e230dd2SCorentin Chary         vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
11603e230dd2SCorentin Chary         vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
11613e230dd2SCorentin Chary         vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_BEGIN);
1162bd023f95SCorentin Chary         vnc_unlock_output(vs);
11633e230dd2SCorentin Chary         vnc_flush(vs);
11643e230dd2SCorentin Chary         break;
11653e230dd2SCorentin Chary     }
11663e230dd2SCorentin Chary }
11673e230dd2SCorentin Chary 
11683e230dd2SCorentin Chary static void audio_capture_destroy(void *opaque)
11693e230dd2SCorentin Chary {
11703e230dd2SCorentin Chary }
11713e230dd2SCorentin Chary 
11723e230dd2SCorentin Chary static void audio_capture(void *opaque, void *buf, int size)
11733e230dd2SCorentin Chary {
11743e230dd2SCorentin Chary     VncState *vs = opaque;
11753e230dd2SCorentin Chary 
1176bd023f95SCorentin Chary     vnc_lock_output(vs);
11773e230dd2SCorentin Chary     vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
11783e230dd2SCorentin Chary     vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
11793e230dd2SCorentin Chary     vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_DATA);
11803e230dd2SCorentin Chary     vnc_write_u32(vs, size);
11813e230dd2SCorentin Chary     vnc_write(vs, buf, size);
1182bd023f95SCorentin Chary     vnc_unlock_output(vs);
11833e230dd2SCorentin Chary     vnc_flush(vs);
11843e230dd2SCorentin Chary }
11853e230dd2SCorentin Chary 
11863e230dd2SCorentin Chary static void audio_add(VncState *vs)
11873e230dd2SCorentin Chary {
11883e230dd2SCorentin Chary     struct audio_capture_ops ops;
11893e230dd2SCorentin Chary 
11903e230dd2SCorentin Chary     if (vs->audio_cap) {
1191027a79c3SCole Robinson         error_report("audio already running");
11923e230dd2SCorentin Chary         return;
11933e230dd2SCorentin Chary     }
11943e230dd2SCorentin Chary 
11953e230dd2SCorentin Chary     ops.notify = audio_capture_notify;
11963e230dd2SCorentin Chary     ops.destroy = audio_capture_destroy;
11973e230dd2SCorentin Chary     ops.capture = audio_capture;
11983e230dd2SCorentin Chary 
11993e230dd2SCorentin Chary     vs->audio_cap = AUD_add_capture(&vs->as, &ops, vs);
12003e230dd2SCorentin Chary     if (!vs->audio_cap) {
1201027a79c3SCole Robinson         error_report("Failed to add audio capture");
12023e230dd2SCorentin Chary     }
12033e230dd2SCorentin Chary }
12043e230dd2SCorentin Chary 
12053e230dd2SCorentin Chary static void audio_del(VncState *vs)
12063e230dd2SCorentin Chary {
12073e230dd2SCorentin Chary     if (vs->audio_cap) {
12083e230dd2SCorentin Chary         AUD_del_capture(vs->audio_cap, vs);
12093e230dd2SCorentin Chary         vs->audio_cap = NULL;
12103e230dd2SCorentin Chary     }
12113e230dd2SCorentin Chary }
12123e230dd2SCorentin Chary 
12133e230dd2SCorentin Chary static void vnc_disconnect_start(VncState *vs)
12143e230dd2SCorentin Chary {
12153e230dd2SCorentin Chary     if (vs->csock == -1)
12163e230dd2SCorentin Chary         return;
12178cf36489SGerd Hoffmann     vnc_set_share_mode(vs, VNC_SHARE_MODE_DISCONNECTED);
121882e1cc4bSFam Zheng     qemu_set_fd_handler(vs->csock, NULL, NULL, NULL);
12193e230dd2SCorentin Chary     closesocket(vs->csock);
12203e230dd2SCorentin Chary     vs->csock = -1;
12213e230dd2SCorentin Chary }
12223e230dd2SCorentin Chary 
12237536ee4bSTim Hardeck void vnc_disconnect_finish(VncState *vs)
12243e230dd2SCorentin Chary {
12257d964c9dSCorentin Chary     int i;
12267d964c9dSCorentin Chary 
1227bd023f95SCorentin Chary     vnc_jobs_join(vs); /* Wait encoding jobs */
1228bd023f95SCorentin Chary 
1229bd023f95SCorentin Chary     vnc_lock_output(vs);
1230fb6ba0d5SWenchao Xia     vnc_qmp_event(vs, QAPI_EVENT_VNC_DISCONNECTED);
12313e230dd2SCorentin Chary 
12323e230dd2SCorentin Chary     buffer_free(&vs->input);
12333e230dd2SCorentin Chary     buffer_free(&vs->output);
12347536ee4bSTim Hardeck #ifdef CONFIG_VNC_WS
12357536ee4bSTim Hardeck     buffer_free(&vs->ws_input);
12367536ee4bSTim Hardeck     buffer_free(&vs->ws_output);
12377536ee4bSTim Hardeck #endif /* CONFIG_VNC_WS */
12383e230dd2SCorentin Chary 
1239fb6ba0d5SWenchao Xia     qapi_free_VncClientInfo(vs->info);
12403e230dd2SCorentin Chary 
12413e230dd2SCorentin Chary     vnc_zlib_clear(vs);
12423e230dd2SCorentin Chary     vnc_tight_clear(vs);
1243148954faSCorentin Chary     vnc_zrle_clear(vs);
12443e230dd2SCorentin Chary 
12453e230dd2SCorentin Chary #ifdef CONFIG_VNC_TLS
12463e230dd2SCorentin Chary     vnc_tls_client_cleanup(vs);
12473e230dd2SCorentin Chary #endif /* CONFIG_VNC_TLS */
12483e230dd2SCorentin Chary #ifdef CONFIG_VNC_SASL
12493e230dd2SCorentin Chary     vnc_sasl_client_cleanup(vs);
12503e230dd2SCorentin Chary #endif /* CONFIG_VNC_SASL */
12513e230dd2SCorentin Chary     audio_del(vs);
12527bc9318bSGerd Hoffmann     vnc_release_modifiers(vs);
12533e230dd2SCorentin Chary 
12546fd8e79aSTim Hardeck     if (vs->initialized) {
12553e230dd2SCorentin Chary         QTAILQ_REMOVE(&vs->vd->clients, vs, next);
12566fd8e79aSTim Hardeck         qemu_remove_mouse_mode_change_notifier(&vs->mouse_mode_notifier);
12576fd8e79aSTim Hardeck     }
12583e230dd2SCorentin Chary 
12593e230dd2SCorentin Chary     if (vs->vd->lock_key_sync)
12603e230dd2SCorentin Chary         qemu_remove_led_event_handler(vs->led);
1261bd023f95SCorentin Chary     vnc_unlock_output(vs);
1262bd023f95SCorentin Chary 
1263bd023f95SCorentin Chary     qemu_mutex_destroy(&vs->output_mutex);
12646fd8e79aSTim Hardeck     if (vs->bh != NULL) {
1265175b2a6eSCorentin Chary         qemu_bh_delete(vs->bh);
12666fd8e79aSTim Hardeck     }
1267175b2a6eSCorentin Chary     buffer_free(&vs->jobs_buffer);
1268175b2a6eSCorentin Chary 
12697d964c9dSCorentin Chary     for (i = 0; i < VNC_STAT_ROWS; ++i) {
12707267c094SAnthony Liguori         g_free(vs->lossy_rect[i]);
12717d964c9dSCorentin Chary     }
12727267c094SAnthony Liguori     g_free(vs->lossy_rect);
12737267c094SAnthony Liguori     g_free(vs);
12743e230dd2SCorentin Chary }
12753e230dd2SCorentin Chary 
12763e230dd2SCorentin Chary int vnc_client_io_error(VncState *vs, int ret, int last_errno)
12773e230dd2SCorentin Chary {
12783e230dd2SCorentin Chary     if (ret == 0 || ret == -1) {
12793e230dd2SCorentin Chary         if (ret == -1) {
12803e230dd2SCorentin Chary             switch (last_errno) {
12813e230dd2SCorentin Chary                 case EINTR:
12823e230dd2SCorentin Chary                 case EAGAIN:
12833e230dd2SCorentin Chary #ifdef _WIN32
12843e230dd2SCorentin Chary                 case WSAEWOULDBLOCK:
12853e230dd2SCorentin Chary #endif
12863e230dd2SCorentin Chary                     return 0;
12873e230dd2SCorentin Chary                 default:
12883e230dd2SCorentin Chary                     break;
12893e230dd2SCorentin Chary             }
12903e230dd2SCorentin Chary         }
12913e230dd2SCorentin Chary 
12923e230dd2SCorentin Chary         VNC_DEBUG("Closing down client sock: ret %d, errno %d\n",
12933e230dd2SCorentin Chary                   ret, ret < 0 ? last_errno : 0);
12943e230dd2SCorentin Chary         vnc_disconnect_start(vs);
12953e230dd2SCorentin Chary 
12963e230dd2SCorentin Chary         return 0;
12973e230dd2SCorentin Chary     }
12983e230dd2SCorentin Chary     return ret;
12993e230dd2SCorentin Chary }
13003e230dd2SCorentin Chary 
13013e230dd2SCorentin Chary 
13023e230dd2SCorentin Chary void vnc_client_error(VncState *vs)
13033e230dd2SCorentin Chary {
13043e230dd2SCorentin Chary     VNC_DEBUG("Closing down client sock: protocol error\n");
13053e230dd2SCorentin Chary     vnc_disconnect_start(vs);
13063e230dd2SCorentin Chary }
13073e230dd2SCorentin Chary 
13080057a0d5STim Hardeck #ifdef CONFIG_VNC_TLS
13090057a0d5STim Hardeck static long vnc_client_write_tls(gnutls_session_t *session,
13100057a0d5STim Hardeck                                  const uint8_t *data,
13110057a0d5STim Hardeck                                  size_t datalen)
13120057a0d5STim Hardeck {
13130057a0d5STim Hardeck     long ret = gnutls_write(*session, data, datalen);
13140057a0d5STim Hardeck     if (ret < 0) {
13150057a0d5STim Hardeck         if (ret == GNUTLS_E_AGAIN) {
13160057a0d5STim Hardeck             errno = EAGAIN;
13170057a0d5STim Hardeck         } else {
13180057a0d5STim Hardeck             errno = EIO;
13190057a0d5STim Hardeck         }
13200057a0d5STim Hardeck         ret = -1;
13210057a0d5STim Hardeck     }
13220057a0d5STim Hardeck     return ret;
13230057a0d5STim Hardeck }
13240057a0d5STim Hardeck #endif /* CONFIG_VNC_TLS */
13253e230dd2SCorentin Chary 
13263e230dd2SCorentin Chary /*
13273e230dd2SCorentin Chary  * Called to write a chunk of data to the client socket. The data may
13283e230dd2SCorentin Chary  * be the raw data, or may have already been encoded by SASL.
13293e230dd2SCorentin Chary  * The data will be written either straight onto the socket, or
13303e230dd2SCorentin Chary  * written via the GNUTLS wrappers, if TLS/SSL encryption is enabled
13313e230dd2SCorentin Chary  *
13323e230dd2SCorentin Chary  * NB, it is theoretically possible to have 2 layers of encryption,
13333e230dd2SCorentin Chary  * both SASL, and this TLS layer. It is highly unlikely in practice
13343e230dd2SCorentin Chary  * though, since SASL encryption will typically be a no-op if TLS
13353e230dd2SCorentin Chary  * is active
13363e230dd2SCorentin Chary  *
13373e230dd2SCorentin Chary  * Returns the number of bytes written, which may be less than
13383e230dd2SCorentin Chary  * the requested 'datalen' if the socket would block. Returns
13393e230dd2SCorentin Chary  * -1 on error, and disconnects the client socket.
13403e230dd2SCorentin Chary  */
13413e230dd2SCorentin Chary long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen)
13423e230dd2SCorentin Chary {
13433e230dd2SCorentin Chary     long ret;
13443e230dd2SCorentin Chary #ifdef CONFIG_VNC_TLS
13453e230dd2SCorentin Chary     if (vs->tls.session) {
13460057a0d5STim Hardeck         ret = vnc_client_write_tls(&vs->tls.session, data, datalen);
13470057a0d5STim Hardeck     } else {
13483e230dd2SCorentin Chary #endif /* CONFIG_VNC_TLS */
13493e230dd2SCorentin Chary         ret = send(vs->csock, (const void *)data, datalen, 0);
13500057a0d5STim Hardeck #ifdef CONFIG_VNC_TLS
13510057a0d5STim Hardeck     }
13520057a0d5STim Hardeck #endif /* CONFIG_VNC_TLS */
13533e230dd2SCorentin Chary     VNC_DEBUG("Wrote wire %p %zd -> %ld\n", data, datalen, ret);
13543e230dd2SCorentin Chary     return vnc_client_io_error(vs, ret, socket_error());
13553e230dd2SCorentin Chary }
13563e230dd2SCorentin Chary 
13573e230dd2SCorentin Chary 
13583e230dd2SCorentin Chary /*
13593e230dd2SCorentin Chary  * Called to write buffered data to the client socket, when not
13603e230dd2SCorentin Chary  * using any SASL SSF encryption layers. Will write as much data
13613e230dd2SCorentin Chary  * as possible without blocking. If all buffered data is written,
13623e230dd2SCorentin Chary  * will switch the FD poll() handler back to read monitoring.
13633e230dd2SCorentin Chary  *
13643e230dd2SCorentin Chary  * Returns the number of bytes written, which may be less than
13653e230dd2SCorentin Chary  * the buffered output data if the socket would block. Returns
13663e230dd2SCorentin Chary  * -1 on error, and disconnects the client socket.
13673e230dd2SCorentin Chary  */
13683e230dd2SCorentin Chary static long vnc_client_write_plain(VncState *vs)
13693e230dd2SCorentin Chary {
13703e230dd2SCorentin Chary     long ret;
13713e230dd2SCorentin Chary 
13723e230dd2SCorentin Chary #ifdef CONFIG_VNC_SASL
13733e230dd2SCorentin Chary     VNC_DEBUG("Write Plain: Pending output %p size %zd offset %zd. Wait SSF %d\n",
13743e230dd2SCorentin Chary               vs->output.buffer, vs->output.capacity, vs->output.offset,
13753e230dd2SCorentin Chary               vs->sasl.waitWriteSSF);
13763e230dd2SCorentin Chary 
13773e230dd2SCorentin Chary     if (vs->sasl.conn &&
13783e230dd2SCorentin Chary         vs->sasl.runSSF &&
13793e230dd2SCorentin Chary         vs->sasl.waitWriteSSF) {
13803e230dd2SCorentin Chary         ret = vnc_client_write_buf(vs, vs->output.buffer, vs->sasl.waitWriteSSF);
13813e230dd2SCorentin Chary         if (ret)
13823e230dd2SCorentin Chary             vs->sasl.waitWriteSSF -= ret;
13833e230dd2SCorentin Chary     } else
13843e230dd2SCorentin Chary #endif /* CONFIG_VNC_SASL */
13853e230dd2SCorentin Chary         ret = vnc_client_write_buf(vs, vs->output.buffer, vs->output.offset);
13863e230dd2SCorentin Chary     if (!ret)
13873e230dd2SCorentin Chary         return 0;
13883e230dd2SCorentin Chary 
138932ed2680STim Hardeck     buffer_advance(&vs->output, ret);
13903e230dd2SCorentin Chary 
13913e230dd2SCorentin Chary     if (vs->output.offset == 0) {
139282e1cc4bSFam Zheng         qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
13933e230dd2SCorentin Chary     }
13943e230dd2SCorentin Chary 
13953e230dd2SCorentin Chary     return ret;
13963e230dd2SCorentin Chary }
13973e230dd2SCorentin Chary 
13983e230dd2SCorentin Chary 
13993e230dd2SCorentin Chary /*
14003e230dd2SCorentin Chary  * First function called whenever there is data to be written to
14013e230dd2SCorentin Chary  * the client socket. Will delegate actual work according to whether
14023e230dd2SCorentin Chary  * SASL SSF layers are enabled (thus requiring encryption calls)
14033e230dd2SCorentin Chary  */
1404bd023f95SCorentin Chary static void vnc_client_write_locked(void *opaque)
14053e230dd2SCorentin Chary {
14063e230dd2SCorentin Chary     VncState *vs = opaque;
14073e230dd2SCorentin Chary 
14083e230dd2SCorentin Chary #ifdef CONFIG_VNC_SASL
14093e230dd2SCorentin Chary     if (vs->sasl.conn &&
14103e230dd2SCorentin Chary         vs->sasl.runSSF &&
14113e230dd2SCorentin Chary         !vs->sasl.waitWriteSSF) {
14123e230dd2SCorentin Chary         vnc_client_write_sasl(vs);
14133e230dd2SCorentin Chary     } else
14143e230dd2SCorentin Chary #endif /* CONFIG_VNC_SASL */
14157536ee4bSTim Hardeck     {
14167536ee4bSTim Hardeck #ifdef CONFIG_VNC_WS
14177536ee4bSTim Hardeck         if (vs->encode_ws) {
14187536ee4bSTim Hardeck             vnc_client_write_ws(vs);
14197536ee4bSTim Hardeck         } else
14207536ee4bSTim Hardeck #endif /* CONFIG_VNC_WS */
14217536ee4bSTim Hardeck         {
14223e230dd2SCorentin Chary             vnc_client_write_plain(vs);
14233e230dd2SCorentin Chary         }
14247536ee4bSTim Hardeck     }
14257536ee4bSTim Hardeck }
14263e230dd2SCorentin Chary 
1427bd023f95SCorentin Chary void vnc_client_write(void *opaque)
1428bd023f95SCorentin Chary {
1429bd023f95SCorentin Chary     VncState *vs = opaque;
1430bd023f95SCorentin Chary 
1431bd023f95SCorentin Chary     vnc_lock_output(vs);
14327536ee4bSTim Hardeck     if (vs->output.offset
14337536ee4bSTim Hardeck #ifdef CONFIG_VNC_WS
14347536ee4bSTim Hardeck             || vs->ws_output.offset
14357536ee4bSTim Hardeck #endif
14367536ee4bSTim Hardeck             ) {
1437bd023f95SCorentin Chary         vnc_client_write_locked(opaque);
1438ac71103dSYoshiaki Tamura     } else if (vs->csock != -1) {
143982e1cc4bSFam Zheng         qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
1440bd023f95SCorentin Chary     }
1441bd023f95SCorentin Chary     vnc_unlock_output(vs);
1442bd023f95SCorentin Chary }
1443bd023f95SCorentin Chary 
14443e230dd2SCorentin Chary void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting)
14453e230dd2SCorentin Chary {
14463e230dd2SCorentin Chary     vs->read_handler = func;
14473e230dd2SCorentin Chary     vs->read_handler_expect = expecting;
14483e230dd2SCorentin Chary }
14493e230dd2SCorentin Chary 
14500057a0d5STim Hardeck #ifdef CONFIG_VNC_TLS
14510057a0d5STim Hardeck static long vnc_client_read_tls(gnutls_session_t *session, uint8_t *data,
14520057a0d5STim Hardeck                                 size_t datalen)
14530057a0d5STim Hardeck {
14540057a0d5STim Hardeck     long ret = gnutls_read(*session, data, datalen);
14550057a0d5STim Hardeck     if (ret < 0) {
14560057a0d5STim Hardeck         if (ret == GNUTLS_E_AGAIN) {
14570057a0d5STim Hardeck             errno = EAGAIN;
14580057a0d5STim Hardeck         } else {
14590057a0d5STim Hardeck             errno = EIO;
14600057a0d5STim Hardeck         }
14610057a0d5STim Hardeck         ret = -1;
14620057a0d5STim Hardeck     }
14630057a0d5STim Hardeck     return ret;
14640057a0d5STim Hardeck }
14650057a0d5STim Hardeck #endif /* CONFIG_VNC_TLS */
14663e230dd2SCorentin Chary 
14673e230dd2SCorentin Chary /*
14683e230dd2SCorentin Chary  * Called to read a chunk of data from the client socket. The data may
14693e230dd2SCorentin Chary  * be the raw data, or may need to be further decoded by SASL.
14703e230dd2SCorentin Chary  * The data will be read either straight from to the socket, or
14713e230dd2SCorentin Chary  * read via the GNUTLS wrappers, if TLS/SSL encryption is enabled
14723e230dd2SCorentin Chary  *
14733e230dd2SCorentin Chary  * NB, it is theoretically possible to have 2 layers of encryption,
14743e230dd2SCorentin Chary  * both SASL, and this TLS layer. It is highly unlikely in practice
14753e230dd2SCorentin Chary  * though, since SASL encryption will typically be a no-op if TLS
14763e230dd2SCorentin Chary  * is active
14773e230dd2SCorentin Chary  *
14783e230dd2SCorentin Chary  * Returns the number of bytes read, which may be less than
14793e230dd2SCorentin Chary  * the requested 'datalen' if the socket would block. Returns
14803e230dd2SCorentin Chary  * -1 on error, and disconnects the client socket.
14813e230dd2SCorentin Chary  */
14823e230dd2SCorentin Chary long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen)
14833e230dd2SCorentin Chary {
14843e230dd2SCorentin Chary     long ret;
14853e230dd2SCorentin Chary #ifdef CONFIG_VNC_TLS
14863e230dd2SCorentin Chary     if (vs->tls.session) {
14870057a0d5STim Hardeck         ret = vnc_client_read_tls(&vs->tls.session, data, datalen);
14880057a0d5STim Hardeck     } else {
14893e230dd2SCorentin Chary #endif /* CONFIG_VNC_TLS */
149000aa0040SBlue Swirl         ret = qemu_recv(vs->csock, data, datalen, 0);
14910057a0d5STim Hardeck #ifdef CONFIG_VNC_TLS
14920057a0d5STim Hardeck     }
14930057a0d5STim Hardeck #endif /* CONFIG_VNC_TLS */
14943e230dd2SCorentin Chary     VNC_DEBUG("Read wire %p %zd -> %ld\n", data, datalen, ret);
14953e230dd2SCorentin Chary     return vnc_client_io_error(vs, ret, socket_error());
14963e230dd2SCorentin Chary }
14973e230dd2SCorentin Chary 
14983e230dd2SCorentin Chary 
14993e230dd2SCorentin Chary /*
15003e230dd2SCorentin Chary  * Called to read data from the client socket to the input buffer,
15013e230dd2SCorentin Chary  * when not using any SASL SSF encryption layers. Will read as much
15023e230dd2SCorentin Chary  * data as possible without blocking.
15033e230dd2SCorentin Chary  *
15043e230dd2SCorentin Chary  * Returns the number of bytes read. Returns -1 on error, and
15053e230dd2SCorentin Chary  * disconnects the client socket.
15063e230dd2SCorentin Chary  */
15073e230dd2SCorentin Chary static long vnc_client_read_plain(VncState *vs)
15083e230dd2SCorentin Chary {
15093e230dd2SCorentin Chary     int ret;
15103e230dd2SCorentin Chary     VNC_DEBUG("Read plain %p size %zd offset %zd\n",
15113e230dd2SCorentin Chary               vs->input.buffer, vs->input.capacity, vs->input.offset);
15123e230dd2SCorentin Chary     buffer_reserve(&vs->input, 4096);
15133e230dd2SCorentin Chary     ret = vnc_client_read_buf(vs, buffer_end(&vs->input), 4096);
15143e230dd2SCorentin Chary     if (!ret)
15153e230dd2SCorentin Chary         return 0;
15163e230dd2SCorentin Chary     vs->input.offset += ret;
15173e230dd2SCorentin Chary     return ret;
15183e230dd2SCorentin Chary }
15193e230dd2SCorentin Chary 
1520175b2a6eSCorentin Chary static void vnc_jobs_bh(void *opaque)
1521175b2a6eSCorentin Chary {
1522175b2a6eSCorentin Chary     VncState *vs = opaque;
1523175b2a6eSCorentin Chary 
1524175b2a6eSCorentin Chary     vnc_jobs_consume_buffer(vs);
1525175b2a6eSCorentin Chary }
15263e230dd2SCorentin Chary 
15273e230dd2SCorentin Chary /*
15283e230dd2SCorentin Chary  * First function called whenever there is more data to be read from
15293e230dd2SCorentin Chary  * the client socket. Will delegate actual work according to whether
15303e230dd2SCorentin Chary  * SASL SSF layers are enabled (thus requiring decryption calls)
15313e230dd2SCorentin Chary  */
15323e230dd2SCorentin Chary void vnc_client_read(void *opaque)
15333e230dd2SCorentin Chary {
15343e230dd2SCorentin Chary     VncState *vs = opaque;
15353e230dd2SCorentin Chary     long ret;
15363e230dd2SCorentin Chary 
15373e230dd2SCorentin Chary #ifdef CONFIG_VNC_SASL
15383e230dd2SCorentin Chary     if (vs->sasl.conn && vs->sasl.runSSF)
15393e230dd2SCorentin Chary         ret = vnc_client_read_sasl(vs);
15403e230dd2SCorentin Chary     else
15413e230dd2SCorentin Chary #endif /* CONFIG_VNC_SASL */
15427536ee4bSTim Hardeck #ifdef CONFIG_VNC_WS
15437536ee4bSTim Hardeck         if (vs->encode_ws) {
15447536ee4bSTim Hardeck             ret = vnc_client_read_ws(vs);
15457536ee4bSTim Hardeck             if (ret == -1) {
15467536ee4bSTim Hardeck                 vnc_disconnect_start(vs);
15477536ee4bSTim Hardeck                 return;
15487536ee4bSTim Hardeck             } else if (ret == -2) {
15497536ee4bSTim Hardeck                 vnc_client_error(vs);
15507536ee4bSTim Hardeck                 return;
15517536ee4bSTim Hardeck             }
15527536ee4bSTim Hardeck         } else
15537536ee4bSTim Hardeck #endif /* CONFIG_VNC_WS */
15547536ee4bSTim Hardeck         {
15553e230dd2SCorentin Chary         ret = vnc_client_read_plain(vs);
15567536ee4bSTim Hardeck         }
15573e230dd2SCorentin Chary     if (!ret) {
15583e230dd2SCorentin Chary         if (vs->csock == -1)
15593e230dd2SCorentin Chary             vnc_disconnect_finish(vs);
15603e230dd2SCorentin Chary         return;
15613e230dd2SCorentin Chary     }
15623e230dd2SCorentin Chary 
15633e230dd2SCorentin Chary     while (vs->read_handler && vs->input.offset >= vs->read_handler_expect) {
15643e230dd2SCorentin Chary         size_t len = vs->read_handler_expect;
15653e230dd2SCorentin Chary         int ret;
15663e230dd2SCorentin Chary 
15673e230dd2SCorentin Chary         ret = vs->read_handler(vs, vs->input.buffer, len);
15683e230dd2SCorentin Chary         if (vs->csock == -1) {
15693e230dd2SCorentin Chary             vnc_disconnect_finish(vs);
15703e230dd2SCorentin Chary             return;
15713e230dd2SCorentin Chary         }
15723e230dd2SCorentin Chary 
15733e230dd2SCorentin Chary         if (!ret) {
157432ed2680STim Hardeck             buffer_advance(&vs->input, len);
15753e230dd2SCorentin Chary         } else {
15763e230dd2SCorentin Chary             vs->read_handler_expect = ret;
15773e230dd2SCorentin Chary         }
15783e230dd2SCorentin Chary     }
15793e230dd2SCorentin Chary }
15803e230dd2SCorentin Chary 
15813e230dd2SCorentin Chary void vnc_write(VncState *vs, const void *data, size_t len)
15823e230dd2SCorentin Chary {
15833e230dd2SCorentin Chary     buffer_reserve(&vs->output, len);
15843e230dd2SCorentin Chary 
15853e230dd2SCorentin Chary     if (vs->csock != -1 && buffer_empty(&vs->output)) {
158682e1cc4bSFam Zheng         qemu_set_fd_handler(vs->csock, vnc_client_read, vnc_client_write, vs);
15873e230dd2SCorentin Chary     }
15883e230dd2SCorentin Chary 
15893e230dd2SCorentin Chary     buffer_append(&vs->output, data, len);
15903e230dd2SCorentin Chary }
15913e230dd2SCorentin Chary 
15923e230dd2SCorentin Chary void vnc_write_s32(VncState *vs, int32_t value)
15933e230dd2SCorentin Chary {
15943e230dd2SCorentin Chary     vnc_write_u32(vs, *(uint32_t *)&value);
15953e230dd2SCorentin Chary }
15963e230dd2SCorentin Chary 
15973e230dd2SCorentin Chary void vnc_write_u32(VncState *vs, uint32_t value)
15983e230dd2SCorentin Chary {
15993e230dd2SCorentin Chary     uint8_t buf[4];
16003e230dd2SCorentin Chary 
16013e230dd2SCorentin Chary     buf[0] = (value >> 24) & 0xFF;
16023e230dd2SCorentin Chary     buf[1] = (value >> 16) & 0xFF;
16033e230dd2SCorentin Chary     buf[2] = (value >>  8) & 0xFF;
16043e230dd2SCorentin Chary     buf[3] = value & 0xFF;
16053e230dd2SCorentin Chary 
16063e230dd2SCorentin Chary     vnc_write(vs, buf, 4);
16073e230dd2SCorentin Chary }
16083e230dd2SCorentin Chary 
16093e230dd2SCorentin Chary void vnc_write_u16(VncState *vs, uint16_t value)
16103e230dd2SCorentin Chary {
16113e230dd2SCorentin Chary     uint8_t buf[2];
16123e230dd2SCorentin Chary 
16133e230dd2SCorentin Chary     buf[0] = (value >> 8) & 0xFF;
16143e230dd2SCorentin Chary     buf[1] = value & 0xFF;
16153e230dd2SCorentin Chary 
16163e230dd2SCorentin Chary     vnc_write(vs, buf, 2);
16173e230dd2SCorentin Chary }
16183e230dd2SCorentin Chary 
16193e230dd2SCorentin Chary void vnc_write_u8(VncState *vs, uint8_t value)
16203e230dd2SCorentin Chary {
16213e230dd2SCorentin Chary     vnc_write(vs, (char *)&value, 1);
16223e230dd2SCorentin Chary }
16233e230dd2SCorentin Chary 
16243e230dd2SCorentin Chary void vnc_flush(VncState *vs)
16253e230dd2SCorentin Chary {
1626bd023f95SCorentin Chary     vnc_lock_output(vs);
16277536ee4bSTim Hardeck     if (vs->csock != -1 && (vs->output.offset
16287536ee4bSTim Hardeck #ifdef CONFIG_VNC_WS
16297536ee4bSTim Hardeck                 || vs->ws_output.offset
16307536ee4bSTim Hardeck #endif
16317536ee4bSTim Hardeck                 )) {
1632bd023f95SCorentin Chary         vnc_client_write_locked(vs);
1633bd023f95SCorentin Chary     }
1634bd023f95SCorentin Chary     vnc_unlock_output(vs);
16353e230dd2SCorentin Chary }
16363e230dd2SCorentin Chary 
163771a8cdecSBlue Swirl static uint8_t read_u8(uint8_t *data, size_t offset)
16383e230dd2SCorentin Chary {
16393e230dd2SCorentin Chary     return data[offset];
16403e230dd2SCorentin Chary }
16413e230dd2SCorentin Chary 
164271a8cdecSBlue Swirl static uint16_t read_u16(uint8_t *data, size_t offset)
16433e230dd2SCorentin Chary {
16443e230dd2SCorentin Chary     return ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF);
16453e230dd2SCorentin Chary }
16463e230dd2SCorentin Chary 
164771a8cdecSBlue Swirl static int32_t read_s32(uint8_t *data, size_t offset)
16483e230dd2SCorentin Chary {
16493e230dd2SCorentin Chary     return (int32_t)((data[offset] << 24) | (data[offset + 1] << 16) |
16503e230dd2SCorentin Chary                      (data[offset + 2] << 8) | data[offset + 3]);
16513e230dd2SCorentin Chary }
16523e230dd2SCorentin Chary 
16533e230dd2SCorentin Chary uint32_t read_u32(uint8_t *data, size_t offset)
16543e230dd2SCorentin Chary {
16553e230dd2SCorentin Chary     return ((data[offset] << 24) | (data[offset + 1] << 16) |
16563e230dd2SCorentin Chary             (data[offset + 2] << 8) | data[offset + 3]);
16573e230dd2SCorentin Chary }
16583e230dd2SCorentin Chary 
16593e230dd2SCorentin Chary static void client_cut_text(VncState *vs, size_t len, uint8_t *text)
16603e230dd2SCorentin Chary {
16613e230dd2SCorentin Chary }
16623e230dd2SCorentin Chary 
16639e8dd451SJan Kiszka static void check_pointer_type_change(Notifier *notifier, void *data)
16643e230dd2SCorentin Chary {
16653e230dd2SCorentin Chary     VncState *vs = container_of(notifier, VncState, mouse_mode_notifier);
166614768ebaSGerd Hoffmann     int absolute = qemu_input_is_absolute();
16673e230dd2SCorentin Chary 
16683e230dd2SCorentin Chary     if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE) && vs->absolute != absolute) {
1669bd023f95SCorentin Chary         vnc_lock_output(vs);
16703e230dd2SCorentin Chary         vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
16713e230dd2SCorentin Chary         vnc_write_u8(vs, 0);
16723e230dd2SCorentin Chary         vnc_write_u16(vs, 1);
16733e230dd2SCorentin Chary         vnc_framebuffer_update(vs, absolute, 0,
1674bea60dd7SPeter Lieven                                pixman_image_get_width(vs->vd->server),
1675bea60dd7SPeter Lieven                                pixman_image_get_height(vs->vd->server),
16763e230dd2SCorentin Chary                                VNC_ENCODING_POINTER_TYPE_CHANGE);
1677bd023f95SCorentin Chary         vnc_unlock_output(vs);
16783e230dd2SCorentin Chary         vnc_flush(vs);
16793e230dd2SCorentin Chary     }
16803e230dd2SCorentin Chary     vs->absolute = absolute;
16813e230dd2SCorentin Chary }
16823e230dd2SCorentin Chary 
16833e230dd2SCorentin Chary static void pointer_event(VncState *vs, int button_mask, int x, int y)
16843e230dd2SCorentin Chary {
168514768ebaSGerd Hoffmann     static uint32_t bmap[INPUT_BUTTON_MAX] = {
168614768ebaSGerd Hoffmann         [INPUT_BUTTON_LEFT]       = 0x01,
168714768ebaSGerd Hoffmann         [INPUT_BUTTON_MIDDLE]     = 0x02,
168814768ebaSGerd Hoffmann         [INPUT_BUTTON_RIGHT]      = 0x04,
168914768ebaSGerd Hoffmann         [INPUT_BUTTON_WHEEL_UP]   = 0x08,
169014768ebaSGerd Hoffmann         [INPUT_BUTTON_WHEEL_DOWN] = 0x10,
169114768ebaSGerd Hoffmann     };
169214768ebaSGerd Hoffmann     QemuConsole *con = vs->vd->dcl.con;
1693bea60dd7SPeter Lieven     int width = pixman_image_get_width(vs->vd->server);
1694bea60dd7SPeter Lieven     int height = pixman_image_get_height(vs->vd->server);
16953e230dd2SCorentin Chary 
169614768ebaSGerd Hoffmann     if (vs->last_bmask != button_mask) {
169714768ebaSGerd Hoffmann         qemu_input_update_buttons(con, bmap, vs->last_bmask, button_mask);
169814768ebaSGerd Hoffmann         vs->last_bmask = button_mask;
169914768ebaSGerd Hoffmann     }
17003e230dd2SCorentin Chary 
17013e230dd2SCorentin Chary     if (vs->absolute) {
170214768ebaSGerd Hoffmann         qemu_input_queue_abs(con, INPUT_AXIS_X, x, width);
170314768ebaSGerd Hoffmann         qemu_input_queue_abs(con, INPUT_AXIS_Y, y, height);
17043e230dd2SCorentin Chary     } else if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE)) {
170514768ebaSGerd Hoffmann         qemu_input_queue_rel(con, INPUT_AXIS_X, x - 0x7FFF);
170614768ebaSGerd Hoffmann         qemu_input_queue_rel(con, INPUT_AXIS_Y, y - 0x7FFF);
17073e230dd2SCorentin Chary     } else {
170814768ebaSGerd Hoffmann         if (vs->last_x != -1) {
170914768ebaSGerd Hoffmann             qemu_input_queue_rel(con, INPUT_AXIS_X, x - vs->last_x);
171014768ebaSGerd Hoffmann             qemu_input_queue_rel(con, INPUT_AXIS_Y, y - vs->last_y);
171114768ebaSGerd Hoffmann         }
17123e230dd2SCorentin Chary         vs->last_x = x;
17133e230dd2SCorentin Chary         vs->last_y = y;
17143e230dd2SCorentin Chary     }
171514768ebaSGerd Hoffmann     qemu_input_event_sync();
17163e230dd2SCorentin Chary }
17173e230dd2SCorentin Chary 
17183e230dd2SCorentin Chary static void reset_keys(VncState *vs)
17193e230dd2SCorentin Chary {
17203e230dd2SCorentin Chary     int i;
17213e230dd2SCorentin Chary     for(i = 0; i < 256; i++) {
17223e230dd2SCorentin Chary         if (vs->modifiers_state[i]) {
17238d447d10SGerd Hoffmann             qemu_input_event_send_key_number(vs->vd->dcl.con, i, false);
17243e230dd2SCorentin Chary             vs->modifiers_state[i] = 0;
17253e230dd2SCorentin Chary         }
17263e230dd2SCorentin Chary     }
17273e230dd2SCorentin Chary }
17283e230dd2SCorentin Chary 
17293e230dd2SCorentin Chary static void press_key(VncState *vs, int keysym)
17303e230dd2SCorentin Chary {
17313e230dd2SCorentin Chary     int keycode = keysym2scancode(vs->vd->kbd_layout, keysym) & SCANCODE_KEYMASK;
17328d447d10SGerd Hoffmann     qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, true);
17332deb4accSGerd Hoffmann     qemu_input_event_send_key_delay(0);
17348d447d10SGerd Hoffmann     qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, false);
17352deb4accSGerd Hoffmann     qemu_input_event_send_key_delay(0);
17363e230dd2SCorentin Chary }
17373e230dd2SCorentin Chary 
1738ab99e5c1SLei Li static int current_led_state(VncState *vs)
1739ab99e5c1SLei Li {
1740ab99e5c1SLei Li     int ledstate = 0;
1741ab99e5c1SLei Li 
1742ab99e5c1SLei Li     if (vs->modifiers_state[0x46]) {
1743ab99e5c1SLei Li         ledstate |= QEMU_SCROLL_LOCK_LED;
1744ab99e5c1SLei Li     }
1745ab99e5c1SLei Li     if (vs->modifiers_state[0x45]) {
1746ab99e5c1SLei Li         ledstate |= QEMU_NUM_LOCK_LED;
1747ab99e5c1SLei Li     }
1748ab99e5c1SLei Li     if (vs->modifiers_state[0x3a]) {
1749ab99e5c1SLei Li         ledstate |= QEMU_CAPS_LOCK_LED;
1750ab99e5c1SLei Li     }
1751ab99e5c1SLei Li 
1752ab99e5c1SLei Li     return ledstate;
1753ab99e5c1SLei Li }
1754ab99e5c1SLei Li 
1755ab99e5c1SLei Li static void vnc_led_state_change(VncState *vs)
1756ab99e5c1SLei Li {
1757ab99e5c1SLei Li     int ledstate = 0;
1758ab99e5c1SLei Li 
1759ab99e5c1SLei Li     if (!vnc_has_feature(vs, VNC_FEATURE_LED_STATE)) {
1760ab99e5c1SLei Li         return;
1761ab99e5c1SLei Li     }
1762ab99e5c1SLei Li 
1763ab99e5c1SLei Li     ledstate = current_led_state(vs);
1764ab99e5c1SLei Li     vnc_lock_output(vs);
1765ab99e5c1SLei Li     vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
1766ab99e5c1SLei Li     vnc_write_u8(vs, 0);
1767ab99e5c1SLei Li     vnc_write_u16(vs, 1);
1768ab99e5c1SLei Li     vnc_framebuffer_update(vs, 0, 0, 1, 1, VNC_ENCODING_LED_STATE);
1769ab99e5c1SLei Li     vnc_write_u8(vs, ledstate);
1770ab99e5c1SLei Li     vnc_unlock_output(vs);
1771ab99e5c1SLei Li     vnc_flush(vs);
1772ab99e5c1SLei Li }
1773ab99e5c1SLei Li 
17743e230dd2SCorentin Chary static void kbd_leds(void *opaque, int ledstate)
17753e230dd2SCorentin Chary {
17763e230dd2SCorentin Chary     VncState *vs = opaque;
177796f3d174SLei Li     int caps, num, scr;
17781483adcfSLei Li     bool has_changed = (ledstate != current_led_state(vs));
17793e230dd2SCorentin Chary 
178040066175SGerd Hoffmann     trace_vnc_key_guest_leds((ledstate & QEMU_CAPS_LOCK_LED),
178140066175SGerd Hoffmann                              (ledstate & QEMU_NUM_LOCK_LED),
178240066175SGerd Hoffmann                              (ledstate & QEMU_SCROLL_LOCK_LED));
178340066175SGerd Hoffmann 
17843e230dd2SCorentin Chary     caps = ledstate & QEMU_CAPS_LOCK_LED ? 1 : 0;
17853e230dd2SCorentin Chary     num  = ledstate & QEMU_NUM_LOCK_LED  ? 1 : 0;
178696f3d174SLei Li     scr  = ledstate & QEMU_SCROLL_LOCK_LED ? 1 : 0;
17873e230dd2SCorentin Chary 
17883e230dd2SCorentin Chary     if (vs->modifiers_state[0x3a] != caps) {
17893e230dd2SCorentin Chary         vs->modifiers_state[0x3a] = caps;
17903e230dd2SCorentin Chary     }
17913e230dd2SCorentin Chary     if (vs->modifiers_state[0x45] != num) {
17923e230dd2SCorentin Chary         vs->modifiers_state[0x45] = num;
17933e230dd2SCorentin Chary     }
179496f3d174SLei Li     if (vs->modifiers_state[0x46] != scr) {
179596f3d174SLei Li         vs->modifiers_state[0x46] = scr;
179696f3d174SLei Li     }
1797ab99e5c1SLei Li 
1798ab99e5c1SLei Li     /* Sending the current led state message to the client */
17991483adcfSLei Li     if (has_changed) {
1800ab99e5c1SLei Li         vnc_led_state_change(vs);
1801ab99e5c1SLei Li     }
18023e230dd2SCorentin Chary }
18033e230dd2SCorentin Chary 
18043e230dd2SCorentin Chary static void do_key_event(VncState *vs, int down, int keycode, int sym)
18053e230dd2SCorentin Chary {
18063e230dd2SCorentin Chary     /* QEMU console switch */
18073e230dd2SCorentin Chary     switch(keycode) {
18083e230dd2SCorentin Chary     case 0x2a:                          /* Left Shift */
18093e230dd2SCorentin Chary     case 0x36:                          /* Right Shift */
18103e230dd2SCorentin Chary     case 0x1d:                          /* Left CTRL */
18113e230dd2SCorentin Chary     case 0x9d:                          /* Right CTRL */
18123e230dd2SCorentin Chary     case 0x38:                          /* Left ALT */
18133e230dd2SCorentin Chary     case 0xb8:                          /* Right ALT */
18143e230dd2SCorentin Chary         if (down)
18153e230dd2SCorentin Chary             vs->modifiers_state[keycode] = 1;
18163e230dd2SCorentin Chary         else
18173e230dd2SCorentin Chary             vs->modifiers_state[keycode] = 0;
18183e230dd2SCorentin Chary         break;
18193e230dd2SCorentin Chary     case 0x02 ... 0x0a: /* '1' to '9' keys */
18201d0d59feSGerd Hoffmann         if (vs->vd->dcl.con == NULL &&
18211d0d59feSGerd Hoffmann             down && vs->modifiers_state[0x1d] && vs->modifiers_state[0x38]) {
18223e230dd2SCorentin Chary             /* Reset the modifiers sent to the current console */
18233e230dd2SCorentin Chary             reset_keys(vs);
18243e230dd2SCorentin Chary             console_select(keycode - 0x02);
18253e230dd2SCorentin Chary             return;
18263e230dd2SCorentin Chary         }
18273e230dd2SCorentin Chary         break;
18283e230dd2SCorentin Chary     case 0x3a:                        /* CapsLock */
18293e230dd2SCorentin Chary     case 0x45:                        /* NumLock */
18303e230dd2SCorentin Chary         if (down)
18313e230dd2SCorentin Chary             vs->modifiers_state[keycode] ^= 1;
18323e230dd2SCorentin Chary         break;
18333e230dd2SCorentin Chary     }
18343e230dd2SCorentin Chary 
1835e7b2aaccSLei Li     /* Turn off the lock state sync logic if the client support the led
1836e7b2aaccSLei Li        state extension.
1837e7b2aaccSLei Li     */
18389892088bSGerd Hoffmann     if (down && vs->vd->lock_key_sync &&
1839e7b2aaccSLei Li         !vnc_has_feature(vs, VNC_FEATURE_LED_STATE) &&
18403e230dd2SCorentin Chary         keycode_is_keypad(vs->vd->kbd_layout, keycode)) {
18413e230dd2SCorentin Chary         /* If the numlock state needs to change then simulate an additional
18423e230dd2SCorentin Chary            keypress before sending this one.  This will happen if the user
18433e230dd2SCorentin Chary            toggles numlock away from the VNC window.
18443e230dd2SCorentin Chary         */
18453e230dd2SCorentin Chary         if (keysym_is_numlock(vs->vd->kbd_layout, sym & 0xFFFF)) {
18463e230dd2SCorentin Chary             if (!vs->modifiers_state[0x45]) {
184740066175SGerd Hoffmann                 trace_vnc_key_sync_numlock(true);
18483e230dd2SCorentin Chary                 vs->modifiers_state[0x45] = 1;
18493e230dd2SCorentin Chary                 press_key(vs, 0xff7f);
18503e230dd2SCorentin Chary             }
18513e230dd2SCorentin Chary         } else {
18523e230dd2SCorentin Chary             if (vs->modifiers_state[0x45]) {
185340066175SGerd Hoffmann                 trace_vnc_key_sync_numlock(false);
18543e230dd2SCorentin Chary                 vs->modifiers_state[0x45] = 0;
18553e230dd2SCorentin Chary                 press_key(vs, 0xff7f);
18563e230dd2SCorentin Chary             }
18573e230dd2SCorentin Chary         }
18583e230dd2SCorentin Chary     }
18593e230dd2SCorentin Chary 
18609892088bSGerd Hoffmann     if (down && vs->vd->lock_key_sync &&
1861e7b2aaccSLei Li         !vnc_has_feature(vs, VNC_FEATURE_LED_STATE) &&
18623e230dd2SCorentin Chary         ((sym >= 'A' && sym <= 'Z') || (sym >= 'a' && sym <= 'z'))) {
18633e230dd2SCorentin Chary         /* If the capslock state needs to change then simulate an additional
18643e230dd2SCorentin Chary            keypress before sending this one.  This will happen if the user
18653e230dd2SCorentin Chary            toggles capslock away from the VNC window.
18663e230dd2SCorentin Chary         */
18673e230dd2SCorentin Chary         int uppercase = !!(sym >= 'A' && sym <= 'Z');
18683e230dd2SCorentin Chary         int shift = !!(vs->modifiers_state[0x2a] | vs->modifiers_state[0x36]);
18693e230dd2SCorentin Chary         int capslock = !!(vs->modifiers_state[0x3a]);
18703e230dd2SCorentin Chary         if (capslock) {
18713e230dd2SCorentin Chary             if (uppercase == shift) {
187240066175SGerd Hoffmann                 trace_vnc_key_sync_capslock(false);
18733e230dd2SCorentin Chary                 vs->modifiers_state[0x3a] = 0;
18743e230dd2SCorentin Chary                 press_key(vs, 0xffe5);
18753e230dd2SCorentin Chary             }
18763e230dd2SCorentin Chary         } else {
18773e230dd2SCorentin Chary             if (uppercase != shift) {
187840066175SGerd Hoffmann                 trace_vnc_key_sync_capslock(true);
18793e230dd2SCorentin Chary                 vs->modifiers_state[0x3a] = 1;
18803e230dd2SCorentin Chary                 press_key(vs, 0xffe5);
18813e230dd2SCorentin Chary             }
18823e230dd2SCorentin Chary         }
18833e230dd2SCorentin Chary     }
18843e230dd2SCorentin Chary 
188581c0d5a6SGerd Hoffmann     if (qemu_console_is_graphic(NULL)) {
18868d447d10SGerd Hoffmann         qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, down);
18873e230dd2SCorentin Chary     } else {
1888e26437c2SGerd Hoffmann         bool numlock = vs->modifiers_state[0x45];
1889e26437c2SGerd Hoffmann         bool control = (vs->modifiers_state[0x1d] ||
1890e26437c2SGerd Hoffmann                         vs->modifiers_state[0x9d]);
18913e230dd2SCorentin Chary         /* QEMU console emulation */
18923e230dd2SCorentin Chary         if (down) {
18933e230dd2SCorentin Chary             switch (keycode) {
18943e230dd2SCorentin Chary             case 0x2a:                          /* Left Shift */
18953e230dd2SCorentin Chary             case 0x36:                          /* Right Shift */
18963e230dd2SCorentin Chary             case 0x1d:                          /* Left CTRL */
18973e230dd2SCorentin Chary             case 0x9d:                          /* Right CTRL */
18983e230dd2SCorentin Chary             case 0x38:                          /* Left ALT */
18993e230dd2SCorentin Chary             case 0xb8:                          /* Right ALT */
19003e230dd2SCorentin Chary                 break;
19013e230dd2SCorentin Chary             case 0xc8:
19023e230dd2SCorentin Chary                 kbd_put_keysym(QEMU_KEY_UP);
19033e230dd2SCorentin Chary                 break;
19043e230dd2SCorentin Chary             case 0xd0:
19053e230dd2SCorentin Chary                 kbd_put_keysym(QEMU_KEY_DOWN);
19063e230dd2SCorentin Chary                 break;
19073e230dd2SCorentin Chary             case 0xcb:
19083e230dd2SCorentin Chary                 kbd_put_keysym(QEMU_KEY_LEFT);
19093e230dd2SCorentin Chary                 break;
19103e230dd2SCorentin Chary             case 0xcd:
19113e230dd2SCorentin Chary                 kbd_put_keysym(QEMU_KEY_RIGHT);
19123e230dd2SCorentin Chary                 break;
19133e230dd2SCorentin Chary             case 0xd3:
19143e230dd2SCorentin Chary                 kbd_put_keysym(QEMU_KEY_DELETE);
19153e230dd2SCorentin Chary                 break;
19163e230dd2SCorentin Chary             case 0xc7:
19173e230dd2SCorentin Chary                 kbd_put_keysym(QEMU_KEY_HOME);
19183e230dd2SCorentin Chary                 break;
19193e230dd2SCorentin Chary             case 0xcf:
19203e230dd2SCorentin Chary                 kbd_put_keysym(QEMU_KEY_END);
19213e230dd2SCorentin Chary                 break;
19223e230dd2SCorentin Chary             case 0xc9:
19233e230dd2SCorentin Chary                 kbd_put_keysym(QEMU_KEY_PAGEUP);
19243e230dd2SCorentin Chary                 break;
19253e230dd2SCorentin Chary             case 0xd1:
19263e230dd2SCorentin Chary                 kbd_put_keysym(QEMU_KEY_PAGEDOWN);
19273e230dd2SCorentin Chary                 break;
19283e230dd2SCorentin Chary 
19293e230dd2SCorentin Chary             case 0x47:
19303e230dd2SCorentin Chary                 kbd_put_keysym(numlock ? '7' : QEMU_KEY_HOME);
19313e230dd2SCorentin Chary                 break;
19323e230dd2SCorentin Chary             case 0x48:
19333e230dd2SCorentin Chary                 kbd_put_keysym(numlock ? '8' : QEMU_KEY_UP);
19343e230dd2SCorentin Chary                 break;
19353e230dd2SCorentin Chary             case 0x49:
19363e230dd2SCorentin Chary                 kbd_put_keysym(numlock ? '9' : QEMU_KEY_PAGEUP);
19373e230dd2SCorentin Chary                 break;
19383e230dd2SCorentin Chary             case 0x4b:
19393e230dd2SCorentin Chary                 kbd_put_keysym(numlock ? '4' : QEMU_KEY_LEFT);
19403e230dd2SCorentin Chary                 break;
19413e230dd2SCorentin Chary             case 0x4c:
19423e230dd2SCorentin Chary                 kbd_put_keysym('5');
19433e230dd2SCorentin Chary                 break;
19443e230dd2SCorentin Chary             case 0x4d:
19453e230dd2SCorentin Chary                 kbd_put_keysym(numlock ? '6' : QEMU_KEY_RIGHT);
19463e230dd2SCorentin Chary                 break;
19473e230dd2SCorentin Chary             case 0x4f:
19483e230dd2SCorentin Chary                 kbd_put_keysym(numlock ? '1' : QEMU_KEY_END);
19493e230dd2SCorentin Chary                 break;
19503e230dd2SCorentin Chary             case 0x50:
19513e230dd2SCorentin Chary                 kbd_put_keysym(numlock ? '2' : QEMU_KEY_DOWN);
19523e230dd2SCorentin Chary                 break;
19533e230dd2SCorentin Chary             case 0x51:
19543e230dd2SCorentin Chary                 kbd_put_keysym(numlock ? '3' : QEMU_KEY_PAGEDOWN);
19553e230dd2SCorentin Chary                 break;
19563e230dd2SCorentin Chary             case 0x52:
19573e230dd2SCorentin Chary                 kbd_put_keysym('0');
19583e230dd2SCorentin Chary                 break;
19593e230dd2SCorentin Chary             case 0x53:
19603e230dd2SCorentin Chary                 kbd_put_keysym(numlock ? '.' : QEMU_KEY_DELETE);
19613e230dd2SCorentin Chary                 break;
19623e230dd2SCorentin Chary 
19633e230dd2SCorentin Chary             case 0xb5:
19643e230dd2SCorentin Chary                 kbd_put_keysym('/');
19653e230dd2SCorentin Chary                 break;
19663e230dd2SCorentin Chary             case 0x37:
19673e230dd2SCorentin Chary                 kbd_put_keysym('*');
19683e230dd2SCorentin Chary                 break;
19693e230dd2SCorentin Chary             case 0x4a:
19703e230dd2SCorentin Chary                 kbd_put_keysym('-');
19713e230dd2SCorentin Chary                 break;
19723e230dd2SCorentin Chary             case 0x4e:
19733e230dd2SCorentin Chary                 kbd_put_keysym('+');
19743e230dd2SCorentin Chary                 break;
19753e230dd2SCorentin Chary             case 0x9c:
19763e230dd2SCorentin Chary                 kbd_put_keysym('\n');
19773e230dd2SCorentin Chary                 break;
19783e230dd2SCorentin Chary 
19793e230dd2SCorentin Chary             default:
1980e26437c2SGerd Hoffmann                 if (control) {
1981e26437c2SGerd Hoffmann                     kbd_put_keysym(sym & 0x1f);
1982e26437c2SGerd Hoffmann                 } else {
19833e230dd2SCorentin Chary                     kbd_put_keysym(sym);
1984e26437c2SGerd Hoffmann                 }
19853e230dd2SCorentin Chary                 break;
19863e230dd2SCorentin Chary             }
19873e230dd2SCorentin Chary         }
19883e230dd2SCorentin Chary     }
19893e230dd2SCorentin Chary }
19903e230dd2SCorentin Chary 
19917bc9318bSGerd Hoffmann static void vnc_release_modifiers(VncState *vs)
19927bc9318bSGerd Hoffmann {
19937bc9318bSGerd Hoffmann     static const int keycodes[] = {
19947bc9318bSGerd Hoffmann         /* shift, control, alt keys, both left & right */
19957bc9318bSGerd Hoffmann         0x2a, 0x36, 0x1d, 0x9d, 0x38, 0xb8,
19967bc9318bSGerd Hoffmann     };
19977bc9318bSGerd Hoffmann     int i, keycode;
19987bc9318bSGerd Hoffmann 
199981c0d5a6SGerd Hoffmann     if (!qemu_console_is_graphic(NULL)) {
20007bc9318bSGerd Hoffmann         return;
20017bc9318bSGerd Hoffmann     }
20027bc9318bSGerd Hoffmann     for (i = 0; i < ARRAY_SIZE(keycodes); i++) {
20037bc9318bSGerd Hoffmann         keycode = keycodes[i];
20047bc9318bSGerd Hoffmann         if (!vs->modifiers_state[keycode]) {
20057bc9318bSGerd Hoffmann             continue;
20067bc9318bSGerd Hoffmann         }
20078d447d10SGerd Hoffmann         qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, false);
20087bc9318bSGerd Hoffmann     }
20097bc9318bSGerd Hoffmann }
20107bc9318bSGerd Hoffmann 
201140066175SGerd Hoffmann static const char *code2name(int keycode)
201240066175SGerd Hoffmann {
201340066175SGerd Hoffmann     return QKeyCode_lookup[qemu_input_key_number_to_qcode(keycode)];
201440066175SGerd Hoffmann }
201540066175SGerd Hoffmann 
20163e230dd2SCorentin Chary static void key_event(VncState *vs, int down, uint32_t sym)
20173e230dd2SCorentin Chary {
20183e230dd2SCorentin Chary     int keycode;
20193e230dd2SCorentin Chary     int lsym = sym;
20203e230dd2SCorentin Chary 
202181c0d5a6SGerd Hoffmann     if (lsym >= 'A' && lsym <= 'Z' && qemu_console_is_graphic(NULL)) {
20223e230dd2SCorentin Chary         lsym = lsym - 'A' + 'a';
20233e230dd2SCorentin Chary     }
20243e230dd2SCorentin Chary 
20253e230dd2SCorentin Chary     keycode = keysym2scancode(vs->vd->kbd_layout, lsym & 0xFFFF) & SCANCODE_KEYMASK;
202640066175SGerd Hoffmann     trace_vnc_key_event_map(down, sym, keycode, code2name(keycode));
20273e230dd2SCorentin Chary     do_key_event(vs, down, keycode, sym);
20283e230dd2SCorentin Chary }
20293e230dd2SCorentin Chary 
20303e230dd2SCorentin Chary static void ext_key_event(VncState *vs, int down,
20313e230dd2SCorentin Chary                           uint32_t sym, uint16_t keycode)
20323e230dd2SCorentin Chary {
20333e230dd2SCorentin Chary     /* if the user specifies a keyboard layout, always use it */
203440066175SGerd Hoffmann     if (keyboard_layout) {
20353e230dd2SCorentin Chary         key_event(vs, down, sym);
203640066175SGerd Hoffmann     } else {
203740066175SGerd Hoffmann         trace_vnc_key_event_ext(down, sym, keycode, code2name(keycode));
20383e230dd2SCorentin Chary         do_key_event(vs, down, keycode, sym);
20393e230dd2SCorentin Chary     }
204040066175SGerd Hoffmann }
20413e230dd2SCorentin Chary 
20423e230dd2SCorentin Chary static void framebuffer_update_request(VncState *vs, int incremental,
2043bea60dd7SPeter Lieven                                        int x, int y, int w, int h)
20443e230dd2SCorentin Chary {
2045bea60dd7SPeter Lieven     int width = pixman_image_get_width(vs->vd->server);
2046bea60dd7SPeter Lieven     int height = pixman_image_get_height(vs->vd->server);
20473e230dd2SCorentin Chary 
20483e230dd2SCorentin Chary     vs->need_update = 1;
2049bea60dd7SPeter Lieven 
2050bea60dd7SPeter Lieven     if (incremental) {
2051bea60dd7SPeter Lieven         return;
20523e230dd2SCorentin Chary     }
2053bea60dd7SPeter Lieven 
205407535a89SStephan Kulow     vs->force_update = 1;
2055bea60dd7SPeter Lieven     vnc_set_area_dirty(vs->dirty, width, height, x, y, w, h);
20563e230dd2SCorentin Chary }
20573e230dd2SCorentin Chary 
20583e230dd2SCorentin Chary static void send_ext_key_event_ack(VncState *vs)
20593e230dd2SCorentin Chary {
2060bd023f95SCorentin Chary     vnc_lock_output(vs);
20613e230dd2SCorentin Chary     vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
20623e230dd2SCorentin Chary     vnc_write_u8(vs, 0);
20633e230dd2SCorentin Chary     vnc_write_u16(vs, 1);
2064d39fa6d8SGerd Hoffmann     vnc_framebuffer_update(vs, 0, 0,
2065bea60dd7SPeter Lieven                            pixman_image_get_width(vs->vd->server),
2066bea60dd7SPeter Lieven                            pixman_image_get_height(vs->vd->server),
20673e230dd2SCorentin Chary                            VNC_ENCODING_EXT_KEY_EVENT);
2068bd023f95SCorentin Chary     vnc_unlock_output(vs);
20693e230dd2SCorentin Chary     vnc_flush(vs);
20703e230dd2SCorentin Chary }
20713e230dd2SCorentin Chary 
20723e230dd2SCorentin Chary static void send_ext_audio_ack(VncState *vs)
20733e230dd2SCorentin Chary {
2074bd023f95SCorentin Chary     vnc_lock_output(vs);
20753e230dd2SCorentin Chary     vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
20763e230dd2SCorentin Chary     vnc_write_u8(vs, 0);
20773e230dd2SCorentin Chary     vnc_write_u16(vs, 1);
2078d39fa6d8SGerd Hoffmann     vnc_framebuffer_update(vs, 0, 0,
2079bea60dd7SPeter Lieven                            pixman_image_get_width(vs->vd->server),
2080bea60dd7SPeter Lieven                            pixman_image_get_height(vs->vd->server),
20813e230dd2SCorentin Chary                            VNC_ENCODING_AUDIO);
2082bd023f95SCorentin Chary     vnc_unlock_output(vs);
20833e230dd2SCorentin Chary     vnc_flush(vs);
20843e230dd2SCorentin Chary }
20853e230dd2SCorentin Chary 
20863e230dd2SCorentin Chary static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
20873e230dd2SCorentin Chary {
20883e230dd2SCorentin Chary     int i;
20893e230dd2SCorentin Chary     unsigned int enc = 0;
20903e230dd2SCorentin Chary 
20913e230dd2SCorentin Chary     vs->features = 0;
20923e230dd2SCorentin Chary     vs->vnc_encoding = 0;
2093d1af0e05SCorentin Chary     vs->tight.compression = 9;
2094d1af0e05SCorentin Chary     vs->tight.quality = -1; /* Lossless by default */
20953e230dd2SCorentin Chary     vs->absolute = -1;
20963e230dd2SCorentin Chary 
20973e230dd2SCorentin Chary     /*
20983e230dd2SCorentin Chary      * Start from the end because the encodings are sent in order of preference.
2099e5bed759SDong Xu Wang      * This way the preferred encoding (first encoding defined in the array)
21003e230dd2SCorentin Chary      * will be set at the end of the loop.
21013e230dd2SCorentin Chary      */
21023e230dd2SCorentin Chary     for (i = n_encodings - 1; i >= 0; i--) {
21033e230dd2SCorentin Chary         enc = encodings[i];
21043e230dd2SCorentin Chary         switch (enc) {
21053e230dd2SCorentin Chary         case VNC_ENCODING_RAW:
21063e230dd2SCorentin Chary             vs->vnc_encoding = enc;
21073e230dd2SCorentin Chary             break;
21083e230dd2SCorentin Chary         case VNC_ENCODING_COPYRECT:
21093e230dd2SCorentin Chary             vs->features |= VNC_FEATURE_COPYRECT_MASK;
21103e230dd2SCorentin Chary             break;
21113e230dd2SCorentin Chary         case VNC_ENCODING_HEXTILE:
21123e230dd2SCorentin Chary             vs->features |= VNC_FEATURE_HEXTILE_MASK;
21133e230dd2SCorentin Chary             vs->vnc_encoding = enc;
21143e230dd2SCorentin Chary             break;
21153e230dd2SCorentin Chary         case VNC_ENCODING_TIGHT:
21163e230dd2SCorentin Chary             vs->features |= VNC_FEATURE_TIGHT_MASK;
21173e230dd2SCorentin Chary             vs->vnc_encoding = enc;
21183e230dd2SCorentin Chary             break;
2119fe3e7f2dSJoel Martin #ifdef CONFIG_VNC_PNG
2120efe556adSCorentin Chary         case VNC_ENCODING_TIGHT_PNG:
2121efe556adSCorentin Chary             vs->features |= VNC_FEATURE_TIGHT_PNG_MASK;
2122efe556adSCorentin Chary             vs->vnc_encoding = enc;
2123efe556adSCorentin Chary             break;
2124fe3e7f2dSJoel Martin #endif
21253e230dd2SCorentin Chary         case VNC_ENCODING_ZLIB:
21263e230dd2SCorentin Chary             vs->features |= VNC_FEATURE_ZLIB_MASK;
21273e230dd2SCorentin Chary             vs->vnc_encoding = enc;
21283e230dd2SCorentin Chary             break;
2129148954faSCorentin Chary         case VNC_ENCODING_ZRLE:
2130148954faSCorentin Chary             vs->features |= VNC_FEATURE_ZRLE_MASK;
2131148954faSCorentin Chary             vs->vnc_encoding = enc;
2132148954faSCorentin Chary             break;
2133148954faSCorentin Chary         case VNC_ENCODING_ZYWRLE:
2134148954faSCorentin Chary             vs->features |= VNC_FEATURE_ZYWRLE_MASK;
2135148954faSCorentin Chary             vs->vnc_encoding = enc;
2136148954faSCorentin Chary             break;
21373e230dd2SCorentin Chary         case VNC_ENCODING_DESKTOPRESIZE:
21383e230dd2SCorentin Chary             vs->features |= VNC_FEATURE_RESIZE_MASK;
21393e230dd2SCorentin Chary             break;
21403e230dd2SCorentin Chary         case VNC_ENCODING_POINTER_TYPE_CHANGE:
21413e230dd2SCorentin Chary             vs->features |= VNC_FEATURE_POINTER_TYPE_CHANGE_MASK;
21423e230dd2SCorentin Chary             break;
21433e230dd2SCorentin Chary         case VNC_ENCODING_RICH_CURSOR:
21443e230dd2SCorentin Chary             vs->features |= VNC_FEATURE_RICH_CURSOR_MASK;
21453e230dd2SCorentin Chary             break;
21463e230dd2SCorentin Chary         case VNC_ENCODING_EXT_KEY_EVENT:
21473e230dd2SCorentin Chary             send_ext_key_event_ack(vs);
21483e230dd2SCorentin Chary             break;
21493e230dd2SCorentin Chary         case VNC_ENCODING_AUDIO:
21503e230dd2SCorentin Chary             send_ext_audio_ack(vs);
21513e230dd2SCorentin Chary             break;
21523e230dd2SCorentin Chary         case VNC_ENCODING_WMVi:
21533e230dd2SCorentin Chary             vs->features |= VNC_FEATURE_WMVI_MASK;
21543e230dd2SCorentin Chary             break;
2155ab99e5c1SLei Li         case VNC_ENCODING_LED_STATE:
2156ab99e5c1SLei Li             vs->features |= VNC_FEATURE_LED_STATE_MASK;
2157ab99e5c1SLei Li             break;
21583e230dd2SCorentin Chary         case VNC_ENCODING_COMPRESSLEVEL0 ... VNC_ENCODING_COMPRESSLEVEL0 + 9:
2159d1af0e05SCorentin Chary             vs->tight.compression = (enc & 0x0F);
21603e230dd2SCorentin Chary             break;
21613e230dd2SCorentin Chary         case VNC_ENCODING_QUALITYLEVEL0 ... VNC_ENCODING_QUALITYLEVEL0 + 9:
2162b31f519eSCorentin Chary             if (vs->vd->lossy) {
2163d1af0e05SCorentin Chary                 vs->tight.quality = (enc & 0x0F);
2164b31f519eSCorentin Chary             }
21653e230dd2SCorentin Chary             break;
21663e230dd2SCorentin Chary         default:
21673e230dd2SCorentin Chary             VNC_DEBUG("Unknown encoding: %d (0x%.8x): %d\n", i, enc, enc);
21683e230dd2SCorentin Chary             break;
21693e230dd2SCorentin Chary         }
21703e230dd2SCorentin Chary     }
21713e230dd2SCorentin Chary     vnc_desktop_resize(vs);
21729e8dd451SJan Kiszka     check_pointer_type_change(&vs->mouse_mode_notifier, NULL);
2173ab99e5c1SLei Li     vnc_led_state_change(vs);
21743e230dd2SCorentin Chary }
21753e230dd2SCorentin Chary 
21763e230dd2SCorentin Chary static void set_pixel_conversion(VncState *vs)
21773e230dd2SCorentin Chary {
21789f64916dSGerd Hoffmann     pixman_format_code_t fmt = qemu_pixman_get_format(&vs->client_pf);
21799f64916dSGerd Hoffmann 
21809f64916dSGerd Hoffmann     if (fmt == VNC_SERVER_FB_FORMAT) {
21813e230dd2SCorentin Chary         vs->write_pixels = vnc_write_pixels_copy;
21823e230dd2SCorentin Chary         vnc_hextile_set_pixel_conversion(vs, 0);
21833e230dd2SCorentin Chary     } else {
21843e230dd2SCorentin Chary         vs->write_pixels = vnc_write_pixels_generic;
21853e230dd2SCorentin Chary         vnc_hextile_set_pixel_conversion(vs, 1);
21863e230dd2SCorentin Chary     }
21873e230dd2SCorentin Chary }
21883e230dd2SCorentin Chary 
21893e230dd2SCorentin Chary static void set_pixel_format(VncState *vs,
21903e230dd2SCorentin Chary                              int bits_per_pixel, int depth,
21913e230dd2SCorentin Chary                              int big_endian_flag, int true_color_flag,
21923e230dd2SCorentin Chary                              int red_max, int green_max, int blue_max,
21933e230dd2SCorentin Chary                              int red_shift, int green_shift, int blue_shift)
21943e230dd2SCorentin Chary {
21953e230dd2SCorentin Chary     if (!true_color_flag) {
21963e230dd2SCorentin Chary         vnc_client_error(vs);
21973e230dd2SCorentin Chary         return;
21983e230dd2SCorentin Chary     }
21993e230dd2SCorentin Chary 
2200e6908bfeSPetr Matousek     switch (bits_per_pixel) {
2201e6908bfeSPetr Matousek     case 8:
2202e6908bfeSPetr Matousek     case 16:
2203e6908bfeSPetr Matousek     case 32:
2204e6908bfeSPetr Matousek         break;
2205e6908bfeSPetr Matousek     default:
2206e6908bfeSPetr Matousek         vnc_client_error(vs);
2207e6908bfeSPetr Matousek         return;
2208e6908bfeSPetr Matousek     }
2209e6908bfeSPetr Matousek 
22109f64916dSGerd Hoffmann     vs->client_pf.rmax = red_max;
22119f64916dSGerd Hoffmann     vs->client_pf.rbits = hweight_long(red_max);
22129f64916dSGerd Hoffmann     vs->client_pf.rshift = red_shift;
22139f64916dSGerd Hoffmann     vs->client_pf.rmask = red_max << red_shift;
22149f64916dSGerd Hoffmann     vs->client_pf.gmax = green_max;
22159f64916dSGerd Hoffmann     vs->client_pf.gbits = hweight_long(green_max);
22169f64916dSGerd Hoffmann     vs->client_pf.gshift = green_shift;
22179f64916dSGerd Hoffmann     vs->client_pf.gmask = green_max << green_shift;
22189f64916dSGerd Hoffmann     vs->client_pf.bmax = blue_max;
22199f64916dSGerd Hoffmann     vs->client_pf.bbits = hweight_long(blue_max);
22209f64916dSGerd Hoffmann     vs->client_pf.bshift = blue_shift;
22219f64916dSGerd Hoffmann     vs->client_pf.bmask = blue_max << blue_shift;
22229f64916dSGerd Hoffmann     vs->client_pf.bits_per_pixel = bits_per_pixel;
22239f64916dSGerd Hoffmann     vs->client_pf.bytes_per_pixel = bits_per_pixel / 8;
22249f64916dSGerd Hoffmann     vs->client_pf.depth = bits_per_pixel == 32 ? 24 : bits_per_pixel;
22259f64916dSGerd Hoffmann     vs->client_be = big_endian_flag;
22263e230dd2SCorentin Chary 
22273e230dd2SCorentin Chary     set_pixel_conversion(vs);
22283e230dd2SCorentin Chary 
22291d0d59feSGerd Hoffmann     graphic_hw_invalidate(vs->vd->dcl.con);
22301d0d59feSGerd Hoffmann     graphic_hw_update(vs->vd->dcl.con);
22313e230dd2SCorentin Chary }
22323e230dd2SCorentin Chary 
22333e230dd2SCorentin Chary static void pixel_format_message (VncState *vs) {
22343e230dd2SCorentin Chary     char pad[3] = { 0, 0, 0 };
22353e230dd2SCorentin Chary 
22369f64916dSGerd Hoffmann     vs->client_pf = qemu_default_pixelformat(32);
22379f64916dSGerd Hoffmann 
22389f64916dSGerd Hoffmann     vnc_write_u8(vs, vs->client_pf.bits_per_pixel); /* bits-per-pixel */
22399f64916dSGerd Hoffmann     vnc_write_u8(vs, vs->client_pf.depth); /* depth */
22403e230dd2SCorentin Chary 
22413e230dd2SCorentin Chary #ifdef HOST_WORDS_BIGENDIAN
22423e230dd2SCorentin Chary     vnc_write_u8(vs, 1);             /* big-endian-flag */
22433e230dd2SCorentin Chary #else
22443e230dd2SCorentin Chary     vnc_write_u8(vs, 0);             /* big-endian-flag */
22453e230dd2SCorentin Chary #endif
22463e230dd2SCorentin Chary     vnc_write_u8(vs, 1);             /* true-color-flag */
22479f64916dSGerd Hoffmann     vnc_write_u16(vs, vs->client_pf.rmax);     /* red-max */
22489f64916dSGerd Hoffmann     vnc_write_u16(vs, vs->client_pf.gmax);     /* green-max */
22499f64916dSGerd Hoffmann     vnc_write_u16(vs, vs->client_pf.bmax);     /* blue-max */
22509f64916dSGerd Hoffmann     vnc_write_u8(vs, vs->client_pf.rshift);    /* red-shift */
22519f64916dSGerd Hoffmann     vnc_write_u8(vs, vs->client_pf.gshift);    /* green-shift */
22529f64916dSGerd Hoffmann     vnc_write_u8(vs, vs->client_pf.bshift);    /* blue-shift */
22539f64916dSGerd Hoffmann     vnc_write(vs, pad, 3);           /* padding */
22543e230dd2SCorentin Chary 
22553e230dd2SCorentin Chary     vnc_hextile_set_pixel_conversion(vs, 0);
22563e230dd2SCorentin Chary     vs->write_pixels = vnc_write_pixels_copy;
22573e230dd2SCorentin Chary }
22583e230dd2SCorentin Chary 
22593e230dd2SCorentin Chary static void vnc_colordepth(VncState *vs)
22603e230dd2SCorentin Chary {
22613e230dd2SCorentin Chary     if (vnc_has_feature(vs, VNC_FEATURE_WMVI)) {
22623e230dd2SCorentin Chary         /* Sending a WMVi message to notify the client*/
2263bd023f95SCorentin Chary         vnc_lock_output(vs);
22643e230dd2SCorentin Chary         vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
22653e230dd2SCorentin Chary         vnc_write_u8(vs, 0);
22663e230dd2SCorentin Chary         vnc_write_u16(vs, 1); /* number of rects */
2267d39fa6d8SGerd Hoffmann         vnc_framebuffer_update(vs, 0, 0,
2268bea60dd7SPeter Lieven                                pixman_image_get_width(vs->vd->server),
2269bea60dd7SPeter Lieven                                pixman_image_get_height(vs->vd->server),
2270d39fa6d8SGerd Hoffmann                                VNC_ENCODING_WMVi);
22713e230dd2SCorentin Chary         pixel_format_message(vs);
2272bd023f95SCorentin Chary         vnc_unlock_output(vs);
22733e230dd2SCorentin Chary         vnc_flush(vs);
22743e230dd2SCorentin Chary     } else {
22753e230dd2SCorentin Chary         set_pixel_conversion(vs);
22763e230dd2SCorentin Chary     }
22773e230dd2SCorentin Chary }
22783e230dd2SCorentin Chary 
22793e230dd2SCorentin Chary static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
22803e230dd2SCorentin Chary {
22813e230dd2SCorentin Chary     int i;
22823e230dd2SCorentin Chary     uint16_t limit;
22833e230dd2SCorentin Chary     VncDisplay *vd = vs->vd;
22843e230dd2SCorentin Chary 
22853e230dd2SCorentin Chary     if (data[0] > 3) {
22860f7b2864SGerd Hoffmann         update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
22873e230dd2SCorentin Chary     }
22883e230dd2SCorentin Chary 
22893e230dd2SCorentin Chary     switch (data[0]) {
22903e230dd2SCorentin Chary     case VNC_MSG_CLIENT_SET_PIXEL_FORMAT:
22913e230dd2SCorentin Chary         if (len == 1)
22923e230dd2SCorentin Chary             return 20;
22933e230dd2SCorentin Chary 
22943e230dd2SCorentin Chary         set_pixel_format(vs, read_u8(data, 4), read_u8(data, 5),
22953e230dd2SCorentin Chary                          read_u8(data, 6), read_u8(data, 7),
22963e230dd2SCorentin Chary                          read_u16(data, 8), read_u16(data, 10),
22973e230dd2SCorentin Chary                          read_u16(data, 12), read_u8(data, 14),
22983e230dd2SCorentin Chary                          read_u8(data, 15), read_u8(data, 16));
22993e230dd2SCorentin Chary         break;
23003e230dd2SCorentin Chary     case VNC_MSG_CLIENT_SET_ENCODINGS:
23013e230dd2SCorentin Chary         if (len == 1)
23023e230dd2SCorentin Chary             return 4;
23033e230dd2SCorentin Chary 
23043e230dd2SCorentin Chary         if (len == 4) {
23053e230dd2SCorentin Chary             limit = read_u16(data, 2);
23063e230dd2SCorentin Chary             if (limit > 0)
23073e230dd2SCorentin Chary                 return 4 + (limit * 4);
23083e230dd2SCorentin Chary         } else
23093e230dd2SCorentin Chary             limit = read_u16(data, 2);
23103e230dd2SCorentin Chary 
23113e230dd2SCorentin Chary         for (i = 0; i < limit; i++) {
23123e230dd2SCorentin Chary             int32_t val = read_s32(data, 4 + (i * 4));
23133e230dd2SCorentin Chary             memcpy(data + 4 + (i * 4), &val, sizeof(val));
23143e230dd2SCorentin Chary         }
23153e230dd2SCorentin Chary 
23163e230dd2SCorentin Chary         set_encodings(vs, (int32_t *)(data + 4), limit);
23173e230dd2SCorentin Chary         break;
23183e230dd2SCorentin Chary     case VNC_MSG_CLIENT_FRAMEBUFFER_UPDATE_REQUEST:
23193e230dd2SCorentin Chary         if (len == 1)
23203e230dd2SCorentin Chary             return 10;
23213e230dd2SCorentin Chary 
23223e230dd2SCorentin Chary         framebuffer_update_request(vs,
23233e230dd2SCorentin Chary                                    read_u8(data, 1), read_u16(data, 2), read_u16(data, 4),
23243e230dd2SCorentin Chary                                    read_u16(data, 6), read_u16(data, 8));
23253e230dd2SCorentin Chary         break;
23263e230dd2SCorentin Chary     case VNC_MSG_CLIENT_KEY_EVENT:
23273e230dd2SCorentin Chary         if (len == 1)
23283e230dd2SCorentin Chary             return 8;
23293e230dd2SCorentin Chary 
23303e230dd2SCorentin Chary         key_event(vs, read_u8(data, 1), read_u32(data, 4));
23313e230dd2SCorentin Chary         break;
23323e230dd2SCorentin Chary     case VNC_MSG_CLIENT_POINTER_EVENT:
23333e230dd2SCorentin Chary         if (len == 1)
23343e230dd2SCorentin Chary             return 6;
23353e230dd2SCorentin Chary 
23363e230dd2SCorentin Chary         pointer_event(vs, read_u8(data, 1), read_u16(data, 2), read_u16(data, 4));
23373e230dd2SCorentin Chary         break;
23383e230dd2SCorentin Chary     case VNC_MSG_CLIENT_CUT_TEXT:
2339f9a70e79SPeter Lieven         if (len == 1) {
23403e230dd2SCorentin Chary             return 8;
2341f9a70e79SPeter Lieven         }
23423e230dd2SCorentin Chary         if (len == 8) {
23433e230dd2SCorentin Chary             uint32_t dlen = read_u32(data, 4);
2344f9a70e79SPeter Lieven             if (dlen > (1 << 20)) {
2345f9a70e79SPeter Lieven                 error_report("vnc: client_cut_text msg payload has %u bytes"
2346f9a70e79SPeter Lieven                              " which exceeds our limit of 1MB.", dlen);
2347f9a70e79SPeter Lieven                 vnc_client_error(vs);
2348f9a70e79SPeter Lieven                 break;
2349f9a70e79SPeter Lieven             }
2350f9a70e79SPeter Lieven             if (dlen > 0) {
23513e230dd2SCorentin Chary                 return 8 + dlen;
23523e230dd2SCorentin Chary             }
2353f9a70e79SPeter Lieven         }
23543e230dd2SCorentin Chary 
23553e230dd2SCorentin Chary         client_cut_text(vs, read_u32(data, 4), data + 8);
23563e230dd2SCorentin Chary         break;
23573e230dd2SCorentin Chary     case VNC_MSG_CLIENT_QEMU:
23583e230dd2SCorentin Chary         if (len == 1)
23593e230dd2SCorentin Chary             return 2;
23603e230dd2SCorentin Chary 
23613e230dd2SCorentin Chary         switch (read_u8(data, 1)) {
23623e230dd2SCorentin Chary         case VNC_MSG_CLIENT_QEMU_EXT_KEY_EVENT:
23633e230dd2SCorentin Chary             if (len == 2)
23643e230dd2SCorentin Chary                 return 12;
23653e230dd2SCorentin Chary 
23663e230dd2SCorentin Chary             ext_key_event(vs, read_u16(data, 2),
23673e230dd2SCorentin Chary                           read_u32(data, 4), read_u32(data, 8));
23683e230dd2SCorentin Chary             break;
23693e230dd2SCorentin Chary         case VNC_MSG_CLIENT_QEMU_AUDIO:
23703e230dd2SCorentin Chary             if (len == 2)
23713e230dd2SCorentin Chary                 return 4;
23723e230dd2SCorentin Chary 
23733e230dd2SCorentin Chary             switch (read_u16 (data, 2)) {
23743e230dd2SCorentin Chary             case VNC_MSG_CLIENT_QEMU_AUDIO_ENABLE:
23753e230dd2SCorentin Chary                 audio_add(vs);
23763e230dd2SCorentin Chary                 break;
23773e230dd2SCorentin Chary             case VNC_MSG_CLIENT_QEMU_AUDIO_DISABLE:
23783e230dd2SCorentin Chary                 audio_del(vs);
23793e230dd2SCorentin Chary                 break;
23803e230dd2SCorentin Chary             case VNC_MSG_CLIENT_QEMU_AUDIO_SET_FORMAT:
23813e230dd2SCorentin Chary                 if (len == 4)
23823e230dd2SCorentin Chary                     return 10;
23833e230dd2SCorentin Chary                 switch (read_u8(data, 4)) {
23843e230dd2SCorentin Chary                 case 0: vs->as.fmt = AUD_FMT_U8; break;
23853e230dd2SCorentin Chary                 case 1: vs->as.fmt = AUD_FMT_S8; break;
23863e230dd2SCorentin Chary                 case 2: vs->as.fmt = AUD_FMT_U16; break;
23873e230dd2SCorentin Chary                 case 3: vs->as.fmt = AUD_FMT_S16; break;
23883e230dd2SCorentin Chary                 case 4: vs->as.fmt = AUD_FMT_U32; break;
23893e230dd2SCorentin Chary                 case 5: vs->as.fmt = AUD_FMT_S32; break;
23903e230dd2SCorentin Chary                 default:
2391153130cdSDaniel P. Berrange                     VNC_DEBUG("Invalid audio format %d\n", read_u8(data, 4));
23923e230dd2SCorentin Chary                     vnc_client_error(vs);
23933e230dd2SCorentin Chary                     break;
23943e230dd2SCorentin Chary                 }
23953e230dd2SCorentin Chary                 vs->as.nchannels = read_u8(data, 5);
23963e230dd2SCorentin Chary                 if (vs->as.nchannels != 1 && vs->as.nchannels != 2) {
2397153130cdSDaniel P. Berrange                     VNC_DEBUG("Invalid audio channel coount %d\n",
23983e230dd2SCorentin Chary                               read_u8(data, 5));
23993e230dd2SCorentin Chary                     vnc_client_error(vs);
24003e230dd2SCorentin Chary                     break;
24013e230dd2SCorentin Chary                 }
24023e230dd2SCorentin Chary                 vs->as.freq = read_u32(data, 6);
24033e230dd2SCorentin Chary                 break;
24043e230dd2SCorentin Chary             default:
2405153130cdSDaniel P. Berrange                 VNC_DEBUG("Invalid audio message %d\n", read_u8(data, 4));
24063e230dd2SCorentin Chary                 vnc_client_error(vs);
24073e230dd2SCorentin Chary                 break;
24083e230dd2SCorentin Chary             }
24093e230dd2SCorentin Chary             break;
24103e230dd2SCorentin Chary 
24113e230dd2SCorentin Chary         default:
2412153130cdSDaniel P. Berrange             VNC_DEBUG("Msg: %d\n", read_u16(data, 0));
24133e230dd2SCorentin Chary             vnc_client_error(vs);
24143e230dd2SCorentin Chary             break;
24153e230dd2SCorentin Chary         }
24163e230dd2SCorentin Chary         break;
24173e230dd2SCorentin Chary     default:
2418153130cdSDaniel P. Berrange         VNC_DEBUG("Msg: %d\n", data[0]);
24193e230dd2SCorentin Chary         vnc_client_error(vs);
24203e230dd2SCorentin Chary         break;
24213e230dd2SCorentin Chary     }
24223e230dd2SCorentin Chary 
24233e230dd2SCorentin Chary     vnc_read_when(vs, protocol_client_msg, 1);
24243e230dd2SCorentin Chary     return 0;
24253e230dd2SCorentin Chary }
24263e230dd2SCorentin Chary 
24273e230dd2SCorentin Chary static int protocol_client_init(VncState *vs, uint8_t *data, size_t len)
24283e230dd2SCorentin Chary {
24293e230dd2SCorentin Chary     char buf[1024];
24308cf36489SGerd Hoffmann     VncShareMode mode;
24313e230dd2SCorentin Chary     int size;
24323e230dd2SCorentin Chary 
24338cf36489SGerd Hoffmann     mode = data[0] ? VNC_SHARE_MODE_SHARED : VNC_SHARE_MODE_EXCLUSIVE;
24348cf36489SGerd Hoffmann     switch (vs->vd->share_policy) {
24358cf36489SGerd Hoffmann     case VNC_SHARE_POLICY_IGNORE:
24368cf36489SGerd Hoffmann         /*
24378cf36489SGerd Hoffmann          * Ignore the shared flag.  Nothing to do here.
24388cf36489SGerd Hoffmann          *
24398cf36489SGerd Hoffmann          * Doesn't conform to the rfb spec but is traditional qemu
24408cf36489SGerd Hoffmann          * behavior, thus left here as option for compatibility
24418cf36489SGerd Hoffmann          * reasons.
24428cf36489SGerd Hoffmann          */
24438cf36489SGerd Hoffmann         break;
24448cf36489SGerd Hoffmann     case VNC_SHARE_POLICY_ALLOW_EXCLUSIVE:
24458cf36489SGerd Hoffmann         /*
24468cf36489SGerd Hoffmann          * Policy: Allow clients ask for exclusive access.
24478cf36489SGerd Hoffmann          *
24488cf36489SGerd Hoffmann          * Implementation: When a client asks for exclusive access,
24498cf36489SGerd Hoffmann          * disconnect all others. Shared connects are allowed as long
24508cf36489SGerd Hoffmann          * as no exclusive connection exists.
24518cf36489SGerd Hoffmann          *
24528cf36489SGerd Hoffmann          * This is how the rfb spec suggests to handle the shared flag.
24538cf36489SGerd Hoffmann          */
24548cf36489SGerd Hoffmann         if (mode == VNC_SHARE_MODE_EXCLUSIVE) {
24558cf36489SGerd Hoffmann             VncState *client;
24568cf36489SGerd Hoffmann             QTAILQ_FOREACH(client, &vs->vd->clients, next) {
24578cf36489SGerd Hoffmann                 if (vs == client) {
24588cf36489SGerd Hoffmann                     continue;
24598cf36489SGerd Hoffmann                 }
24608cf36489SGerd Hoffmann                 if (client->share_mode != VNC_SHARE_MODE_EXCLUSIVE &&
24618cf36489SGerd Hoffmann                     client->share_mode != VNC_SHARE_MODE_SHARED) {
24628cf36489SGerd Hoffmann                     continue;
24638cf36489SGerd Hoffmann                 }
24648cf36489SGerd Hoffmann                 vnc_disconnect_start(client);
24658cf36489SGerd Hoffmann             }
24668cf36489SGerd Hoffmann         }
24678cf36489SGerd Hoffmann         if (mode == VNC_SHARE_MODE_SHARED) {
24688cf36489SGerd Hoffmann             if (vs->vd->num_exclusive > 0) {
24698cf36489SGerd Hoffmann                 vnc_disconnect_start(vs);
24708cf36489SGerd Hoffmann                 return 0;
24718cf36489SGerd Hoffmann             }
24728cf36489SGerd Hoffmann         }
24738cf36489SGerd Hoffmann         break;
24748cf36489SGerd Hoffmann     case VNC_SHARE_POLICY_FORCE_SHARED:
24758cf36489SGerd Hoffmann         /*
24768cf36489SGerd Hoffmann          * Policy: Shared connects only.
24778cf36489SGerd Hoffmann          * Implementation: Disallow clients asking for exclusive access.
24788cf36489SGerd Hoffmann          *
24798cf36489SGerd Hoffmann          * Useful for shared desktop sessions where you don't want
24808cf36489SGerd Hoffmann          * someone forgetting to say -shared when running the vnc
24818cf36489SGerd Hoffmann          * client disconnect everybody else.
24828cf36489SGerd Hoffmann          */
24838cf36489SGerd Hoffmann         if (mode == VNC_SHARE_MODE_EXCLUSIVE) {
24848cf36489SGerd Hoffmann             vnc_disconnect_start(vs);
24858cf36489SGerd Hoffmann             return 0;
24868cf36489SGerd Hoffmann         }
24878cf36489SGerd Hoffmann         break;
24888cf36489SGerd Hoffmann     }
24898cf36489SGerd Hoffmann     vnc_set_share_mode(vs, mode);
24908cf36489SGerd Hoffmann 
2491e5f34cddSGerd Hoffmann     if (vs->vd->num_shared > vs->vd->connections_limit) {
2492e5f34cddSGerd Hoffmann         vnc_disconnect_start(vs);
2493e5f34cddSGerd Hoffmann         return 0;
2494e5f34cddSGerd Hoffmann     }
2495e5f34cddSGerd Hoffmann 
2496bea60dd7SPeter Lieven     vs->client_width = pixman_image_get_width(vs->vd->server);
2497bea60dd7SPeter Lieven     vs->client_height = pixman_image_get_height(vs->vd->server);
24983e230dd2SCorentin Chary     vnc_write_u16(vs, vs->client_width);
24993e230dd2SCorentin Chary     vnc_write_u16(vs, vs->client_height);
25003e230dd2SCorentin Chary 
25013e230dd2SCorentin Chary     pixel_format_message(vs);
25023e230dd2SCorentin Chary 
25033e230dd2SCorentin Chary     if (qemu_name)
25043e230dd2SCorentin Chary         size = snprintf(buf, sizeof(buf), "QEMU (%s)", qemu_name);
25053e230dd2SCorentin Chary     else
25063e230dd2SCorentin Chary         size = snprintf(buf, sizeof(buf), "QEMU");
25073e230dd2SCorentin Chary 
25083e230dd2SCorentin Chary     vnc_write_u32(vs, size);
25093e230dd2SCorentin Chary     vnc_write(vs, buf, size);
25103e230dd2SCorentin Chary     vnc_flush(vs);
25113e230dd2SCorentin Chary 
25123e230dd2SCorentin Chary     vnc_client_cache_auth(vs);
2513fb6ba0d5SWenchao Xia     vnc_qmp_event(vs, QAPI_EVENT_VNC_INITIALIZED);
25143e230dd2SCorentin Chary 
25153e230dd2SCorentin Chary     vnc_read_when(vs, protocol_client_msg, 1);
25163e230dd2SCorentin Chary 
25173e230dd2SCorentin Chary     return 0;
25183e230dd2SCorentin Chary }
25193e230dd2SCorentin Chary 
25203e230dd2SCorentin Chary void start_client_init(VncState *vs)
25213e230dd2SCorentin Chary {
25223e230dd2SCorentin Chary     vnc_read_when(vs, protocol_client_init, 1);
25233e230dd2SCorentin Chary }
25243e230dd2SCorentin Chary 
25253e230dd2SCorentin Chary static void make_challenge(VncState *vs)
25263e230dd2SCorentin Chary {
25273e230dd2SCorentin Chary     int i;
25283e230dd2SCorentin Chary 
25293e230dd2SCorentin Chary     srand(time(NULL)+getpid()+getpid()*987654+rand());
25303e230dd2SCorentin Chary 
25313e230dd2SCorentin Chary     for (i = 0 ; i < sizeof(vs->challenge) ; i++)
25323e230dd2SCorentin Chary         vs->challenge[i] = (int) (256.0*rand()/(RAND_MAX+1.0));
25333e230dd2SCorentin Chary }
25343e230dd2SCorentin Chary 
25353e230dd2SCorentin Chary static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len)
25363e230dd2SCorentin Chary {
25373e230dd2SCorentin Chary     unsigned char response[VNC_AUTH_CHALLENGE_SIZE];
25383e230dd2SCorentin Chary     int i, j, pwlen;
25393e230dd2SCorentin Chary     unsigned char key[8];
25403c9405a0SGerd Hoffmann     time_t now = time(NULL);
25413e230dd2SCorentin Chary 
25421cd20f8bSAnthony Liguori     if (!vs->vd->password) {
25433e230dd2SCorentin Chary         VNC_DEBUG("No password configured on server");
25446bffdf0fSGerd Hoffmann         goto reject;
25453e230dd2SCorentin Chary     }
25463c9405a0SGerd Hoffmann     if (vs->vd->expires < now) {
25473c9405a0SGerd Hoffmann         VNC_DEBUG("Password is expired");
25483c9405a0SGerd Hoffmann         goto reject;
25493c9405a0SGerd Hoffmann     }
25503e230dd2SCorentin Chary 
25513e230dd2SCorentin Chary     memcpy(response, vs->challenge, VNC_AUTH_CHALLENGE_SIZE);
25523e230dd2SCorentin Chary 
25533e230dd2SCorentin Chary     /* Calculate the expected challenge response */
25543e230dd2SCorentin Chary     pwlen = strlen(vs->vd->password);
25553e230dd2SCorentin Chary     for (i=0; i<sizeof(key); i++)
25563e230dd2SCorentin Chary         key[i] = i<pwlen ? vs->vd->password[i] : 0;
25573e230dd2SCorentin Chary     deskey(key, EN0);
25583e230dd2SCorentin Chary     for (j = 0; j < VNC_AUTH_CHALLENGE_SIZE; j += 8)
25593e230dd2SCorentin Chary         des(response+j, response+j);
25603e230dd2SCorentin Chary 
25613e230dd2SCorentin Chary     /* Compare expected vs actual challenge response */
25623e230dd2SCorentin Chary     if (memcmp(response, data, VNC_AUTH_CHALLENGE_SIZE) != 0) {
2563e5bed759SDong Xu Wang         VNC_DEBUG("Client challenge response did not match\n");
25646bffdf0fSGerd Hoffmann         goto reject;
25656bffdf0fSGerd Hoffmann     } else {
25666bffdf0fSGerd Hoffmann         VNC_DEBUG("Accepting VNC challenge response\n");
25676bffdf0fSGerd Hoffmann         vnc_write_u32(vs, 0); /* Accept auth */
25686bffdf0fSGerd Hoffmann         vnc_flush(vs);
25696bffdf0fSGerd Hoffmann 
25706bffdf0fSGerd Hoffmann         start_client_init(vs);
25716bffdf0fSGerd Hoffmann     }
25726bffdf0fSGerd Hoffmann     return 0;
25736bffdf0fSGerd Hoffmann 
25746bffdf0fSGerd Hoffmann reject:
25753e230dd2SCorentin Chary     vnc_write_u32(vs, 1); /* Reject auth */
25763e230dd2SCorentin Chary     if (vs->minor >= 8) {
25773e230dd2SCorentin Chary         static const char err[] = "Authentication failed";
25783e230dd2SCorentin Chary         vnc_write_u32(vs, sizeof(err));
25793e230dd2SCorentin Chary         vnc_write(vs, err, sizeof(err));
25803e230dd2SCorentin Chary     }
25813e230dd2SCorentin Chary     vnc_flush(vs);
25823e230dd2SCorentin Chary     vnc_client_error(vs);
25833e230dd2SCorentin Chary     return 0;
25843e230dd2SCorentin Chary }
25853e230dd2SCorentin Chary 
25863e230dd2SCorentin Chary void start_auth_vnc(VncState *vs)
25873e230dd2SCorentin Chary {
25883e230dd2SCorentin Chary     make_challenge(vs);
25893e230dd2SCorentin Chary     /* Send client a 'random' challenge */
25903e230dd2SCorentin Chary     vnc_write(vs, vs->challenge, sizeof(vs->challenge));
25913e230dd2SCorentin Chary     vnc_flush(vs);
25923e230dd2SCorentin Chary 
25933e230dd2SCorentin Chary     vnc_read_when(vs, protocol_client_auth_vnc, sizeof(vs->challenge));
25943e230dd2SCorentin Chary }
25953e230dd2SCorentin Chary 
25963e230dd2SCorentin Chary 
25973e230dd2SCorentin Chary static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len)
25983e230dd2SCorentin Chary {
25993e230dd2SCorentin Chary     /* We only advertise 1 auth scheme at a time, so client
26003e230dd2SCorentin Chary      * must pick the one we sent. Verify this */
26017e7e2ebcSDaniel P. Berrange     if (data[0] != vs->auth) { /* Reject auth */
26023e230dd2SCorentin Chary        VNC_DEBUG("Reject auth %d because it didn't match advertized\n", (int)data[0]);
26033e230dd2SCorentin Chary        vnc_write_u32(vs, 1);
26043e230dd2SCorentin Chary        if (vs->minor >= 8) {
26053e230dd2SCorentin Chary            static const char err[] = "Authentication failed";
26063e230dd2SCorentin Chary            vnc_write_u32(vs, sizeof(err));
26073e230dd2SCorentin Chary            vnc_write(vs, err, sizeof(err));
26083e230dd2SCorentin Chary        }
26093e230dd2SCorentin Chary        vnc_client_error(vs);
26103e230dd2SCorentin Chary     } else { /* Accept requested auth */
26113e230dd2SCorentin Chary        VNC_DEBUG("Client requested auth %d\n", (int)data[0]);
26127e7e2ebcSDaniel P. Berrange        switch (vs->auth) {
26133e230dd2SCorentin Chary        case VNC_AUTH_NONE:
26143e230dd2SCorentin Chary            VNC_DEBUG("Accept auth none\n");
26153e230dd2SCorentin Chary            if (vs->minor >= 8) {
26163e230dd2SCorentin Chary                vnc_write_u32(vs, 0); /* Accept auth completion */
26173e230dd2SCorentin Chary                vnc_flush(vs);
26183e230dd2SCorentin Chary            }
26193e230dd2SCorentin Chary            start_client_init(vs);
26203e230dd2SCorentin Chary            break;
26213e230dd2SCorentin Chary 
26223e230dd2SCorentin Chary        case VNC_AUTH_VNC:
26233e230dd2SCorentin Chary            VNC_DEBUG("Start VNC auth\n");
26243e230dd2SCorentin Chary            start_auth_vnc(vs);
26253e230dd2SCorentin Chary            break;
26263e230dd2SCorentin Chary 
26273e230dd2SCorentin Chary #ifdef CONFIG_VNC_TLS
26283e230dd2SCorentin Chary        case VNC_AUTH_VENCRYPT:
26293a93113aSDong Xu Wang            VNC_DEBUG("Accept VeNCrypt auth\n");
26303e230dd2SCorentin Chary            start_auth_vencrypt(vs);
26313e230dd2SCorentin Chary            break;
26323e230dd2SCorentin Chary #endif /* CONFIG_VNC_TLS */
26333e230dd2SCorentin Chary 
26343e230dd2SCorentin Chary #ifdef CONFIG_VNC_SASL
26353e230dd2SCorentin Chary        case VNC_AUTH_SASL:
26363e230dd2SCorentin Chary            VNC_DEBUG("Accept SASL auth\n");
26373e230dd2SCorentin Chary            start_auth_sasl(vs);
26383e230dd2SCorentin Chary            break;
26393e230dd2SCorentin Chary #endif /* CONFIG_VNC_SASL */
26403e230dd2SCorentin Chary 
26413e230dd2SCorentin Chary        default: /* Should not be possible, but just in case */
26427e7e2ebcSDaniel P. Berrange            VNC_DEBUG("Reject auth %d server code bug\n", vs->auth);
26433e230dd2SCorentin Chary            vnc_write_u8(vs, 1);
26443e230dd2SCorentin Chary            if (vs->minor >= 8) {
26453e230dd2SCorentin Chary                static const char err[] = "Authentication failed";
26463e230dd2SCorentin Chary                vnc_write_u32(vs, sizeof(err));
26473e230dd2SCorentin Chary                vnc_write(vs, err, sizeof(err));
26483e230dd2SCorentin Chary            }
26493e230dd2SCorentin Chary            vnc_client_error(vs);
26503e230dd2SCorentin Chary        }
26513e230dd2SCorentin Chary     }
26523e230dd2SCorentin Chary     return 0;
26533e230dd2SCorentin Chary }
26543e230dd2SCorentin Chary 
26553e230dd2SCorentin Chary static int protocol_version(VncState *vs, uint8_t *version, size_t len)
26563e230dd2SCorentin Chary {
26573e230dd2SCorentin Chary     char local[13];
26583e230dd2SCorentin Chary 
26593e230dd2SCorentin Chary     memcpy(local, version, 12);
26603e230dd2SCorentin Chary     local[12] = 0;
26613e230dd2SCorentin Chary 
26623e230dd2SCorentin Chary     if (sscanf(local, "RFB %03d.%03d\n", &vs->major, &vs->minor) != 2) {
26633e230dd2SCorentin Chary         VNC_DEBUG("Malformed protocol version %s\n", local);
26643e230dd2SCorentin Chary         vnc_client_error(vs);
26653e230dd2SCorentin Chary         return 0;
26663e230dd2SCorentin Chary     }
26673e230dd2SCorentin Chary     VNC_DEBUG("Client request protocol version %d.%d\n", vs->major, vs->minor);
26683e230dd2SCorentin Chary     if (vs->major != 3 ||
26693e230dd2SCorentin Chary         (vs->minor != 3 &&
26703e230dd2SCorentin Chary          vs->minor != 4 &&
26713e230dd2SCorentin Chary          vs->minor != 5 &&
26723e230dd2SCorentin Chary          vs->minor != 7 &&
26733e230dd2SCorentin Chary          vs->minor != 8)) {
26743e230dd2SCorentin Chary         VNC_DEBUG("Unsupported client version\n");
26753e230dd2SCorentin Chary         vnc_write_u32(vs, VNC_AUTH_INVALID);
26763e230dd2SCorentin Chary         vnc_flush(vs);
26773e230dd2SCorentin Chary         vnc_client_error(vs);
26783e230dd2SCorentin Chary         return 0;
26793e230dd2SCorentin Chary     }
26803e230dd2SCorentin Chary     /* Some broken clients report v3.4 or v3.5, which spec requires to be treated
26813e230dd2SCorentin Chary      * as equivalent to v3.3 by servers
26823e230dd2SCorentin Chary      */
26833e230dd2SCorentin Chary     if (vs->minor == 4 || vs->minor == 5)
26843e230dd2SCorentin Chary         vs->minor = 3;
26853e230dd2SCorentin Chary 
26863e230dd2SCorentin Chary     if (vs->minor == 3) {
26877e7e2ebcSDaniel P. Berrange         if (vs->auth == VNC_AUTH_NONE) {
26883e230dd2SCorentin Chary             VNC_DEBUG("Tell client auth none\n");
26897e7e2ebcSDaniel P. Berrange             vnc_write_u32(vs, vs->auth);
26903e230dd2SCorentin Chary             vnc_flush(vs);
26913e230dd2SCorentin Chary             start_client_init(vs);
26927e7e2ebcSDaniel P. Berrange        } else if (vs->auth == VNC_AUTH_VNC) {
26933e230dd2SCorentin Chary             VNC_DEBUG("Tell client VNC auth\n");
26947e7e2ebcSDaniel P. Berrange             vnc_write_u32(vs, vs->auth);
26953e230dd2SCorentin Chary             vnc_flush(vs);
26963e230dd2SCorentin Chary             start_auth_vnc(vs);
26973e230dd2SCorentin Chary        } else {
26987e7e2ebcSDaniel P. Berrange             VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->auth);
26993e230dd2SCorentin Chary             vnc_write_u32(vs, VNC_AUTH_INVALID);
27003e230dd2SCorentin Chary             vnc_flush(vs);
27013e230dd2SCorentin Chary             vnc_client_error(vs);
27023e230dd2SCorentin Chary        }
27033e230dd2SCorentin Chary     } else {
27047e7e2ebcSDaniel P. Berrange         VNC_DEBUG("Telling client we support auth %d\n", vs->auth);
27053e230dd2SCorentin Chary         vnc_write_u8(vs, 1); /* num auth */
27067e7e2ebcSDaniel P. Berrange         vnc_write_u8(vs, vs->auth);
27073e230dd2SCorentin Chary         vnc_read_when(vs, protocol_client_auth, 1);
27083e230dd2SCorentin Chary         vnc_flush(vs);
27093e230dd2SCorentin Chary     }
27103e230dd2SCorentin Chary 
27113e230dd2SCorentin Chary     return 0;
27123e230dd2SCorentin Chary }
27133e230dd2SCorentin Chary 
2714999342a0SCorentin Chary static VncRectStat *vnc_stat_rect(VncDisplay *vd, int x, int y)
2715999342a0SCorentin Chary {
2716999342a0SCorentin Chary     struct VncSurface *vs = &vd->guest;
2717999342a0SCorentin Chary 
2718999342a0SCorentin Chary     return &vs->stats[y / VNC_STAT_RECT][x / VNC_STAT_RECT];
2719999342a0SCorentin Chary }
2720999342a0SCorentin Chary 
27217d964c9dSCorentin Chary void vnc_sent_lossy_rect(VncState *vs, int x, int y, int w, int h)
27227d964c9dSCorentin Chary {
27237d964c9dSCorentin Chary     int i, j;
27247d964c9dSCorentin Chary 
27257d964c9dSCorentin Chary     w = (x + w) / VNC_STAT_RECT;
27267d964c9dSCorentin Chary     h = (y + h) / VNC_STAT_RECT;
27277d964c9dSCorentin Chary     x /= VNC_STAT_RECT;
27287d964c9dSCorentin Chary     y /= VNC_STAT_RECT;
27297d964c9dSCorentin Chary 
2730207f328aSCorentin Chary     for (j = y; j <= h; j++) {
2731207f328aSCorentin Chary         for (i = x; i <= w; i++) {
27327d964c9dSCorentin Chary             vs->lossy_rect[j][i] = 1;
27337d964c9dSCorentin Chary         }
27347d964c9dSCorentin Chary     }
27357d964c9dSCorentin Chary }
27367d964c9dSCorentin Chary 
27377d964c9dSCorentin Chary static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y)
27387d964c9dSCorentin Chary {
27397d964c9dSCorentin Chary     VncState *vs;
27407d964c9dSCorentin Chary     int sty = y / VNC_STAT_RECT;
27417d964c9dSCorentin Chary     int stx = x / VNC_STAT_RECT;
27427d964c9dSCorentin Chary     int has_dirty = 0;
27437d964c9dSCorentin Chary 
27447d964c9dSCorentin Chary     y = y / VNC_STAT_RECT * VNC_STAT_RECT;
27457d964c9dSCorentin Chary     x = x / VNC_STAT_RECT * VNC_STAT_RECT;
27467d964c9dSCorentin Chary 
27477d964c9dSCorentin Chary     QTAILQ_FOREACH(vs, &vd->clients, next) {
2748bc2429b9SCorentin Chary         int j;
27497d964c9dSCorentin Chary 
27507d964c9dSCorentin Chary         /* kernel send buffers are full -> refresh later */
27517d964c9dSCorentin Chary         if (vs->output.offset) {
27527d964c9dSCorentin Chary             continue;
27537d964c9dSCorentin Chary         }
27547d964c9dSCorentin Chary 
27557d964c9dSCorentin Chary         if (!vs->lossy_rect[sty][stx]) {
27567d964c9dSCorentin Chary             continue;
27577d964c9dSCorentin Chary         }
2758207f328aSCorentin Chary 
27597d964c9dSCorentin Chary         vs->lossy_rect[sty][stx] = 0;
27607d964c9dSCorentin Chary         for (j = 0; j < VNC_STAT_RECT; ++j) {
2761b4c85ddcSPeter Lieven             bitmap_set(vs->dirty[y + j],
2762b4c85ddcSPeter Lieven                        x / VNC_DIRTY_PIXELS_PER_BIT,
2763b4c85ddcSPeter Lieven                        VNC_STAT_RECT / VNC_DIRTY_PIXELS_PER_BIT);
27647d964c9dSCorentin Chary         }
27657d964c9dSCorentin Chary         has_dirty++;
27667d964c9dSCorentin Chary     }
2767207f328aSCorentin Chary 
27687d964c9dSCorentin Chary     return has_dirty;
27697d964c9dSCorentin Chary }
27707d964c9dSCorentin Chary 
27717d964c9dSCorentin Chary static int vnc_update_stats(VncDisplay *vd,  struct timeval * tv)
2772999342a0SCorentin Chary {
27739f64916dSGerd Hoffmann     int width = pixman_image_get_width(vd->guest.fb);
27749f64916dSGerd Hoffmann     int height = pixman_image_get_height(vd->guest.fb);
2775999342a0SCorentin Chary     int x, y;
2776999342a0SCorentin Chary     struct timeval res;
27777d964c9dSCorentin Chary     int has_dirty = 0;
2778999342a0SCorentin Chary 
27799f64916dSGerd Hoffmann     for (y = 0; y < height; y += VNC_STAT_RECT) {
27809f64916dSGerd Hoffmann         for (x = 0; x < width; x += VNC_STAT_RECT) {
2781999342a0SCorentin Chary             VncRectStat *rect = vnc_stat_rect(vd, x, y);
2782999342a0SCorentin Chary 
2783999342a0SCorentin Chary             rect->updated = false;
2784999342a0SCorentin Chary         }
2785999342a0SCorentin Chary     }
2786999342a0SCorentin Chary 
2787ad620c29SBlue Swirl     qemu_timersub(tv, &VNC_REFRESH_STATS, &res);
2788999342a0SCorentin Chary 
2789999342a0SCorentin Chary     if (timercmp(&vd->guest.last_freq_check, &res, >)) {
27907d964c9dSCorentin Chary         return has_dirty;
2791999342a0SCorentin Chary     }
2792999342a0SCorentin Chary     vd->guest.last_freq_check = *tv;
2793999342a0SCorentin Chary 
27949f64916dSGerd Hoffmann     for (y = 0; y < height; y += VNC_STAT_RECT) {
27959f64916dSGerd Hoffmann         for (x = 0; x < width; x += VNC_STAT_RECT) {
2796999342a0SCorentin Chary             VncRectStat *rect= vnc_stat_rect(vd, x, y);
2797999342a0SCorentin Chary             int count = ARRAY_SIZE(rect->times);
2798999342a0SCorentin Chary             struct timeval min, max;
2799999342a0SCorentin Chary 
2800999342a0SCorentin Chary             if (!timerisset(&rect->times[count - 1])) {
2801999342a0SCorentin Chary                 continue ;
2802999342a0SCorentin Chary             }
2803999342a0SCorentin Chary 
2804999342a0SCorentin Chary             max = rect->times[(rect->idx + count - 1) % count];
2805ad620c29SBlue Swirl             qemu_timersub(tv, &max, &res);
2806999342a0SCorentin Chary 
2807999342a0SCorentin Chary             if (timercmp(&res, &VNC_REFRESH_LOSSY, >)) {
2808999342a0SCorentin Chary                 rect->freq = 0;
28097d964c9dSCorentin Chary                 has_dirty += vnc_refresh_lossy_rect(vd, x, y);
2810999342a0SCorentin Chary                 memset(rect->times, 0, sizeof (rect->times));
2811999342a0SCorentin Chary                 continue ;
2812999342a0SCorentin Chary             }
2813999342a0SCorentin Chary 
2814999342a0SCorentin Chary             min = rect->times[rect->idx];
2815999342a0SCorentin Chary             max = rect->times[(rect->idx + count - 1) % count];
2816ad620c29SBlue Swirl             qemu_timersub(&max, &min, &res);
2817999342a0SCorentin Chary 
2818999342a0SCorentin Chary             rect->freq = res.tv_sec + res.tv_usec / 1000000.;
2819999342a0SCorentin Chary             rect->freq /= count;
2820999342a0SCorentin Chary             rect->freq = 1. / rect->freq;
2821999342a0SCorentin Chary         }
2822999342a0SCorentin Chary     }
28237d964c9dSCorentin Chary     return has_dirty;
2824999342a0SCorentin Chary }
2825999342a0SCorentin Chary 
2826999342a0SCorentin Chary double vnc_update_freq(VncState *vs, int x, int y, int w, int h)
2827999342a0SCorentin Chary {
2828999342a0SCorentin Chary     int i, j;
2829999342a0SCorentin Chary     double total = 0;
2830999342a0SCorentin Chary     int num = 0;
2831999342a0SCorentin Chary 
2832999342a0SCorentin Chary     x =  (x / VNC_STAT_RECT) * VNC_STAT_RECT;
2833999342a0SCorentin Chary     y =  (y / VNC_STAT_RECT) * VNC_STAT_RECT;
2834999342a0SCorentin Chary 
2835999342a0SCorentin Chary     for (j = y; j <= y + h; j += VNC_STAT_RECT) {
2836999342a0SCorentin Chary         for (i = x; i <= x + w; i += VNC_STAT_RECT) {
2837999342a0SCorentin Chary             total += vnc_stat_rect(vs->vd, i, j)->freq;
2838999342a0SCorentin Chary             num++;
2839999342a0SCorentin Chary         }
2840999342a0SCorentin Chary     }
2841999342a0SCorentin Chary 
2842999342a0SCorentin Chary     if (num) {
2843999342a0SCorentin Chary         return total / num;
2844999342a0SCorentin Chary     } else {
2845999342a0SCorentin Chary         return 0;
2846999342a0SCorentin Chary     }
2847999342a0SCorentin Chary }
2848999342a0SCorentin Chary 
2849999342a0SCorentin Chary static void vnc_rect_updated(VncDisplay *vd, int x, int y, struct timeval * tv)
2850999342a0SCorentin Chary {
2851999342a0SCorentin Chary     VncRectStat *rect;
2852999342a0SCorentin Chary 
2853999342a0SCorentin Chary     rect = vnc_stat_rect(vd, x, y);
2854999342a0SCorentin Chary     if (rect->updated) {
2855999342a0SCorentin Chary         return ;
2856999342a0SCorentin Chary     }
2857999342a0SCorentin Chary     rect->times[rect->idx] = *tv;
2858999342a0SCorentin Chary     rect->idx = (rect->idx + 1) % ARRAY_SIZE(rect->times);
2859999342a0SCorentin Chary     rect->updated = true;
2860999342a0SCorentin Chary }
2861999342a0SCorentin Chary 
28623e230dd2SCorentin Chary static int vnc_refresh_server_surface(VncDisplay *vd)
28633e230dd2SCorentin Chary {
2864bea60dd7SPeter Lieven     int width = MIN(pixman_image_get_width(vd->guest.fb),
2865bea60dd7SPeter Lieven                     pixman_image_get_width(vd->server));
2866bea60dd7SPeter Lieven     int height = MIN(pixman_image_get_height(vd->guest.fb),
2867bea60dd7SPeter Lieven                      pixman_image_get_height(vd->server));
2868bea60dd7SPeter Lieven     int cmp_bytes, server_stride, min_stride, guest_stride, y = 0;
286912b316d4SPeter Lieven     uint8_t *guest_row0 = NULL, *server_row0;
28703e230dd2SCorentin Chary     VncState *vs;
28713e230dd2SCorentin Chary     int has_dirty = 0;
28729f64916dSGerd Hoffmann     pixman_image_t *tmpbuf = NULL;
28733e230dd2SCorentin Chary 
287480e0c8c3SCorentin Chary     struct timeval tv = { 0, 0 };
2875999342a0SCorentin Chary 
287680e0c8c3SCorentin Chary     if (!vd->non_adaptive) {
2877999342a0SCorentin Chary         gettimeofday(&tv, NULL);
28787d964c9dSCorentin Chary         has_dirty = vnc_update_stats(vd, &tv);
287980e0c8c3SCorentin Chary     }
2880999342a0SCorentin Chary 
28813e230dd2SCorentin Chary     /*
28823e230dd2SCorentin Chary      * Walk through the guest dirty map.
28833e230dd2SCorentin Chary      * Check and copy modified bits from guest to server surface.
28843e230dd2SCorentin Chary      * Update server dirty map.
28853e230dd2SCorentin Chary      */
2886bea60dd7SPeter Lieven     server_row0 = (uint8_t *)pixman_image_get_data(vd->server);
2887bea60dd7SPeter Lieven     server_stride = guest_stride = pixman_image_get_stride(vd->server);
2888bea60dd7SPeter Lieven     cmp_bytes = MIN(VNC_DIRTY_PIXELS_PER_BIT * VNC_SERVER_FB_BYTES,
2889bea60dd7SPeter Lieven                     server_stride);
28909f64916dSGerd Hoffmann     if (vd->guest.format != VNC_SERVER_FB_FORMAT) {
28919f64916dSGerd Hoffmann         int width = pixman_image_get_width(vd->server);
28929f64916dSGerd Hoffmann         tmpbuf = qemu_pixman_linebuf_create(VNC_SERVER_FB_FORMAT, width);
289312b316d4SPeter Lieven     } else {
289412b316d4SPeter Lieven         guest_row0 = (uint8_t *)pixman_image_get_data(vd->guest.fb);
289512b316d4SPeter Lieven         guest_stride = pixman_image_get_stride(vd->guest.fb);
28969f64916dSGerd Hoffmann     }
2897bea60dd7SPeter Lieven     min_stride = MIN(server_stride, guest_stride);
289812b316d4SPeter Lieven 
289912b316d4SPeter Lieven     for (;;) {
29003e230dd2SCorentin Chary         int x;
290112b316d4SPeter Lieven         uint8_t *guest_ptr, *server_ptr;
290212b316d4SPeter Lieven         unsigned long offset = find_next_bit((unsigned long *) &vd->guest.dirty,
290312b316d4SPeter Lieven                                              height * VNC_DIRTY_BPL(&vd->guest),
290412b316d4SPeter Lieven                                              y * VNC_DIRTY_BPL(&vd->guest));
290512b316d4SPeter Lieven         if (offset == height * VNC_DIRTY_BPL(&vd->guest)) {
290612b316d4SPeter Lieven             /* no more dirty bits */
290712b316d4SPeter Lieven             break;
290812b316d4SPeter Lieven         }
290912b316d4SPeter Lieven         y = offset / VNC_DIRTY_BPL(&vd->guest);
291012b316d4SPeter Lieven         x = offset % VNC_DIRTY_BPL(&vd->guest);
291112b316d4SPeter Lieven 
291212b316d4SPeter Lieven         server_ptr = server_row0 + y * server_stride + x * cmp_bytes;
29133e230dd2SCorentin Chary 
29149f64916dSGerd Hoffmann         if (vd->guest.format != VNC_SERVER_FB_FORMAT) {
2915bc210eb1SGerd Hoffmann             qemu_pixman_linebuf_fill(tmpbuf, vd->guest.fb, width, 0, y);
29169f64916dSGerd Hoffmann             guest_ptr = (uint8_t *)pixman_image_get_data(tmpbuf);
29179f64916dSGerd Hoffmann         } else {
291812b316d4SPeter Lieven             guest_ptr = guest_row0 + y * guest_stride;
29199f64916dSGerd Hoffmann         }
292012b316d4SPeter Lieven         guest_ptr += x * cmp_bytes;
29213e230dd2SCorentin Chary 
292212b316d4SPeter Lieven         for (; x < DIV_ROUND_UP(width, VNC_DIRTY_PIXELS_PER_BIT);
292312b316d4SPeter Lieven              x++, guest_ptr += cmp_bytes, server_ptr += cmp_bytes) {
2924bea60dd7SPeter Lieven             int _cmp_bytes = cmp_bytes;
292512b316d4SPeter Lieven             if (!test_and_clear_bit(x, vd->guest.dirty[y])) {
29263e230dd2SCorentin Chary                 continue;
2927b4c85ddcSPeter Lieven             }
2928bea60dd7SPeter Lieven             if ((x + 1) * cmp_bytes > min_stride) {
2929bea60dd7SPeter Lieven                 _cmp_bytes = min_stride - x * cmp_bytes;
2930bea60dd7SPeter Lieven             }
2931bea60dd7SPeter Lieven             if (memcmp(server_ptr, guest_ptr, _cmp_bytes) == 0) {
29323e230dd2SCorentin Chary                 continue;
2933b4c85ddcSPeter Lieven             }
2934bea60dd7SPeter Lieven             memcpy(server_ptr, guest_ptr, _cmp_bytes);
293512b316d4SPeter Lieven             if (!vd->non_adaptive) {
293612b316d4SPeter Lieven                 vnc_rect_updated(vd, x * VNC_DIRTY_PIXELS_PER_BIT,
293712b316d4SPeter Lieven                                  y, &tv);
293812b316d4SPeter Lieven             }
29393e230dd2SCorentin Chary             QTAILQ_FOREACH(vs, &vd->clients, next) {
294012b316d4SPeter Lieven                 set_bit(x, vs->dirty[y]);
29413e230dd2SCorentin Chary             }
29423e230dd2SCorentin Chary             has_dirty++;
29433e230dd2SCorentin Chary         }
294412b316d4SPeter Lieven 
294512b316d4SPeter Lieven         y++;
29463e230dd2SCorentin Chary     }
29479f64916dSGerd Hoffmann     qemu_pixman_image_unref(tmpbuf);
29483e230dd2SCorentin Chary     return has_dirty;
29493e230dd2SCorentin Chary }
29503e230dd2SCorentin Chary 
29510f7b2864SGerd Hoffmann static void vnc_refresh(DisplayChangeListener *dcl)
29523e230dd2SCorentin Chary {
29530f7b2864SGerd Hoffmann     VncDisplay *vd = container_of(dcl, VncDisplay, dcl);
29543e230dd2SCorentin Chary     VncState *vs, *vn;
29553e230dd2SCorentin Chary     int has_dirty, rects = 0;
29563e230dd2SCorentin Chary 
29579d6b2070SChenLiang     if (QTAILQ_EMPTY(&vd->clients)) {
29589d6b2070SChenLiang         update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_MAX);
29599d6b2070SChenLiang         return;
29609d6b2070SChenLiang     }
29619d6b2070SChenLiang 
29621d0d59feSGerd Hoffmann     graphic_hw_update(vd->dcl.con);
29633e230dd2SCorentin Chary 
2964bd023f95SCorentin Chary     if (vnc_trylock_display(vd)) {
29650f7b2864SGerd Hoffmann         update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
2966bd023f95SCorentin Chary         return;
2967bd023f95SCorentin Chary     }
2968bd023f95SCorentin Chary 
29693e230dd2SCorentin Chary     has_dirty = vnc_refresh_server_surface(vd);
2970bd023f95SCorentin Chary     vnc_unlock_display(vd);
29713e230dd2SCorentin Chary 
29723e230dd2SCorentin Chary     QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) {
297338ee14f4SGerd Hoffmann         rects += vnc_update_client(vs, has_dirty, false);
29743e230dd2SCorentin Chary         /* vs might be free()ed here */
29753e230dd2SCorentin Chary     }
2976bd023f95SCorentin Chary 
29773e230dd2SCorentin Chary     if (has_dirty && rects) {
29780f7b2864SGerd Hoffmann         vd->dcl.update_interval /= 2;
29790f7b2864SGerd Hoffmann         if (vd->dcl.update_interval < VNC_REFRESH_INTERVAL_BASE) {
29800f7b2864SGerd Hoffmann             vd->dcl.update_interval = VNC_REFRESH_INTERVAL_BASE;
29810f7b2864SGerd Hoffmann         }
29823e230dd2SCorentin Chary     } else {
29830f7b2864SGerd Hoffmann         vd->dcl.update_interval += VNC_REFRESH_INTERVAL_INC;
29840f7b2864SGerd Hoffmann         if (vd->dcl.update_interval > VNC_REFRESH_INTERVAL_MAX) {
29850f7b2864SGerd Hoffmann             vd->dcl.update_interval = VNC_REFRESH_INTERVAL_MAX;
29863e230dd2SCorentin Chary         }
29873e230dd2SCorentin Chary     }
29883e230dd2SCorentin Chary }
29893e230dd2SCorentin Chary 
29902c8cf549SMichael Tokarev static void vnc_connect(VncDisplay *vd, int csock,
29912c8cf549SMichael Tokarev                         bool skipauth, bool websocket)
29923e230dd2SCorentin Chary {
29937267c094SAnthony Liguori     VncState *vs = g_malloc0(sizeof(VncState));
29947d964c9dSCorentin Chary     int i;
29957d964c9dSCorentin Chary 
29963e230dd2SCorentin Chary     vs->csock = csock;
2997d616ccc5SGerd Hoffmann     vs->vd = vd;
29987e7e2ebcSDaniel P. Berrange 
29997e7e2ebcSDaniel P. Berrange     if (skipauth) {
30007e7e2ebcSDaniel P. Berrange 	vs->auth = VNC_AUTH_NONE;
30017e7e2ebcSDaniel P. Berrange 	vs->subauth = VNC_AUTH_INVALID;
30027e7e2ebcSDaniel P. Berrange     } else {
3003f9148c8aSDaniel P. Berrange         if (websocket) {
3004f9148c8aSDaniel P. Berrange             vs->auth = vd->ws_auth;
3005f9148c8aSDaniel P. Berrange             vs->subauth = VNC_AUTH_INVALID;
3006f9148c8aSDaniel P. Berrange         } else {
30077e7e2ebcSDaniel P. Berrange             vs->auth = vd->auth;
30087e7e2ebcSDaniel P. Berrange             vs->subauth = vd->subauth;
30097e7e2ebcSDaniel P. Berrange         }
3010f9148c8aSDaniel P. Berrange     }
3011f9148c8aSDaniel P. Berrange     VNC_DEBUG("Client sock=%d ws=%d auth=%d subauth=%d\n",
3012f9148c8aSDaniel P. Berrange               csock, websocket, vs->auth, vs->subauth);
30137e7e2ebcSDaniel P. Berrange 
30147267c094SAnthony Liguori     vs->lossy_rect = g_malloc0(VNC_STAT_ROWS * sizeof (*vs->lossy_rect));
30157d964c9dSCorentin Chary     for (i = 0; i < VNC_STAT_ROWS; ++i) {
30167267c094SAnthony Liguori         vs->lossy_rect[i] = g_malloc0(VNC_STAT_COLS * sizeof (uint8_t));
30177d964c9dSCorentin Chary     }
30183e230dd2SCorentin Chary 
30193e230dd2SCorentin Chary     VNC_DEBUG("New client on socket %d\n", csock);
30200f7b2864SGerd Hoffmann     update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
3021f9e8caccSStefan Hajnoczi     qemu_set_nonblock(vs->csock);
30227536ee4bSTim Hardeck #ifdef CONFIG_VNC_WS
30237536ee4bSTim Hardeck     if (websocket) {
30247536ee4bSTim Hardeck         vs->websocket = 1;
30250057a0d5STim Hardeck #ifdef CONFIG_VNC_TLS
3026f9148c8aSDaniel P. Berrange         if (vd->ws_tls) {
302782e1cc4bSFam Zheng             qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io, NULL, vs);
30280057a0d5STim Hardeck         } else
30290057a0d5STim Hardeck #endif /* CONFIG_VNC_TLS */
30300057a0d5STim Hardeck         {
303182e1cc4bSFam Zheng             qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs);
30320057a0d5STim Hardeck         }
30337536ee4bSTim Hardeck     } else
30347536ee4bSTim Hardeck #endif /* CONFIG_VNC_WS */
30357536ee4bSTim Hardeck     {
303682e1cc4bSFam Zheng         qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
30377536ee4bSTim Hardeck     }
30383e230dd2SCorentin Chary 
30393e230dd2SCorentin Chary     vnc_client_cache_addr(vs);
3040fb6ba0d5SWenchao Xia     vnc_qmp_event(vs, QAPI_EVENT_VNC_CONNECTED);
30418cf36489SGerd Hoffmann     vnc_set_share_mode(vs, VNC_SHARE_MODE_CONNECTING);
30423e230dd2SCorentin Chary 
30437536ee4bSTim Hardeck #ifdef CONFIG_VNC_WS
30447536ee4bSTim Hardeck     if (!vs->websocket)
30457536ee4bSTim Hardeck #endif
30467536ee4bSTim Hardeck     {
30477536ee4bSTim Hardeck         vnc_init_state(vs);
30487536ee4bSTim Hardeck     }
3049e5f34cddSGerd Hoffmann 
3050e5f34cddSGerd Hoffmann     if (vd->num_connecting > vd->connections_limit) {
3051e5f34cddSGerd Hoffmann         QTAILQ_FOREACH(vs, &vd->clients, next) {
3052e5f34cddSGerd Hoffmann             if (vs->share_mode == VNC_SHARE_MODE_CONNECTING) {
3053e5f34cddSGerd Hoffmann                 vnc_disconnect_start(vs);
3054e5f34cddSGerd Hoffmann                 return;
3055e5f34cddSGerd Hoffmann             }
3056e5f34cddSGerd Hoffmann         }
3057e5f34cddSGerd Hoffmann     }
30587536ee4bSTim Hardeck }
30597536ee4bSTim Hardeck 
30607536ee4bSTim Hardeck void vnc_init_state(VncState *vs)
30617536ee4bSTim Hardeck {
30626fd8e79aSTim Hardeck     vs->initialized = true;
30637536ee4bSTim Hardeck     VncDisplay *vd = vs->vd;
30647536ee4bSTim Hardeck 
30653e230dd2SCorentin Chary     vs->last_x = -1;
30663e230dd2SCorentin Chary     vs->last_y = -1;
30673e230dd2SCorentin Chary 
30683e230dd2SCorentin Chary     vs->as.freq = 44100;
30693e230dd2SCorentin Chary     vs->as.nchannels = 2;
30703e230dd2SCorentin Chary     vs->as.fmt = AUD_FMT_S16;
30713e230dd2SCorentin Chary     vs->as.endianness = 0;
30723e230dd2SCorentin Chary 
3073bd023f95SCorentin Chary     qemu_mutex_init(&vs->output_mutex);
3074175b2a6eSCorentin Chary     vs->bh = qemu_bh_new(vnc_jobs_bh, vs);
3075bd023f95SCorentin Chary 
3076e5f34cddSGerd Hoffmann     QTAILQ_INSERT_TAIL(&vd->clients, vs, next);
30773e230dd2SCorentin Chary 
30781d0d59feSGerd Hoffmann     graphic_hw_update(vd->dcl.con);
30793e230dd2SCorentin Chary 
30803e230dd2SCorentin Chary     vnc_write(vs, "RFB 003.008\n", 12);
30813e230dd2SCorentin Chary     vnc_flush(vs);
30823e230dd2SCorentin Chary     vnc_read_when(vs, protocol_version, 12);
30833e230dd2SCorentin Chary     reset_keys(vs);
30843e230dd2SCorentin Chary     if (vs->vd->lock_key_sync)
30853e230dd2SCorentin Chary         vs->led = qemu_add_led_event_handler(kbd_leds, vs);
30863e230dd2SCorentin Chary 
30873e230dd2SCorentin Chary     vs->mouse_mode_notifier.notify = check_pointer_type_change;
30883e230dd2SCorentin Chary     qemu_add_mouse_mode_change_notifier(&vs->mouse_mode_notifier);
30893e230dd2SCorentin Chary 
30903e230dd2SCorentin Chary     /* vs might be free()ed here */
30913e230dd2SCorentin Chary }
30923e230dd2SCorentin Chary 
30937536ee4bSTim Hardeck static void vnc_listen_read(void *opaque, bool websocket)
30943e230dd2SCorentin Chary {
30953e230dd2SCorentin Chary     VncDisplay *vs = opaque;
30963e230dd2SCorentin Chary     struct sockaddr_in addr;
30973e230dd2SCorentin Chary     socklen_t addrlen = sizeof(addr);
30987536ee4bSTim Hardeck     int csock;
30993e230dd2SCorentin Chary 
31003e230dd2SCorentin Chary     /* Catch-up */
31011d0d59feSGerd Hoffmann     graphic_hw_update(vs->dcl.con);
31027536ee4bSTim Hardeck #ifdef CONFIG_VNC_WS
31037536ee4bSTim Hardeck     if (websocket) {
31047536ee4bSTim Hardeck         csock = qemu_accept(vs->lwebsock, (struct sockaddr *)&addr, &addrlen);
31057536ee4bSTim Hardeck     } else
31067536ee4bSTim Hardeck #endif /* CONFIG_VNC_WS */
31077536ee4bSTim Hardeck     {
31087536ee4bSTim Hardeck         csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen);
31097536ee4bSTim Hardeck     }
31103e230dd2SCorentin Chary 
31113e230dd2SCorentin Chary     if (csock != -1) {
311286152436SPeter Lieven         socket_set_nodelay(csock);
31132c8cf549SMichael Tokarev         vnc_connect(vs, csock, false, websocket);
31143e230dd2SCorentin Chary     }
31153e230dd2SCorentin Chary }
31163e230dd2SCorentin Chary 
31177536ee4bSTim Hardeck static void vnc_listen_regular_read(void *opaque)
31187536ee4bSTim Hardeck {
31192c8cf549SMichael Tokarev     vnc_listen_read(opaque, false);
31207536ee4bSTim Hardeck }
31217536ee4bSTim Hardeck 
31227536ee4bSTim Hardeck #ifdef CONFIG_VNC_WS
31237536ee4bSTim Hardeck static void vnc_listen_websocket_read(void *opaque)
31247536ee4bSTim Hardeck {
31252c8cf549SMichael Tokarev     vnc_listen_read(opaque, true);
31267536ee4bSTim Hardeck }
31277536ee4bSTim Hardeck #endif /* CONFIG_VNC_WS */
31287536ee4bSTim Hardeck 
31297c20b4a3SGerd Hoffmann static const DisplayChangeListenerOps dcl_ops = {
31307c20b4a3SGerd Hoffmann     .dpy_name             = "vnc",
31310f7b2864SGerd Hoffmann     .dpy_refresh          = vnc_refresh,
31327c20b4a3SGerd Hoffmann     .dpy_gfx_copy         = vnc_dpy_copy,
31337c20b4a3SGerd Hoffmann     .dpy_gfx_update       = vnc_dpy_update,
3134c12aeb86SGerd Hoffmann     .dpy_gfx_switch       = vnc_dpy_switch,
313534da30afSBenjamin Herrenschmidt     .dpy_gfx_check_format = qemu_pixman_check_format,
31367c20b4a3SGerd Hoffmann     .dpy_mouse_set        = vnc_mouse_set,
31377c20b4a3SGerd Hoffmann     .dpy_cursor_define    = vnc_dpy_cursor_define,
31387c20b4a3SGerd Hoffmann };
31397c20b4a3SGerd Hoffmann 
314014f7143eSGerd Hoffmann void vnc_display_init(const char *id)
31413e230dd2SCorentin Chary {
31424db14629SGerd Hoffmann     VncDisplay *vs;
31434db14629SGerd Hoffmann 
31444db14629SGerd Hoffmann     if (vnc_display_find(id) != NULL) {
31454db14629SGerd Hoffmann         return;
31464db14629SGerd Hoffmann     }
31474db14629SGerd Hoffmann     vs = g_malloc0(sizeof(*vs));
31483e230dd2SCorentin Chary 
314914f7143eSGerd Hoffmann     vs->id = strdup(id);
3150d616ccc5SGerd Hoffmann     QTAILQ_INSERT_TAIL(&vnc_displays, vs, next);
31513e230dd2SCorentin Chary 
31523e230dd2SCorentin Chary     vs->lsock = -1;
31537536ee4bSTim Hardeck #ifdef CONFIG_VNC_WS
31547536ee4bSTim Hardeck     vs->lwebsock = -1;
31557536ee4bSTim Hardeck #endif
31563e230dd2SCorentin Chary 
31573e230dd2SCorentin Chary     QTAILQ_INIT(&vs->clients);
31583c9405a0SGerd Hoffmann     vs->expires = TIME_MAX;
31593e230dd2SCorentin Chary 
316040066175SGerd Hoffmann     if (keyboard_layout) {
316140066175SGerd Hoffmann         trace_vnc_key_map_init(keyboard_layout);
31623e230dd2SCorentin Chary         vs->kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout);
316340066175SGerd Hoffmann     } else {
31643e230dd2SCorentin Chary         vs->kbd_layout = init_keyboard_layout(name2keysym, "en-us");
316540066175SGerd Hoffmann     }
31663e230dd2SCorentin Chary 
31673e230dd2SCorentin Chary     if (!vs->kbd_layout)
31683e230dd2SCorentin Chary         exit(1);
31693e230dd2SCorentin Chary 
3170bd023f95SCorentin Chary     qemu_mutex_init(&vs->mutex);
3171bd023f95SCorentin Chary     vnc_start_worker_thread();
3172bd023f95SCorentin Chary 
317321ef45d7SGerd Hoffmann     vs->dcl.ops = &dcl_ops;
31745209089fSGerd Hoffmann     register_displaychangelistener(&vs->dcl);
31753e230dd2SCorentin Chary }
31763e230dd2SCorentin Chary 
31773e230dd2SCorentin Chary 
317814f7143eSGerd Hoffmann static void vnc_display_close(VncDisplay *vs)
31793e230dd2SCorentin Chary {
31803e230dd2SCorentin Chary     if (!vs)
31813e230dd2SCorentin Chary         return;
3182bf7aa45eSGerd Hoffmann     vs->enabled = false;
3183bf7aa45eSGerd Hoffmann     vs->is_unix = false;
31843e230dd2SCorentin Chary     if (vs->lsock != -1) {
318582e1cc4bSFam Zheng         qemu_set_fd_handler(vs->lsock, NULL, NULL, NULL);
31863e230dd2SCorentin Chary         close(vs->lsock);
31873e230dd2SCorentin Chary         vs->lsock = -1;
31883e230dd2SCorentin Chary     }
31897536ee4bSTim Hardeck #ifdef CONFIG_VNC_WS
3190bf7aa45eSGerd Hoffmann     vs->ws_enabled = false;
31917536ee4bSTim Hardeck     if (vs->lwebsock != -1) {
319282e1cc4bSFam Zheng         qemu_set_fd_handler(vs->lwebsock, NULL, NULL, NULL);
31937536ee4bSTim Hardeck         close(vs->lwebsock);
31947536ee4bSTim Hardeck         vs->lwebsock = -1;
31957536ee4bSTim Hardeck     }
31967536ee4bSTim Hardeck #endif /* CONFIG_VNC_WS */
31973e230dd2SCorentin Chary     vs->auth = VNC_AUTH_INVALID;
31983e230dd2SCorentin Chary     vs->subauth = VNC_AUTH_INVALID;
3199d169f04bSDaniel P. Berrange #ifdef CONFIG_VNC_TLS
32003e230dd2SCorentin Chary     vs->tls.x509verify = 0;
32013e230dd2SCorentin Chary #endif
32023e230dd2SCorentin Chary }
32033e230dd2SCorentin Chary 
320414f7143eSGerd Hoffmann int vnc_display_password(const char *id, const char *password)
32051cd20f8bSAnthony Liguori {
320614f7143eSGerd Hoffmann     VncDisplay *vs = vnc_display_find(id);
32071cd20f8bSAnthony Liguori 
32081cd20f8bSAnthony Liguori     if (!vs) {
3209a6aa9d3eSLuiz Capitulino         return -EINVAL;
32101cd20f8bSAnthony Liguori     }
3211cf864569SGerd Hoffmann     if (vs->auth == VNC_AUTH_NONE) {
3212cf864569SGerd Hoffmann         error_printf_unless_qmp("If you want use passwords please enable "
3213cf864569SGerd Hoffmann                                 "password auth using '-vnc ${dpy},password'.");
3214cf864569SGerd Hoffmann         return -EINVAL;
32151cd20f8bSAnthony Liguori     }
32161cd20f8bSAnthony Liguori 
32177267c094SAnthony Liguori     g_free(vs->password);
32187267c094SAnthony Liguori     vs->password = g_strdup(password);
3219a6aa9d3eSLuiz Capitulino 
3220a6aa9d3eSLuiz Capitulino     return 0;
32213e230dd2SCorentin Chary }
32223e230dd2SCorentin Chary 
322314f7143eSGerd Hoffmann int vnc_display_pw_expire(const char *id, time_t expires)
32243c9405a0SGerd Hoffmann {
322514f7143eSGerd Hoffmann     VncDisplay *vs = vnc_display_find(id);
32263c9405a0SGerd Hoffmann 
32271643f2b2SGerd Hoffmann     if (!vs) {
32281643f2b2SGerd Hoffmann         return -EINVAL;
32291643f2b2SGerd Hoffmann     }
32301643f2b2SGerd Hoffmann 
32313c9405a0SGerd Hoffmann     vs->expires = expires;
32323c9405a0SGerd Hoffmann     return 0;
32333c9405a0SGerd Hoffmann }
32343c9405a0SGerd Hoffmann 
323514f7143eSGerd Hoffmann char *vnc_display_local_addr(const char *id)
32363e230dd2SCorentin Chary {
323714f7143eSGerd Hoffmann     VncDisplay *vs = vnc_display_find(id);
32383e230dd2SCorentin Chary 
32399e0ff75eSGerd Hoffmann     assert(vs);
32403e230dd2SCorentin Chary     return vnc_socket_local_addr("%s:%s", vs->lsock);
32413e230dd2SCorentin Chary }
32423e230dd2SCorentin Chary 
32434db14629SGerd Hoffmann static QemuOptsList qemu_vnc_opts = {
32444db14629SGerd Hoffmann     .name = "vnc",
32454db14629SGerd Hoffmann     .head = QTAILQ_HEAD_INITIALIZER(qemu_vnc_opts.head),
32464db14629SGerd Hoffmann     .implied_opt_name = "vnc",
32474db14629SGerd Hoffmann     .desc = {
32484db14629SGerd Hoffmann         {
32494db14629SGerd Hoffmann             .name = "vnc",
32504db14629SGerd Hoffmann             .type = QEMU_OPT_STRING,
32514db14629SGerd Hoffmann         },{
32524db14629SGerd Hoffmann             .name = "websocket",
32534db14629SGerd Hoffmann             .type = QEMU_OPT_STRING,
32544db14629SGerd Hoffmann         },{
32554db14629SGerd Hoffmann             .name = "x509",
32564db14629SGerd Hoffmann             .type = QEMU_OPT_STRING,
32574db14629SGerd Hoffmann         },{
32584db14629SGerd Hoffmann             .name = "share",
32594db14629SGerd Hoffmann             .type = QEMU_OPT_STRING,
32604db14629SGerd Hoffmann         },{
32611d0d59feSGerd Hoffmann             .name = "display",
32621d0d59feSGerd Hoffmann             .type = QEMU_OPT_STRING,
32631d0d59feSGerd Hoffmann         },{
32641d0d59feSGerd Hoffmann             .name = "head",
32651d0d59feSGerd Hoffmann             .type = QEMU_OPT_NUMBER,
32661d0d59feSGerd Hoffmann         },{
3267e5f34cddSGerd Hoffmann             .name = "connections",
3268e5f34cddSGerd Hoffmann             .type = QEMU_OPT_NUMBER,
3269e5f34cddSGerd Hoffmann         },{
327088428b7aSGonglei             .name = "to",
327188428b7aSGonglei             .type = QEMU_OPT_NUMBER,
327288428b7aSGonglei         },{
327388428b7aSGonglei             .name = "ipv4",
327488428b7aSGonglei             .type = QEMU_OPT_BOOL,
327588428b7aSGonglei         },{
327688428b7aSGonglei             .name = "ipv6",
327788428b7aSGonglei             .type = QEMU_OPT_BOOL,
327888428b7aSGonglei         },{
32794db14629SGerd Hoffmann             .name = "password",
32804db14629SGerd Hoffmann             .type = QEMU_OPT_BOOL,
32814db14629SGerd Hoffmann         },{
32824db14629SGerd Hoffmann             .name = "reverse",
32834db14629SGerd Hoffmann             .type = QEMU_OPT_BOOL,
32844db14629SGerd Hoffmann         },{
32854db14629SGerd Hoffmann             .name = "lock-key-sync",
32864db14629SGerd Hoffmann             .type = QEMU_OPT_BOOL,
32874db14629SGerd Hoffmann         },{
32884db14629SGerd Hoffmann             .name = "sasl",
32894db14629SGerd Hoffmann             .type = QEMU_OPT_BOOL,
32904db14629SGerd Hoffmann         },{
32914db14629SGerd Hoffmann             .name = "tls",
32924db14629SGerd Hoffmann             .type = QEMU_OPT_BOOL,
32934db14629SGerd Hoffmann         },{
32944db14629SGerd Hoffmann             .name = "x509verify",
32958c7d0645SDaniel P. Berrange             .type = QEMU_OPT_STRING,
32964db14629SGerd Hoffmann         },{
32974db14629SGerd Hoffmann             .name = "acl",
32984db14629SGerd Hoffmann             .type = QEMU_OPT_BOOL,
32994db14629SGerd Hoffmann         },{
33004db14629SGerd Hoffmann             .name = "lossy",
33014db14629SGerd Hoffmann             .type = QEMU_OPT_BOOL,
33024db14629SGerd Hoffmann         },{
33034db14629SGerd Hoffmann             .name = "non-adaptive",
33044db14629SGerd Hoffmann             .type = QEMU_OPT_BOOL,
33054db14629SGerd Hoffmann         },
33064db14629SGerd Hoffmann         { /* end of list */ }
33074db14629SGerd Hoffmann     },
33084db14629SGerd Hoffmann };
33094db14629SGerd Hoffmann 
33100dd72e15SDaniel P. Berrange 
33110dd72e15SDaniel P. Berrange static void
33120dd72e15SDaniel P. Berrange vnc_display_setup_auth(VncDisplay *vs,
33130dd72e15SDaniel P. Berrange                        bool password,
33140dd72e15SDaniel P. Berrange                        bool sasl,
33150dd72e15SDaniel P. Berrange                        bool tls,
3316f9148c8aSDaniel P. Berrange                        bool x509,
3317f9148c8aSDaniel P. Berrange                        bool websocket)
33180dd72e15SDaniel P. Berrange {
33190dd72e15SDaniel P. Berrange     /*
33200dd72e15SDaniel P. Berrange      * We have a choice of 3 authentication options
33210dd72e15SDaniel P. Berrange      *
33220dd72e15SDaniel P. Berrange      *   1. none
33230dd72e15SDaniel P. Berrange      *   2. vnc
33240dd72e15SDaniel P. Berrange      *   3. sasl
33250dd72e15SDaniel P. Berrange      *
33260dd72e15SDaniel P. Berrange      * The channel can be run in 2 modes
33270dd72e15SDaniel P. Berrange      *
33280dd72e15SDaniel P. Berrange      *   1. clear
33290dd72e15SDaniel P. Berrange      *   2. tls
33300dd72e15SDaniel P. Berrange      *
33310dd72e15SDaniel P. Berrange      * And TLS can use 2 types of credentials
33320dd72e15SDaniel P. Berrange      *
33330dd72e15SDaniel P. Berrange      *   1. anon
33340dd72e15SDaniel P. Berrange      *   2. x509
33350dd72e15SDaniel P. Berrange      *
33360dd72e15SDaniel P. Berrange      * We thus have 9 possible logical combinations
33370dd72e15SDaniel P. Berrange      *
33380dd72e15SDaniel P. Berrange      *   1. clear + none
33390dd72e15SDaniel P. Berrange      *   2. clear + vnc
33400dd72e15SDaniel P. Berrange      *   3. clear + sasl
33410dd72e15SDaniel P. Berrange      *   4. tls + anon + none
33420dd72e15SDaniel P. Berrange      *   5. tls + anon + vnc
33430dd72e15SDaniel P. Berrange      *   6. tls + anon + sasl
33440dd72e15SDaniel P. Berrange      *   7. tls + x509 + none
33450dd72e15SDaniel P. Berrange      *   8. tls + x509 + vnc
33460dd72e15SDaniel P. Berrange      *   9. tls + x509 + sasl
33470dd72e15SDaniel P. Berrange      *
33480dd72e15SDaniel P. Berrange      * These need to be mapped into the VNC auth schemes
33490dd72e15SDaniel P. Berrange      * in an appropriate manner. In regular VNC, all the
33500dd72e15SDaniel P. Berrange      * TLS options get mapped into VNC_AUTH_VENCRYPT
33510dd72e15SDaniel P. Berrange      * sub-auth types.
3352f9148c8aSDaniel P. Berrange      *
3353f9148c8aSDaniel P. Berrange      * In websockets, the https:// protocol already provides
3354f9148c8aSDaniel P. Berrange      * TLS support, so there is no need to make use of the
3355f9148c8aSDaniel P. Berrange      * VeNCrypt extension. Furthermore, websockets browser
3356f9148c8aSDaniel P. Berrange      * clients could not use VeNCrypt even if they wanted to,
3357f9148c8aSDaniel P. Berrange      * as they cannot control when the TLS handshake takes
3358f9148c8aSDaniel P. Berrange      * place. Thus there is no option but to rely on https://,
3359f9148c8aSDaniel P. Berrange      * meaning combinations 4->6 and 7->9 will be mapped to
3360f9148c8aSDaniel P. Berrange      * VNC auth schemes in the same way as combos 1->3.
3361f9148c8aSDaniel P. Berrange      *
3362f9148c8aSDaniel P. Berrange      * Regardless of fact that we have a different mapping to
3363f9148c8aSDaniel P. Berrange      * VNC auth mechs for plain VNC vs websockets VNC, the end
3364f9148c8aSDaniel P. Berrange      * result has the same security characteristics.
33650dd72e15SDaniel P. Berrange      */
33660dd72e15SDaniel P. Berrange     if (password) {
33670dd72e15SDaniel P. Berrange         if (tls) {
33680dd72e15SDaniel P. Berrange             vs->auth = VNC_AUTH_VENCRYPT;
3369f9148c8aSDaniel P. Berrange             if (websocket) {
3370f9148c8aSDaniel P. Berrange                 vs->ws_tls = true;
3371f9148c8aSDaniel P. Berrange             }
33720dd72e15SDaniel P. Berrange             if (x509) {
33730dd72e15SDaniel P. Berrange                 VNC_DEBUG("Initializing VNC server with x509 password auth\n");
33740dd72e15SDaniel P. Berrange                 vs->subauth = VNC_AUTH_VENCRYPT_X509VNC;
33750dd72e15SDaniel P. Berrange             } else {
33760dd72e15SDaniel P. Berrange                 VNC_DEBUG("Initializing VNC server with TLS password auth\n");
33770dd72e15SDaniel P. Berrange                 vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC;
33780dd72e15SDaniel P. Berrange             }
33790dd72e15SDaniel P. Berrange         } else {
33800dd72e15SDaniel P. Berrange             VNC_DEBUG("Initializing VNC server with password auth\n");
33810dd72e15SDaniel P. Berrange             vs->auth = VNC_AUTH_VNC;
33820dd72e15SDaniel P. Berrange             vs->subauth = VNC_AUTH_INVALID;
33830dd72e15SDaniel P. Berrange         }
3384f9148c8aSDaniel P. Berrange         if (websocket) {
3385f9148c8aSDaniel P. Berrange             vs->ws_auth = VNC_AUTH_VNC;
3386f9148c8aSDaniel P. Berrange         } else {
3387f9148c8aSDaniel P. Berrange             vs->ws_auth = VNC_AUTH_INVALID;
3388f9148c8aSDaniel P. Berrange         }
33890dd72e15SDaniel P. Berrange     } else if (sasl) {
33900dd72e15SDaniel P. Berrange         if (tls) {
33910dd72e15SDaniel P. Berrange             vs->auth = VNC_AUTH_VENCRYPT;
3392f9148c8aSDaniel P. Berrange             if (websocket) {
3393f9148c8aSDaniel P. Berrange                 vs->ws_tls = true;
3394f9148c8aSDaniel P. Berrange             }
33950dd72e15SDaniel P. Berrange             if (x509) {
33960dd72e15SDaniel P. Berrange                 VNC_DEBUG("Initializing VNC server with x509 SASL auth\n");
33970dd72e15SDaniel P. Berrange                 vs->subauth = VNC_AUTH_VENCRYPT_X509SASL;
33980dd72e15SDaniel P. Berrange             } else {
33990dd72e15SDaniel P. Berrange                 VNC_DEBUG("Initializing VNC server with TLS SASL auth\n");
34000dd72e15SDaniel P. Berrange                 vs->subauth = VNC_AUTH_VENCRYPT_TLSSASL;
34010dd72e15SDaniel P. Berrange             }
34020dd72e15SDaniel P. Berrange         } else {
34030dd72e15SDaniel P. Berrange             VNC_DEBUG("Initializing VNC server with SASL auth\n");
34040dd72e15SDaniel P. Berrange             vs->auth = VNC_AUTH_SASL;
34050dd72e15SDaniel P. Berrange             vs->subauth = VNC_AUTH_INVALID;
34060dd72e15SDaniel P. Berrange         }
3407f9148c8aSDaniel P. Berrange         if (websocket) {
3408f9148c8aSDaniel P. Berrange             vs->ws_auth = VNC_AUTH_SASL;
3409f9148c8aSDaniel P. Berrange         } else {
3410f9148c8aSDaniel P. Berrange             vs->ws_auth = VNC_AUTH_INVALID;
3411f9148c8aSDaniel P. Berrange         }
34120dd72e15SDaniel P. Berrange     } else {
34130dd72e15SDaniel P. Berrange         if (tls) {
34140dd72e15SDaniel P. Berrange             vs->auth = VNC_AUTH_VENCRYPT;
3415f9148c8aSDaniel P. Berrange             if (websocket) {
3416f9148c8aSDaniel P. Berrange                 vs->ws_tls = true;
3417f9148c8aSDaniel P. Berrange             }
34180dd72e15SDaniel P. Berrange             if (x509) {
34190dd72e15SDaniel P. Berrange                 VNC_DEBUG("Initializing VNC server with x509 no auth\n");
34200dd72e15SDaniel P. Berrange                 vs->subauth = VNC_AUTH_VENCRYPT_X509NONE;
34210dd72e15SDaniel P. Berrange             } else {
34220dd72e15SDaniel P. Berrange                 VNC_DEBUG("Initializing VNC server with TLS no auth\n");
34230dd72e15SDaniel P. Berrange                 vs->subauth = VNC_AUTH_VENCRYPT_TLSNONE;
34240dd72e15SDaniel P. Berrange             }
34250dd72e15SDaniel P. Berrange         } else {
34260dd72e15SDaniel P. Berrange             VNC_DEBUG("Initializing VNC server with no auth\n");
34270dd72e15SDaniel P. Berrange             vs->auth = VNC_AUTH_NONE;
34280dd72e15SDaniel P. Berrange             vs->subauth = VNC_AUTH_INVALID;
34290dd72e15SDaniel P. Berrange         }
3430f9148c8aSDaniel P. Berrange         if (websocket) {
3431f9148c8aSDaniel P. Berrange             vs->ws_auth = VNC_AUTH_NONE;
3432f9148c8aSDaniel P. Berrange         } else {
3433f9148c8aSDaniel P. Berrange             vs->ws_auth = VNC_AUTH_INVALID;
3434f9148c8aSDaniel P. Berrange         }
34350dd72e15SDaniel P. Berrange     }
34360dd72e15SDaniel P. Berrange }
34370dd72e15SDaniel P. Berrange 
34384db14629SGerd Hoffmann void vnc_display_open(const char *id, Error **errp)
34393e230dd2SCorentin Chary {
344014f7143eSGerd Hoffmann     VncDisplay *vs = vnc_display_find(id);
34414db14629SGerd Hoffmann     QemuOpts *opts = qemu_opts_find(&qemu_vnc_opts, id);
3442e5560329SGerd Hoffmann     QemuOpts *sopts, *wsopts;
3443e2a11d9dSGonglei     const char *share, *device_id;
34441d0d59feSGerd Hoffmann     QemuConsole *con;
3445a2c72de0SGonglei     bool password = false;
3446a2c72de0SGonglei     bool reverse = false;
3447e2a11d9dSGonglei     const char *vnc;
3448e2a11d9dSGonglei     const char *has_to;
3449e5560329SGerd Hoffmann     char *h;
3450e2a11d9dSGonglei     bool has_ipv4 = false;
3451e2a11d9dSGonglei     bool has_ipv6 = false;
34524478aa76SGerd Hoffmann     const char *websocket;
3453a2c72de0SGonglei     bool tls = false, x509 = false;
3454d169f04bSDaniel P. Berrange #ifdef CONFIG_VNC_TLS
34554db14629SGerd Hoffmann     const char *path;
34563e230dd2SCorentin Chary #endif
3457a2c72de0SGonglei     bool sasl = false;
3458d169f04bSDaniel P. Berrange #ifdef CONFIG_VNC_SASL
34593e230dd2SCorentin Chary     int saslErr;
34603e230dd2SCorentin Chary #endif
34612ded6ad7SBlue Swirl #if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL)
34623e230dd2SCorentin Chary     int acl = 0;
34632ded6ad7SBlue Swirl #endif
34643e230dd2SCorentin Chary     int lock_key_sync = 1;
34653e230dd2SCorentin Chary 
3466d616ccc5SGerd Hoffmann     if (!vs) {
34672d55f0e8SPaolo Bonzini         error_setg(errp, "VNC display not active");
34682d55f0e8SPaolo Bonzini         return;
34692d55f0e8SPaolo Bonzini     }
347014f7143eSGerd Hoffmann     vnc_display_close(vs);
34714db14629SGerd Hoffmann 
34724db14629SGerd Hoffmann     if (!opts) {
34732d55f0e8SPaolo Bonzini         return;
34744db14629SGerd Hoffmann     }
3475e2a11d9dSGonglei     vnc = qemu_opt_get(opts, "vnc");
3476e2a11d9dSGonglei     if (!vnc || strcmp(vnc, "none") == 0) {
34774db14629SGerd Hoffmann         return;
34784db14629SGerd Hoffmann     }
3479e2a11d9dSGonglei 
3480e5560329SGerd Hoffmann     sopts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort);
3481e5560329SGerd Hoffmann     wsopts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort);
3482e5560329SGerd Hoffmann 
3483e5560329SGerd Hoffmann     h = strrchr(vnc, ':');
3484e5560329SGerd Hoffmann     if (h) {
3485274c3b52SJán Tomko         char *host;
3486274c3b52SJán Tomko         size_t hlen = h - vnc;
3487274c3b52SJán Tomko 
3488274c3b52SJán Tomko         if (vnc[0] == '[' && vnc[hlen - 1] == ']') {
3489274c3b52SJán Tomko             host = g_strndup(vnc + 1, hlen - 2);
3490274c3b52SJán Tomko         } else {
3491274c3b52SJán Tomko             host = g_strndup(vnc, hlen);
3492274c3b52SJán Tomko         }
3493e5560329SGerd Hoffmann         qemu_opt_set(sopts, "host", host, &error_abort);
3494e5560329SGerd Hoffmann         qemu_opt_set(wsopts, "host", host, &error_abort);
3495e5560329SGerd Hoffmann         qemu_opt_set(sopts, "port", h+1, &error_abort);
3496e5560329SGerd Hoffmann         g_free(host);
3497e5560329SGerd Hoffmann     } else {
3498e5560329SGerd Hoffmann         error_setg(errp, "no vnc port specified");
3499e5560329SGerd Hoffmann         goto fail;
3500e2a11d9dSGonglei     }
3501e5560329SGerd Hoffmann 
3502e5560329SGerd Hoffmann     has_to = qemu_opt_get(opts, "to");
3503e2a11d9dSGonglei     has_ipv4 = qemu_opt_get_bool(opts, "ipv4", false);
3504e2a11d9dSGonglei     has_ipv6 = qemu_opt_get_bool(opts, "ipv6", false);
3505e5560329SGerd Hoffmann     if (has_to) {
3506e5560329SGerd Hoffmann         qemu_opt_set(sopts, "to", has_to, &error_abort);
3507e5560329SGerd Hoffmann         qemu_opt_set(wsopts, "to", has_to, &error_abort);
3508e5560329SGerd Hoffmann     }
3509e5560329SGerd Hoffmann     if (has_ipv4) {
3510e5560329SGerd Hoffmann         qemu_opt_set(sopts, "ipv4", "on", &error_abort);
3511e5560329SGerd Hoffmann         qemu_opt_set(wsopts, "ipv4", "on", &error_abort);
3512e5560329SGerd Hoffmann     }
3513e5560329SGerd Hoffmann     if (has_ipv6) {
3514e5560329SGerd Hoffmann         qemu_opt_set(sopts, "ipv6", "on", &error_abort);
3515e5560329SGerd Hoffmann         qemu_opt_set(wsopts, "ipv6", "on", &error_abort);
3516e5560329SGerd Hoffmann     }
35173e230dd2SCorentin Chary 
35184db14629SGerd Hoffmann     password = qemu_opt_get_bool(opts, "password", false);
35194db14629SGerd Hoffmann     if (password && fips_get_state()) {
35202d55f0e8SPaolo Bonzini         error_setg(errp,
35210f66998fSPaul Moore                    "VNC password auth disabled due to FIPS mode, "
35220f66998fSPaul Moore                    "consider using the VeNCrypt or SASL authentication "
35232d55f0e8SPaolo Bonzini                    "methods as an alternative");
35241ce52c78SPaolo Bonzini         goto fail;
35250f66998fSPaul Moore     }
35264db14629SGerd Hoffmann 
35274db14629SGerd Hoffmann     reverse = qemu_opt_get_bool(opts, "reverse", false);
35284db14629SGerd Hoffmann     lock_key_sync = qemu_opt_get_bool(opts, "lock-key-sync", true);
35294db14629SGerd Hoffmann     sasl = qemu_opt_get_bool(opts, "sasl", false);
3530d169f04bSDaniel P. Berrange #ifndef CONFIG_VNC_SASL
3531d169f04bSDaniel P. Berrange     if (sasl) {
3532d169f04bSDaniel P. Berrange         error_setg(errp, "VNC SASL auth requires cyrus-sasl support");
3533d169f04bSDaniel P. Berrange         goto fail;
3534d169f04bSDaniel P. Berrange     }
3535d169f04bSDaniel P. Berrange #endif /* CONFIG_VNC_SASL */
35364db14629SGerd Hoffmann     tls  = qemu_opt_get_bool(opts, "tls", false);
3537d169f04bSDaniel P. Berrange #ifdef CONFIG_VNC_TLS
35384db14629SGerd Hoffmann     path = qemu_opt_get(opts, "x509");
35398c7d0645SDaniel P. Berrange     if (!path) {
35408c7d0645SDaniel P. Berrange         path = qemu_opt_get(opts, "x509verify");
35418c7d0645SDaniel P. Berrange         if (path) {
35428c7d0645SDaniel P. Berrange             vs->tls.x509verify = true;
35438c7d0645SDaniel P. Berrange         }
35448c7d0645SDaniel P. Berrange     }
35454db14629SGerd Hoffmann     if (path) {
3546a2c72de0SGonglei         x509 = true;
35473e230dd2SCorentin Chary         if (vnc_tls_set_x509_creds_dir(vs, path) < 0) {
35484db14629SGerd Hoffmann             error_setg(errp, "Failed to find x509 certificates/keys in %s",
35494db14629SGerd Hoffmann                        path);
35501ce52c78SPaolo Bonzini             goto fail;
35513e230dd2SCorentin Chary         }
35523e230dd2SCorentin Chary     }
3553d169f04bSDaniel P. Berrange #else /* ! CONFIG_VNC_TLS */
3554d169f04bSDaniel P. Berrange     if (tls) {
3555d169f04bSDaniel P. Berrange         error_setg(errp, "VNC TLS auth requires gnutls support");
3556d169f04bSDaniel P. Berrange         goto fail;
3557d169f04bSDaniel P. Berrange     }
3558d169f04bSDaniel P. Berrange #endif /* ! CONFIG_VNC_TLS */
35592ded6ad7SBlue Swirl #if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL)
35604db14629SGerd Hoffmann     acl = qemu_opt_get_bool(opts, "acl", false);
35612ded6ad7SBlue Swirl #endif
35624db14629SGerd Hoffmann 
35634db14629SGerd Hoffmann     share = qemu_opt_get(opts, "share");
35644db14629SGerd Hoffmann     if (share) {
35654db14629SGerd Hoffmann         if (strcmp(share, "ignore") == 0) {
35668cf36489SGerd Hoffmann             vs->share_policy = VNC_SHARE_POLICY_IGNORE;
35674db14629SGerd Hoffmann         } else if (strcmp(share, "allow-exclusive") == 0) {
35688cf36489SGerd Hoffmann             vs->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE;
35694db14629SGerd Hoffmann         } else if (strcmp(share, "force-shared") == 0) {
35708cf36489SGerd Hoffmann             vs->share_policy = VNC_SHARE_POLICY_FORCE_SHARED;
35718cf36489SGerd Hoffmann         } else {
35722d55f0e8SPaolo Bonzini             error_setg(errp, "unknown vnc share= option");
35731ce52c78SPaolo Bonzini             goto fail;
35748cf36489SGerd Hoffmann         }
35754db14629SGerd Hoffmann     } else {
35764db14629SGerd Hoffmann         vs->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE;
35773e230dd2SCorentin Chary     }
3578e5f34cddSGerd Hoffmann     vs->connections_limit = qemu_opt_get_number(opts, "connections", 32);
35793e230dd2SCorentin Chary 
35804db14629SGerd Hoffmann     websocket = qemu_opt_get(opts, "websocket");
35814db14629SGerd Hoffmann     if (websocket) {
3582d169f04bSDaniel P. Berrange #ifdef CONFIG_VNC_WS
3583bf7aa45eSGerd Hoffmann         vs->ws_enabled = true;
3584e5560329SGerd Hoffmann         qemu_opt_set(wsopts, "port", websocket, &error_abort);
3585d169f04bSDaniel P. Berrange #else /* ! CONFIG_VNC_WS */
3586d169f04bSDaniel P. Berrange         error_setg(errp, "Websockets protocol requires gnutls support");
3587d169f04bSDaniel P. Berrange         goto fail;
3588d169f04bSDaniel P. Berrange #endif /* ! CONFIG_VNC_WS */
35894db14629SGerd Hoffmann     }
35904db14629SGerd Hoffmann 
35914db14629SGerd Hoffmann #ifdef CONFIG_VNC_JPEG
35924db14629SGerd Hoffmann     vs->lossy = qemu_opt_get_bool(opts, "lossy", false);
35934db14629SGerd Hoffmann #endif
35944db14629SGerd Hoffmann     vs->non_adaptive = qemu_opt_get_bool(opts, "non-adaptive", false);
3595e22492d3SPeter Lieven     /* adaptive updates are only used with tight encoding and
3596e22492d3SPeter Lieven      * if lossy updates are enabled so we can disable all the
3597e22492d3SPeter Lieven      * calculations otherwise */
3598e22492d3SPeter Lieven     if (!vs->lossy) {
3599e22492d3SPeter Lieven         vs->non_adaptive = true;
3600e22492d3SPeter Lieven     }
3601e22492d3SPeter Lieven 
36023e230dd2SCorentin Chary #ifdef CONFIG_VNC_TLS
36033e230dd2SCorentin Chary     if (acl && x509 && vs->tls.x509verify) {
3604c8496408SGerd Hoffmann         char *aclname;
3605c8496408SGerd Hoffmann 
3606c8496408SGerd Hoffmann         if (strcmp(vs->id, "default") == 0) {
3607c8496408SGerd Hoffmann             aclname = g_strdup("vnc.x509dname");
3608c8496408SGerd Hoffmann         } else {
3609c8496408SGerd Hoffmann             aclname = g_strdup_printf("vnc.%s.x509dname", vs->id);
3610c8496408SGerd Hoffmann         }
3611c8496408SGerd Hoffmann         vs->tls.acl = qemu_acl_init(aclname);
3612c8496408SGerd Hoffmann         g_free(aclname);
36133e230dd2SCorentin Chary     }
36143e230dd2SCorentin Chary #endif
36153e230dd2SCorentin Chary #ifdef CONFIG_VNC_SASL
36163e230dd2SCorentin Chary     if (acl && sasl) {
3617c8496408SGerd Hoffmann         char *aclname;
3618c8496408SGerd Hoffmann 
3619c8496408SGerd Hoffmann         if (strcmp(vs->id, "default") == 0) {
3620c8496408SGerd Hoffmann             aclname = g_strdup("vnc.username");
3621c8496408SGerd Hoffmann         } else {
3622c8496408SGerd Hoffmann             aclname = g_strdup_printf("vnc.%s.username", vs->id);
3623c8496408SGerd Hoffmann         }
3624c8496408SGerd Hoffmann         vs->sasl.acl = qemu_acl_init(aclname);
3625c8496408SGerd Hoffmann         g_free(aclname);
36263e230dd2SCorentin Chary     }
36273e230dd2SCorentin Chary #endif
36283e230dd2SCorentin Chary 
3629f9148c8aSDaniel P. Berrange     vnc_display_setup_auth(vs, password, sasl, tls, x509, websocket);
36303e230dd2SCorentin Chary 
36313e230dd2SCorentin Chary #ifdef CONFIG_VNC_SASL
36323e230dd2SCorentin Chary     if ((saslErr = sasl_server_init(NULL, "qemu")) != SASL_OK) {
36332d55f0e8SPaolo Bonzini         error_setg(errp, "Failed to initialize SASL auth: %s",
36343e230dd2SCorentin Chary                    sasl_errstring(saslErr, NULL, NULL));
36351ce52c78SPaolo Bonzini         goto fail;
36363e230dd2SCorentin Chary     }
36373e230dd2SCorentin Chary #endif
36383e230dd2SCorentin Chary     vs->lock_key_sync = lock_key_sync;
36393e230dd2SCorentin Chary 
36401d0d59feSGerd Hoffmann     device_id = qemu_opt_get(opts, "display");
36411d0d59feSGerd Hoffmann     if (device_id) {
36421d0d59feSGerd Hoffmann         DeviceState *dev;
36431d0d59feSGerd Hoffmann         int head = qemu_opt_get_number(opts, "head", 0);
36441d0d59feSGerd Hoffmann 
36451d0d59feSGerd Hoffmann         dev = qdev_find_recursive(sysbus_get_default(), device_id);
36461d0d59feSGerd Hoffmann         if (dev == NULL) {
3647f3cf80e8SMarkus Armbruster             error_setg(errp, "Device '%s' not found", device_id);
36481d0d59feSGerd Hoffmann             goto fail;
36491d0d59feSGerd Hoffmann         }
36501d0d59feSGerd Hoffmann 
36511d0d59feSGerd Hoffmann         con = qemu_console_lookup_by_device(dev, head);
36521d0d59feSGerd Hoffmann         if (con == NULL) {
36531d0d59feSGerd Hoffmann             error_setg(errp, "Device %s is not bound to a QemuConsole",
36541d0d59feSGerd Hoffmann                        device_id);
36551d0d59feSGerd Hoffmann             goto fail;
36561d0d59feSGerd Hoffmann         }
36571d0d59feSGerd Hoffmann     } else {
36581d0d59feSGerd Hoffmann         con = NULL;
36591d0d59feSGerd Hoffmann     }
36601d0d59feSGerd Hoffmann 
36611d0d59feSGerd Hoffmann     if (con != vs->dcl.con) {
36621d0d59feSGerd Hoffmann         unregister_displaychangelistener(&vs->dcl);
36631d0d59feSGerd Hoffmann         vs->dcl.con = con;
36641d0d59feSGerd Hoffmann         register_displaychangelistener(&vs->dcl);
36651d0d59feSGerd Hoffmann     }
36661d0d59feSGerd Hoffmann 
36673e230dd2SCorentin Chary     if (reverse) {
36683e230dd2SCorentin Chary         /* connect to viewer */
3669007fcd3eSPaolo Bonzini         int csock;
36703e230dd2SCorentin Chary         vs->lsock = -1;
36717536ee4bSTim Hardeck #ifdef CONFIG_VNC_WS
36727536ee4bSTim Hardeck         vs->lwebsock = -1;
36737536ee4bSTim Hardeck #endif
3674e5560329SGerd Hoffmann         if (strncmp(vnc, "unix:", 5) == 0) {
3675e5560329SGerd Hoffmann             csock = unix_connect(vnc+5, errp);
3676007fcd3eSPaolo Bonzini         } else {
3677e5560329SGerd Hoffmann             csock = inet_connect(vnc, errp);
36783e230dd2SCorentin Chary         }
3679007fcd3eSPaolo Bonzini         if (csock < 0) {
3680007fcd3eSPaolo Bonzini             goto fail;
3681007fcd3eSPaolo Bonzini         }
36822c8cf549SMichael Tokarev         vnc_connect(vs, csock, false, false);
36833e230dd2SCorentin Chary     } else {
36843e230dd2SCorentin Chary         /* listen for connects */
3685e5560329SGerd Hoffmann         if (strncmp(vnc, "unix:", 5) == 0) {
3686e5560329SGerd Hoffmann             vs->lsock = unix_listen(vnc+5, NULL, 0, errp);
36873d00ac1aSCole Robinson             if (vs->lsock < 0) {
36883d00ac1aSCole Robinson                 goto fail;
36893d00ac1aSCole Robinson             }
3690bf7aa45eSGerd Hoffmann             vs->is_unix = true;
36913e230dd2SCorentin Chary         } else {
3692e5560329SGerd Hoffmann             vs->lsock = inet_listen_opts(sopts, 5900, errp);
3693c1c1619cSPaolo Bonzini             if (vs->lsock < 0) {
36941ce52c78SPaolo Bonzini                 goto fail;
36951ce52c78SPaolo Bonzini             }
36967536ee4bSTim Hardeck #ifdef CONFIG_VNC_WS
3697bf7aa45eSGerd Hoffmann             if (vs->ws_enabled) {
3698e5560329SGerd Hoffmann                 vs->lwebsock = inet_listen_opts(wsopts, 0, errp);
36997536ee4bSTim Hardeck                 if (vs->lwebsock < 0) {
3700b3c33f91SGonglei                     if (vs->lsock != -1) {
37017536ee4bSTim Hardeck                         close(vs->lsock);
37027536ee4bSTim Hardeck                         vs->lsock = -1;
37037536ee4bSTim Hardeck                     }
37047536ee4bSTim Hardeck                     goto fail;
37057536ee4bSTim Hardeck                 }
37067536ee4bSTim Hardeck             }
37077536ee4bSTim Hardeck #endif /* CONFIG_VNC_WS */
37087536ee4bSTim Hardeck         }
3709bf7aa45eSGerd Hoffmann         vs->enabled = true;
371082e1cc4bSFam Zheng         qemu_set_fd_handler(vs->lsock, vnc_listen_regular_read, NULL, vs);
37117536ee4bSTim Hardeck #ifdef CONFIG_VNC_WS
3712bf7aa45eSGerd Hoffmann         if (vs->ws_enabled) {
371382e1cc4bSFam Zheng             qemu_set_fd_handler(vs->lwebsock, vnc_listen_websocket_read,
371482e1cc4bSFam Zheng                                 NULL, vs);
37157536ee4bSTim Hardeck         }
37167536ee4bSTim Hardeck #endif /* CONFIG_VNC_WS */
37173e230dd2SCorentin Chary     }
3718e5560329SGerd Hoffmann     qemu_opts_del(sopts);
3719e5560329SGerd Hoffmann     qemu_opts_del(wsopts);
37202d55f0e8SPaolo Bonzini     return;
37211ce52c78SPaolo Bonzini 
37221ce52c78SPaolo Bonzini fail:
3723e5560329SGerd Hoffmann     qemu_opts_del(sopts);
3724e5560329SGerd Hoffmann     qemu_opts_del(wsopts);
3725bf7aa45eSGerd Hoffmann     vs->enabled = false;
37267536ee4bSTim Hardeck #ifdef CONFIG_VNC_WS
3727bf7aa45eSGerd Hoffmann     vs->ws_enabled = false;
37287536ee4bSTim Hardeck #endif /* CONFIG_VNC_WS */
37293e230dd2SCorentin Chary }
373013661089SDaniel P. Berrange 
373114f7143eSGerd Hoffmann void vnc_display_add_client(const char *id, int csock, bool skipauth)
373213661089SDaniel P. Berrange {
373314f7143eSGerd Hoffmann     VncDisplay *vs = vnc_display_find(id);
373413661089SDaniel P. Berrange 
3735d616ccc5SGerd Hoffmann     if (!vs) {
3736d616ccc5SGerd Hoffmann         return;
3737d616ccc5SGerd Hoffmann     }
37382c8cf549SMichael Tokarev     vnc_connect(vs, csock, skipauth, false);
373913661089SDaniel P. Berrange }
37404db14629SGerd Hoffmann 
37419634f4e3SGerd Hoffmann static void vnc_auto_assign_id(QemuOptsList *olist, QemuOpts *opts)
37422779672fSGonglei {
37432779672fSGonglei     int i = 2;
37442779672fSGonglei     char *id;
37452779672fSGonglei 
37462779672fSGonglei     id = g_strdup("default");
37472779672fSGonglei     while (qemu_opts_find(olist, id)) {
37482779672fSGonglei         g_free(id);
37492779672fSGonglei         id = g_strdup_printf("vnc%d", i++);
37502779672fSGonglei     }
37512779672fSGonglei     qemu_opts_set_id(opts, id);
37522779672fSGonglei }
37532779672fSGonglei 
375470b94331SMarkus Armbruster QemuOpts *vnc_parse(const char *str, Error **errp)
37554db14629SGerd Hoffmann {
37564db14629SGerd Hoffmann     QemuOptsList *olist = qemu_find_opts("vnc");
375770b94331SMarkus Armbruster     QemuOpts *opts = qemu_opts_parse(olist, str, true, errp);
375881607cbfSGonglei     const char *id;
37594db14629SGerd Hoffmann 
376081607cbfSGonglei     if (!opts) {
376181607cbfSGonglei         return NULL;
376281607cbfSGonglei     }
376381607cbfSGonglei 
376481607cbfSGonglei     id = qemu_opts_id(opts);
37654db14629SGerd Hoffmann     if (!id) {
37664db14629SGerd Hoffmann         /* auto-assign id if not present */
37672779672fSGonglei         vnc_auto_assign_id(olist, opts);
37689634f4e3SGerd Hoffmann     }
37699634f4e3SGerd Hoffmann     return opts;
37704db14629SGerd Hoffmann }
37714db14629SGerd Hoffmann 
377228d0de7aSMarkus Armbruster int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp)
37739634f4e3SGerd Hoffmann {
37749634f4e3SGerd Hoffmann     Error *local_err = NULL;
37759634f4e3SGerd Hoffmann     char *id = (char *)qemu_opts_id(opts);
37769634f4e3SGerd Hoffmann 
37779634f4e3SGerd Hoffmann     assert(id);
37784db14629SGerd Hoffmann     vnc_display_init(id);
37794db14629SGerd Hoffmann     vnc_display_open(id, &local_err);
37804db14629SGerd Hoffmann     if (local_err != NULL) {
3781bc119048SCole Robinson         error_report("Failed to start VNC server: %s",
37824db14629SGerd Hoffmann                      error_get_pretty(local_err));
37834db14629SGerd Hoffmann         error_free(local_err);
37844db14629SGerd Hoffmann         exit(1);
37854db14629SGerd Hoffmann     }
37864db14629SGerd Hoffmann     return 0;
37874db14629SGerd Hoffmann }
37884db14629SGerd Hoffmann 
37894db14629SGerd Hoffmann static void vnc_register_config(void)
37904db14629SGerd Hoffmann {
37914db14629SGerd Hoffmann     qemu_add_opts(&qemu_vnc_opts);
37924db14629SGerd Hoffmann }
37934db14629SGerd Hoffmann machine_init(vnc_register_config);
3794