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 27e16f4c87SPeter Maydell #include "qemu/osdep.h" 283e230dd2SCorentin Chary #include "vnc.h" 29bd023f95SCorentin Chary #include "vnc-jobs.h" 3040066175SGerd Hoffmann #include "trace.h" 3113d4ff07SMarkus Armbruster #include "hw/qdev-core.h" 329c17d615SPaolo Bonzini #include "sysemu/sysemu.h" 33d49b6836SMarkus Armbruster #include "qemu/error-report.h" 34db725815SMarkus Armbruster #include "qemu/main-loop.h" 350b8fa32fSMarkus Armbruster #include "qemu/module.h" 36922a01a0SMarkus Armbruster #include "qemu/option.h" 371de7afc9SPaolo Bonzini #include "qemu/sockets.h" 381de7afc9SPaolo Bonzini #include "qemu/timer.h" 39b76806d4SDaniel P. Berrange #include "authz/list.h" 404db14629SGerd Hoffmann #include "qemu/config-file.h" 415d75648bSMarkus Armbruster #include "qapi/qapi-emit-events.h" 425d75648bSMarkus Armbruster #include "qapi/qapi-events-ui.h" 43e688df6bSMarkus Armbruster #include "qapi/error.h" 449af23989SMarkus Armbruster #include "qapi/qapi-commands-ui.h" 458d447d10SGerd Hoffmann #include "ui/input.h" 468e9b0d24SDaniel P. Berrange #include "crypto/hash.h" 473e305e4aSDaniel P. Berrange #include "crypto/tlscredsanon.h" 483e305e4aSDaniel P. Berrange #include "crypto/tlscredsx509.h" 49f7b2502cSRichard Henderson #include "crypto/random.h" 503e305e4aSDaniel P. Berrange #include "qom/object_interfaces.h" 51f348b6d1SVeronia Bahaa #include "qemu/cutils.h" 5257a6d6d5SDaniel P. Berrange #include "io/dns-resolver.h" 533e230dd2SCorentin Chary 540f7b2864SGerd Hoffmann #define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT 553e230dd2SCorentin Chary #define VNC_REFRESH_INTERVAL_INC 50 560f7b2864SGerd Hoffmann #define VNC_REFRESH_INTERVAL_MAX GUI_REFRESH_INTERVAL_IDLE 57999342a0SCorentin Chary static const struct timeval VNC_REFRESH_STATS = { 0, 500000 }; 58999342a0SCorentin Chary static const struct timeval VNC_REFRESH_LOSSY = { 2, 0 }; 593e230dd2SCorentin Chary 603e230dd2SCorentin Chary #include "vnc_keysym.h" 61800567a6SDaniel P. Berrange #include "crypto/cipher.h" 623e230dd2SCorentin Chary 63d616ccc5SGerd Hoffmann static QTAILQ_HEAD(, VncDisplay) vnc_displays = 64d616ccc5SGerd Hoffmann QTAILQ_HEAD_INITIALIZER(vnc_displays); 653e230dd2SCorentin Chary 663e230dd2SCorentin Chary static int vnc_cursor_define(VncState *vs); 67e2b72cb6SDaniel P. Berrange static void vnc_update_throttle_offset(VncState *vs); 683e230dd2SCorentin Chary 698cf36489SGerd Hoffmann static void vnc_set_share_mode(VncState *vs, VncShareMode mode) 708cf36489SGerd Hoffmann { 718cf36489SGerd Hoffmann #ifdef _VNC_DEBUG 728cf36489SGerd Hoffmann static const char *mn[] = { 738cf36489SGerd Hoffmann [0] = "undefined", 748cf36489SGerd Hoffmann [VNC_SHARE_MODE_CONNECTING] = "connecting", 758cf36489SGerd Hoffmann [VNC_SHARE_MODE_SHARED] = "shared", 768cf36489SGerd Hoffmann [VNC_SHARE_MODE_EXCLUSIVE] = "exclusive", 778cf36489SGerd Hoffmann [VNC_SHARE_MODE_DISCONNECTED] = "disconnected", 788cf36489SGerd Hoffmann }; 7904d2529dSDaniel P. Berrange fprintf(stderr, "%s/%p: %s -> %s\n", __func__, 8004d2529dSDaniel P. Berrange vs->ioc, mn[vs->share_mode], mn[mode]); 818cf36489SGerd Hoffmann #endif 828cf36489SGerd Hoffmann 83e5f34cddSGerd Hoffmann switch (vs->share_mode) { 84e5f34cddSGerd Hoffmann case VNC_SHARE_MODE_CONNECTING: 85e5f34cddSGerd Hoffmann vs->vd->num_connecting--; 86e5f34cddSGerd Hoffmann break; 87e5f34cddSGerd Hoffmann case VNC_SHARE_MODE_SHARED: 88e5f34cddSGerd Hoffmann vs->vd->num_shared--; 89e5f34cddSGerd Hoffmann break; 90e5f34cddSGerd Hoffmann case VNC_SHARE_MODE_EXCLUSIVE: 918cf36489SGerd Hoffmann vs->vd->num_exclusive--; 92e5f34cddSGerd Hoffmann break; 93e5f34cddSGerd Hoffmann default: 94e5f34cddSGerd Hoffmann break; 958cf36489SGerd Hoffmann } 96e5f34cddSGerd Hoffmann 978cf36489SGerd Hoffmann vs->share_mode = mode; 98e5f34cddSGerd Hoffmann 99e5f34cddSGerd Hoffmann switch (vs->share_mode) { 100e5f34cddSGerd Hoffmann case VNC_SHARE_MODE_CONNECTING: 101e5f34cddSGerd Hoffmann vs->vd->num_connecting++; 102e5f34cddSGerd Hoffmann break; 103e5f34cddSGerd Hoffmann case VNC_SHARE_MODE_SHARED: 104e5f34cddSGerd Hoffmann vs->vd->num_shared++; 105e5f34cddSGerd Hoffmann break; 106e5f34cddSGerd Hoffmann case VNC_SHARE_MODE_EXCLUSIVE: 1078cf36489SGerd Hoffmann vs->vd->num_exclusive++; 108e5f34cddSGerd Hoffmann break; 109e5f34cddSGerd Hoffmann default: 110e5f34cddSGerd Hoffmann break; 1118cf36489SGerd Hoffmann } 1128cf36489SGerd Hoffmann } 1138cf36489SGerd Hoffmann 1143e230dd2SCorentin Chary 115bd269ebcSMarkus Armbruster static void vnc_init_basic_info(SocketAddress *addr, 11698481bfcSEric Blake VncBasicInfo *info, 11798481bfcSEric Blake Error **errp) 1183e230dd2SCorentin Chary { 11904d2529dSDaniel P. Berrange switch (addr->type) { 120bd269ebcSMarkus Armbruster case SOCKET_ADDRESS_TYPE_INET: 121bd269ebcSMarkus Armbruster info->host = g_strdup(addr->u.inet.host); 122bd269ebcSMarkus Armbruster info->service = g_strdup(addr->u.inet.port); 123bd269ebcSMarkus Armbruster if (addr->u.inet.ipv6) { 12404d2529dSDaniel P. Berrange info->family = NETWORK_ADDRESS_FAMILY_IPV6; 12504d2529dSDaniel P. Berrange } else { 12604d2529dSDaniel P. Berrange info->family = NETWORK_ADDRESS_FAMILY_IPV4; 12704d2529dSDaniel P. Berrange } 12804d2529dSDaniel P. Berrange break; 1293e230dd2SCorentin Chary 130bd269ebcSMarkus Armbruster case SOCKET_ADDRESS_TYPE_UNIX: 13104d2529dSDaniel P. Berrange info->host = g_strdup(""); 132bd269ebcSMarkus Armbruster info->service = g_strdup(addr->u.q_unix.path); 13304d2529dSDaniel P. Berrange info->family = NETWORK_ADDRESS_FAMILY_UNIX; 13404d2529dSDaniel P. Berrange break; 13504d2529dSDaniel P. Berrange 136bd269ebcSMarkus Armbruster case SOCKET_ADDRESS_TYPE_VSOCK: 137bd269ebcSMarkus Armbruster case SOCKET_ADDRESS_TYPE_FD: 138a6c76285SMarkus Armbruster error_setg(errp, "Unsupported socket address type %s", 139977c736fSMarkus Armbruster SocketAddressType_str(addr->type)); 14004d2529dSDaniel P. Berrange break; 141a6c76285SMarkus Armbruster default: 142a6c76285SMarkus Armbruster abort(); 14304d2529dSDaniel P. Berrange } 14404d2529dSDaniel P. Berrange 14598481bfcSEric Blake return; 1463e230dd2SCorentin Chary } 1473e230dd2SCorentin Chary 14804d2529dSDaniel P. Berrange static void vnc_init_basic_info_from_server_addr(QIOChannelSocket *ioc, 14904d2529dSDaniel P. Berrange VncBasicInfo *info, 15098481bfcSEric Blake Error **errp) 1513e230dd2SCorentin Chary { 152bd269ebcSMarkus Armbruster SocketAddress *addr = NULL; 1533e230dd2SCorentin Chary 154624cdd46SDaniel P. Berrange if (!ioc) { 155624cdd46SDaniel P. Berrange error_setg(errp, "No listener socket available"); 156624cdd46SDaniel P. Berrange return; 157624cdd46SDaniel P. Berrange } 158624cdd46SDaniel P. Berrange 15904d2529dSDaniel P. Berrange addr = qio_channel_socket_get_local_address(ioc, errp); 16004d2529dSDaniel P. Berrange if (!addr) { 16198481bfcSEric Blake return; 1623e230dd2SCorentin Chary } 1633e230dd2SCorentin Chary 16404d2529dSDaniel P. Berrange vnc_init_basic_info(addr, info, errp); 165bd269ebcSMarkus Armbruster qapi_free_SocketAddress(addr); 1663e230dd2SCorentin Chary } 1673e230dd2SCorentin Chary 16804d2529dSDaniel P. Berrange static void vnc_init_basic_info_from_remote_addr(QIOChannelSocket *ioc, 16904d2529dSDaniel P. Berrange VncBasicInfo *info, 17098481bfcSEric Blake Error **errp) 1713e230dd2SCorentin Chary { 172bd269ebcSMarkus Armbruster SocketAddress *addr = NULL; 1733e230dd2SCorentin Chary 17404d2529dSDaniel P. Berrange addr = qio_channel_socket_get_remote_address(ioc, errp); 17504d2529dSDaniel P. Berrange if (!addr) { 17698481bfcSEric Blake return; 1773e230dd2SCorentin Chary } 1783e230dd2SCorentin Chary 17904d2529dSDaniel P. Berrange vnc_init_basic_info(addr, info, errp); 180bd269ebcSMarkus Armbruster qapi_free_SocketAddress(addr); 1813e230dd2SCorentin Chary } 1823e230dd2SCorentin Chary 1833e230dd2SCorentin Chary static const char *vnc_auth_name(VncDisplay *vd) { 1843e230dd2SCorentin Chary switch (vd->auth) { 1853e230dd2SCorentin Chary case VNC_AUTH_INVALID: 1863e230dd2SCorentin Chary return "invalid"; 1873e230dd2SCorentin Chary case VNC_AUTH_NONE: 1883e230dd2SCorentin Chary return "none"; 1893e230dd2SCorentin Chary case VNC_AUTH_VNC: 1903e230dd2SCorentin Chary return "vnc"; 1913e230dd2SCorentin Chary case VNC_AUTH_RA2: 1923e230dd2SCorentin Chary return "ra2"; 1933e230dd2SCorentin Chary case VNC_AUTH_RA2NE: 1943e230dd2SCorentin Chary return "ra2ne"; 1953e230dd2SCorentin Chary case VNC_AUTH_TIGHT: 1963e230dd2SCorentin Chary return "tight"; 1973e230dd2SCorentin Chary case VNC_AUTH_ULTRA: 1983e230dd2SCorentin Chary return "ultra"; 1993e230dd2SCorentin Chary case VNC_AUTH_TLS: 2003e230dd2SCorentin Chary return "tls"; 2013e230dd2SCorentin Chary case VNC_AUTH_VENCRYPT: 2023e230dd2SCorentin Chary switch (vd->subauth) { 2033e230dd2SCorentin Chary case VNC_AUTH_VENCRYPT_PLAIN: 2043e230dd2SCorentin Chary return "vencrypt+plain"; 2053e230dd2SCorentin Chary case VNC_AUTH_VENCRYPT_TLSNONE: 2063e230dd2SCorentin Chary return "vencrypt+tls+none"; 2073e230dd2SCorentin Chary case VNC_AUTH_VENCRYPT_TLSVNC: 2083e230dd2SCorentin Chary return "vencrypt+tls+vnc"; 2093e230dd2SCorentin Chary case VNC_AUTH_VENCRYPT_TLSPLAIN: 2103e230dd2SCorentin Chary return "vencrypt+tls+plain"; 2113e230dd2SCorentin Chary case VNC_AUTH_VENCRYPT_X509NONE: 2123e230dd2SCorentin Chary return "vencrypt+x509+none"; 2133e230dd2SCorentin Chary case VNC_AUTH_VENCRYPT_X509VNC: 2143e230dd2SCorentin Chary return "vencrypt+x509+vnc"; 2153e230dd2SCorentin Chary case VNC_AUTH_VENCRYPT_X509PLAIN: 2163e230dd2SCorentin Chary return "vencrypt+x509+plain"; 2173e230dd2SCorentin Chary case VNC_AUTH_VENCRYPT_TLSSASL: 2183e230dd2SCorentin Chary return "vencrypt+tls+sasl"; 2193e230dd2SCorentin Chary case VNC_AUTH_VENCRYPT_X509SASL: 2203e230dd2SCorentin Chary return "vencrypt+x509+sasl"; 2213e230dd2SCorentin Chary default: 2223e230dd2SCorentin Chary return "vencrypt"; 2233e230dd2SCorentin Chary } 2243e230dd2SCorentin Chary case VNC_AUTH_SASL: 2253e230dd2SCorentin Chary return "sasl"; 2263e230dd2SCorentin Chary } 2273e230dd2SCorentin Chary return "unknown"; 2283e230dd2SCorentin Chary } 2293e230dd2SCorentin Chary 230d616ccc5SGerd Hoffmann static VncServerInfo *vnc_server_info_get(VncDisplay *vd) 2313e230dd2SCorentin Chary { 232fb6ba0d5SWenchao Xia VncServerInfo *info; 23398481bfcSEric Blake Error *err = NULL; 2343e230dd2SCorentin Chary 23513e1d0e7SDaniel P. Berrange if (!vd->listener || !vd->listener->nsioc) { 2364ee74fa7SDaniel P. Berrange return NULL; 2374ee74fa7SDaniel P. Berrange } 2384ee74fa7SDaniel P. Berrange 2393e7f136dSDaniel P. Berrange info = g_malloc0(sizeof(*info)); 24013e1d0e7SDaniel P. Berrange vnc_init_basic_info_from_server_addr(vd->listener->sioc[0], 241ddf21908SEric Blake qapi_VncServerInfo_base(info), &err); 242fb6ba0d5SWenchao Xia info->has_auth = true; 243d616ccc5SGerd Hoffmann info->auth = g_strdup(vnc_auth_name(vd)); 24498481bfcSEric Blake if (err) { 24598481bfcSEric Blake qapi_free_VncServerInfo(info); 24698481bfcSEric Blake info = NULL; 24798481bfcSEric Blake error_free(err); 24898481bfcSEric Blake } 249fb6ba0d5SWenchao Xia return info; 2503e230dd2SCorentin Chary } 2513e230dd2SCorentin Chary 2523e230dd2SCorentin Chary static void vnc_client_cache_auth(VncState *client) 2533e230dd2SCorentin Chary { 2543e230dd2SCorentin Chary if (!client->info) { 2553e230dd2SCorentin Chary return; 2563e230dd2SCorentin Chary } 2573e230dd2SCorentin Chary 2583e305e4aSDaniel P. Berrange if (client->tls) { 2593e305e4aSDaniel P. Berrange client->info->x509_dname = 2603e305e4aSDaniel P. Berrange qcrypto_tls_session_get_peer_name(client->tls); 2613e305e4aSDaniel P. Berrange client->info->has_x509_dname = 2623e305e4aSDaniel P. Berrange client->info->x509_dname != NULL; 2633e230dd2SCorentin Chary } 2643e230dd2SCorentin Chary #ifdef CONFIG_VNC_SASL 2653e230dd2SCorentin Chary if (client->sasl.conn && 2663e230dd2SCorentin Chary client->sasl.username) { 267fb6ba0d5SWenchao Xia client->info->has_sasl_username = true; 268fb6ba0d5SWenchao Xia client->info->sasl_username = g_strdup(client->sasl.username); 2693e230dd2SCorentin Chary } 2703e230dd2SCorentin Chary #endif 2713e230dd2SCorentin Chary } 2723e230dd2SCorentin Chary 2733e230dd2SCorentin Chary static void vnc_client_cache_addr(VncState *client) 2743e230dd2SCorentin Chary { 27598481bfcSEric Blake Error *err = NULL; 2763e230dd2SCorentin Chary 277fb6ba0d5SWenchao Xia client->info = g_malloc0(sizeof(*client->info)); 27804d2529dSDaniel P. Berrange vnc_init_basic_info_from_remote_addr(client->sioc, 279ddf21908SEric Blake qapi_VncClientInfo_base(client->info), 28098481bfcSEric Blake &err); 281e1b3d477SGerd Hoffmann client->info->websocket = client->websocket; 28298481bfcSEric Blake if (err) { 28398481bfcSEric Blake qapi_free_VncClientInfo(client->info); 28498481bfcSEric Blake client->info = NULL; 28598481bfcSEric Blake error_free(err); 286fb6ba0d5SWenchao Xia } 2873e230dd2SCorentin Chary } 2883e230dd2SCorentin Chary 289fb6ba0d5SWenchao Xia static void vnc_qmp_event(VncState *vs, QAPIEvent event) 2903e230dd2SCorentin Chary { 291fb6ba0d5SWenchao Xia VncServerInfo *si; 2923e230dd2SCorentin Chary 2933e230dd2SCorentin Chary if (!vs->info) { 2943e230dd2SCorentin Chary return; 2953e230dd2SCorentin Chary } 2963e230dd2SCorentin Chary 297d616ccc5SGerd Hoffmann si = vnc_server_info_get(vs->vd); 298fb6ba0d5SWenchao Xia if (!si) { 2993e230dd2SCorentin Chary return; 3003e230dd2SCorentin Chary } 3013e230dd2SCorentin Chary 302fb6ba0d5SWenchao Xia switch (event) { 303fb6ba0d5SWenchao Xia case QAPI_EVENT_VNC_CONNECTED: 3043ab72385SPeter Xu qapi_event_send_vnc_connected(si, qapi_VncClientInfo_base(vs->info)); 305fb6ba0d5SWenchao Xia break; 306fb6ba0d5SWenchao Xia case QAPI_EVENT_VNC_INITIALIZED: 3073ab72385SPeter Xu qapi_event_send_vnc_initialized(si, vs->info); 308fb6ba0d5SWenchao Xia break; 309fb6ba0d5SWenchao Xia case QAPI_EVENT_VNC_DISCONNECTED: 3103ab72385SPeter Xu qapi_event_send_vnc_disconnected(si, vs->info); 311fb6ba0d5SWenchao Xia break; 312fb6ba0d5SWenchao Xia default: 313fb6ba0d5SWenchao Xia break; 314fb6ba0d5SWenchao Xia } 3153e230dd2SCorentin Chary 316fb6ba0d5SWenchao Xia qapi_free_VncServerInfo(si); 3173e230dd2SCorentin Chary } 3183e230dd2SCorentin Chary 3192b54aa87SLuiz Capitulino static VncClientInfo *qmp_query_vnc_client(const VncState *client) 3203e230dd2SCorentin Chary { 3212b54aa87SLuiz Capitulino VncClientInfo *info; 32204d2529dSDaniel P. Berrange Error *err = NULL; 3232b54aa87SLuiz Capitulino 3242b54aa87SLuiz Capitulino info = g_malloc0(sizeof(*info)); 32504d2529dSDaniel P. Berrange 32604d2529dSDaniel P. Berrange vnc_init_basic_info_from_remote_addr(client->sioc, 32704d2529dSDaniel P. Berrange qapi_VncClientInfo_base(info), 32804d2529dSDaniel P. Berrange &err); 32904d2529dSDaniel P. Berrange if (err) { 33004d2529dSDaniel P. Berrange error_free(err); 33104d2529dSDaniel P. Berrange qapi_free_VncClientInfo(info); 33204d2529dSDaniel P. Berrange return NULL; 33304d2529dSDaniel P. Berrange } 33404d2529dSDaniel P. Berrange 335ddf21908SEric Blake info->websocket = client->websocket; 3363e230dd2SCorentin Chary 3373e305e4aSDaniel P. Berrange if (client->tls) { 3383e305e4aSDaniel P. Berrange info->x509_dname = qcrypto_tls_session_get_peer_name(client->tls); 3393e305e4aSDaniel P. Berrange info->has_x509_dname = info->x509_dname != NULL; 3402b54aa87SLuiz Capitulino } 3413e230dd2SCorentin Chary #ifdef CONFIG_VNC_SASL 3422b54aa87SLuiz Capitulino if (client->sasl.conn && client->sasl.username) { 3432b54aa87SLuiz Capitulino info->has_sasl_username = true; 3442b54aa87SLuiz Capitulino info->sasl_username = g_strdup(client->sasl.username); 3452b54aa87SLuiz Capitulino } 3463e230dd2SCorentin Chary #endif 3472b54aa87SLuiz Capitulino 3482b54aa87SLuiz Capitulino return info; 3493e230dd2SCorentin Chary } 3503e230dd2SCorentin Chary 351d616ccc5SGerd Hoffmann static VncDisplay *vnc_display_find(const char *id) 352d616ccc5SGerd Hoffmann { 353d616ccc5SGerd Hoffmann VncDisplay *vd; 354d616ccc5SGerd Hoffmann 355d616ccc5SGerd Hoffmann if (id == NULL) { 356d616ccc5SGerd Hoffmann return QTAILQ_FIRST(&vnc_displays); 357d616ccc5SGerd Hoffmann } 358d616ccc5SGerd Hoffmann QTAILQ_FOREACH(vd, &vnc_displays, next) { 359d616ccc5SGerd Hoffmann if (strcmp(id, vd->id) == 0) { 360d616ccc5SGerd Hoffmann return vd; 361d616ccc5SGerd Hoffmann } 362d616ccc5SGerd Hoffmann } 363d616ccc5SGerd Hoffmann return NULL; 364d616ccc5SGerd Hoffmann } 365d616ccc5SGerd Hoffmann 3662d29a436SGerd Hoffmann static VncClientInfoList *qmp_query_client_list(VncDisplay *vd) 3672d29a436SGerd Hoffmann { 3682d29a436SGerd Hoffmann VncClientInfoList *cinfo, *prev = NULL; 3692d29a436SGerd Hoffmann VncState *client; 3702d29a436SGerd Hoffmann 3712d29a436SGerd Hoffmann QTAILQ_FOREACH(client, &vd->clients, next) { 3722d29a436SGerd Hoffmann cinfo = g_new0(VncClientInfoList, 1); 3732d29a436SGerd Hoffmann cinfo->value = qmp_query_vnc_client(client); 3742d29a436SGerd Hoffmann cinfo->next = prev; 3752d29a436SGerd Hoffmann prev = cinfo; 3762d29a436SGerd Hoffmann } 3772d29a436SGerd Hoffmann return prev; 3782d29a436SGerd Hoffmann } 3792d29a436SGerd Hoffmann 3802b54aa87SLuiz Capitulino VncInfo *qmp_query_vnc(Error **errp) 3813e230dd2SCorentin Chary { 3822b54aa87SLuiz Capitulino VncInfo *info = g_malloc0(sizeof(*info)); 383d616ccc5SGerd Hoffmann VncDisplay *vd = vnc_display_find(NULL); 384bd269ebcSMarkus Armbruster SocketAddress *addr = NULL; 3853e230dd2SCorentin Chary 38613e1d0e7SDaniel P. Berrange if (vd == NULL || !vd->listener || !vd->listener->nsioc) { 3872b54aa87SLuiz Capitulino info->enabled = false; 3883e230dd2SCorentin Chary } else { 3892b54aa87SLuiz Capitulino info->enabled = true; 3902b54aa87SLuiz Capitulino 3912b54aa87SLuiz Capitulino /* for compatibility with the original command */ 3922b54aa87SLuiz Capitulino info->has_clients = true; 3932d29a436SGerd Hoffmann info->clients = qmp_query_client_list(vd); 3943e230dd2SCorentin Chary 39513e1d0e7SDaniel P. Berrange addr = qio_channel_socket_get_local_address(vd->listener->sioc[0], 39613e1d0e7SDaniel P. Berrange errp); 39704d2529dSDaniel P. Berrange if (!addr) { 3982b54aa87SLuiz Capitulino goto out_error; 3992b54aa87SLuiz Capitulino } 4003e230dd2SCorentin Chary 40104d2529dSDaniel P. Berrange switch (addr->type) { 402bd269ebcSMarkus Armbruster case SOCKET_ADDRESS_TYPE_INET: 403bd269ebcSMarkus Armbruster info->host = g_strdup(addr->u.inet.host); 404bd269ebcSMarkus Armbruster info->service = g_strdup(addr->u.inet.port); 405bd269ebcSMarkus Armbruster if (addr->u.inet.ipv6) { 40604d2529dSDaniel P. Berrange info->family = NETWORK_ADDRESS_FAMILY_IPV6; 40704d2529dSDaniel P. Berrange } else { 40804d2529dSDaniel P. Berrange info->family = NETWORK_ADDRESS_FAMILY_IPV4; 40904d2529dSDaniel P. Berrange } 41004d2529dSDaniel P. Berrange break; 41104d2529dSDaniel P. Berrange 412bd269ebcSMarkus Armbruster case SOCKET_ADDRESS_TYPE_UNIX: 41304d2529dSDaniel P. Berrange info->host = g_strdup(""); 414bd269ebcSMarkus Armbruster info->service = g_strdup(addr->u.q_unix.path); 41504d2529dSDaniel P. Berrange info->family = NETWORK_ADDRESS_FAMILY_UNIX; 41604d2529dSDaniel P. Berrange break; 41704d2529dSDaniel P. Berrange 418bd269ebcSMarkus Armbruster case SOCKET_ADDRESS_TYPE_VSOCK: 419bd269ebcSMarkus Armbruster case SOCKET_ADDRESS_TYPE_FD: 420a6c76285SMarkus Armbruster error_setg(errp, "Unsupported socket address type %s", 421977c736fSMarkus Armbruster SocketAddressType_str(addr->type)); 4222b54aa87SLuiz Capitulino goto out_error; 423a6c76285SMarkus Armbruster default: 424a6c76285SMarkus Armbruster abort(); 4253e230dd2SCorentin Chary } 4262b54aa87SLuiz Capitulino 4272b54aa87SLuiz Capitulino info->has_host = true; 4282b54aa87SLuiz Capitulino info->has_service = true; 4292b54aa87SLuiz Capitulino info->has_family = true; 4302b54aa87SLuiz Capitulino 4312b54aa87SLuiz Capitulino info->has_auth = true; 432d616ccc5SGerd Hoffmann info->auth = g_strdup(vnc_auth_name(vd)); 4333e230dd2SCorentin Chary } 4342b54aa87SLuiz Capitulino 435bd269ebcSMarkus Armbruster qapi_free_SocketAddress(addr); 4362b54aa87SLuiz Capitulino return info; 4372b54aa87SLuiz Capitulino 4382b54aa87SLuiz Capitulino out_error: 439bd269ebcSMarkus Armbruster qapi_free_SocketAddress(addr); 4402b54aa87SLuiz Capitulino qapi_free_VncInfo(info); 4412b54aa87SLuiz Capitulino return NULL; 4423e230dd2SCorentin Chary } 4433e230dd2SCorentin Chary 4442a7e6857SDaniel P. Berrange 4452a7e6857SDaniel P. Berrange static void qmp_query_auth(int auth, int subauth, 4462a7e6857SDaniel P. Berrange VncPrimaryAuth *qmp_auth, 4472a7e6857SDaniel P. Berrange VncVencryptSubAuth *qmp_vencrypt, 4482a7e6857SDaniel P. Berrange bool *qmp_has_vencrypt); 4492a7e6857SDaniel P. Berrange 4502a7e6857SDaniel P. Berrange static VncServerInfo2List *qmp_query_server_entry(QIOChannelSocket *ioc, 4514478aa76SGerd Hoffmann bool websocket, 4522a7e6857SDaniel P. Berrange int auth, 4532a7e6857SDaniel P. Berrange int subauth, 4542a7e6857SDaniel P. Berrange VncServerInfo2List *prev) 455df887684SGerd Hoffmann { 4562a7e6857SDaniel P. Berrange VncServerInfo2List *list; 4572a7e6857SDaniel P. Berrange VncServerInfo2 *info; 45804d2529dSDaniel P. Berrange Error *err = NULL; 459bd269ebcSMarkus Armbruster SocketAddress *addr; 460df887684SGerd Hoffmann 4619261ef5eSMarkus Armbruster addr = qio_channel_socket_get_local_address(ioc, NULL); 46204d2529dSDaniel P. Berrange if (!addr) { 463df887684SGerd Hoffmann return prev; 464df887684SGerd Hoffmann } 465df887684SGerd Hoffmann 4662a7e6857SDaniel P. Berrange info = g_new0(VncServerInfo2, 1); 4672a7e6857SDaniel P. Berrange vnc_init_basic_info(addr, qapi_VncServerInfo2_base(info), &err); 468bd269ebcSMarkus Armbruster qapi_free_SocketAddress(addr); 46904d2529dSDaniel P. Berrange if (err) { 4702a7e6857SDaniel P. Berrange qapi_free_VncServerInfo2(info); 47104d2529dSDaniel P. Berrange error_free(err); 47204d2529dSDaniel P. Berrange return prev; 47304d2529dSDaniel P. Berrange } 4744478aa76SGerd Hoffmann info->websocket = websocket; 475df887684SGerd Hoffmann 4762a7e6857SDaniel P. Berrange qmp_query_auth(auth, subauth, &info->auth, 4772a7e6857SDaniel P. Berrange &info->vencrypt, &info->has_vencrypt); 4782a7e6857SDaniel P. Berrange 4792a7e6857SDaniel P. Berrange list = g_new0(VncServerInfo2List, 1); 480df887684SGerd Hoffmann list->value = info; 481df887684SGerd Hoffmann list->next = prev; 482df887684SGerd Hoffmann return list; 483df887684SGerd Hoffmann } 484df887684SGerd Hoffmann 4852a7e6857SDaniel P. Berrange static void qmp_query_auth(int auth, int subauth, 4862a7e6857SDaniel P. Berrange VncPrimaryAuth *qmp_auth, 4872a7e6857SDaniel P. Berrange VncVencryptSubAuth *qmp_vencrypt, 4882a7e6857SDaniel P. Berrange bool *qmp_has_vencrypt) 489df887684SGerd Hoffmann { 4902a7e6857SDaniel P. Berrange switch (auth) { 491df887684SGerd Hoffmann case VNC_AUTH_VNC: 4922a7e6857SDaniel P. Berrange *qmp_auth = VNC_PRIMARY_AUTH_VNC; 493df887684SGerd Hoffmann break; 494df887684SGerd Hoffmann case VNC_AUTH_RA2: 4952a7e6857SDaniel P. Berrange *qmp_auth = VNC_PRIMARY_AUTH_RA2; 496df887684SGerd Hoffmann break; 497df887684SGerd Hoffmann case VNC_AUTH_RA2NE: 4982a7e6857SDaniel P. Berrange *qmp_auth = VNC_PRIMARY_AUTH_RA2NE; 499df887684SGerd Hoffmann break; 500df887684SGerd Hoffmann case VNC_AUTH_TIGHT: 5012a7e6857SDaniel P. Berrange *qmp_auth = VNC_PRIMARY_AUTH_TIGHT; 502df887684SGerd Hoffmann break; 503df887684SGerd Hoffmann case VNC_AUTH_ULTRA: 5042a7e6857SDaniel P. Berrange *qmp_auth = VNC_PRIMARY_AUTH_ULTRA; 505df887684SGerd Hoffmann break; 506df887684SGerd Hoffmann case VNC_AUTH_TLS: 5072a7e6857SDaniel P. Berrange *qmp_auth = VNC_PRIMARY_AUTH_TLS; 508df887684SGerd Hoffmann break; 509df887684SGerd Hoffmann case VNC_AUTH_VENCRYPT: 5102a7e6857SDaniel P. Berrange *qmp_auth = VNC_PRIMARY_AUTH_VENCRYPT; 5112a7e6857SDaniel P. Berrange *qmp_has_vencrypt = true; 5122a7e6857SDaniel P. Berrange switch (subauth) { 513df887684SGerd Hoffmann case VNC_AUTH_VENCRYPT_PLAIN: 5142a7e6857SDaniel P. Berrange *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_PLAIN; 515df887684SGerd Hoffmann break; 516df887684SGerd Hoffmann case VNC_AUTH_VENCRYPT_TLSNONE: 5172a7e6857SDaniel P. Berrange *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_NONE; 518df887684SGerd Hoffmann break; 519df887684SGerd Hoffmann case VNC_AUTH_VENCRYPT_TLSVNC: 5202a7e6857SDaniel P. Berrange *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_VNC; 521df887684SGerd Hoffmann break; 522df887684SGerd Hoffmann case VNC_AUTH_VENCRYPT_TLSPLAIN: 5232a7e6857SDaniel P. Berrange *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_PLAIN; 524df887684SGerd Hoffmann break; 525df887684SGerd Hoffmann case VNC_AUTH_VENCRYPT_X509NONE: 5262a7e6857SDaniel P. Berrange *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_NONE; 527df887684SGerd Hoffmann break; 528df887684SGerd Hoffmann case VNC_AUTH_VENCRYPT_X509VNC: 5292a7e6857SDaniel P. Berrange *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_VNC; 530df887684SGerd Hoffmann break; 531df887684SGerd Hoffmann case VNC_AUTH_VENCRYPT_X509PLAIN: 5322a7e6857SDaniel P. Berrange *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_PLAIN; 533df887684SGerd Hoffmann break; 534df887684SGerd Hoffmann case VNC_AUTH_VENCRYPT_TLSSASL: 5352a7e6857SDaniel P. Berrange *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_SASL; 536df887684SGerd Hoffmann break; 537df887684SGerd Hoffmann case VNC_AUTH_VENCRYPT_X509SASL: 5382a7e6857SDaniel P. Berrange *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_SASL; 539df887684SGerd Hoffmann break; 540df887684SGerd Hoffmann default: 5412a7e6857SDaniel P. Berrange *qmp_has_vencrypt = false; 542df887684SGerd Hoffmann break; 543df887684SGerd Hoffmann } 544df887684SGerd Hoffmann break; 545df887684SGerd Hoffmann case VNC_AUTH_SASL: 5462a7e6857SDaniel P. Berrange *qmp_auth = VNC_PRIMARY_AUTH_SASL; 547df887684SGerd Hoffmann break; 548df887684SGerd Hoffmann case VNC_AUTH_NONE: 549df887684SGerd Hoffmann default: 5502a7e6857SDaniel P. Berrange *qmp_auth = VNC_PRIMARY_AUTH_NONE; 551df887684SGerd Hoffmann break; 552df887684SGerd Hoffmann } 553df887684SGerd Hoffmann } 554df887684SGerd Hoffmann 555df887684SGerd Hoffmann VncInfo2List *qmp_query_vnc_servers(Error **errp) 556df887684SGerd Hoffmann { 557df887684SGerd Hoffmann VncInfo2List *item, *prev = NULL; 558df887684SGerd Hoffmann VncInfo2 *info; 559df887684SGerd Hoffmann VncDisplay *vd; 560df887684SGerd Hoffmann DeviceState *dev; 5614ee74fa7SDaniel P. Berrange size_t i; 562df887684SGerd Hoffmann 563df887684SGerd Hoffmann QTAILQ_FOREACH(vd, &vnc_displays, next) { 564df887684SGerd Hoffmann info = g_new0(VncInfo2, 1); 565df887684SGerd Hoffmann info->id = g_strdup(vd->id); 566df887684SGerd Hoffmann info->clients = qmp_query_client_list(vd); 5672a7e6857SDaniel P. Berrange qmp_query_auth(vd->auth, vd->subauth, &info->auth, 5682a7e6857SDaniel P. Berrange &info->vencrypt, &info->has_vencrypt); 569df887684SGerd Hoffmann if (vd->dcl.con) { 570df887684SGerd Hoffmann dev = DEVICE(object_property_get_link(OBJECT(vd->dcl.con), 571552d7f49SMarkus Armbruster "device", &error_abort)); 572df887684SGerd Hoffmann info->has_display = true; 573df887684SGerd Hoffmann info->display = g_strdup(dev->id); 574df887684SGerd Hoffmann } 57513e1d0e7SDaniel P. Berrange for (i = 0; vd->listener != NULL && i < vd->listener->nsioc; i++) { 57604d2529dSDaniel P. Berrange info->server = qmp_query_server_entry( 57713e1d0e7SDaniel P. Berrange vd->listener->sioc[i], false, vd->auth, vd->subauth, 57813e1d0e7SDaniel P. Berrange info->server); 579df887684SGerd Hoffmann } 58013e1d0e7SDaniel P. Berrange for (i = 0; vd->wslistener != NULL && i < vd->wslistener->nsioc; i++) { 58104d2529dSDaniel P. Berrange info->server = qmp_query_server_entry( 58213e1d0e7SDaniel P. Berrange vd->wslistener->sioc[i], true, vd->ws_auth, 5834ee74fa7SDaniel P. Berrange vd->ws_subauth, info->server); 584df887684SGerd Hoffmann } 585df887684SGerd Hoffmann 586df887684SGerd Hoffmann item = g_new0(VncInfo2List, 1); 587df887684SGerd Hoffmann item->value = info; 588df887684SGerd Hoffmann item->next = prev; 589df887684SGerd Hoffmann prev = item; 590df887684SGerd Hoffmann } 591df887684SGerd Hoffmann return prev; 592df887684SGerd Hoffmann } 593df887684SGerd Hoffmann 5943e230dd2SCorentin Chary /* TODO 5953e230dd2SCorentin Chary 1) Get the queue working for IO. 5963e230dd2SCorentin Chary 2) there is some weirdness when using the -S option (the screen is grey 5973e230dd2SCorentin Chary and not totally invalidated 5983e230dd2SCorentin Chary 3) resolutions > 1024 5993e230dd2SCorentin Chary */ 6003e230dd2SCorentin Chary 6016af998dbSDaniel P. Berrange static int vnc_update_client(VncState *vs, int has_dirty); 6023e230dd2SCorentin Chary static void vnc_disconnect_start(VncState *vs); 6033e230dd2SCorentin Chary 6043e230dd2SCorentin Chary static void vnc_colordepth(VncState *vs); 6053e230dd2SCorentin Chary static void framebuffer_update_request(VncState *vs, int incremental, 6063e230dd2SCorentin Chary int x_position, int y_position, 6073e230dd2SCorentin Chary int w, int h); 6080f7b2864SGerd Hoffmann static void vnc_refresh(DisplayChangeListener *dcl); 6093e230dd2SCorentin Chary static int vnc_refresh_server_surface(VncDisplay *vd); 6103e230dd2SCorentin Chary 611d05959c2SGerd Hoffmann static int vnc_width(VncDisplay *vd) 612d05959c2SGerd Hoffmann { 613d05959c2SGerd Hoffmann return MIN(VNC_MAX_WIDTH, ROUND_UP(surface_width(vd->ds), 614d05959c2SGerd Hoffmann VNC_DIRTY_PIXELS_PER_BIT)); 615d05959c2SGerd Hoffmann } 616d05959c2SGerd Hoffmann 617d05959c2SGerd Hoffmann static int vnc_height(VncDisplay *vd) 618d05959c2SGerd Hoffmann { 619d05959c2SGerd Hoffmann return MIN(VNC_MAX_HEIGHT, surface_height(vd->ds)); 620d05959c2SGerd Hoffmann } 621d05959c2SGerd Hoffmann 622bea60dd7SPeter Lieven static void vnc_set_area_dirty(DECLARE_BITMAP(dirty[VNC_MAX_HEIGHT], 623bea60dd7SPeter Lieven VNC_MAX_WIDTH / VNC_DIRTY_PIXELS_PER_BIT), 624f7b3d68cSGerd Hoffmann VncDisplay *vd, 625f7b3d68cSGerd Hoffmann int x, int y, int w, int h) 626f7b3d68cSGerd Hoffmann { 627f7b3d68cSGerd Hoffmann int width = vnc_width(vd); 628f7b3d68cSGerd Hoffmann int height = vnc_height(vd); 629f7b3d68cSGerd Hoffmann 63091937225SPeter Lieven /* this is needed this to ensure we updated all affected 63191937225SPeter Lieven * blocks if x % VNC_DIRTY_PIXELS_PER_BIT != 0 */ 632b4c85ddcSPeter Lieven w += (x % VNC_DIRTY_PIXELS_PER_BIT); 633b4c85ddcSPeter Lieven x -= (x % VNC_DIRTY_PIXELS_PER_BIT); 6343e230dd2SCorentin Chary 6359f64916dSGerd Hoffmann x = MIN(x, width); 6369f64916dSGerd Hoffmann y = MIN(y, height); 6379f64916dSGerd Hoffmann w = MIN(x + w, width) - x; 63891937225SPeter Lieven h = MIN(y + h, height); 6393e230dd2SCorentin Chary 640b4c85ddcSPeter Lieven for (; y < h; y++) { 641bea60dd7SPeter Lieven bitmap_set(dirty[y], x / VNC_DIRTY_PIXELS_PER_BIT, 64291937225SPeter Lieven DIV_ROUND_UP(w, VNC_DIRTY_PIXELS_PER_BIT)); 643b4c85ddcSPeter Lieven } 6443e230dd2SCorentin Chary } 6453e230dd2SCorentin Chary 646bea60dd7SPeter Lieven static void vnc_dpy_update(DisplayChangeListener *dcl, 647bea60dd7SPeter Lieven int x, int y, int w, int h) 648bea60dd7SPeter Lieven { 649bea60dd7SPeter Lieven VncDisplay *vd = container_of(dcl, VncDisplay, dcl); 650bea60dd7SPeter Lieven struct VncSurface *s = &vd->guest; 651bea60dd7SPeter Lieven 652f7b3d68cSGerd Hoffmann vnc_set_area_dirty(s->dirty, vd, x, y, w, h); 653bea60dd7SPeter Lieven } 654bea60dd7SPeter Lieven 6553e230dd2SCorentin Chary void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h, 6563e230dd2SCorentin Chary int32_t encoding) 6573e230dd2SCorentin Chary { 6583e230dd2SCorentin Chary vnc_write_u16(vs, x); 6593e230dd2SCorentin Chary vnc_write_u16(vs, y); 6603e230dd2SCorentin Chary vnc_write_u16(vs, w); 6613e230dd2SCorentin Chary vnc_write_u16(vs, h); 6623e230dd2SCorentin Chary 6633e230dd2SCorentin Chary vnc_write_s32(vs, encoding); 6643e230dd2SCorentin Chary } 6653e230dd2SCorentin Chary 66632ed2680STim Hardeck 6673e230dd2SCorentin Chary static void vnc_desktop_resize(VncState *vs) 6683e230dd2SCorentin Chary { 66904d2529dSDaniel P. Berrange if (vs->ioc == NULL || !vnc_has_feature(vs, VNC_FEATURE_RESIZE)) { 6703e230dd2SCorentin Chary return; 6713e230dd2SCorentin Chary } 672bea60dd7SPeter Lieven if (vs->client_width == pixman_image_get_width(vs->vd->server) && 673bea60dd7SPeter Lieven vs->client_height == pixman_image_get_height(vs->vd->server)) { 6743e230dd2SCorentin Chary return; 6753e230dd2SCorentin Chary } 6764c956bd8SDaniel P. Berrange 6774c956bd8SDaniel P. Berrange assert(pixman_image_get_width(vs->vd->server) < 65536 && 6784c956bd8SDaniel P. Berrange pixman_image_get_width(vs->vd->server) >= 0); 6794c956bd8SDaniel P. Berrange assert(pixman_image_get_height(vs->vd->server) < 65536 && 6804c956bd8SDaniel P. Berrange pixman_image_get_height(vs->vd->server) >= 0); 681bea60dd7SPeter Lieven vs->client_width = pixman_image_get_width(vs->vd->server); 682bea60dd7SPeter Lieven vs->client_height = pixman_image_get_height(vs->vd->server); 683bd023f95SCorentin Chary vnc_lock_output(vs); 6843e230dd2SCorentin Chary vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE); 6853e230dd2SCorentin Chary vnc_write_u8(vs, 0); 6863e230dd2SCorentin Chary vnc_write_u16(vs, 1); /* number of rects */ 6873e230dd2SCorentin Chary vnc_framebuffer_update(vs, 0, 0, vs->client_width, vs->client_height, 6883e230dd2SCorentin Chary VNC_ENCODING_DESKTOPRESIZE); 689bd023f95SCorentin Chary vnc_unlock_output(vs); 6903e230dd2SCorentin Chary vnc_flush(vs); 6913e230dd2SCorentin Chary } 6923e230dd2SCorentin Chary 693bd023f95SCorentin Chary static void vnc_abort_display_jobs(VncDisplay *vd) 694bd023f95SCorentin Chary { 695bd023f95SCorentin Chary VncState *vs; 696bd023f95SCorentin Chary 697bd023f95SCorentin Chary QTAILQ_FOREACH(vs, &vd->clients, next) { 698bd023f95SCorentin Chary vnc_lock_output(vs); 699bd023f95SCorentin Chary vs->abort = true; 700bd023f95SCorentin Chary vnc_unlock_output(vs); 701bd023f95SCorentin Chary } 702bd023f95SCorentin Chary QTAILQ_FOREACH(vs, &vd->clients, next) { 703bd023f95SCorentin Chary vnc_jobs_join(vs); 704bd023f95SCorentin Chary } 705bd023f95SCorentin Chary QTAILQ_FOREACH(vs, &vd->clients, next) { 706bd023f95SCorentin Chary vnc_lock_output(vs); 707bbcdeb62SGerd Hoffmann if (vs->update == VNC_STATE_UPDATE_NONE && 708bbcdeb62SGerd Hoffmann vs->job_update != VNC_STATE_UPDATE_NONE) { 709bbcdeb62SGerd Hoffmann /* job aborted before completion */ 710bbcdeb62SGerd Hoffmann vs->update = vs->job_update; 711bbcdeb62SGerd Hoffmann vs->job_update = VNC_STATE_UPDATE_NONE; 712bbcdeb62SGerd Hoffmann } 713bd023f95SCorentin Chary vs->abort = false; 714bd023f95SCorentin Chary vnc_unlock_output(vs); 715bd023f95SCorentin Chary } 716bd023f95SCorentin Chary } 717bd023f95SCorentin Chary 7189f64916dSGerd Hoffmann int vnc_server_fb_stride(VncDisplay *vd) 7199f64916dSGerd Hoffmann { 7209f64916dSGerd Hoffmann return pixman_image_get_stride(vd->server); 7219f64916dSGerd Hoffmann } 7229f64916dSGerd Hoffmann 7239f64916dSGerd Hoffmann void *vnc_server_fb_ptr(VncDisplay *vd, int x, int y) 7249f64916dSGerd Hoffmann { 7259f64916dSGerd Hoffmann uint8_t *ptr; 7269f64916dSGerd Hoffmann 7279f64916dSGerd Hoffmann ptr = (uint8_t *)pixman_image_get_data(vd->server); 7289f64916dSGerd Hoffmann ptr += y * vnc_server_fb_stride(vd); 7299f64916dSGerd Hoffmann ptr += x * VNC_SERVER_FB_BYTES; 7309f64916dSGerd Hoffmann return ptr; 7319f64916dSGerd Hoffmann } 7329f64916dSGerd Hoffmann 733453f842bSGerd Hoffmann static void vnc_update_server_surface(VncDisplay *vd) 734453f842bSGerd Hoffmann { 735b69a553bSDaniel P. Berrange int width, height; 736b69a553bSDaniel P. Berrange 737453f842bSGerd Hoffmann qemu_pixman_image_unref(vd->server); 738453f842bSGerd Hoffmann vd->server = NULL; 739453f842bSGerd Hoffmann 740c7628bffSGerd Hoffmann if (QTAILQ_EMPTY(&vd->clients)) { 741c7628bffSGerd Hoffmann return; 742c7628bffSGerd Hoffmann } 743c7628bffSGerd Hoffmann 744b69a553bSDaniel P. Berrange width = vnc_width(vd); 745b69a553bSDaniel P. Berrange height = vnc_height(vd); 746453f842bSGerd Hoffmann vd->server = pixman_image_create_bits(VNC_SERVER_FB_FORMAT, 747b69a553bSDaniel P. Berrange width, height, 748453f842bSGerd Hoffmann NULL, 0); 749b69a553bSDaniel P. Berrange 750b69a553bSDaniel P. Berrange memset(vd->guest.dirty, 0x00, sizeof(vd->guest.dirty)); 751b69a553bSDaniel P. Berrange vnc_set_area_dirty(vd->guest.dirty, vd, 0, 0, 752b69a553bSDaniel P. Berrange width, height); 753453f842bSGerd Hoffmann } 754453f842bSGerd Hoffmann 75561e77a5fSGerd Hoffmann static bool vnc_check_pageflip(DisplaySurface *s1, 75661e77a5fSGerd Hoffmann DisplaySurface *s2) 75761e77a5fSGerd Hoffmann { 75861e77a5fSGerd Hoffmann return (s1 != NULL && 75961e77a5fSGerd Hoffmann s2 != NULL && 76061e77a5fSGerd Hoffmann surface_width(s1) == surface_width(s2) && 76161e77a5fSGerd Hoffmann surface_height(s1) == surface_height(s2) && 76261e77a5fSGerd Hoffmann surface_format(s1) == surface_format(s2)); 76361e77a5fSGerd Hoffmann 76461e77a5fSGerd Hoffmann } 76561e77a5fSGerd Hoffmann 766c12aeb86SGerd Hoffmann static void vnc_dpy_switch(DisplayChangeListener *dcl, 767c12aeb86SGerd Hoffmann DisplaySurface *surface) 7683e230dd2SCorentin Chary { 7692e5567c9SGerd Hoffmann static const char placeholder_msg[] = 7702e5567c9SGerd Hoffmann "Display output is not active."; 7712e5567c9SGerd Hoffmann static DisplaySurface *placeholder; 77221ef45d7SGerd Hoffmann VncDisplay *vd = container_of(dcl, VncDisplay, dcl); 77361e77a5fSGerd Hoffmann bool pageflip = vnc_check_pageflip(vd->ds, surface); 7743e230dd2SCorentin Chary VncState *vs; 7753e230dd2SCorentin Chary 7762e5567c9SGerd Hoffmann if (surface == NULL) { 7772e5567c9SGerd Hoffmann if (placeholder == NULL) { 7782e5567c9SGerd Hoffmann placeholder = qemu_create_message_surface(640, 480, placeholder_msg); 7792e5567c9SGerd Hoffmann } 7802e5567c9SGerd Hoffmann surface = placeholder; 7812e5567c9SGerd Hoffmann } 7822e5567c9SGerd Hoffmann 783bd023f95SCorentin Chary vnc_abort_display_jobs(vd); 784453f842bSGerd Hoffmann vd->ds = surface; 785bd023f95SCorentin Chary 7863e230dd2SCorentin Chary /* guest surface */ 7879f64916dSGerd Hoffmann qemu_pixman_image_unref(vd->guest.fb); 788d39fa6d8SGerd Hoffmann vd->guest.fb = pixman_image_ref(surface->image); 789d39fa6d8SGerd Hoffmann vd->guest.format = surface->format; 7903e230dd2SCorentin Chary 79161e77a5fSGerd Hoffmann if (pageflip) { 79261e77a5fSGerd Hoffmann vnc_set_area_dirty(vd->guest.dirty, vd, 0, 0, 79361e77a5fSGerd Hoffmann surface_width(surface), 79461e77a5fSGerd Hoffmann surface_height(surface)); 79561e77a5fSGerd Hoffmann return; 79661e77a5fSGerd Hoffmann } 79761e77a5fSGerd Hoffmann 79861e77a5fSGerd Hoffmann /* server surface */ 79961e77a5fSGerd Hoffmann vnc_update_server_surface(vd); 80061e77a5fSGerd Hoffmann 8013e230dd2SCorentin Chary QTAILQ_FOREACH(vs, &vd->clients, next) { 8023e230dd2SCorentin Chary vnc_colordepth(vs); 8033e230dd2SCorentin Chary vnc_desktop_resize(vs); 8043e230dd2SCorentin Chary if (vs->vd->cursor) { 8053e230dd2SCorentin Chary vnc_cursor_define(vs); 8063e230dd2SCorentin Chary } 807bea60dd7SPeter Lieven memset(vs->dirty, 0x00, sizeof(vs->dirty)); 808f7b3d68cSGerd Hoffmann vnc_set_area_dirty(vs->dirty, vd, 0, 0, 809b69a553bSDaniel P. Berrange vnc_width(vd), 810b69a553bSDaniel P. Berrange vnc_height(vd)); 811e2b72cb6SDaniel P. Berrange vnc_update_throttle_offset(vs); 8123e230dd2SCorentin Chary } 8133e230dd2SCorentin Chary } 8143e230dd2SCorentin Chary 8153e230dd2SCorentin Chary /* fastest code */ 8169f64916dSGerd Hoffmann static void vnc_write_pixels_copy(VncState *vs, 8173e230dd2SCorentin Chary void *pixels, int size) 8183e230dd2SCorentin Chary { 8193e230dd2SCorentin Chary vnc_write(vs, pixels, size); 8203e230dd2SCorentin Chary } 8213e230dd2SCorentin Chary 8223e230dd2SCorentin Chary /* slowest but generic code. */ 8233e230dd2SCorentin Chary void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v) 8243e230dd2SCorentin Chary { 8253e230dd2SCorentin Chary uint8_t r, g, b; 8263e230dd2SCorentin Chary 8279f64916dSGerd Hoffmann #if VNC_SERVER_FB_FORMAT == PIXMAN_FORMAT(32, PIXMAN_TYPE_ARGB, 0, 8, 8, 8) 8289f64916dSGerd Hoffmann r = (((v & 0x00ff0000) >> 16) << vs->client_pf.rbits) >> 8; 8299f64916dSGerd Hoffmann g = (((v & 0x0000ff00) >> 8) << vs->client_pf.gbits) >> 8; 8309f64916dSGerd Hoffmann b = (((v & 0x000000ff) >> 0) << vs->client_pf.bbits) >> 8; 8319f64916dSGerd Hoffmann #else 8329f64916dSGerd Hoffmann # error need some bits here if you change VNC_SERVER_FB_FORMAT 8339f64916dSGerd Hoffmann #endif 8349f64916dSGerd Hoffmann v = (r << vs->client_pf.rshift) | 8359f64916dSGerd Hoffmann (g << vs->client_pf.gshift) | 8369f64916dSGerd Hoffmann (b << vs->client_pf.bshift); 8379f64916dSGerd Hoffmann switch (vs->client_pf.bytes_per_pixel) { 8383e230dd2SCorentin Chary case 1: 8393e230dd2SCorentin Chary buf[0] = v; 8403e230dd2SCorentin Chary break; 8413e230dd2SCorentin Chary case 2: 8429f64916dSGerd Hoffmann if (vs->client_be) { 8433e230dd2SCorentin Chary buf[0] = v >> 8; 8443e230dd2SCorentin Chary buf[1] = v; 8453e230dd2SCorentin Chary } else { 8463e230dd2SCorentin Chary buf[1] = v >> 8; 8473e230dd2SCorentin Chary buf[0] = v; 8483e230dd2SCorentin Chary } 8493e230dd2SCorentin Chary break; 8503e230dd2SCorentin Chary default: 8513e230dd2SCorentin Chary case 4: 8529f64916dSGerd Hoffmann if (vs->client_be) { 8533e230dd2SCorentin Chary buf[0] = v >> 24; 8543e230dd2SCorentin Chary buf[1] = v >> 16; 8553e230dd2SCorentin Chary buf[2] = v >> 8; 8563e230dd2SCorentin Chary buf[3] = v; 8573e230dd2SCorentin Chary } else { 8583e230dd2SCorentin Chary buf[3] = v >> 24; 8593e230dd2SCorentin Chary buf[2] = v >> 16; 8603e230dd2SCorentin Chary buf[1] = v >> 8; 8613e230dd2SCorentin Chary buf[0] = v; 8623e230dd2SCorentin Chary } 8633e230dd2SCorentin Chary break; 8643e230dd2SCorentin Chary } 8653e230dd2SCorentin Chary } 8663e230dd2SCorentin Chary 8679f64916dSGerd Hoffmann static void vnc_write_pixels_generic(VncState *vs, 8683e230dd2SCorentin Chary void *pixels1, int size) 8693e230dd2SCorentin Chary { 8703e230dd2SCorentin Chary uint8_t buf[4]; 8713e230dd2SCorentin Chary 8729f64916dSGerd Hoffmann if (VNC_SERVER_FB_BYTES == 4) { 8733e230dd2SCorentin Chary uint32_t *pixels = pixels1; 8743e230dd2SCorentin Chary int n, i; 8753e230dd2SCorentin Chary n = size >> 2; 8763e230dd2SCorentin Chary for (i = 0; i < n; i++) { 8773e230dd2SCorentin Chary vnc_convert_pixel(vs, buf, pixels[i]); 8789f64916dSGerd Hoffmann vnc_write(vs, buf, vs->client_pf.bytes_per_pixel); 8793e230dd2SCorentin Chary } 8803e230dd2SCorentin Chary } 8813e230dd2SCorentin Chary } 8823e230dd2SCorentin Chary 8833e230dd2SCorentin Chary int vnc_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) 8843e230dd2SCorentin Chary { 8853e230dd2SCorentin Chary int i; 8863e230dd2SCorentin Chary uint8_t *row; 8873e230dd2SCorentin Chary VncDisplay *vd = vs->vd; 8883e230dd2SCorentin Chary 8899f64916dSGerd Hoffmann row = vnc_server_fb_ptr(vd, x, y); 8903e230dd2SCorentin Chary for (i = 0; i < h; i++) { 8919f64916dSGerd Hoffmann vs->write_pixels(vs, row, w * VNC_SERVER_FB_BYTES); 8929f64916dSGerd Hoffmann row += vnc_server_fb_stride(vd); 8933e230dd2SCorentin Chary } 8943e230dd2SCorentin Chary return 1; 8953e230dd2SCorentin Chary } 8963e230dd2SCorentin Chary 897bd023f95SCorentin Chary int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) 8983e230dd2SCorentin Chary { 8993e230dd2SCorentin Chary int n = 0; 9003e230dd2SCorentin Chary 9013e230dd2SCorentin Chary switch(vs->vnc_encoding) { 9023e230dd2SCorentin Chary case VNC_ENCODING_ZLIB: 9033e230dd2SCorentin Chary n = vnc_zlib_send_framebuffer_update(vs, x, y, w, h); 9043e230dd2SCorentin Chary break; 9053e230dd2SCorentin Chary case VNC_ENCODING_HEXTILE: 9063e230dd2SCorentin Chary vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_HEXTILE); 9073e230dd2SCorentin Chary n = vnc_hextile_send_framebuffer_update(vs, x, y, w, h); 9083e230dd2SCorentin Chary break; 9093e230dd2SCorentin Chary case VNC_ENCODING_TIGHT: 9103e230dd2SCorentin Chary n = vnc_tight_send_framebuffer_update(vs, x, y, w, h); 9113e230dd2SCorentin Chary break; 912efe556adSCorentin Chary case VNC_ENCODING_TIGHT_PNG: 913efe556adSCorentin Chary n = vnc_tight_png_send_framebuffer_update(vs, x, y, w, h); 914efe556adSCorentin Chary break; 915148954faSCorentin Chary case VNC_ENCODING_ZRLE: 916148954faSCorentin Chary n = vnc_zrle_send_framebuffer_update(vs, x, y, w, h); 917148954faSCorentin Chary break; 918148954faSCorentin Chary case VNC_ENCODING_ZYWRLE: 919148954faSCorentin Chary n = vnc_zywrle_send_framebuffer_update(vs, x, y, w, h); 920148954faSCorentin Chary break; 9213e230dd2SCorentin Chary default: 922de3f7de7SPeter Lieven vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_RAW); 923de3f7de7SPeter Lieven n = vnc_raw_send_framebuffer_update(vs, x, y, w, h); 9240780ec7bSGerd Hoffmann break; 925de3f7de7SPeter Lieven } 9263e230dd2SCorentin Chary return n; 9273e230dd2SCorentin Chary } 9283e230dd2SCorentin Chary 9297c20b4a3SGerd Hoffmann static void vnc_mouse_set(DisplayChangeListener *dcl, 9307c20b4a3SGerd Hoffmann int x, int y, int visible) 9313e230dd2SCorentin Chary { 9323e230dd2SCorentin Chary /* can we ask the client(s) to move the pointer ??? */ 9333e230dd2SCorentin Chary } 9343e230dd2SCorentin Chary 9353e230dd2SCorentin Chary static int vnc_cursor_define(VncState *vs) 9363e230dd2SCorentin Chary { 9373e230dd2SCorentin Chary QEMUCursor *c = vs->vd->cursor; 9383e230dd2SCorentin Chary int isize; 9393e230dd2SCorentin Chary 940074a86d0SGerd Hoffmann if (vnc_has_feature(vs, VNC_FEATURE_ALPHA_CURSOR)) { 941074a86d0SGerd Hoffmann vnc_lock_output(vs); 942074a86d0SGerd Hoffmann vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE); 943074a86d0SGerd Hoffmann vnc_write_u8(vs, 0); /* padding */ 944074a86d0SGerd Hoffmann vnc_write_u16(vs, 1); /* # of rects */ 945074a86d0SGerd Hoffmann vnc_framebuffer_update(vs, c->hot_x, c->hot_y, c->width, c->height, 946074a86d0SGerd Hoffmann VNC_ENCODING_ALPHA_CURSOR); 947074a86d0SGerd Hoffmann vnc_write_s32(vs, VNC_ENCODING_RAW); 948074a86d0SGerd Hoffmann vnc_write(vs, c->data, c->width * c->height * 4); 949074a86d0SGerd Hoffmann vnc_unlock_output(vs); 950074a86d0SGerd Hoffmann return 0; 951074a86d0SGerd Hoffmann } 9523e230dd2SCorentin Chary if (vnc_has_feature(vs, VNC_FEATURE_RICH_CURSOR)) { 953d01f9595SCorentin Chary vnc_lock_output(vs); 9543e230dd2SCorentin Chary vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE); 9553e230dd2SCorentin Chary vnc_write_u8(vs, 0); /* padding */ 9563e230dd2SCorentin Chary vnc_write_u16(vs, 1); /* # of rects */ 9573e230dd2SCorentin Chary vnc_framebuffer_update(vs, c->hot_x, c->hot_y, c->width, c->height, 9583e230dd2SCorentin Chary VNC_ENCODING_RICH_CURSOR); 9599f64916dSGerd Hoffmann isize = c->width * c->height * vs->client_pf.bytes_per_pixel; 9609f64916dSGerd Hoffmann vnc_write_pixels_generic(vs, c->data, isize); 9613e230dd2SCorentin Chary vnc_write(vs, vs->vd->cursor_mask, vs->vd->cursor_msize); 962d01f9595SCorentin Chary vnc_unlock_output(vs); 9633e230dd2SCorentin Chary return 0; 9643e230dd2SCorentin Chary } 9653e230dd2SCorentin Chary return -1; 9663e230dd2SCorentin Chary } 9673e230dd2SCorentin Chary 9687c20b4a3SGerd Hoffmann static void vnc_dpy_cursor_define(DisplayChangeListener *dcl, 9697c20b4a3SGerd Hoffmann QEMUCursor *c) 9703e230dd2SCorentin Chary { 971d616ccc5SGerd Hoffmann VncDisplay *vd = container_of(dcl, VncDisplay, dcl); 9723e230dd2SCorentin Chary VncState *vs; 9733e230dd2SCorentin Chary 9743e230dd2SCorentin Chary cursor_put(vd->cursor); 9757267c094SAnthony Liguori g_free(vd->cursor_mask); 9763e230dd2SCorentin Chary 9773e230dd2SCorentin Chary vd->cursor = c; 9783e230dd2SCorentin Chary cursor_get(vd->cursor); 9793e230dd2SCorentin Chary vd->cursor_msize = cursor_get_mono_bpl(c) * c->height; 9807267c094SAnthony Liguori vd->cursor_mask = g_malloc0(vd->cursor_msize); 9813e230dd2SCorentin Chary cursor_get_mono_mask(c, 0, vd->cursor_mask); 9823e230dd2SCorentin Chary 9833e230dd2SCorentin Chary QTAILQ_FOREACH(vs, &vd->clients, next) { 9843e230dd2SCorentin Chary vnc_cursor_define(vs); 9853e230dd2SCorentin Chary } 9863e230dd2SCorentin Chary } 9873e230dd2SCorentin Chary 9884769a881SChih-Min Chao static int find_and_clear_dirty_height(VncState *vs, 9896c71a539SCorentin Chary int y, int last_x, int x, int height) 9903e230dd2SCorentin Chary { 9913e230dd2SCorentin Chary int h; 9923e230dd2SCorentin Chary 9936c71a539SCorentin Chary for (h = 1; h < (height - y); h++) { 994bc2429b9SCorentin Chary if (!test_bit(last_x, vs->dirty[y + h])) { 9953e230dd2SCorentin Chary break; 996bc2429b9SCorentin Chary } 997863d7c91SPeter Lieven bitmap_clear(vs->dirty[y + h], last_x, x - last_x); 9983e230dd2SCorentin Chary } 9993e230dd2SCorentin Chary 10003e230dd2SCorentin Chary return h; 10013e230dd2SCorentin Chary } 10023e230dd2SCorentin Chary 1003e2b72cb6SDaniel P. Berrange /* 1004e2b72cb6SDaniel P. Berrange * Figure out how much pending data we should allow in the output 1005e2b72cb6SDaniel P. Berrange * buffer before we throttle incremental display updates, and/or 1006e2b72cb6SDaniel P. Berrange * drop audio samples. 1007e2b72cb6SDaniel P. Berrange * 1008e2b72cb6SDaniel P. Berrange * We allow for equiv of 1 full display's worth of FB updates, 1009e2b72cb6SDaniel P. Berrange * and 1 second of audio samples. If audio backlog was larger 1010e2b72cb6SDaniel P. Berrange * than that the client would already suffering awful audio 1011e2b72cb6SDaniel P. Berrange * glitches, so dropping samples is no worse really). 1012e2b72cb6SDaniel P. Berrange */ 1013e2b72cb6SDaniel P. Berrange static void vnc_update_throttle_offset(VncState *vs) 1014e2b72cb6SDaniel P. Berrange { 1015e2b72cb6SDaniel P. Berrange size_t offset = 1016e2b72cb6SDaniel P. Berrange vs->client_width * vs->client_height * vs->client_pf.bytes_per_pixel; 1017e2b72cb6SDaniel P. Berrange 1018e2b72cb6SDaniel P. Berrange if (vs->audio_cap) { 1019e2b72cb6SDaniel P. Berrange int bps; 1020e2b72cb6SDaniel P. Berrange switch (vs->as.fmt) { 1021e2b72cb6SDaniel P. Berrange default: 102285bc5852SKővágó, Zoltán case AUDIO_FORMAT_U8: 102385bc5852SKővágó, Zoltán case AUDIO_FORMAT_S8: 1024e2b72cb6SDaniel P. Berrange bps = 1; 1025e2b72cb6SDaniel P. Berrange break; 102685bc5852SKővágó, Zoltán case AUDIO_FORMAT_U16: 102785bc5852SKővágó, Zoltán case AUDIO_FORMAT_S16: 1028e2b72cb6SDaniel P. Berrange bps = 2; 1029e2b72cb6SDaniel P. Berrange break; 103085bc5852SKővágó, Zoltán case AUDIO_FORMAT_U32: 103185bc5852SKővágó, Zoltán case AUDIO_FORMAT_S32: 1032e2b72cb6SDaniel P. Berrange bps = 4; 1033e2b72cb6SDaniel P. Berrange break; 1034e2b72cb6SDaniel P. Berrange } 1035cf070658SDaniel P. Berrangé offset += vs->as.freq * bps * vs->as.nchannels; 1036e2b72cb6SDaniel P. Berrange } 1037e2b72cb6SDaniel P. Berrange 1038e2b72cb6SDaniel P. Berrange /* Put a floor of 1MB on offset, so that if we have a large pending 1039e2b72cb6SDaniel P. Berrange * buffer and the display is resized to a small size & back again 1040e2b72cb6SDaniel P. Berrange * we don't suddenly apply a tiny send limit 1041e2b72cb6SDaniel P. Berrange */ 1042e2b72cb6SDaniel P. Berrange offset = MAX(offset, 1024 * 1024); 1043e2b72cb6SDaniel P. Berrange 10446aa22a29SDaniel P. Berrange if (vs->throttle_output_offset != offset) { 10456aa22a29SDaniel P. Berrange trace_vnc_client_throttle_threshold( 10466aa22a29SDaniel P. Berrange vs, vs->ioc, vs->throttle_output_offset, offset, vs->client_width, 10476aa22a29SDaniel P. Berrange vs->client_height, vs->client_pf.bytes_per_pixel, vs->audio_cap); 10486aa22a29SDaniel P. Berrange } 10496aa22a29SDaniel P. Berrange 1050e2b72cb6SDaniel P. Berrange vs->throttle_output_offset = offset; 1051e2b72cb6SDaniel P. Berrange } 1052e2b72cb6SDaniel P. Berrange 10530bad8342SDaniel P. Berrange static bool vnc_should_update(VncState *vs) 10540bad8342SDaniel P. Berrange { 10550bad8342SDaniel P. Berrange switch (vs->update) { 10560bad8342SDaniel P. Berrange case VNC_STATE_UPDATE_NONE: 10570bad8342SDaniel P. Berrange break; 10580bad8342SDaniel P. Berrange case VNC_STATE_UPDATE_INCREMENTAL: 1059e2b72cb6SDaniel P. Berrange /* Only allow incremental updates if the pending send queue 1060ada8d2e4SDaniel P. Berrange * is less than the permitted threshold, and the job worker 1061ada8d2e4SDaniel P. Berrange * is completely idle. 10620bad8342SDaniel P. Berrange */ 1063ada8d2e4SDaniel P. Berrange if (vs->output.offset < vs->throttle_output_offset && 1064ada8d2e4SDaniel P. Berrange vs->job_update == VNC_STATE_UPDATE_NONE) { 10650bad8342SDaniel P. Berrange return true; 10660bad8342SDaniel P. Berrange } 10676aa22a29SDaniel P. Berrange trace_vnc_client_throttle_incremental( 10686aa22a29SDaniel P. Berrange vs, vs->ioc, vs->job_update, vs->output.offset); 10690bad8342SDaniel P. Berrange break; 10700bad8342SDaniel P. Berrange case VNC_STATE_UPDATE_FORCE: 1071ada8d2e4SDaniel P. Berrange /* Only allow forced updates if the pending send queue 1072ada8d2e4SDaniel P. Berrange * does not contain a previous forced update, and the 1073ada8d2e4SDaniel P. Berrange * job worker is completely idle. 1074ada8d2e4SDaniel P. Berrange * 1075ada8d2e4SDaniel P. Berrange * Note this means we'll queue a forced update, even if 1076ada8d2e4SDaniel P. Berrange * the output buffer size is otherwise over the throttle 1077ada8d2e4SDaniel P. Berrange * output limit. 1078ada8d2e4SDaniel P. Berrange */ 1079ada8d2e4SDaniel P. Berrange if (vs->force_update_offset == 0 && 1080ada8d2e4SDaniel P. Berrange vs->job_update == VNC_STATE_UPDATE_NONE) { 10810bad8342SDaniel P. Berrange return true; 10820bad8342SDaniel P. Berrange } 10836aa22a29SDaniel P. Berrange trace_vnc_client_throttle_forced( 10846aa22a29SDaniel P. Berrange vs, vs->ioc, vs->job_update, vs->force_update_offset); 1085ada8d2e4SDaniel P. Berrange break; 1086ada8d2e4SDaniel P. Berrange } 10870bad8342SDaniel P. Berrange return false; 10880bad8342SDaniel P. Berrange } 10890bad8342SDaniel P. Berrange 10906af998dbSDaniel P. Berrange static int vnc_update_client(VncState *vs, int has_dirty) 10913e230dd2SCorentin Chary { 10923e230dd2SCorentin Chary VncDisplay *vd = vs->vd; 1093bd023f95SCorentin Chary VncJob *job; 10943e230dd2SCorentin Chary int y; 10952f487a3dSPeter Lieven int height, width; 1096bd023f95SCorentin Chary int n = 0; 1097bd023f95SCorentin Chary 1098b939eb89SDaniel P. Berrange if (vs->disconnecting) { 1099b939eb89SDaniel P. Berrange vnc_disconnect_finish(vs); 1100b939eb89SDaniel P. Berrange return 0; 1101b939eb89SDaniel P. Berrange } 1102b939eb89SDaniel P. Berrange 1103b939eb89SDaniel P. Berrange vs->has_dirty += has_dirty; 11040bad8342SDaniel P. Berrange if (!vnc_should_update(vs)) { 11053e230dd2SCorentin Chary return 0; 1106b939eb89SDaniel P. Berrange } 11073e230dd2SCorentin Chary 1108fef1bbadSDaniel P. Berrange if (!vs->has_dirty && vs->update != VNC_STATE_UPDATE_FORCE) { 11093e230dd2SCorentin Chary return 0; 1110b939eb89SDaniel P. Berrange } 11113e230dd2SCorentin Chary 11123e230dd2SCorentin Chary /* 11133e230dd2SCorentin Chary * Send screen updates to the vnc client using the server 11143e230dd2SCorentin Chary * surface and server dirty map. guest surface updates 11153e230dd2SCorentin Chary * happening in parallel don't disturb us, the next pass will 11163e230dd2SCorentin Chary * send them to the client. 11173e230dd2SCorentin Chary */ 1118bd023f95SCorentin Chary job = vnc_job_new(vs); 11193e230dd2SCorentin Chary 1120bea60dd7SPeter Lieven height = pixman_image_get_height(vd->server); 1121bea60dd7SPeter Lieven width = pixman_image_get_width(vd->server); 11223e230dd2SCorentin Chary 112312b316d4SPeter Lieven y = 0; 112412b316d4SPeter Lieven for (;;) { 112512b316d4SPeter Lieven int x, h; 112612b316d4SPeter Lieven unsigned long x2; 112712b316d4SPeter Lieven unsigned long offset = find_next_bit((unsigned long *) &vs->dirty, 112812b316d4SPeter Lieven height * VNC_DIRTY_BPL(vs), 112912b316d4SPeter Lieven y * VNC_DIRTY_BPL(vs)); 113012b316d4SPeter Lieven if (offset == height * VNC_DIRTY_BPL(vs)) { 113112b316d4SPeter Lieven /* no more dirty bits */ 113212b316d4SPeter Lieven break; 11333e230dd2SCorentin Chary } 113412b316d4SPeter Lieven y = offset / VNC_DIRTY_BPL(vs); 113512b316d4SPeter Lieven x = offset % VNC_DIRTY_BPL(vs); 113612b316d4SPeter Lieven x2 = find_next_zero_bit((unsigned long *) &vs->dirty[y], 113712b316d4SPeter Lieven VNC_DIRTY_BPL(vs), x); 113812b316d4SPeter Lieven bitmap_clear(vs->dirty[y], x, x2 - x); 113912b316d4SPeter Lieven h = find_and_clear_dirty_height(vs, y, x, x2, height); 11402f487a3dSPeter Lieven x2 = MIN(x2, width / VNC_DIRTY_PIXELS_PER_BIT); 11412f487a3dSPeter Lieven if (x2 > x) { 114212b316d4SPeter Lieven n += vnc_job_add_rect(job, x * VNC_DIRTY_PIXELS_PER_BIT, y, 114312b316d4SPeter Lieven (x2 - x) * VNC_DIRTY_PIXELS_PER_BIT, h); 11443e230dd2SCorentin Chary } 11450e7d6f60SPeter Lieven if (!x && x2 == width / VNC_DIRTY_PIXELS_PER_BIT) { 11460e7d6f60SPeter Lieven y += h; 11470e7d6f60SPeter Lieven if (y == height) { 11480e7d6f60SPeter Lieven break; 11490e7d6f60SPeter Lieven } 11500e7d6f60SPeter Lieven } 11512f487a3dSPeter Lieven } 1152bd023f95SCorentin Chary 1153ada8d2e4SDaniel P. Berrange vs->job_update = vs->update; 1154728a7ac9SDaniel P. Berrange vs->update = VNC_STATE_UPDATE_NONE; 1155ada8d2e4SDaniel P. Berrange vnc_job_push(job); 115663658280SGerd Hoffmann vs->has_dirty = 0; 1157bd023f95SCorentin Chary return n; 11583e230dd2SCorentin Chary } 11593e230dd2SCorentin Chary 11603e230dd2SCorentin Chary /* audio */ 11613e230dd2SCorentin Chary static void audio_capture_notify(void *opaque, audcnotification_e cmd) 11623e230dd2SCorentin Chary { 11633e230dd2SCorentin Chary VncState *vs = opaque; 11643e230dd2SCorentin Chary 1165f31f9c10SGerd Hoffmann assert(vs->magic == VNC_MAGIC); 11663e230dd2SCorentin Chary switch (cmd) { 11673e230dd2SCorentin Chary case AUD_CNOTIFY_DISABLE: 1168bd023f95SCorentin Chary vnc_lock_output(vs); 11693e230dd2SCorentin Chary vnc_write_u8(vs, VNC_MSG_SERVER_QEMU); 11703e230dd2SCorentin Chary vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO); 11713e230dd2SCorentin Chary vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_END); 1172bd023f95SCorentin Chary vnc_unlock_output(vs); 11733e230dd2SCorentin Chary vnc_flush(vs); 11743e230dd2SCorentin Chary break; 11753e230dd2SCorentin Chary 11763e230dd2SCorentin Chary case AUD_CNOTIFY_ENABLE: 1177bd023f95SCorentin Chary vnc_lock_output(vs); 11783e230dd2SCorentin Chary vnc_write_u8(vs, VNC_MSG_SERVER_QEMU); 11793e230dd2SCorentin Chary vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO); 11803e230dd2SCorentin Chary vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_BEGIN); 1181bd023f95SCorentin Chary vnc_unlock_output(vs); 11823e230dd2SCorentin Chary vnc_flush(vs); 11833e230dd2SCorentin Chary break; 11843e230dd2SCorentin Chary } 11853e230dd2SCorentin Chary } 11863e230dd2SCorentin Chary 11873e230dd2SCorentin Chary static void audio_capture_destroy(void *opaque) 11883e230dd2SCorentin Chary { 11893e230dd2SCorentin Chary } 11903e230dd2SCorentin Chary 119157a878edSPhilippe Mathieu-Daudé static void audio_capture(void *opaque, const void *buf, int size) 11923e230dd2SCorentin Chary { 11933e230dd2SCorentin Chary VncState *vs = opaque; 11943e230dd2SCorentin Chary 1195f31f9c10SGerd Hoffmann assert(vs->magic == VNC_MAGIC); 1196bd023f95SCorentin Chary vnc_lock_output(vs); 1197e2b72cb6SDaniel P. Berrange if (vs->output.offset < vs->throttle_output_offset) { 11983e230dd2SCorentin Chary vnc_write_u8(vs, VNC_MSG_SERVER_QEMU); 11993e230dd2SCorentin Chary vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO); 12003e230dd2SCorentin Chary vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_DATA); 12013e230dd2SCorentin Chary vnc_write_u32(vs, size); 12023e230dd2SCorentin Chary vnc_write(vs, buf, size); 12036aa22a29SDaniel P. Berrange } else { 12046aa22a29SDaniel P. Berrange trace_vnc_client_throttle_audio(vs, vs->ioc, vs->output.offset); 1205e2b72cb6SDaniel P. Berrange } 1206bd023f95SCorentin Chary vnc_unlock_output(vs); 12073e230dd2SCorentin Chary vnc_flush(vs); 12083e230dd2SCorentin Chary } 12093e230dd2SCorentin Chary 12103e230dd2SCorentin Chary static void audio_add(VncState *vs) 12113e230dd2SCorentin Chary { 12123e230dd2SCorentin Chary struct audio_capture_ops ops; 12133e230dd2SCorentin Chary 12143e230dd2SCorentin Chary if (vs->audio_cap) { 1215027a79c3SCole Robinson error_report("audio already running"); 12163e230dd2SCorentin Chary return; 12173e230dd2SCorentin Chary } 12183e230dd2SCorentin Chary 12193e230dd2SCorentin Chary ops.notify = audio_capture_notify; 12203e230dd2SCorentin Chary ops.destroy = audio_capture_destroy; 12213e230dd2SCorentin Chary ops.capture = audio_capture; 12223e230dd2SCorentin Chary 1223f0b9f36dSKővágó, Zoltán vs->audio_cap = AUD_add_capture(vs->vd->audio_state, &vs->as, &ops, vs); 12243e230dd2SCorentin Chary if (!vs->audio_cap) { 1225027a79c3SCole Robinson error_report("Failed to add audio capture"); 12263e230dd2SCorentin Chary } 12273e230dd2SCorentin Chary } 12283e230dd2SCorentin Chary 12293e230dd2SCorentin Chary static void audio_del(VncState *vs) 12303e230dd2SCorentin Chary { 12313e230dd2SCorentin Chary if (vs->audio_cap) { 12323e230dd2SCorentin Chary AUD_del_capture(vs->audio_cap, vs); 12333e230dd2SCorentin Chary vs->audio_cap = NULL; 12343e230dd2SCorentin Chary } 12353e230dd2SCorentin Chary } 12363e230dd2SCorentin Chary 12373e230dd2SCorentin Chary static void vnc_disconnect_start(VncState *vs) 12383e230dd2SCorentin Chary { 123904d2529dSDaniel P. Berrange if (vs->disconnecting) { 12403e230dd2SCorentin Chary return; 124104d2529dSDaniel P. Berrange } 1242ad6374c4SDaniel P. Berrange trace_vnc_client_disconnect_start(vs, vs->ioc); 12438cf36489SGerd Hoffmann vnc_set_share_mode(vs, VNC_SHARE_MODE_DISCONNECTED); 124404d2529dSDaniel P. Berrange if (vs->ioc_tag) { 124504d2529dSDaniel P. Berrange g_source_remove(vs->ioc_tag); 1246a75d6f07SBrandon Carpenter vs->ioc_tag = 0; 124704d2529dSDaniel P. Berrange } 124804d2529dSDaniel P. Berrange qio_channel_close(vs->ioc, NULL); 124904d2529dSDaniel P. Berrange vs->disconnecting = TRUE; 12503e230dd2SCorentin Chary } 12513e230dd2SCorentin Chary 12527536ee4bSTim Hardeck void vnc_disconnect_finish(VncState *vs) 12533e230dd2SCorentin Chary { 12547d964c9dSCorentin Chary int i; 12557d964c9dSCorentin Chary 1256ad6374c4SDaniel P. Berrange trace_vnc_client_disconnect_finish(vs, vs->ioc); 1257ad6374c4SDaniel P. Berrange 1258bd023f95SCorentin Chary vnc_jobs_join(vs); /* Wait encoding jobs */ 1259bd023f95SCorentin Chary 1260bd023f95SCorentin Chary vnc_lock_output(vs); 1261fb6ba0d5SWenchao Xia vnc_qmp_event(vs, QAPI_EVENT_VNC_DISCONNECTED); 12623e230dd2SCorentin Chary 12633e230dd2SCorentin Chary buffer_free(&vs->input); 12643e230dd2SCorentin Chary buffer_free(&vs->output); 12653e230dd2SCorentin Chary 1266fb6ba0d5SWenchao Xia qapi_free_VncClientInfo(vs->info); 12673e230dd2SCorentin Chary 12683e230dd2SCorentin Chary vnc_zlib_clear(vs); 12693e230dd2SCorentin Chary vnc_tight_clear(vs); 1270148954faSCorentin Chary vnc_zrle_clear(vs); 12713e230dd2SCorentin Chary 12723e230dd2SCorentin Chary #ifdef CONFIG_VNC_SASL 12733e230dd2SCorentin Chary vnc_sasl_client_cleanup(vs); 12743e230dd2SCorentin Chary #endif /* CONFIG_VNC_SASL */ 12753e230dd2SCorentin Chary audio_del(vs); 1276c2f2ba49SGerd Hoffmann qkbd_state_lift_all_keys(vs->vd->kbd); 12773e230dd2SCorentin Chary 127890cd03a3SDaniel P. Berrange if (vs->mouse_mode_notifier.notify != NULL) { 12796fd8e79aSTim Hardeck qemu_remove_mouse_mode_change_notifier(&vs->mouse_mode_notifier); 128090cd03a3SDaniel P. Berrange } 128190cd03a3SDaniel P. Berrange QTAILQ_REMOVE(&vs->vd->clients, vs, next); 1282c7628bffSGerd Hoffmann if (QTAILQ_EMPTY(&vs->vd->clients)) { 1283c7628bffSGerd Hoffmann /* last client gone */ 1284c7628bffSGerd Hoffmann vnc_update_server_surface(vs->vd); 1285c7628bffSGerd Hoffmann } 12863e230dd2SCorentin Chary 1287bd023f95SCorentin Chary vnc_unlock_output(vs); 1288bd023f95SCorentin Chary 1289bd023f95SCorentin Chary qemu_mutex_destroy(&vs->output_mutex); 12906fd8e79aSTim Hardeck if (vs->bh != NULL) { 1291175b2a6eSCorentin Chary qemu_bh_delete(vs->bh); 12926fd8e79aSTim Hardeck } 1293175b2a6eSCorentin Chary buffer_free(&vs->jobs_buffer); 1294175b2a6eSCorentin Chary 12957d964c9dSCorentin Chary for (i = 0; i < VNC_STAT_ROWS; ++i) { 12967267c094SAnthony Liguori g_free(vs->lossy_rect[i]); 12977d964c9dSCorentin Chary } 12987267c094SAnthony Liguori g_free(vs->lossy_rect); 129904d2529dSDaniel P. Berrange 130004d2529dSDaniel P. Berrange object_unref(OBJECT(vs->ioc)); 130104d2529dSDaniel P. Berrange vs->ioc = NULL; 130204d2529dSDaniel P. Berrange object_unref(OBJECT(vs->sioc)); 130304d2529dSDaniel P. Berrange vs->sioc = NULL; 1304f31f9c10SGerd Hoffmann vs->magic = 0; 13056bf21f3dSLi Qiang g_free(vs->zrle); 13066bf21f3dSLi Qiang g_free(vs->tight); 13077267c094SAnthony Liguori g_free(vs); 13083e230dd2SCorentin Chary } 13093e230dd2SCorentin Chary 131034ab29c2SVladimir Sementsov-Ogievskiy size_t vnc_client_io_error(VncState *vs, ssize_t ret, Error *err) 13113e230dd2SCorentin Chary { 131204d2529dSDaniel P. Berrange if (ret <= 0) { 131304d2529dSDaniel P. Berrange if (ret == 0) { 1314ad6374c4SDaniel P. Berrange trace_vnc_client_eof(vs, vs->ioc); 1315537848eeSMichael Tokarev vnc_disconnect_start(vs); 131604d2529dSDaniel P. Berrange } else if (ret != QIO_CHANNEL_ERR_BLOCK) { 1317ad6374c4SDaniel P. Berrange trace_vnc_client_io_error(vs, vs->ioc, 131834ab29c2SVladimir Sementsov-Ogievskiy err ? error_get_pretty(err) : "Unknown"); 1319537848eeSMichael Tokarev vnc_disconnect_start(vs); 13203e230dd2SCorentin Chary } 13213e230dd2SCorentin Chary 132234ab29c2SVladimir Sementsov-Ogievskiy error_free(err); 13233e230dd2SCorentin Chary return 0; 13243e230dd2SCorentin Chary } 13253e230dd2SCorentin Chary return ret; 13263e230dd2SCorentin Chary } 13273e230dd2SCorentin Chary 13283e230dd2SCorentin Chary 13293e230dd2SCorentin Chary void vnc_client_error(VncState *vs) 13303e230dd2SCorentin Chary { 13313e230dd2SCorentin Chary VNC_DEBUG("Closing down client sock: protocol error\n"); 13323e230dd2SCorentin Chary vnc_disconnect_start(vs); 13333e230dd2SCorentin Chary } 13343e230dd2SCorentin Chary 13353e305e4aSDaniel P. Berrange 13363e230dd2SCorentin Chary /* 13373e230dd2SCorentin Chary * Called to write a chunk of data to the client socket. The data may 13383e230dd2SCorentin Chary * be the raw data, or may have already been encoded by SASL. 13393e230dd2SCorentin Chary * The data will be written either straight onto the socket, or 13403e230dd2SCorentin Chary * written via the GNUTLS wrappers, if TLS/SSL encryption is enabled 13413e230dd2SCorentin Chary * 13423e230dd2SCorentin Chary * NB, it is theoretically possible to have 2 layers of encryption, 13433e230dd2SCorentin Chary * both SASL, and this TLS layer. It is highly unlikely in practice 13443e230dd2SCorentin Chary * though, since SASL encryption will typically be a no-op if TLS 13453e230dd2SCorentin Chary * is active 13463e230dd2SCorentin Chary * 13473e230dd2SCorentin Chary * Returns the number of bytes written, which may be less than 13483e230dd2SCorentin Chary * the requested 'datalen' if the socket would block. Returns 134930b80fd5SDaniel P. Berrange * 0 on I/O error, and disconnects the client socket. 13503e230dd2SCorentin Chary */ 135130b80fd5SDaniel P. Berrange size_t vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen) 13523e230dd2SCorentin Chary { 135304d2529dSDaniel P. Berrange Error *err = NULL; 1354fdd1ab6aSDaniel P. Berrange ssize_t ret; 135534ab29c2SVladimir Sementsov-Ogievskiy ret = qio_channel_write(vs->ioc, (const char *)data, datalen, &err); 13563e230dd2SCorentin Chary VNC_DEBUG("Wrote wire %p %zd -> %ld\n", data, datalen, ret); 135734ab29c2SVladimir Sementsov-Ogievskiy return vnc_client_io_error(vs, ret, err); 13583e230dd2SCorentin Chary } 13593e230dd2SCorentin Chary 13603e230dd2SCorentin Chary 13613e230dd2SCorentin Chary /* 13623e230dd2SCorentin Chary * Called to write buffered data to the client socket, when not 13633e230dd2SCorentin Chary * using any SASL SSF encryption layers. Will write as much data 13643e230dd2SCorentin Chary * as possible without blocking. If all buffered data is written, 13653e230dd2SCorentin Chary * will switch the FD poll() handler back to read monitoring. 13663e230dd2SCorentin Chary * 13673e230dd2SCorentin Chary * Returns the number of bytes written, which may be less than 13683e230dd2SCorentin Chary * the buffered output data if the socket would block. Returns 136930b80fd5SDaniel P. Berrange * 0 on I/O error, and disconnects the client socket. 13703e230dd2SCorentin Chary */ 137130b80fd5SDaniel P. Berrange static size_t vnc_client_write_plain(VncState *vs) 13723e230dd2SCorentin Chary { 13736aa22a29SDaniel P. Berrange size_t offset; 137430b80fd5SDaniel P. Berrange size_t ret; 13753e230dd2SCorentin Chary 13763e230dd2SCorentin Chary #ifdef CONFIG_VNC_SASL 13773e230dd2SCorentin Chary VNC_DEBUG("Write Plain: Pending output %p size %zd offset %zd. Wait SSF %d\n", 13783e230dd2SCorentin Chary vs->output.buffer, vs->output.capacity, vs->output.offset, 13793e230dd2SCorentin Chary vs->sasl.waitWriteSSF); 13803e230dd2SCorentin Chary 13813e230dd2SCorentin Chary if (vs->sasl.conn && 13823e230dd2SCorentin Chary vs->sasl.runSSF && 13833e230dd2SCorentin Chary vs->sasl.waitWriteSSF) { 13843e230dd2SCorentin Chary ret = vnc_client_write_buf(vs, vs->output.buffer, vs->sasl.waitWriteSSF); 13853e230dd2SCorentin Chary if (ret) 13863e230dd2SCorentin Chary vs->sasl.waitWriteSSF -= ret; 13873e230dd2SCorentin Chary } else 13883e230dd2SCorentin Chary #endif /* CONFIG_VNC_SASL */ 13893e230dd2SCorentin Chary ret = vnc_client_write_buf(vs, vs->output.buffer, vs->output.offset); 13903e230dd2SCorentin Chary if (!ret) 13913e230dd2SCorentin Chary return 0; 13923e230dd2SCorentin Chary 1393ada8d2e4SDaniel P. Berrange if (ret >= vs->force_update_offset) { 13946aa22a29SDaniel P. Berrange if (vs->force_update_offset != 0) { 13956aa22a29SDaniel P. Berrange trace_vnc_client_unthrottle_forced(vs, vs->ioc); 13966aa22a29SDaniel P. Berrange } 1397ada8d2e4SDaniel P. Berrange vs->force_update_offset = 0; 1398ada8d2e4SDaniel P. Berrange } else { 1399ada8d2e4SDaniel P. Berrange vs->force_update_offset -= ret; 1400ada8d2e4SDaniel P. Berrange } 14016aa22a29SDaniel P. Berrange offset = vs->output.offset; 140232ed2680STim Hardeck buffer_advance(&vs->output, ret); 14036aa22a29SDaniel P. Berrange if (offset >= vs->throttle_output_offset && 14046aa22a29SDaniel P. Berrange vs->output.offset < vs->throttle_output_offset) { 14056aa22a29SDaniel P. Berrange trace_vnc_client_unthrottle_incremental(vs, vs->ioc, vs->output.offset); 14066aa22a29SDaniel P. Berrange } 14073e230dd2SCorentin Chary 14083e230dd2SCorentin Chary if (vs->output.offset == 0) { 140904d2529dSDaniel P. Berrange if (vs->ioc_tag) { 141004d2529dSDaniel P. Berrange g_source_remove(vs->ioc_tag); 141104d2529dSDaniel P. Berrange } 141204d2529dSDaniel P. Berrange vs->ioc_tag = qio_channel_add_watch( 14132ddafce7SDing Hui vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR, 14142ddafce7SDing Hui vnc_client_io, vs, NULL); 14153e230dd2SCorentin Chary } 14163e230dd2SCorentin Chary 14173e230dd2SCorentin Chary return ret; 14183e230dd2SCorentin Chary } 14193e230dd2SCorentin Chary 14203e230dd2SCorentin Chary 14213e230dd2SCorentin Chary /* 14223e230dd2SCorentin Chary * First function called whenever there is data to be written to 14233e230dd2SCorentin Chary * the client socket. Will delegate actual work according to whether 14243e230dd2SCorentin Chary * SASL SSF layers are enabled (thus requiring encryption calls) 14253e230dd2SCorentin Chary */ 142604d2529dSDaniel P. Berrange static void vnc_client_write_locked(VncState *vs) 14273e230dd2SCorentin Chary { 14283e230dd2SCorentin Chary #ifdef CONFIG_VNC_SASL 14293e230dd2SCorentin Chary if (vs->sasl.conn && 14303e230dd2SCorentin Chary vs->sasl.runSSF && 14313e230dd2SCorentin Chary !vs->sasl.waitWriteSSF) { 14323e230dd2SCorentin Chary vnc_client_write_sasl(vs); 14333e230dd2SCorentin Chary } else 14343e230dd2SCorentin Chary #endif /* CONFIG_VNC_SASL */ 14357536ee4bSTim Hardeck { 14363e230dd2SCorentin Chary vnc_client_write_plain(vs); 14373e230dd2SCorentin Chary } 14387536ee4bSTim Hardeck } 14393e230dd2SCorentin Chary 144004d2529dSDaniel P. Berrange static void vnc_client_write(VncState *vs) 1441bd023f95SCorentin Chary { 1442f31f9c10SGerd Hoffmann assert(vs->magic == VNC_MAGIC); 1443bd023f95SCorentin Chary vnc_lock_output(vs); 1444d5f04223SDaniel P. Berrange if (vs->output.offset) { 144504d2529dSDaniel P. Berrange vnc_client_write_locked(vs); 144604d2529dSDaniel P. Berrange } else if (vs->ioc != NULL) { 144704d2529dSDaniel P. Berrange if (vs->ioc_tag) { 144804d2529dSDaniel P. Berrange g_source_remove(vs->ioc_tag); 144904d2529dSDaniel P. Berrange } 145004d2529dSDaniel P. Berrange vs->ioc_tag = qio_channel_add_watch( 14512ddafce7SDing Hui vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR, 14522ddafce7SDing Hui vnc_client_io, vs, NULL); 1453bd023f95SCorentin Chary } 1454bd023f95SCorentin Chary vnc_unlock_output(vs); 1455bd023f95SCorentin Chary } 1456bd023f95SCorentin Chary 14573e230dd2SCorentin Chary void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting) 14583e230dd2SCorentin Chary { 14593e230dd2SCorentin Chary vs->read_handler = func; 14603e230dd2SCorentin Chary vs->read_handler_expect = expecting; 14613e230dd2SCorentin Chary } 14623e230dd2SCorentin Chary 14633e230dd2SCorentin Chary 14643e230dd2SCorentin Chary /* 14653e230dd2SCorentin Chary * Called to read a chunk of data from the client socket. The data may 14663e230dd2SCorentin Chary * be the raw data, or may need to be further decoded by SASL. 14673e230dd2SCorentin Chary * The data will be read either straight from to the socket, or 14683e230dd2SCorentin Chary * read via the GNUTLS wrappers, if TLS/SSL encryption is enabled 14693e230dd2SCorentin Chary * 14703e230dd2SCorentin Chary * NB, it is theoretically possible to have 2 layers of encryption, 14713e230dd2SCorentin Chary * both SASL, and this TLS layer. It is highly unlikely in practice 14723e230dd2SCorentin Chary * though, since SASL encryption will typically be a no-op if TLS 14733e230dd2SCorentin Chary * is active 14743e230dd2SCorentin Chary * 14753e230dd2SCorentin Chary * Returns the number of bytes read, which may be less than 14763e230dd2SCorentin Chary * the requested 'datalen' if the socket would block. Returns 147730b80fd5SDaniel P. Berrange * 0 on I/O error or EOF, and disconnects the client socket. 14783e230dd2SCorentin Chary */ 147930b80fd5SDaniel P. Berrange size_t vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen) 14803e230dd2SCorentin Chary { 1481fdd1ab6aSDaniel P. Berrange ssize_t ret; 148204d2529dSDaniel P. Berrange Error *err = NULL; 148334ab29c2SVladimir Sementsov-Ogievskiy ret = qio_channel_read(vs->ioc, (char *)data, datalen, &err); 14843e230dd2SCorentin Chary VNC_DEBUG("Read wire %p %zd -> %ld\n", data, datalen, ret); 148534ab29c2SVladimir Sementsov-Ogievskiy return vnc_client_io_error(vs, ret, err); 14863e230dd2SCorentin Chary } 14873e230dd2SCorentin Chary 14883e230dd2SCorentin Chary 14893e230dd2SCorentin Chary /* 14903e230dd2SCorentin Chary * Called to read data from the client socket to the input buffer, 14913e230dd2SCorentin Chary * when not using any SASL SSF encryption layers. Will read as much 14923e230dd2SCorentin Chary * data as possible without blocking. 14933e230dd2SCorentin Chary * 149430b80fd5SDaniel P. Berrange * Returns the number of bytes read, which may be less than 149530b80fd5SDaniel P. Berrange * the requested 'datalen' if the socket would block. Returns 149630b80fd5SDaniel P. Berrange * 0 on I/O error or EOF, and disconnects the client socket. 14973e230dd2SCorentin Chary */ 149830b80fd5SDaniel P. Berrange static size_t vnc_client_read_plain(VncState *vs) 14993e230dd2SCorentin Chary { 150030b80fd5SDaniel P. Berrange size_t ret; 15013e230dd2SCorentin Chary VNC_DEBUG("Read plain %p size %zd offset %zd\n", 15023e230dd2SCorentin Chary vs->input.buffer, vs->input.capacity, vs->input.offset); 15033e230dd2SCorentin Chary buffer_reserve(&vs->input, 4096); 15043e230dd2SCorentin Chary ret = vnc_client_read_buf(vs, buffer_end(&vs->input), 4096); 15053e230dd2SCorentin Chary if (!ret) 15063e230dd2SCorentin Chary return 0; 15073e230dd2SCorentin Chary vs->input.offset += ret; 15083e230dd2SCorentin Chary return ret; 15093e230dd2SCorentin Chary } 15103e230dd2SCorentin Chary 1511175b2a6eSCorentin Chary static void vnc_jobs_bh(void *opaque) 1512175b2a6eSCorentin Chary { 1513175b2a6eSCorentin Chary VncState *vs = opaque; 1514175b2a6eSCorentin Chary 1515f31f9c10SGerd Hoffmann assert(vs->magic == VNC_MAGIC); 1516175b2a6eSCorentin Chary vnc_jobs_consume_buffer(vs); 1517175b2a6eSCorentin Chary } 15183e230dd2SCorentin Chary 15193e230dd2SCorentin Chary /* 15203e230dd2SCorentin Chary * First function called whenever there is more data to be read from 15213e230dd2SCorentin Chary * the client socket. Will delegate actual work according to whether 15223e230dd2SCorentin Chary * SASL SSF layers are enabled (thus requiring decryption calls) 1523ea697449SDaniel P. Berrange * Returns 0 on success, -1 if client disconnected 15243e230dd2SCorentin Chary */ 1525ea697449SDaniel P. Berrange static int vnc_client_read(VncState *vs) 15263e230dd2SCorentin Chary { 152730b80fd5SDaniel P. Berrange size_t ret; 15283e230dd2SCorentin Chary 15293e230dd2SCorentin Chary #ifdef CONFIG_VNC_SASL 15303e230dd2SCorentin Chary if (vs->sasl.conn && vs->sasl.runSSF) 15313e230dd2SCorentin Chary ret = vnc_client_read_sasl(vs); 15323e230dd2SCorentin Chary else 15333e230dd2SCorentin Chary #endif /* CONFIG_VNC_SASL */ 15343e230dd2SCorentin Chary ret = vnc_client_read_plain(vs); 15353e230dd2SCorentin Chary if (!ret) { 153604d2529dSDaniel P. Berrange if (vs->disconnecting) { 15373e230dd2SCorentin Chary vnc_disconnect_finish(vs); 1538ea697449SDaniel P. Berrange return -1; 153904d2529dSDaniel P. Berrange } 1540ea697449SDaniel P. Berrange return 0; 15413e230dd2SCorentin Chary } 15423e230dd2SCorentin Chary 15433e230dd2SCorentin Chary while (vs->read_handler && vs->input.offset >= vs->read_handler_expect) { 15443e230dd2SCorentin Chary size_t len = vs->read_handler_expect; 15453e230dd2SCorentin Chary int ret; 15463e230dd2SCorentin Chary 15473e230dd2SCorentin Chary ret = vs->read_handler(vs, vs->input.buffer, len); 154804d2529dSDaniel P. Berrange if (vs->disconnecting) { 15493e230dd2SCorentin Chary vnc_disconnect_finish(vs); 1550ea697449SDaniel P. Berrange return -1; 15513e230dd2SCorentin Chary } 15523e230dd2SCorentin Chary 15533e230dd2SCorentin Chary if (!ret) { 155432ed2680STim Hardeck buffer_advance(&vs->input, len); 15553e230dd2SCorentin Chary } else { 15563e230dd2SCorentin Chary vs->read_handler_expect = ret; 15573e230dd2SCorentin Chary } 15583e230dd2SCorentin Chary } 1559ea697449SDaniel P. Berrange return 0; 15603e230dd2SCorentin Chary } 15613e230dd2SCorentin Chary 156204d2529dSDaniel P. Berrange gboolean vnc_client_io(QIOChannel *ioc G_GNUC_UNUSED, 156304d2529dSDaniel P. Berrange GIOCondition condition, void *opaque) 156404d2529dSDaniel P. Berrange { 156504d2529dSDaniel P. Berrange VncState *vs = opaque; 1566f31f9c10SGerd Hoffmann 1567f31f9c10SGerd Hoffmann assert(vs->magic == VNC_MAGIC); 15682ddafce7SDing Hui 15692ddafce7SDing Hui if (condition & (G_IO_HUP | G_IO_ERR)) { 15702ddafce7SDing Hui vnc_disconnect_start(vs); 15712ddafce7SDing Hui return TRUE; 15722ddafce7SDing Hui } 15732ddafce7SDing Hui 157404d2529dSDaniel P. Berrange if (condition & G_IO_IN) { 1575ea697449SDaniel P. Berrange if (vnc_client_read(vs) < 0) { 15761bc3117aSGerd Hoffmann /* vs is free()ed here */ 15771bc3117aSGerd Hoffmann return TRUE; 1578ea697449SDaniel P. Berrange } 157904d2529dSDaniel P. Berrange } 158004d2529dSDaniel P. Berrange if (condition & G_IO_OUT) { 158104d2529dSDaniel P. Berrange vnc_client_write(vs); 158204d2529dSDaniel P. Berrange } 15831bc3117aSGerd Hoffmann 1584d49b87f0SKlim Kireev if (vs->disconnecting) { 1585d49b87f0SKlim Kireev if (vs->ioc_tag != 0) { 1586d49b87f0SKlim Kireev g_source_remove(vs->ioc_tag); 1587d49b87f0SKlim Kireev } 1588d49b87f0SKlim Kireev vs->ioc_tag = 0; 1589d49b87f0SKlim Kireev } 159004d2529dSDaniel P. Berrange return TRUE; 159104d2529dSDaniel P. Berrange } 159204d2529dSDaniel P. Berrange 159304d2529dSDaniel P. Berrange 1594f887cf16SDaniel P. Berrange /* 1595f887cf16SDaniel P. Berrange * Scale factor to apply to vs->throttle_output_offset when checking for 1596f887cf16SDaniel P. Berrange * hard limit. Worst case normal usage could be x2, if we have a complete 1597f887cf16SDaniel P. Berrange * incremental update and complete forced update in the output buffer. 1598f887cf16SDaniel P. Berrange * So x3 should be good enough, but we pick x5 to be conservative and thus 1599f887cf16SDaniel P. Berrange * (hopefully) never trigger incorrectly. 1600f887cf16SDaniel P. Berrange */ 1601f887cf16SDaniel P. Berrange #define VNC_THROTTLE_OUTPUT_LIMIT_SCALE 5 1602f887cf16SDaniel P. Berrange 16033e230dd2SCorentin Chary void vnc_write(VncState *vs, const void *data, size_t len) 16043e230dd2SCorentin Chary { 1605f31f9c10SGerd Hoffmann assert(vs->magic == VNC_MAGIC); 1606f887cf16SDaniel P. Berrange if (vs->disconnecting) { 1607f887cf16SDaniel P. Berrange return; 1608f887cf16SDaniel P. Berrange } 1609f887cf16SDaniel P. Berrange /* Protection against malicious client/guest to prevent our output 1610f887cf16SDaniel P. Berrange * buffer growing without bound if client stops reading data. This 1611f887cf16SDaniel P. Berrange * should rarely trigger, because we have earlier throttling code 1612f887cf16SDaniel P. Berrange * which stops issuing framebuffer updates and drops audio data 1613f887cf16SDaniel P. Berrange * if the throttle_output_offset value is exceeded. So we only reach 1614f887cf16SDaniel P. Berrange * this higher level if a huge number of pseudo-encodings get 1615f887cf16SDaniel P. Berrange * triggered while data can't be sent on the socket. 1616f887cf16SDaniel P. Berrange * 1617f887cf16SDaniel P. Berrange * NB throttle_output_offset can be zero during early protocol 1618f887cf16SDaniel P. Berrange * handshake, or from the job thread's VncState clone 1619f887cf16SDaniel P. Berrange */ 1620f887cf16SDaniel P. Berrange if (vs->throttle_output_offset != 0 && 1621dffa1de0SDaniel P. Berrangé (vs->output.offset / VNC_THROTTLE_OUTPUT_LIMIT_SCALE) > 1622dffa1de0SDaniel P. Berrangé vs->throttle_output_offset) { 16236aa22a29SDaniel P. Berrange trace_vnc_client_output_limit(vs, vs->ioc, vs->output.offset, 16246aa22a29SDaniel P. Berrange vs->throttle_output_offset); 1625f887cf16SDaniel P. Berrange vnc_disconnect_start(vs); 1626f887cf16SDaniel P. Berrange return; 1627f887cf16SDaniel P. Berrange } 16283e230dd2SCorentin Chary buffer_reserve(&vs->output, len); 16293e230dd2SCorentin Chary 163004d2529dSDaniel P. Berrange if (vs->ioc != NULL && buffer_empty(&vs->output)) { 163104d2529dSDaniel P. Berrange if (vs->ioc_tag) { 163204d2529dSDaniel P. Berrange g_source_remove(vs->ioc_tag); 163304d2529dSDaniel P. Berrange } 163404d2529dSDaniel P. Berrange vs->ioc_tag = qio_channel_add_watch( 16352ddafce7SDing Hui vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_OUT, 16362ddafce7SDing Hui vnc_client_io, vs, NULL); 16373e230dd2SCorentin Chary } 16383e230dd2SCorentin Chary 16393e230dd2SCorentin Chary buffer_append(&vs->output, data, len); 16403e230dd2SCorentin Chary } 16413e230dd2SCorentin Chary 16423e230dd2SCorentin Chary void vnc_write_s32(VncState *vs, int32_t value) 16433e230dd2SCorentin Chary { 16443e230dd2SCorentin Chary vnc_write_u32(vs, *(uint32_t *)&value); 16453e230dd2SCorentin Chary } 16463e230dd2SCorentin Chary 16473e230dd2SCorentin Chary void vnc_write_u32(VncState *vs, uint32_t value) 16483e230dd2SCorentin Chary { 16493e230dd2SCorentin Chary uint8_t buf[4]; 16503e230dd2SCorentin Chary 16513e230dd2SCorentin Chary buf[0] = (value >> 24) & 0xFF; 16523e230dd2SCorentin Chary buf[1] = (value >> 16) & 0xFF; 16533e230dd2SCorentin Chary buf[2] = (value >> 8) & 0xFF; 16543e230dd2SCorentin Chary buf[3] = value & 0xFF; 16553e230dd2SCorentin Chary 16563e230dd2SCorentin Chary vnc_write(vs, buf, 4); 16573e230dd2SCorentin Chary } 16583e230dd2SCorentin Chary 16593e230dd2SCorentin Chary void vnc_write_u16(VncState *vs, uint16_t value) 16603e230dd2SCorentin Chary { 16613e230dd2SCorentin Chary uint8_t buf[2]; 16623e230dd2SCorentin Chary 16633e230dd2SCorentin Chary buf[0] = (value >> 8) & 0xFF; 16643e230dd2SCorentin Chary buf[1] = value & 0xFF; 16653e230dd2SCorentin Chary 16663e230dd2SCorentin Chary vnc_write(vs, buf, 2); 16673e230dd2SCorentin Chary } 16683e230dd2SCorentin Chary 16693e230dd2SCorentin Chary void vnc_write_u8(VncState *vs, uint8_t value) 16703e230dd2SCorentin Chary { 16713e230dd2SCorentin Chary vnc_write(vs, (char *)&value, 1); 16723e230dd2SCorentin Chary } 16733e230dd2SCorentin Chary 16743e230dd2SCorentin Chary void vnc_flush(VncState *vs) 16753e230dd2SCorentin Chary { 1676bd023f95SCorentin Chary vnc_lock_output(vs); 1677d5f04223SDaniel P. Berrange if (vs->ioc != NULL && vs->output.offset) { 1678bd023f95SCorentin Chary vnc_client_write_locked(vs); 1679bd023f95SCorentin Chary } 1680d49b87f0SKlim Kireev if (vs->disconnecting) { 1681d49b87f0SKlim Kireev if (vs->ioc_tag != 0) { 1682d49b87f0SKlim Kireev g_source_remove(vs->ioc_tag); 1683d49b87f0SKlim Kireev } 1684d49b87f0SKlim Kireev vs->ioc_tag = 0; 1685d49b87f0SKlim Kireev } 1686bd023f95SCorentin Chary vnc_unlock_output(vs); 16873e230dd2SCorentin Chary } 16883e230dd2SCorentin Chary 168971a8cdecSBlue Swirl static uint8_t read_u8(uint8_t *data, size_t offset) 16903e230dd2SCorentin Chary { 16913e230dd2SCorentin Chary return data[offset]; 16923e230dd2SCorentin Chary } 16933e230dd2SCorentin Chary 169471a8cdecSBlue Swirl static uint16_t read_u16(uint8_t *data, size_t offset) 16953e230dd2SCorentin Chary { 16963e230dd2SCorentin Chary return ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF); 16973e230dd2SCorentin Chary } 16983e230dd2SCorentin Chary 169971a8cdecSBlue Swirl static int32_t read_s32(uint8_t *data, size_t offset) 17003e230dd2SCorentin Chary { 17013e230dd2SCorentin Chary return (int32_t)((data[offset] << 24) | (data[offset + 1] << 16) | 17023e230dd2SCorentin Chary (data[offset + 2] << 8) | data[offset + 3]); 17033e230dd2SCorentin Chary } 17043e230dd2SCorentin Chary 17053e230dd2SCorentin Chary uint32_t read_u32(uint8_t *data, size_t offset) 17063e230dd2SCorentin Chary { 17073e230dd2SCorentin Chary return ((data[offset] << 24) | (data[offset + 1] << 16) | 17083e230dd2SCorentin Chary (data[offset + 2] << 8) | data[offset + 3]); 17093e230dd2SCorentin Chary } 17103e230dd2SCorentin Chary 17113e230dd2SCorentin Chary static void client_cut_text(VncState *vs, size_t len, uint8_t *text) 17123e230dd2SCorentin Chary { 17133e230dd2SCorentin Chary } 17143e230dd2SCorentin Chary 17159e8dd451SJan Kiszka static void check_pointer_type_change(Notifier *notifier, void *data) 17163e230dd2SCorentin Chary { 17173e230dd2SCorentin Chary VncState *vs = container_of(notifier, VncState, mouse_mode_notifier); 171814768ebaSGerd Hoffmann int absolute = qemu_input_is_absolute(); 17193e230dd2SCorentin Chary 17203e230dd2SCorentin Chary if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE) && vs->absolute != absolute) { 1721bd023f95SCorentin Chary vnc_lock_output(vs); 17223e230dd2SCorentin Chary vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE); 17233e230dd2SCorentin Chary vnc_write_u8(vs, 0); 17243e230dd2SCorentin Chary vnc_write_u16(vs, 1); 17253e230dd2SCorentin Chary vnc_framebuffer_update(vs, absolute, 0, 1726bea60dd7SPeter Lieven pixman_image_get_width(vs->vd->server), 1727bea60dd7SPeter Lieven pixman_image_get_height(vs->vd->server), 17283e230dd2SCorentin Chary VNC_ENCODING_POINTER_TYPE_CHANGE); 1729bd023f95SCorentin Chary vnc_unlock_output(vs); 17303e230dd2SCorentin Chary vnc_flush(vs); 17313e230dd2SCorentin Chary } 17323e230dd2SCorentin Chary vs->absolute = absolute; 17333e230dd2SCorentin Chary } 17343e230dd2SCorentin Chary 17353e230dd2SCorentin Chary static void pointer_event(VncState *vs, int button_mask, int x, int y) 17363e230dd2SCorentin Chary { 17377fb1cf16SEric Blake static uint32_t bmap[INPUT_BUTTON__MAX] = { 173814768ebaSGerd Hoffmann [INPUT_BUTTON_LEFT] = 0x01, 173914768ebaSGerd Hoffmann [INPUT_BUTTON_MIDDLE] = 0x02, 174014768ebaSGerd Hoffmann [INPUT_BUTTON_RIGHT] = 0x04, 1741f22d0af0SGerd Hoffmann [INPUT_BUTTON_WHEEL_UP] = 0x08, 1742f22d0af0SGerd Hoffmann [INPUT_BUTTON_WHEEL_DOWN] = 0x10, 174314768ebaSGerd Hoffmann }; 174414768ebaSGerd Hoffmann QemuConsole *con = vs->vd->dcl.con; 1745bea60dd7SPeter Lieven int width = pixman_image_get_width(vs->vd->server); 1746bea60dd7SPeter Lieven int height = pixman_image_get_height(vs->vd->server); 17473e230dd2SCorentin Chary 174814768ebaSGerd Hoffmann if (vs->last_bmask != button_mask) { 174914768ebaSGerd Hoffmann qemu_input_update_buttons(con, bmap, vs->last_bmask, button_mask); 175014768ebaSGerd Hoffmann vs->last_bmask = button_mask; 175114768ebaSGerd Hoffmann } 17523e230dd2SCorentin Chary 17533e230dd2SCorentin Chary if (vs->absolute) { 17549cfa7ab9SPhilippe Voinov qemu_input_queue_abs(con, INPUT_AXIS_X, x, 0, width); 17559cfa7ab9SPhilippe Voinov qemu_input_queue_abs(con, INPUT_AXIS_Y, y, 0, height); 17563e230dd2SCorentin Chary } else if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE)) { 175714768ebaSGerd Hoffmann qemu_input_queue_rel(con, INPUT_AXIS_X, x - 0x7FFF); 175814768ebaSGerd Hoffmann qemu_input_queue_rel(con, INPUT_AXIS_Y, y - 0x7FFF); 17593e230dd2SCorentin Chary } else { 176014768ebaSGerd Hoffmann if (vs->last_x != -1) { 176114768ebaSGerd Hoffmann qemu_input_queue_rel(con, INPUT_AXIS_X, x - vs->last_x); 176214768ebaSGerd Hoffmann qemu_input_queue_rel(con, INPUT_AXIS_Y, y - vs->last_y); 176314768ebaSGerd Hoffmann } 17643e230dd2SCorentin Chary vs->last_x = x; 17653e230dd2SCorentin Chary vs->last_y = y; 17663e230dd2SCorentin Chary } 176714768ebaSGerd Hoffmann qemu_input_event_sync(); 17683e230dd2SCorentin Chary } 17693e230dd2SCorentin Chary 1770c2f2ba49SGerd Hoffmann static void press_key(VncState *vs, QKeyCode qcode) 17713e230dd2SCorentin Chary { 1772c2f2ba49SGerd Hoffmann qkbd_state_key_event(vs->vd->kbd, qcode, true); 1773c2f2ba49SGerd Hoffmann qkbd_state_key_event(vs->vd->kbd, qcode, false); 17743e230dd2SCorentin Chary } 17753e230dd2SCorentin Chary 1776ab99e5c1SLei Li static void vnc_led_state_change(VncState *vs) 1777ab99e5c1SLei Li { 1778ab99e5c1SLei Li if (!vnc_has_feature(vs, VNC_FEATURE_LED_STATE)) { 1779ab99e5c1SLei Li return; 1780ab99e5c1SLei Li } 1781ab99e5c1SLei Li 1782ab99e5c1SLei Li vnc_lock_output(vs); 1783ab99e5c1SLei Li vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE); 1784ab99e5c1SLei Li vnc_write_u8(vs, 0); 1785ab99e5c1SLei Li vnc_write_u16(vs, 1); 1786ab99e5c1SLei Li vnc_framebuffer_update(vs, 0, 0, 1, 1, VNC_ENCODING_LED_STATE); 1787a54f0d2bSPierre Ossman vnc_write_u8(vs, vs->vd->ledstate); 1788ab99e5c1SLei Li vnc_unlock_output(vs); 1789ab99e5c1SLei Li vnc_flush(vs); 1790ab99e5c1SLei Li } 1791ab99e5c1SLei Li 17923e230dd2SCorentin Chary static void kbd_leds(void *opaque, int ledstate) 17933e230dd2SCorentin Chary { 1794a54f0d2bSPierre Ossman VncDisplay *vd = opaque; 1795a54f0d2bSPierre Ossman VncState *client; 17963e230dd2SCorentin Chary 179740066175SGerd Hoffmann trace_vnc_key_guest_leds((ledstate & QEMU_CAPS_LOCK_LED), 179840066175SGerd Hoffmann (ledstate & QEMU_NUM_LOCK_LED), 179940066175SGerd Hoffmann (ledstate & QEMU_SCROLL_LOCK_LED)); 180040066175SGerd Hoffmann 1801a54f0d2bSPierre Ossman if (ledstate == vd->ledstate) { 1802a54f0d2bSPierre Ossman return; 180396f3d174SLei Li } 1804ab99e5c1SLei Li 1805a54f0d2bSPierre Ossman vd->ledstate = ledstate; 1806a54f0d2bSPierre Ossman 1807a54f0d2bSPierre Ossman QTAILQ_FOREACH(client, &vd->clients, next) { 1808a54f0d2bSPierre Ossman vnc_led_state_change(client); 1809ab99e5c1SLei Li } 18103e230dd2SCorentin Chary } 18113e230dd2SCorentin Chary 18123e230dd2SCorentin Chary static void do_key_event(VncState *vs, int down, int keycode, int sym) 18133e230dd2SCorentin Chary { 1814c2f2ba49SGerd Hoffmann QKeyCode qcode = qemu_input_key_number_to_qcode(keycode); 1815c2f2ba49SGerd Hoffmann 18163e230dd2SCorentin Chary /* QEMU console switch */ 1817c2f2ba49SGerd Hoffmann switch (qcode) { 1818c2f2ba49SGerd Hoffmann case Q_KEY_CODE_1 ... Q_KEY_CODE_9: /* '1' to '9' keys */ 1819c2f2ba49SGerd Hoffmann if (vs->vd->dcl.con == NULL && down && 1820c2f2ba49SGerd Hoffmann qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_CTRL) && 1821c2f2ba49SGerd Hoffmann qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_ALT)) { 18223e230dd2SCorentin Chary /* Reset the modifiers sent to the current console */ 1823c2f2ba49SGerd Hoffmann qkbd_state_lift_all_keys(vs->vd->kbd); 1824c2f2ba49SGerd Hoffmann console_select(qcode - Q_KEY_CODE_1); 18253e230dd2SCorentin Chary return; 18263e230dd2SCorentin Chary } 1827c2f2ba49SGerd Hoffmann default: 18283e230dd2SCorentin Chary break; 18293e230dd2SCorentin Chary } 18303e230dd2SCorentin Chary 1831e7b2aaccSLei Li /* Turn off the lock state sync logic if the client support the led 1832e7b2aaccSLei Li state extension. 1833e7b2aaccSLei Li */ 18349892088bSGerd Hoffmann if (down && vs->vd->lock_key_sync && 1835e7b2aaccSLei Li !vnc_has_feature(vs, VNC_FEATURE_LED_STATE) && 18363e230dd2SCorentin Chary keycode_is_keypad(vs->vd->kbd_layout, keycode)) { 18373e230dd2SCorentin Chary /* If the numlock state needs to change then simulate an additional 18383e230dd2SCorentin Chary keypress before sending this one. This will happen if the user 18393e230dd2SCorentin Chary toggles numlock away from the VNC window. 18403e230dd2SCorentin Chary */ 18413e230dd2SCorentin Chary if (keysym_is_numlock(vs->vd->kbd_layout, sym & 0xFFFF)) { 1842c2f2ba49SGerd Hoffmann if (!qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_NUMLOCK)) { 184340066175SGerd Hoffmann trace_vnc_key_sync_numlock(true); 1844c2f2ba49SGerd Hoffmann press_key(vs, Q_KEY_CODE_NUM_LOCK); 18453e230dd2SCorentin Chary } 18463e230dd2SCorentin Chary } else { 1847c2f2ba49SGerd Hoffmann if (qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_NUMLOCK)) { 184840066175SGerd Hoffmann trace_vnc_key_sync_numlock(false); 1849c2f2ba49SGerd Hoffmann press_key(vs, Q_KEY_CODE_NUM_LOCK); 18503e230dd2SCorentin Chary } 18513e230dd2SCorentin Chary } 18523e230dd2SCorentin Chary } 18533e230dd2SCorentin Chary 18549892088bSGerd Hoffmann if (down && vs->vd->lock_key_sync && 1855e7b2aaccSLei Li !vnc_has_feature(vs, VNC_FEATURE_LED_STATE) && 18563e230dd2SCorentin Chary ((sym >= 'A' && sym <= 'Z') || (sym >= 'a' && sym <= 'z'))) { 18573e230dd2SCorentin Chary /* If the capslock state needs to change then simulate an additional 18583e230dd2SCorentin Chary keypress before sending this one. This will happen if the user 18593e230dd2SCorentin Chary toggles capslock away from the VNC window. 18603e230dd2SCorentin Chary */ 18613e230dd2SCorentin Chary int uppercase = !!(sym >= 'A' && sym <= 'Z'); 1862c2f2ba49SGerd Hoffmann bool shift = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_SHIFT); 1863c2f2ba49SGerd Hoffmann bool capslock = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_CAPSLOCK); 18643e230dd2SCorentin Chary if (capslock) { 18653e230dd2SCorentin Chary if (uppercase == shift) { 186640066175SGerd Hoffmann trace_vnc_key_sync_capslock(false); 1867c2f2ba49SGerd Hoffmann press_key(vs, Q_KEY_CODE_CAPS_LOCK); 18683e230dd2SCorentin Chary } 18693e230dd2SCorentin Chary } else { 18703e230dd2SCorentin Chary if (uppercase != shift) { 187140066175SGerd Hoffmann trace_vnc_key_sync_capslock(true); 1872c2f2ba49SGerd Hoffmann press_key(vs, Q_KEY_CODE_CAPS_LOCK); 18733e230dd2SCorentin Chary } 18743e230dd2SCorentin Chary } 18753e230dd2SCorentin Chary } 18763e230dd2SCorentin Chary 1877c2f2ba49SGerd Hoffmann qkbd_state_key_event(vs->vd->kbd, qcode, down); 1878c2f2ba49SGerd Hoffmann if (!qemu_console_is_graphic(NULL)) { 1879c2f2ba49SGerd Hoffmann bool numlock = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_NUMLOCK); 1880c2f2ba49SGerd Hoffmann bool control = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_CTRL); 18813e230dd2SCorentin Chary /* QEMU console emulation */ 18823e230dd2SCorentin Chary if (down) { 18833e230dd2SCorentin Chary switch (keycode) { 18843e230dd2SCorentin Chary case 0x2a: /* Left Shift */ 18853e230dd2SCorentin Chary case 0x36: /* Right Shift */ 18863e230dd2SCorentin Chary case 0x1d: /* Left CTRL */ 18873e230dd2SCorentin Chary case 0x9d: /* Right CTRL */ 18883e230dd2SCorentin Chary case 0x38: /* Left ALT */ 18893e230dd2SCorentin Chary case 0xb8: /* Right ALT */ 18903e230dd2SCorentin Chary break; 18913e230dd2SCorentin Chary case 0xc8: 18923e230dd2SCorentin Chary kbd_put_keysym(QEMU_KEY_UP); 18933e230dd2SCorentin Chary break; 18943e230dd2SCorentin Chary case 0xd0: 18953e230dd2SCorentin Chary kbd_put_keysym(QEMU_KEY_DOWN); 18963e230dd2SCorentin Chary break; 18973e230dd2SCorentin Chary case 0xcb: 18983e230dd2SCorentin Chary kbd_put_keysym(QEMU_KEY_LEFT); 18993e230dd2SCorentin Chary break; 19003e230dd2SCorentin Chary case 0xcd: 19013e230dd2SCorentin Chary kbd_put_keysym(QEMU_KEY_RIGHT); 19023e230dd2SCorentin Chary break; 19033e230dd2SCorentin Chary case 0xd3: 19043e230dd2SCorentin Chary kbd_put_keysym(QEMU_KEY_DELETE); 19053e230dd2SCorentin Chary break; 19063e230dd2SCorentin Chary case 0xc7: 19073e230dd2SCorentin Chary kbd_put_keysym(QEMU_KEY_HOME); 19083e230dd2SCorentin Chary break; 19093e230dd2SCorentin Chary case 0xcf: 19103e230dd2SCorentin Chary kbd_put_keysym(QEMU_KEY_END); 19113e230dd2SCorentin Chary break; 19123e230dd2SCorentin Chary case 0xc9: 19133e230dd2SCorentin Chary kbd_put_keysym(QEMU_KEY_PAGEUP); 19143e230dd2SCorentin Chary break; 19153e230dd2SCorentin Chary case 0xd1: 19163e230dd2SCorentin Chary kbd_put_keysym(QEMU_KEY_PAGEDOWN); 19173e230dd2SCorentin Chary break; 19183e230dd2SCorentin Chary 19193e230dd2SCorentin Chary case 0x47: 19203e230dd2SCorentin Chary kbd_put_keysym(numlock ? '7' : QEMU_KEY_HOME); 19213e230dd2SCorentin Chary break; 19223e230dd2SCorentin Chary case 0x48: 19233e230dd2SCorentin Chary kbd_put_keysym(numlock ? '8' : QEMU_KEY_UP); 19243e230dd2SCorentin Chary break; 19253e230dd2SCorentin Chary case 0x49: 19263e230dd2SCorentin Chary kbd_put_keysym(numlock ? '9' : QEMU_KEY_PAGEUP); 19273e230dd2SCorentin Chary break; 19283e230dd2SCorentin Chary case 0x4b: 19293e230dd2SCorentin Chary kbd_put_keysym(numlock ? '4' : QEMU_KEY_LEFT); 19303e230dd2SCorentin Chary break; 19313e230dd2SCorentin Chary case 0x4c: 19323e230dd2SCorentin Chary kbd_put_keysym('5'); 19333e230dd2SCorentin Chary break; 19343e230dd2SCorentin Chary case 0x4d: 19353e230dd2SCorentin Chary kbd_put_keysym(numlock ? '6' : QEMU_KEY_RIGHT); 19363e230dd2SCorentin Chary break; 19373e230dd2SCorentin Chary case 0x4f: 19383e230dd2SCorentin Chary kbd_put_keysym(numlock ? '1' : QEMU_KEY_END); 19393e230dd2SCorentin Chary break; 19403e230dd2SCorentin Chary case 0x50: 19413e230dd2SCorentin Chary kbd_put_keysym(numlock ? '2' : QEMU_KEY_DOWN); 19423e230dd2SCorentin Chary break; 19433e230dd2SCorentin Chary case 0x51: 19443e230dd2SCorentin Chary kbd_put_keysym(numlock ? '3' : QEMU_KEY_PAGEDOWN); 19453e230dd2SCorentin Chary break; 19463e230dd2SCorentin Chary case 0x52: 19473e230dd2SCorentin Chary kbd_put_keysym('0'); 19483e230dd2SCorentin Chary break; 19493e230dd2SCorentin Chary case 0x53: 19503e230dd2SCorentin Chary kbd_put_keysym(numlock ? '.' : QEMU_KEY_DELETE); 19513e230dd2SCorentin Chary break; 19523e230dd2SCorentin Chary 19533e230dd2SCorentin Chary case 0xb5: 19543e230dd2SCorentin Chary kbd_put_keysym('/'); 19553e230dd2SCorentin Chary break; 19563e230dd2SCorentin Chary case 0x37: 19573e230dd2SCorentin Chary kbd_put_keysym('*'); 19583e230dd2SCorentin Chary break; 19593e230dd2SCorentin Chary case 0x4a: 19603e230dd2SCorentin Chary kbd_put_keysym('-'); 19613e230dd2SCorentin Chary break; 19623e230dd2SCorentin Chary case 0x4e: 19633e230dd2SCorentin Chary kbd_put_keysym('+'); 19643e230dd2SCorentin Chary break; 19653e230dd2SCorentin Chary case 0x9c: 19663e230dd2SCorentin Chary kbd_put_keysym('\n'); 19673e230dd2SCorentin Chary break; 19683e230dd2SCorentin Chary 19693e230dd2SCorentin Chary default: 1970e26437c2SGerd Hoffmann if (control) { 1971e26437c2SGerd Hoffmann kbd_put_keysym(sym & 0x1f); 1972e26437c2SGerd Hoffmann } else { 19733e230dd2SCorentin Chary kbd_put_keysym(sym); 1974e26437c2SGerd Hoffmann } 19753e230dd2SCorentin Chary break; 19763e230dd2SCorentin Chary } 19773e230dd2SCorentin Chary } 19783e230dd2SCorentin Chary } 19793e230dd2SCorentin Chary } 19803e230dd2SCorentin Chary 198140066175SGerd Hoffmann static const char *code2name(int keycode) 198240066175SGerd Hoffmann { 1983977c736fSMarkus Armbruster return QKeyCode_str(qemu_input_key_number_to_qcode(keycode)); 198440066175SGerd Hoffmann } 198540066175SGerd Hoffmann 19863e230dd2SCorentin Chary static void key_event(VncState *vs, int down, uint32_t sym) 19873e230dd2SCorentin Chary { 19883e230dd2SCorentin Chary int keycode; 19893e230dd2SCorentin Chary int lsym = sym; 19903e230dd2SCorentin Chary 199181c0d5a6SGerd Hoffmann if (lsym >= 'A' && lsym <= 'Z' && qemu_console_is_graphic(NULL)) { 19923e230dd2SCorentin Chary lsym = lsym - 'A' + 'a'; 19933e230dd2SCorentin Chary } 19943e230dd2SCorentin Chary 1995abb4f2c9SGerd Hoffmann keycode = keysym2scancode(vs->vd->kbd_layout, lsym & 0xFFFF, 199619c1b9fdSGerd Hoffmann vs->vd->kbd, down) & SCANCODE_KEYMASK; 199740066175SGerd Hoffmann trace_vnc_key_event_map(down, sym, keycode, code2name(keycode)); 19983e230dd2SCorentin Chary do_key_event(vs, down, keycode, sym); 19993e230dd2SCorentin Chary } 20003e230dd2SCorentin Chary 20013e230dd2SCorentin Chary static void ext_key_event(VncState *vs, int down, 20023e230dd2SCorentin Chary uint32_t sym, uint16_t keycode) 20033e230dd2SCorentin Chary { 20043e230dd2SCorentin Chary /* if the user specifies a keyboard layout, always use it */ 200540066175SGerd Hoffmann if (keyboard_layout) { 20063e230dd2SCorentin Chary key_event(vs, down, sym); 200740066175SGerd Hoffmann } else { 200840066175SGerd Hoffmann trace_vnc_key_event_ext(down, sym, keycode, code2name(keycode)); 20093e230dd2SCorentin Chary do_key_event(vs, down, keycode, sym); 20103e230dd2SCorentin Chary } 201140066175SGerd Hoffmann } 20123e230dd2SCorentin Chary 20133e230dd2SCorentin Chary static void framebuffer_update_request(VncState *vs, int incremental, 2014bea60dd7SPeter Lieven int x, int y, int w, int h) 20153e230dd2SCorentin Chary { 2016bea60dd7SPeter Lieven if (incremental) { 2017fef1bbadSDaniel P. Berrange if (vs->update != VNC_STATE_UPDATE_FORCE) { 2018fef1bbadSDaniel P. Berrange vs->update = VNC_STATE_UPDATE_INCREMENTAL; 20193e230dd2SCorentin Chary } 2020fef1bbadSDaniel P. Berrange } else { 2021fef1bbadSDaniel P. Berrange vs->update = VNC_STATE_UPDATE_FORCE; 2022f7b3d68cSGerd Hoffmann vnc_set_area_dirty(vs->dirty, vs->vd, x, y, w, h); 20233e230dd2SCorentin Chary } 2024fef1bbadSDaniel P. Berrange } 20253e230dd2SCorentin Chary 20263e230dd2SCorentin Chary static void send_ext_key_event_ack(VncState *vs) 20273e230dd2SCorentin Chary { 2028bd023f95SCorentin Chary vnc_lock_output(vs); 20293e230dd2SCorentin Chary vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE); 20303e230dd2SCorentin Chary vnc_write_u8(vs, 0); 20313e230dd2SCorentin Chary vnc_write_u16(vs, 1); 2032d39fa6d8SGerd Hoffmann vnc_framebuffer_update(vs, 0, 0, 2033bea60dd7SPeter Lieven pixman_image_get_width(vs->vd->server), 2034bea60dd7SPeter Lieven pixman_image_get_height(vs->vd->server), 20353e230dd2SCorentin Chary VNC_ENCODING_EXT_KEY_EVENT); 2036bd023f95SCorentin Chary vnc_unlock_output(vs); 20373e230dd2SCorentin Chary vnc_flush(vs); 20383e230dd2SCorentin Chary } 20393e230dd2SCorentin Chary 20403e230dd2SCorentin Chary static void send_ext_audio_ack(VncState *vs) 20413e230dd2SCorentin Chary { 2042bd023f95SCorentin Chary vnc_lock_output(vs); 20433e230dd2SCorentin Chary vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE); 20443e230dd2SCorentin Chary vnc_write_u8(vs, 0); 20453e230dd2SCorentin Chary vnc_write_u16(vs, 1); 2046d39fa6d8SGerd Hoffmann vnc_framebuffer_update(vs, 0, 0, 2047bea60dd7SPeter Lieven pixman_image_get_width(vs->vd->server), 2048bea60dd7SPeter Lieven pixman_image_get_height(vs->vd->server), 20493e230dd2SCorentin Chary VNC_ENCODING_AUDIO); 2050bd023f95SCorentin Chary vnc_unlock_output(vs); 20513e230dd2SCorentin Chary vnc_flush(vs); 20523e230dd2SCorentin Chary } 20533e230dd2SCorentin Chary 20543e230dd2SCorentin Chary static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) 20553e230dd2SCorentin Chary { 20563e230dd2SCorentin Chary int i; 20573e230dd2SCorentin Chary unsigned int enc = 0; 20583e230dd2SCorentin Chary 20593e230dd2SCorentin Chary vs->features = 0; 20603e230dd2SCorentin Chary vs->vnc_encoding = 0; 20616bf21f3dSLi Qiang vs->tight->compression = 9; 20626bf21f3dSLi Qiang vs->tight->quality = -1; /* Lossless by default */ 20633e230dd2SCorentin Chary vs->absolute = -1; 20643e230dd2SCorentin Chary 20653e230dd2SCorentin Chary /* 20663e230dd2SCorentin Chary * Start from the end because the encodings are sent in order of preference. 2067e5bed759SDong Xu Wang * This way the preferred encoding (first encoding defined in the array) 20683e230dd2SCorentin Chary * will be set at the end of the loop. 20693e230dd2SCorentin Chary */ 20703e230dd2SCorentin Chary for (i = n_encodings - 1; i >= 0; i--) { 20713e230dd2SCorentin Chary enc = encodings[i]; 20723e230dd2SCorentin Chary switch (enc) { 20733e230dd2SCorentin Chary case VNC_ENCODING_RAW: 20743e230dd2SCorentin Chary vs->vnc_encoding = enc; 20753e230dd2SCorentin Chary break; 20763e230dd2SCorentin Chary case VNC_ENCODING_HEXTILE: 20773e230dd2SCorentin Chary vs->features |= VNC_FEATURE_HEXTILE_MASK; 20783e230dd2SCorentin Chary vs->vnc_encoding = enc; 20793e230dd2SCorentin Chary break; 20803e230dd2SCorentin Chary case VNC_ENCODING_TIGHT: 20813e230dd2SCorentin Chary vs->features |= VNC_FEATURE_TIGHT_MASK; 20823e230dd2SCorentin Chary vs->vnc_encoding = enc; 20833e230dd2SCorentin Chary break; 2084fe3e7f2dSJoel Martin #ifdef CONFIG_VNC_PNG 2085efe556adSCorentin Chary case VNC_ENCODING_TIGHT_PNG: 2086efe556adSCorentin Chary vs->features |= VNC_FEATURE_TIGHT_PNG_MASK; 2087efe556adSCorentin Chary vs->vnc_encoding = enc; 2088efe556adSCorentin Chary break; 2089fe3e7f2dSJoel Martin #endif 20903e230dd2SCorentin Chary case VNC_ENCODING_ZLIB: 2091557ba0e5SCameron Esfahani /* 2092557ba0e5SCameron Esfahani * VNC_ENCODING_ZRLE compresses better than VNC_ENCODING_ZLIB. 2093557ba0e5SCameron Esfahani * So prioritize ZRLE, even if the client hints that it prefers 2094557ba0e5SCameron Esfahani * ZLIB. 2095557ba0e5SCameron Esfahani */ 2096557ba0e5SCameron Esfahani if ((vs->features & VNC_FEATURE_ZRLE_MASK) == 0) { 20973e230dd2SCorentin Chary vs->features |= VNC_FEATURE_ZLIB_MASK; 20983e230dd2SCorentin Chary vs->vnc_encoding = enc; 2099557ba0e5SCameron Esfahani } 21003e230dd2SCorentin Chary break; 2101148954faSCorentin Chary case VNC_ENCODING_ZRLE: 2102148954faSCorentin Chary vs->features |= VNC_FEATURE_ZRLE_MASK; 2103148954faSCorentin Chary vs->vnc_encoding = enc; 2104148954faSCorentin Chary break; 2105148954faSCorentin Chary case VNC_ENCODING_ZYWRLE: 2106148954faSCorentin Chary vs->features |= VNC_FEATURE_ZYWRLE_MASK; 2107148954faSCorentin Chary vs->vnc_encoding = enc; 2108148954faSCorentin Chary break; 21093e230dd2SCorentin Chary case VNC_ENCODING_DESKTOPRESIZE: 21103e230dd2SCorentin Chary vs->features |= VNC_FEATURE_RESIZE_MASK; 21113e230dd2SCorentin Chary break; 21123e230dd2SCorentin Chary case VNC_ENCODING_POINTER_TYPE_CHANGE: 21133e230dd2SCorentin Chary vs->features |= VNC_FEATURE_POINTER_TYPE_CHANGE_MASK; 21143e230dd2SCorentin Chary break; 21153e230dd2SCorentin Chary case VNC_ENCODING_RICH_CURSOR: 21163e230dd2SCorentin Chary vs->features |= VNC_FEATURE_RICH_CURSOR_MASK; 2117074a86d0SGerd Hoffmann break; 2118074a86d0SGerd Hoffmann case VNC_ENCODING_ALPHA_CURSOR: 2119074a86d0SGerd Hoffmann vs->features |= VNC_FEATURE_ALPHA_CURSOR_MASK; 21203e230dd2SCorentin Chary break; 21213e230dd2SCorentin Chary case VNC_ENCODING_EXT_KEY_EVENT: 21223e230dd2SCorentin Chary send_ext_key_event_ack(vs); 21233e230dd2SCorentin Chary break; 21243e230dd2SCorentin Chary case VNC_ENCODING_AUDIO: 21253e230dd2SCorentin Chary send_ext_audio_ack(vs); 21263e230dd2SCorentin Chary break; 21273e230dd2SCorentin Chary case VNC_ENCODING_WMVi: 21283e230dd2SCorentin Chary vs->features |= VNC_FEATURE_WMVI_MASK; 21293e230dd2SCorentin Chary break; 2130ab99e5c1SLei Li case VNC_ENCODING_LED_STATE: 2131ab99e5c1SLei Li vs->features |= VNC_FEATURE_LED_STATE_MASK; 2132ab99e5c1SLei Li break; 21333e230dd2SCorentin Chary case VNC_ENCODING_COMPRESSLEVEL0 ... VNC_ENCODING_COMPRESSLEVEL0 + 9: 21346bf21f3dSLi Qiang vs->tight->compression = (enc & 0x0F); 21353e230dd2SCorentin Chary break; 21363e230dd2SCorentin Chary case VNC_ENCODING_QUALITYLEVEL0 ... VNC_ENCODING_QUALITYLEVEL0 + 9: 2137b31f519eSCorentin Chary if (vs->vd->lossy) { 21386bf21f3dSLi Qiang vs->tight->quality = (enc & 0x0F); 2139b31f519eSCorentin Chary } 21403e230dd2SCorentin Chary break; 21413e230dd2SCorentin Chary default: 21423e230dd2SCorentin Chary VNC_DEBUG("Unknown encoding: %d (0x%.8x): %d\n", i, enc, enc); 21433e230dd2SCorentin Chary break; 21443e230dd2SCorentin Chary } 21453e230dd2SCorentin Chary } 21463e230dd2SCorentin Chary vnc_desktop_resize(vs); 21479e8dd451SJan Kiszka check_pointer_type_change(&vs->mouse_mode_notifier, NULL); 2148ab99e5c1SLei Li vnc_led_state_change(vs); 2149074a86d0SGerd Hoffmann if (vs->vd->cursor) { 2150074a86d0SGerd Hoffmann vnc_cursor_define(vs); 2151074a86d0SGerd Hoffmann } 21523e230dd2SCorentin Chary } 21533e230dd2SCorentin Chary 21543e230dd2SCorentin Chary static void set_pixel_conversion(VncState *vs) 21553e230dd2SCorentin Chary { 21569f64916dSGerd Hoffmann pixman_format_code_t fmt = qemu_pixman_get_format(&vs->client_pf); 21579f64916dSGerd Hoffmann 21589f64916dSGerd Hoffmann if (fmt == VNC_SERVER_FB_FORMAT) { 21593e230dd2SCorentin Chary vs->write_pixels = vnc_write_pixels_copy; 21603e230dd2SCorentin Chary vnc_hextile_set_pixel_conversion(vs, 0); 21613e230dd2SCorentin Chary } else { 21623e230dd2SCorentin Chary vs->write_pixels = vnc_write_pixels_generic; 21633e230dd2SCorentin Chary vnc_hextile_set_pixel_conversion(vs, 1); 21643e230dd2SCorentin Chary } 21653e230dd2SCorentin Chary } 21663e230dd2SCorentin Chary 21670c426e45SAlexander Graf static void send_color_map(VncState *vs) 21680c426e45SAlexander Graf { 21690c426e45SAlexander Graf int i; 21700c426e45SAlexander Graf 2171*947191b4SPeng Liang vnc_lock_output(vs); 21720c426e45SAlexander Graf vnc_write_u8(vs, VNC_MSG_SERVER_SET_COLOUR_MAP_ENTRIES); 21730c426e45SAlexander Graf vnc_write_u8(vs, 0); /* padding */ 21740c426e45SAlexander Graf vnc_write_u16(vs, 0); /* first color */ 21750c426e45SAlexander Graf vnc_write_u16(vs, 256); /* # of colors */ 21760c426e45SAlexander Graf 21770c426e45SAlexander Graf for (i = 0; i < 256; i++) { 21780c426e45SAlexander Graf PixelFormat *pf = &vs->client_pf; 21790c426e45SAlexander Graf 21800c426e45SAlexander Graf vnc_write_u16(vs, (((i >> pf->rshift) & pf->rmax) << (16 - pf->rbits))); 21810c426e45SAlexander Graf vnc_write_u16(vs, (((i >> pf->gshift) & pf->gmax) << (16 - pf->gbits))); 21820c426e45SAlexander Graf vnc_write_u16(vs, (((i >> pf->bshift) & pf->bmax) << (16 - pf->bbits))); 21830c426e45SAlexander Graf } 2184*947191b4SPeng Liang vnc_unlock_output(vs); 21850c426e45SAlexander Graf } 21860c426e45SAlexander Graf 2187ec9fb41aSGerd Hoffmann static void set_pixel_format(VncState *vs, int bits_per_pixel, 21883e230dd2SCorentin Chary int big_endian_flag, int true_color_flag, 21893e230dd2SCorentin Chary int red_max, int green_max, int blue_max, 21903e230dd2SCorentin Chary int red_shift, int green_shift, int blue_shift) 21913e230dd2SCorentin Chary { 21923e230dd2SCorentin Chary if (!true_color_flag) { 21930c426e45SAlexander Graf /* Expose a reasonable default 256 color map */ 21940c426e45SAlexander Graf bits_per_pixel = 8; 21950c426e45SAlexander Graf red_max = 7; 21960c426e45SAlexander Graf green_max = 7; 21970c426e45SAlexander Graf blue_max = 3; 21980c426e45SAlexander Graf red_shift = 0; 21990c426e45SAlexander Graf green_shift = 3; 22000c426e45SAlexander Graf blue_shift = 6; 22013e230dd2SCorentin Chary } 22023e230dd2SCorentin Chary 2203e6908bfeSPetr Matousek switch (bits_per_pixel) { 2204e6908bfeSPetr Matousek case 8: 2205e6908bfeSPetr Matousek case 16: 2206e6908bfeSPetr Matousek case 32: 2207e6908bfeSPetr Matousek break; 2208e6908bfeSPetr Matousek default: 2209e6908bfeSPetr Matousek vnc_client_error(vs); 2210e6908bfeSPetr Matousek return; 2211e6908bfeSPetr Matousek } 2212e6908bfeSPetr Matousek 22134c65fed8SPrasad J Pandit vs->client_pf.rmax = red_max ? red_max : 0xFF; 22147c9209e7SCédric Le Goater vs->client_pf.rbits = ctpopl(red_max); 22159f64916dSGerd Hoffmann vs->client_pf.rshift = red_shift; 22169f64916dSGerd Hoffmann vs->client_pf.rmask = red_max << red_shift; 22174c65fed8SPrasad J Pandit vs->client_pf.gmax = green_max ? green_max : 0xFF; 22187c9209e7SCédric Le Goater vs->client_pf.gbits = ctpopl(green_max); 22199f64916dSGerd Hoffmann vs->client_pf.gshift = green_shift; 22209f64916dSGerd Hoffmann vs->client_pf.gmask = green_max << green_shift; 22214c65fed8SPrasad J Pandit vs->client_pf.bmax = blue_max ? blue_max : 0xFF; 22227c9209e7SCédric Le Goater vs->client_pf.bbits = ctpopl(blue_max); 22239f64916dSGerd Hoffmann vs->client_pf.bshift = blue_shift; 22249f64916dSGerd Hoffmann vs->client_pf.bmask = blue_max << blue_shift; 22259f64916dSGerd Hoffmann vs->client_pf.bits_per_pixel = bits_per_pixel; 22269f64916dSGerd Hoffmann vs->client_pf.bytes_per_pixel = bits_per_pixel / 8; 22279f64916dSGerd Hoffmann vs->client_pf.depth = bits_per_pixel == 32 ? 24 : bits_per_pixel; 22289f64916dSGerd Hoffmann vs->client_be = big_endian_flag; 22293e230dd2SCorentin Chary 22300c426e45SAlexander Graf if (!true_color_flag) { 22310c426e45SAlexander Graf send_color_map(vs); 22320c426e45SAlexander Graf } 22330c426e45SAlexander Graf 22343e230dd2SCorentin Chary set_pixel_conversion(vs); 22353e230dd2SCorentin Chary 22361d0d59feSGerd Hoffmann graphic_hw_invalidate(vs->vd->dcl.con); 22371d0d59feSGerd Hoffmann graphic_hw_update(vs->vd->dcl.con); 22383e230dd2SCorentin Chary } 22393e230dd2SCorentin Chary 22403e230dd2SCorentin Chary static void pixel_format_message (VncState *vs) { 22413e230dd2SCorentin Chary char pad[3] = { 0, 0, 0 }; 22423e230dd2SCorentin Chary 22439f64916dSGerd Hoffmann vs->client_pf = qemu_default_pixelformat(32); 22449f64916dSGerd Hoffmann 22459f64916dSGerd Hoffmann vnc_write_u8(vs, vs->client_pf.bits_per_pixel); /* bits-per-pixel */ 22469f64916dSGerd Hoffmann vnc_write_u8(vs, vs->client_pf.depth); /* depth */ 22473e230dd2SCorentin Chary 22483e230dd2SCorentin Chary #ifdef HOST_WORDS_BIGENDIAN 22493e230dd2SCorentin Chary vnc_write_u8(vs, 1); /* big-endian-flag */ 22503e230dd2SCorentin Chary #else 22513e230dd2SCorentin Chary vnc_write_u8(vs, 0); /* big-endian-flag */ 22523e230dd2SCorentin Chary #endif 22533e230dd2SCorentin Chary vnc_write_u8(vs, 1); /* true-color-flag */ 22549f64916dSGerd Hoffmann vnc_write_u16(vs, vs->client_pf.rmax); /* red-max */ 22559f64916dSGerd Hoffmann vnc_write_u16(vs, vs->client_pf.gmax); /* green-max */ 22569f64916dSGerd Hoffmann vnc_write_u16(vs, vs->client_pf.bmax); /* blue-max */ 22579f64916dSGerd Hoffmann vnc_write_u8(vs, vs->client_pf.rshift); /* red-shift */ 22589f64916dSGerd Hoffmann vnc_write_u8(vs, vs->client_pf.gshift); /* green-shift */ 22599f64916dSGerd Hoffmann vnc_write_u8(vs, vs->client_pf.bshift); /* blue-shift */ 22609f64916dSGerd Hoffmann vnc_write(vs, pad, 3); /* padding */ 22613e230dd2SCorentin Chary 22623e230dd2SCorentin Chary vnc_hextile_set_pixel_conversion(vs, 0); 22633e230dd2SCorentin Chary vs->write_pixels = vnc_write_pixels_copy; 22643e230dd2SCorentin Chary } 22653e230dd2SCorentin Chary 22663e230dd2SCorentin Chary static void vnc_colordepth(VncState *vs) 22673e230dd2SCorentin Chary { 22683e230dd2SCorentin Chary if (vnc_has_feature(vs, VNC_FEATURE_WMVI)) { 22693e230dd2SCorentin Chary /* Sending a WMVi message to notify the client*/ 2270bd023f95SCorentin Chary vnc_lock_output(vs); 22713e230dd2SCorentin Chary vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE); 22723e230dd2SCorentin Chary vnc_write_u8(vs, 0); 22733e230dd2SCorentin Chary vnc_write_u16(vs, 1); /* number of rects */ 2274d39fa6d8SGerd Hoffmann vnc_framebuffer_update(vs, 0, 0, 2275bea60dd7SPeter Lieven pixman_image_get_width(vs->vd->server), 2276bea60dd7SPeter Lieven pixman_image_get_height(vs->vd->server), 2277d39fa6d8SGerd Hoffmann VNC_ENCODING_WMVi); 22783e230dd2SCorentin Chary pixel_format_message(vs); 2279bd023f95SCorentin Chary vnc_unlock_output(vs); 22803e230dd2SCorentin Chary vnc_flush(vs); 22813e230dd2SCorentin Chary } else { 22823e230dd2SCorentin Chary set_pixel_conversion(vs); 22833e230dd2SCorentin Chary } 22843e230dd2SCorentin Chary } 22853e230dd2SCorentin Chary 22863e230dd2SCorentin Chary static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) 22873e230dd2SCorentin Chary { 22883e230dd2SCorentin Chary int i; 22893e230dd2SCorentin Chary uint16_t limit; 2290cf070658SDaniel P. Berrangé uint32_t freq; 22913e230dd2SCorentin Chary VncDisplay *vd = vs->vd; 22923e230dd2SCorentin Chary 22933e230dd2SCorentin Chary if (data[0] > 3) { 22940f7b2864SGerd Hoffmann update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE); 22953e230dd2SCorentin Chary } 22963e230dd2SCorentin Chary 22973e230dd2SCorentin Chary switch (data[0]) { 22983e230dd2SCorentin Chary case VNC_MSG_CLIENT_SET_PIXEL_FORMAT: 22993e230dd2SCorentin Chary if (len == 1) 23003e230dd2SCorentin Chary return 20; 23013e230dd2SCorentin Chary 2302ec9fb41aSGerd Hoffmann set_pixel_format(vs, read_u8(data, 4), 23033e230dd2SCorentin Chary read_u8(data, 6), read_u8(data, 7), 23043e230dd2SCorentin Chary read_u16(data, 8), read_u16(data, 10), 23053e230dd2SCorentin Chary read_u16(data, 12), read_u8(data, 14), 23063e230dd2SCorentin Chary read_u8(data, 15), read_u8(data, 16)); 23073e230dd2SCorentin Chary break; 23083e230dd2SCorentin Chary case VNC_MSG_CLIENT_SET_ENCODINGS: 23093e230dd2SCorentin Chary if (len == 1) 23103e230dd2SCorentin Chary return 4; 23113e230dd2SCorentin Chary 23123e230dd2SCorentin Chary if (len == 4) { 23133e230dd2SCorentin Chary limit = read_u16(data, 2); 23143e230dd2SCorentin Chary if (limit > 0) 23153e230dd2SCorentin Chary return 4 + (limit * 4); 23163e230dd2SCorentin Chary } else 23173e230dd2SCorentin Chary limit = read_u16(data, 2); 23183e230dd2SCorentin Chary 23193e230dd2SCorentin Chary for (i = 0; i < limit; i++) { 23203e230dd2SCorentin Chary int32_t val = read_s32(data, 4 + (i * 4)); 23213e230dd2SCorentin Chary memcpy(data + 4 + (i * 4), &val, sizeof(val)); 23223e230dd2SCorentin Chary } 23233e230dd2SCorentin Chary 23243e230dd2SCorentin Chary set_encodings(vs, (int32_t *)(data + 4), limit); 23253e230dd2SCorentin Chary break; 23263e230dd2SCorentin Chary case VNC_MSG_CLIENT_FRAMEBUFFER_UPDATE_REQUEST: 23273e230dd2SCorentin Chary if (len == 1) 23283e230dd2SCorentin Chary return 10; 23293e230dd2SCorentin Chary 23303e230dd2SCorentin Chary framebuffer_update_request(vs, 23313e230dd2SCorentin Chary read_u8(data, 1), read_u16(data, 2), read_u16(data, 4), 23323e230dd2SCorentin Chary read_u16(data, 6), read_u16(data, 8)); 23333e230dd2SCorentin Chary break; 23343e230dd2SCorentin Chary case VNC_MSG_CLIENT_KEY_EVENT: 23353e230dd2SCorentin Chary if (len == 1) 23363e230dd2SCorentin Chary return 8; 23373e230dd2SCorentin Chary 23383e230dd2SCorentin Chary key_event(vs, read_u8(data, 1), read_u32(data, 4)); 23393e230dd2SCorentin Chary break; 23403e230dd2SCorentin Chary case VNC_MSG_CLIENT_POINTER_EVENT: 23413e230dd2SCorentin Chary if (len == 1) 23423e230dd2SCorentin Chary return 6; 23433e230dd2SCorentin Chary 23443e230dd2SCorentin Chary pointer_event(vs, read_u8(data, 1), read_u16(data, 2), read_u16(data, 4)); 23453e230dd2SCorentin Chary break; 23463e230dd2SCorentin Chary case VNC_MSG_CLIENT_CUT_TEXT: 2347f9a70e79SPeter Lieven if (len == 1) { 23483e230dd2SCorentin Chary return 8; 2349f9a70e79SPeter Lieven } 23503e230dd2SCorentin Chary if (len == 8) { 23513e230dd2SCorentin Chary uint32_t dlen = read_u32(data, 4); 2352f9a70e79SPeter Lieven if (dlen > (1 << 20)) { 2353f9a70e79SPeter Lieven error_report("vnc: client_cut_text msg payload has %u bytes" 2354f9a70e79SPeter Lieven " which exceeds our limit of 1MB.", dlen); 2355f9a70e79SPeter Lieven vnc_client_error(vs); 2356f9a70e79SPeter Lieven break; 2357f9a70e79SPeter Lieven } 2358f9a70e79SPeter Lieven if (dlen > 0) { 23593e230dd2SCorentin Chary return 8 + dlen; 23603e230dd2SCorentin Chary } 2361f9a70e79SPeter Lieven } 23623e230dd2SCorentin Chary 23633e230dd2SCorentin Chary client_cut_text(vs, read_u32(data, 4), data + 8); 23643e230dd2SCorentin Chary break; 23653e230dd2SCorentin Chary case VNC_MSG_CLIENT_QEMU: 23663e230dd2SCorentin Chary if (len == 1) 23673e230dd2SCorentin Chary return 2; 23683e230dd2SCorentin Chary 23693e230dd2SCorentin Chary switch (read_u8(data, 1)) { 23703e230dd2SCorentin Chary case VNC_MSG_CLIENT_QEMU_EXT_KEY_EVENT: 23713e230dd2SCorentin Chary if (len == 2) 23723e230dd2SCorentin Chary return 12; 23733e230dd2SCorentin Chary 23743e230dd2SCorentin Chary ext_key_event(vs, read_u16(data, 2), 23753e230dd2SCorentin Chary read_u32(data, 4), read_u32(data, 8)); 23763e230dd2SCorentin Chary break; 23773e230dd2SCorentin Chary case VNC_MSG_CLIENT_QEMU_AUDIO: 23783e230dd2SCorentin Chary if (len == 2) 23793e230dd2SCorentin Chary return 4; 23803e230dd2SCorentin Chary 23813e230dd2SCorentin Chary switch (read_u16 (data, 2)) { 23823e230dd2SCorentin Chary case VNC_MSG_CLIENT_QEMU_AUDIO_ENABLE: 23833e230dd2SCorentin Chary audio_add(vs); 23843e230dd2SCorentin Chary break; 23853e230dd2SCorentin Chary case VNC_MSG_CLIENT_QEMU_AUDIO_DISABLE: 23863e230dd2SCorentin Chary audio_del(vs); 23873e230dd2SCorentin Chary break; 23883e230dd2SCorentin Chary case VNC_MSG_CLIENT_QEMU_AUDIO_SET_FORMAT: 23893e230dd2SCorentin Chary if (len == 4) 23903e230dd2SCorentin Chary return 10; 23913e230dd2SCorentin Chary switch (read_u8(data, 4)) { 239285bc5852SKővágó, Zoltán case 0: vs->as.fmt = AUDIO_FORMAT_U8; break; 239385bc5852SKővágó, Zoltán case 1: vs->as.fmt = AUDIO_FORMAT_S8; break; 239485bc5852SKővágó, Zoltán case 2: vs->as.fmt = AUDIO_FORMAT_U16; break; 239585bc5852SKővágó, Zoltán case 3: vs->as.fmt = AUDIO_FORMAT_S16; break; 239685bc5852SKővágó, Zoltán case 4: vs->as.fmt = AUDIO_FORMAT_U32; break; 239785bc5852SKővágó, Zoltán case 5: vs->as.fmt = AUDIO_FORMAT_S32; break; 23983e230dd2SCorentin Chary default: 2399153130cdSDaniel P. Berrange VNC_DEBUG("Invalid audio format %d\n", read_u8(data, 4)); 24003e230dd2SCorentin Chary vnc_client_error(vs); 24013e230dd2SCorentin Chary break; 24023e230dd2SCorentin Chary } 24033e230dd2SCorentin Chary vs->as.nchannels = read_u8(data, 5); 24043e230dd2SCorentin Chary if (vs->as.nchannels != 1 && vs->as.nchannels != 2) { 2405090fdc83SMarc-André Lureau VNC_DEBUG("Invalid audio channel count %d\n", 24063e230dd2SCorentin Chary read_u8(data, 5)); 24073e230dd2SCorentin Chary vnc_client_error(vs); 24083e230dd2SCorentin Chary break; 24093e230dd2SCorentin Chary } 2410cf070658SDaniel P. Berrangé freq = read_u32(data, 6); 2411cf070658SDaniel P. Berrangé /* No official limit for protocol, but 48khz is a sensible 2412cf070658SDaniel P. Berrangé * upper bound for trustworthy clients, and this limit 2413cf070658SDaniel P. Berrangé * protects calculations involving 'vs->as.freq' later. 2414cf070658SDaniel P. Berrangé */ 2415cf070658SDaniel P. Berrangé if (freq > 48000) { 2416cf070658SDaniel P. Berrangé VNC_DEBUG("Invalid audio frequency %u > 48000", freq); 2417cf070658SDaniel P. Berrangé vnc_client_error(vs); 2418cf070658SDaniel P. Berrangé break; 2419cf070658SDaniel P. Berrangé } 2420cf070658SDaniel P. Berrangé vs->as.freq = freq; 24213e230dd2SCorentin Chary break; 24223e230dd2SCorentin Chary default: 2423153130cdSDaniel P. Berrange VNC_DEBUG("Invalid audio message %d\n", read_u8(data, 4)); 24243e230dd2SCorentin Chary vnc_client_error(vs); 24253e230dd2SCorentin Chary break; 24263e230dd2SCorentin Chary } 24273e230dd2SCorentin Chary break; 24283e230dd2SCorentin Chary 24293e230dd2SCorentin Chary default: 2430153130cdSDaniel P. Berrange VNC_DEBUG("Msg: %d\n", read_u16(data, 0)); 24313e230dd2SCorentin Chary vnc_client_error(vs); 24323e230dd2SCorentin Chary break; 24333e230dd2SCorentin Chary } 24343e230dd2SCorentin Chary break; 24353e230dd2SCorentin Chary default: 2436153130cdSDaniel P. Berrange VNC_DEBUG("Msg: %d\n", data[0]); 24373e230dd2SCorentin Chary vnc_client_error(vs); 24383e230dd2SCorentin Chary break; 24393e230dd2SCorentin Chary } 24403e230dd2SCorentin Chary 2441e2b72cb6SDaniel P. Berrange vnc_update_throttle_offset(vs); 24423e230dd2SCorentin Chary vnc_read_when(vs, protocol_client_msg, 1); 24433e230dd2SCorentin Chary return 0; 24443e230dd2SCorentin Chary } 24453e230dd2SCorentin Chary 24463e230dd2SCorentin Chary static int protocol_client_init(VncState *vs, uint8_t *data, size_t len) 24473e230dd2SCorentin Chary { 24483e230dd2SCorentin Chary char buf[1024]; 24498cf36489SGerd Hoffmann VncShareMode mode; 24503e230dd2SCorentin Chary int size; 24513e230dd2SCorentin Chary 24528cf36489SGerd Hoffmann mode = data[0] ? VNC_SHARE_MODE_SHARED : VNC_SHARE_MODE_EXCLUSIVE; 24538cf36489SGerd Hoffmann switch (vs->vd->share_policy) { 24548cf36489SGerd Hoffmann case VNC_SHARE_POLICY_IGNORE: 24558cf36489SGerd Hoffmann /* 24568cf36489SGerd Hoffmann * Ignore the shared flag. Nothing to do here. 24578cf36489SGerd Hoffmann * 24588cf36489SGerd Hoffmann * Doesn't conform to the rfb spec but is traditional qemu 24598cf36489SGerd Hoffmann * behavior, thus left here as option for compatibility 24608cf36489SGerd Hoffmann * reasons. 24618cf36489SGerd Hoffmann */ 24628cf36489SGerd Hoffmann break; 24638cf36489SGerd Hoffmann case VNC_SHARE_POLICY_ALLOW_EXCLUSIVE: 24648cf36489SGerd Hoffmann /* 24658cf36489SGerd Hoffmann * Policy: Allow clients ask for exclusive access. 24668cf36489SGerd Hoffmann * 24678cf36489SGerd Hoffmann * Implementation: When a client asks for exclusive access, 24688cf36489SGerd Hoffmann * disconnect all others. Shared connects are allowed as long 24698cf36489SGerd Hoffmann * as no exclusive connection exists. 24708cf36489SGerd Hoffmann * 24718cf36489SGerd Hoffmann * This is how the rfb spec suggests to handle the shared flag. 24728cf36489SGerd Hoffmann */ 24738cf36489SGerd Hoffmann if (mode == VNC_SHARE_MODE_EXCLUSIVE) { 24748cf36489SGerd Hoffmann VncState *client; 24758cf36489SGerd Hoffmann QTAILQ_FOREACH(client, &vs->vd->clients, next) { 24768cf36489SGerd Hoffmann if (vs == client) { 24778cf36489SGerd Hoffmann continue; 24788cf36489SGerd Hoffmann } 24798cf36489SGerd Hoffmann if (client->share_mode != VNC_SHARE_MODE_EXCLUSIVE && 24808cf36489SGerd Hoffmann client->share_mode != VNC_SHARE_MODE_SHARED) { 24818cf36489SGerd Hoffmann continue; 24828cf36489SGerd Hoffmann } 24838cf36489SGerd Hoffmann vnc_disconnect_start(client); 24848cf36489SGerd Hoffmann } 24858cf36489SGerd Hoffmann } 24868cf36489SGerd Hoffmann if (mode == VNC_SHARE_MODE_SHARED) { 24878cf36489SGerd Hoffmann if (vs->vd->num_exclusive > 0) { 24888cf36489SGerd Hoffmann vnc_disconnect_start(vs); 24898cf36489SGerd Hoffmann return 0; 24908cf36489SGerd Hoffmann } 24918cf36489SGerd Hoffmann } 24928cf36489SGerd Hoffmann break; 24938cf36489SGerd Hoffmann case VNC_SHARE_POLICY_FORCE_SHARED: 24948cf36489SGerd Hoffmann /* 24958cf36489SGerd Hoffmann * Policy: Shared connects only. 24968cf36489SGerd Hoffmann * Implementation: Disallow clients asking for exclusive access. 24978cf36489SGerd Hoffmann * 24988cf36489SGerd Hoffmann * Useful for shared desktop sessions where you don't want 24998cf36489SGerd Hoffmann * someone forgetting to say -shared when running the vnc 25008cf36489SGerd Hoffmann * client disconnect everybody else. 25018cf36489SGerd Hoffmann */ 25028cf36489SGerd Hoffmann if (mode == VNC_SHARE_MODE_EXCLUSIVE) { 25038cf36489SGerd Hoffmann vnc_disconnect_start(vs); 25048cf36489SGerd Hoffmann return 0; 25058cf36489SGerd Hoffmann } 25068cf36489SGerd Hoffmann break; 25078cf36489SGerd Hoffmann } 25088cf36489SGerd Hoffmann vnc_set_share_mode(vs, mode); 25098cf36489SGerd Hoffmann 2510e5f34cddSGerd Hoffmann if (vs->vd->num_shared > vs->vd->connections_limit) { 2511e5f34cddSGerd Hoffmann vnc_disconnect_start(vs); 2512e5f34cddSGerd Hoffmann return 0; 2513e5f34cddSGerd Hoffmann } 2514e5f34cddSGerd Hoffmann 25154c956bd8SDaniel P. Berrange assert(pixman_image_get_width(vs->vd->server) < 65536 && 25164c956bd8SDaniel P. Berrange pixman_image_get_width(vs->vd->server) >= 0); 25174c956bd8SDaniel P. Berrange assert(pixman_image_get_height(vs->vd->server) < 65536 && 25184c956bd8SDaniel P. Berrange pixman_image_get_height(vs->vd->server) >= 0); 2519bea60dd7SPeter Lieven vs->client_width = pixman_image_get_width(vs->vd->server); 2520bea60dd7SPeter Lieven vs->client_height = pixman_image_get_height(vs->vd->server); 25213e230dd2SCorentin Chary vnc_write_u16(vs, vs->client_width); 25223e230dd2SCorentin Chary vnc_write_u16(vs, vs->client_height); 25233e230dd2SCorentin Chary 25243e230dd2SCorentin Chary pixel_format_message(vs); 25253e230dd2SCorentin Chary 252697efe4f9SThomas Huth if (qemu_name) { 25273e230dd2SCorentin Chary size = snprintf(buf, sizeof(buf), "QEMU (%s)", qemu_name); 252897efe4f9SThomas Huth if (size > sizeof(buf)) { 252997efe4f9SThomas Huth size = sizeof(buf); 253097efe4f9SThomas Huth } 253197efe4f9SThomas Huth } else { 25323e230dd2SCorentin Chary size = snprintf(buf, sizeof(buf), "QEMU"); 253397efe4f9SThomas Huth } 25343e230dd2SCorentin Chary 25353e230dd2SCorentin Chary vnc_write_u32(vs, size); 25363e230dd2SCorentin Chary vnc_write(vs, buf, size); 25373e230dd2SCorentin Chary vnc_flush(vs); 25383e230dd2SCorentin Chary 25393e230dd2SCorentin Chary vnc_client_cache_auth(vs); 2540fb6ba0d5SWenchao Xia vnc_qmp_event(vs, QAPI_EVENT_VNC_INITIALIZED); 25413e230dd2SCorentin Chary 25423e230dd2SCorentin Chary vnc_read_when(vs, protocol_client_msg, 1); 25433e230dd2SCorentin Chary 25443e230dd2SCorentin Chary return 0; 25453e230dd2SCorentin Chary } 25463e230dd2SCorentin Chary 25473e230dd2SCorentin Chary void start_client_init(VncState *vs) 25483e230dd2SCorentin Chary { 25493e230dd2SCorentin Chary vnc_read_when(vs, protocol_client_init, 1); 25503e230dd2SCorentin Chary } 25513e230dd2SCorentin Chary 25524347e638SRichard Henderson static void authentication_failed(VncState *vs) 25534347e638SRichard Henderson { 25544347e638SRichard Henderson vnc_write_u32(vs, 1); /* Reject auth */ 25554347e638SRichard Henderson if (vs->minor >= 8) { 25564347e638SRichard Henderson static const char err[] = "Authentication failed"; 25574347e638SRichard Henderson vnc_write_u32(vs, sizeof(err)); 25584347e638SRichard Henderson vnc_write(vs, err, sizeof(err)); 25594347e638SRichard Henderson } 25604347e638SRichard Henderson vnc_flush(vs); 25614347e638SRichard Henderson vnc_client_error(vs); 25624347e638SRichard Henderson } 25634347e638SRichard Henderson 25643e230dd2SCorentin Chary static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len) 25653e230dd2SCorentin Chary { 25663e230dd2SCorentin Chary unsigned char response[VNC_AUTH_CHALLENGE_SIZE]; 2567800567a6SDaniel P. Berrange size_t i, pwlen; 25683e230dd2SCorentin Chary unsigned char key[8]; 25693c9405a0SGerd Hoffmann time_t now = time(NULL); 257060928458SGonglei QCryptoCipher *cipher = NULL; 2571800567a6SDaniel P. Berrange Error *err = NULL; 25723e230dd2SCorentin Chary 25731cd20f8bSAnthony Liguori if (!vs->vd->password) { 25747364dbdaSDaniel P. Berrange trace_vnc_auth_fail(vs, vs->auth, "password is not set", ""); 25756bffdf0fSGerd Hoffmann goto reject; 25763e230dd2SCorentin Chary } 25773c9405a0SGerd Hoffmann if (vs->vd->expires < now) { 25787364dbdaSDaniel P. Berrange trace_vnc_auth_fail(vs, vs->auth, "password is expired", ""); 25793c9405a0SGerd Hoffmann goto reject; 25803c9405a0SGerd Hoffmann } 25813e230dd2SCorentin Chary 25823e230dd2SCorentin Chary memcpy(response, vs->challenge, VNC_AUTH_CHALLENGE_SIZE); 25833e230dd2SCorentin Chary 25843e230dd2SCorentin Chary /* Calculate the expected challenge response */ 25853e230dd2SCorentin Chary pwlen = strlen(vs->vd->password); 25863e230dd2SCorentin Chary for (i=0; i<sizeof(key); i++) 25873e230dd2SCorentin Chary key[i] = i<pwlen ? vs->vd->password[i] : 0; 2588800567a6SDaniel P. Berrange 2589800567a6SDaniel P. Berrange cipher = qcrypto_cipher_new( 2590800567a6SDaniel P. Berrange QCRYPTO_CIPHER_ALG_DES_RFB, 2591800567a6SDaniel P. Berrange QCRYPTO_CIPHER_MODE_ECB, 2592800567a6SDaniel P. Berrange key, G_N_ELEMENTS(key), 2593800567a6SDaniel P. Berrange &err); 2594800567a6SDaniel P. Berrange if (!cipher) { 25957364dbdaSDaniel P. Berrange trace_vnc_auth_fail(vs, vs->auth, "cannot create cipher", 2596800567a6SDaniel P. Berrange error_get_pretty(err)); 2597800567a6SDaniel P. Berrange error_free(err); 2598800567a6SDaniel P. Berrange goto reject; 2599800567a6SDaniel P. Berrange } 2600800567a6SDaniel P. Berrange 2601a1695137SWolfgang Bumiller if (qcrypto_cipher_encrypt(cipher, 2602800567a6SDaniel P. Berrange vs->challenge, 2603800567a6SDaniel P. Berrange response, 2604800567a6SDaniel P. Berrange VNC_AUTH_CHALLENGE_SIZE, 2605800567a6SDaniel P. Berrange &err) < 0) { 26067364dbdaSDaniel P. Berrange trace_vnc_auth_fail(vs, vs->auth, "cannot encrypt challenge response", 2607800567a6SDaniel P. Berrange error_get_pretty(err)); 2608800567a6SDaniel P. Berrange error_free(err); 2609800567a6SDaniel P. Berrange goto reject; 2610800567a6SDaniel P. Berrange } 26113e230dd2SCorentin Chary 26123e230dd2SCorentin Chary /* Compare expected vs actual challenge response */ 26133e230dd2SCorentin Chary if (memcmp(response, data, VNC_AUTH_CHALLENGE_SIZE) != 0) { 26147364dbdaSDaniel P. Berrange trace_vnc_auth_fail(vs, vs->auth, "mis-matched challenge response", ""); 26156bffdf0fSGerd Hoffmann goto reject; 26166bffdf0fSGerd Hoffmann } else { 26177364dbdaSDaniel P. Berrange trace_vnc_auth_pass(vs, vs->auth); 26186bffdf0fSGerd Hoffmann vnc_write_u32(vs, 0); /* Accept auth */ 26196bffdf0fSGerd Hoffmann vnc_flush(vs); 26206bffdf0fSGerd Hoffmann 26216bffdf0fSGerd Hoffmann start_client_init(vs); 26226bffdf0fSGerd Hoffmann } 262360928458SGonglei 262460928458SGonglei qcrypto_cipher_free(cipher); 26256bffdf0fSGerd Hoffmann return 0; 26266bffdf0fSGerd Hoffmann 26276bffdf0fSGerd Hoffmann reject: 26284347e638SRichard Henderson authentication_failed(vs); 262960928458SGonglei qcrypto_cipher_free(cipher); 26303e230dd2SCorentin Chary return 0; 26313e230dd2SCorentin Chary } 26323e230dd2SCorentin Chary 26333e230dd2SCorentin Chary void start_auth_vnc(VncState *vs) 26343e230dd2SCorentin Chary { 2635f7b2502cSRichard Henderson Error *err = NULL; 2636f7b2502cSRichard Henderson 2637f7b2502cSRichard Henderson if (qcrypto_random_bytes(vs->challenge, sizeof(vs->challenge), &err)) { 2638f7b2502cSRichard Henderson trace_vnc_auth_fail(vs, vs->auth, "cannot get random bytes", 2639f7b2502cSRichard Henderson error_get_pretty(err)); 2640f7b2502cSRichard Henderson error_free(err); 2641f7b2502cSRichard Henderson authentication_failed(vs); 2642f7b2502cSRichard Henderson return; 2643f7b2502cSRichard Henderson } 2644f7b2502cSRichard Henderson 26453e230dd2SCorentin Chary /* Send client a 'random' challenge */ 26463e230dd2SCorentin Chary vnc_write(vs, vs->challenge, sizeof(vs->challenge)); 26473e230dd2SCorentin Chary vnc_flush(vs); 26483e230dd2SCorentin Chary 26493e230dd2SCorentin Chary vnc_read_when(vs, protocol_client_auth_vnc, sizeof(vs->challenge)); 26503e230dd2SCorentin Chary } 26513e230dd2SCorentin Chary 26523e230dd2SCorentin Chary 26533e230dd2SCorentin Chary static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len) 26543e230dd2SCorentin Chary { 26553e230dd2SCorentin Chary /* We only advertise 1 auth scheme at a time, so client 26563e230dd2SCorentin Chary * must pick the one we sent. Verify this */ 26577e7e2ebcSDaniel P. Berrange if (data[0] != vs->auth) { /* Reject auth */ 26587364dbdaSDaniel P. Berrange trace_vnc_auth_reject(vs, vs->auth, (int)data[0]); 26594347e638SRichard Henderson authentication_failed(vs); 26603e230dd2SCorentin Chary } else { /* Accept requested auth */ 26617364dbdaSDaniel P. Berrange trace_vnc_auth_start(vs, vs->auth); 26627e7e2ebcSDaniel P. Berrange switch (vs->auth) { 26633e230dd2SCorentin Chary case VNC_AUTH_NONE: 26643e230dd2SCorentin Chary if (vs->minor >= 8) { 26653e230dd2SCorentin Chary vnc_write_u32(vs, 0); /* Accept auth completion */ 26663e230dd2SCorentin Chary vnc_flush(vs); 26673e230dd2SCorentin Chary } 26687364dbdaSDaniel P. Berrange trace_vnc_auth_pass(vs, vs->auth); 26693e230dd2SCorentin Chary start_client_init(vs); 26703e230dd2SCorentin Chary break; 26713e230dd2SCorentin Chary 26723e230dd2SCorentin Chary case VNC_AUTH_VNC: 26733e230dd2SCorentin Chary start_auth_vnc(vs); 26743e230dd2SCorentin Chary break; 26753e230dd2SCorentin Chary 26763e230dd2SCorentin Chary case VNC_AUTH_VENCRYPT: 26773e230dd2SCorentin Chary start_auth_vencrypt(vs); 26783e230dd2SCorentin Chary break; 26793e230dd2SCorentin Chary 26803e230dd2SCorentin Chary #ifdef CONFIG_VNC_SASL 26813e230dd2SCorentin Chary case VNC_AUTH_SASL: 26823e230dd2SCorentin Chary start_auth_sasl(vs); 26833e230dd2SCorentin Chary break; 26843e230dd2SCorentin Chary #endif /* CONFIG_VNC_SASL */ 26853e230dd2SCorentin Chary 26863e230dd2SCorentin Chary default: /* Should not be possible, but just in case */ 26877364dbdaSDaniel P. Berrange trace_vnc_auth_fail(vs, vs->auth, "Unhandled auth method", ""); 26884347e638SRichard Henderson authentication_failed(vs); 26893e230dd2SCorentin Chary } 26903e230dd2SCorentin Chary } 26913e230dd2SCorentin Chary return 0; 26923e230dd2SCorentin Chary } 26933e230dd2SCorentin Chary 26943e230dd2SCorentin Chary static int protocol_version(VncState *vs, uint8_t *version, size_t len) 26953e230dd2SCorentin Chary { 26963e230dd2SCorentin Chary char local[13]; 26973e230dd2SCorentin Chary 26983e230dd2SCorentin Chary memcpy(local, version, 12); 26993e230dd2SCorentin Chary local[12] = 0; 27003e230dd2SCorentin Chary 27013e230dd2SCorentin Chary if (sscanf(local, "RFB %03d.%03d\n", &vs->major, &vs->minor) != 2) { 27023e230dd2SCorentin Chary VNC_DEBUG("Malformed protocol version %s\n", local); 27033e230dd2SCorentin Chary vnc_client_error(vs); 27043e230dd2SCorentin Chary return 0; 27053e230dd2SCorentin Chary } 27063e230dd2SCorentin Chary VNC_DEBUG("Client request protocol version %d.%d\n", vs->major, vs->minor); 27073e230dd2SCorentin Chary if (vs->major != 3 || 27083e230dd2SCorentin Chary (vs->minor != 3 && 27093e230dd2SCorentin Chary vs->minor != 4 && 27103e230dd2SCorentin Chary vs->minor != 5 && 27113e230dd2SCorentin Chary vs->minor != 7 && 27123e230dd2SCorentin Chary vs->minor != 8)) { 27133e230dd2SCorentin Chary VNC_DEBUG("Unsupported client version\n"); 27143e230dd2SCorentin Chary vnc_write_u32(vs, VNC_AUTH_INVALID); 27153e230dd2SCorentin Chary vnc_flush(vs); 27163e230dd2SCorentin Chary vnc_client_error(vs); 27173e230dd2SCorentin Chary return 0; 27183e230dd2SCorentin Chary } 27193e230dd2SCorentin Chary /* Some broken clients report v3.4 or v3.5, which spec requires to be treated 27203e230dd2SCorentin Chary * as equivalent to v3.3 by servers 27213e230dd2SCorentin Chary */ 27223e230dd2SCorentin Chary if (vs->minor == 4 || vs->minor == 5) 27233e230dd2SCorentin Chary vs->minor = 3; 27243e230dd2SCorentin Chary 27253e230dd2SCorentin Chary if (vs->minor == 3) { 27267364dbdaSDaniel P. Berrange trace_vnc_auth_start(vs, vs->auth); 27277e7e2ebcSDaniel P. Berrange if (vs->auth == VNC_AUTH_NONE) { 27287e7e2ebcSDaniel P. Berrange vnc_write_u32(vs, vs->auth); 27293e230dd2SCorentin Chary vnc_flush(vs); 27307364dbdaSDaniel P. Berrange trace_vnc_auth_pass(vs, vs->auth); 27313e230dd2SCorentin Chary start_client_init(vs); 27327e7e2ebcSDaniel P. Berrange } else if (vs->auth == VNC_AUTH_VNC) { 27333e230dd2SCorentin Chary VNC_DEBUG("Tell client VNC auth\n"); 27347e7e2ebcSDaniel P. Berrange vnc_write_u32(vs, vs->auth); 27353e230dd2SCorentin Chary vnc_flush(vs); 27363e230dd2SCorentin Chary start_auth_vnc(vs); 27373e230dd2SCorentin Chary } else { 27387364dbdaSDaniel P. Berrange trace_vnc_auth_fail(vs, vs->auth, 27397364dbdaSDaniel P. Berrange "Unsupported auth method for v3.3", ""); 27403e230dd2SCorentin Chary vnc_write_u32(vs, VNC_AUTH_INVALID); 27413e230dd2SCorentin Chary vnc_flush(vs); 27423e230dd2SCorentin Chary vnc_client_error(vs); 27433e230dd2SCorentin Chary } 27443e230dd2SCorentin Chary } else { 27453e230dd2SCorentin Chary vnc_write_u8(vs, 1); /* num auth */ 27467e7e2ebcSDaniel P. Berrange vnc_write_u8(vs, vs->auth); 27473e230dd2SCorentin Chary vnc_read_when(vs, protocol_client_auth, 1); 27483e230dd2SCorentin Chary vnc_flush(vs); 27493e230dd2SCorentin Chary } 27503e230dd2SCorentin Chary 27513e230dd2SCorentin Chary return 0; 27523e230dd2SCorentin Chary } 27533e230dd2SCorentin Chary 2754999342a0SCorentin Chary static VncRectStat *vnc_stat_rect(VncDisplay *vd, int x, int y) 2755999342a0SCorentin Chary { 2756999342a0SCorentin Chary struct VncSurface *vs = &vd->guest; 2757999342a0SCorentin Chary 2758999342a0SCorentin Chary return &vs->stats[y / VNC_STAT_RECT][x / VNC_STAT_RECT]; 2759999342a0SCorentin Chary } 2760999342a0SCorentin Chary 27617d964c9dSCorentin Chary void vnc_sent_lossy_rect(VncState *vs, int x, int y, int w, int h) 27627d964c9dSCorentin Chary { 27637d964c9dSCorentin Chary int i, j; 27647d964c9dSCorentin Chary 27657d964c9dSCorentin Chary w = (x + w) / VNC_STAT_RECT; 27667d964c9dSCorentin Chary h = (y + h) / VNC_STAT_RECT; 27677d964c9dSCorentin Chary x /= VNC_STAT_RECT; 27687d964c9dSCorentin Chary y /= VNC_STAT_RECT; 27697d964c9dSCorentin Chary 2770207f328aSCorentin Chary for (j = y; j <= h; j++) { 2771207f328aSCorentin Chary for (i = x; i <= w; i++) { 27727d964c9dSCorentin Chary vs->lossy_rect[j][i] = 1; 27737d964c9dSCorentin Chary } 27747d964c9dSCorentin Chary } 27757d964c9dSCorentin Chary } 27767d964c9dSCorentin Chary 27777d964c9dSCorentin Chary static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y) 27787d964c9dSCorentin Chary { 27797d964c9dSCorentin Chary VncState *vs; 27807d964c9dSCorentin Chary int sty = y / VNC_STAT_RECT; 27817d964c9dSCorentin Chary int stx = x / VNC_STAT_RECT; 27827d964c9dSCorentin Chary int has_dirty = 0; 27837d964c9dSCorentin Chary 27845a3804dbSMarc-André Lureau y = QEMU_ALIGN_DOWN(y, VNC_STAT_RECT); 27855a3804dbSMarc-André Lureau x = QEMU_ALIGN_DOWN(x, VNC_STAT_RECT); 27867d964c9dSCorentin Chary 27877d964c9dSCorentin Chary QTAILQ_FOREACH(vs, &vd->clients, next) { 2788bc2429b9SCorentin Chary int j; 27897d964c9dSCorentin Chary 27907d964c9dSCorentin Chary /* kernel send buffers are full -> refresh later */ 27917d964c9dSCorentin Chary if (vs->output.offset) { 27927d964c9dSCorentin Chary continue; 27937d964c9dSCorentin Chary } 27947d964c9dSCorentin Chary 27957d964c9dSCorentin Chary if (!vs->lossy_rect[sty][stx]) { 27967d964c9dSCorentin Chary continue; 27977d964c9dSCorentin Chary } 2798207f328aSCorentin Chary 27997d964c9dSCorentin Chary vs->lossy_rect[sty][stx] = 0; 28007d964c9dSCorentin Chary for (j = 0; j < VNC_STAT_RECT; ++j) { 2801b4c85ddcSPeter Lieven bitmap_set(vs->dirty[y + j], 2802b4c85ddcSPeter Lieven x / VNC_DIRTY_PIXELS_PER_BIT, 2803b4c85ddcSPeter Lieven VNC_STAT_RECT / VNC_DIRTY_PIXELS_PER_BIT); 28047d964c9dSCorentin Chary } 28057d964c9dSCorentin Chary has_dirty++; 28067d964c9dSCorentin Chary } 2807207f328aSCorentin Chary 28087d964c9dSCorentin Chary return has_dirty; 28097d964c9dSCorentin Chary } 28107d964c9dSCorentin Chary 28117d964c9dSCorentin Chary static int vnc_update_stats(VncDisplay *vd, struct timeval * tv) 2812999342a0SCorentin Chary { 2813eebe0b79SGerd Hoffmann int width = MIN(pixman_image_get_width(vd->guest.fb), 2814eebe0b79SGerd Hoffmann pixman_image_get_width(vd->server)); 2815eebe0b79SGerd Hoffmann int height = MIN(pixman_image_get_height(vd->guest.fb), 2816eebe0b79SGerd Hoffmann pixman_image_get_height(vd->server)); 2817999342a0SCorentin Chary int x, y; 2818999342a0SCorentin Chary struct timeval res; 28197d964c9dSCorentin Chary int has_dirty = 0; 2820999342a0SCorentin Chary 28219f64916dSGerd Hoffmann for (y = 0; y < height; y += VNC_STAT_RECT) { 28229f64916dSGerd Hoffmann for (x = 0; x < width; x += VNC_STAT_RECT) { 2823999342a0SCorentin Chary VncRectStat *rect = vnc_stat_rect(vd, x, y); 2824999342a0SCorentin Chary 2825999342a0SCorentin Chary rect->updated = false; 2826999342a0SCorentin Chary } 2827999342a0SCorentin Chary } 2828999342a0SCorentin Chary 2829ad620c29SBlue Swirl qemu_timersub(tv, &VNC_REFRESH_STATS, &res); 2830999342a0SCorentin Chary 2831999342a0SCorentin Chary if (timercmp(&vd->guest.last_freq_check, &res, >)) { 28327d964c9dSCorentin Chary return has_dirty; 2833999342a0SCorentin Chary } 2834999342a0SCorentin Chary vd->guest.last_freq_check = *tv; 2835999342a0SCorentin Chary 28369f64916dSGerd Hoffmann for (y = 0; y < height; y += VNC_STAT_RECT) { 28379f64916dSGerd Hoffmann for (x = 0; x < width; x += VNC_STAT_RECT) { 2838999342a0SCorentin Chary VncRectStat *rect= vnc_stat_rect(vd, x, y); 2839999342a0SCorentin Chary int count = ARRAY_SIZE(rect->times); 2840999342a0SCorentin Chary struct timeval min, max; 2841999342a0SCorentin Chary 2842999342a0SCorentin Chary if (!timerisset(&rect->times[count - 1])) { 2843999342a0SCorentin Chary continue ; 2844999342a0SCorentin Chary } 2845999342a0SCorentin Chary 2846999342a0SCorentin Chary max = rect->times[(rect->idx + count - 1) % count]; 2847ad620c29SBlue Swirl qemu_timersub(tv, &max, &res); 2848999342a0SCorentin Chary 2849999342a0SCorentin Chary if (timercmp(&res, &VNC_REFRESH_LOSSY, >)) { 2850999342a0SCorentin Chary rect->freq = 0; 28517d964c9dSCorentin Chary has_dirty += vnc_refresh_lossy_rect(vd, x, y); 2852999342a0SCorentin Chary memset(rect->times, 0, sizeof (rect->times)); 2853999342a0SCorentin Chary continue ; 2854999342a0SCorentin Chary } 2855999342a0SCorentin Chary 2856999342a0SCorentin Chary min = rect->times[rect->idx]; 2857999342a0SCorentin Chary max = rect->times[(rect->idx + count - 1) % count]; 2858ad620c29SBlue Swirl qemu_timersub(&max, &min, &res); 2859999342a0SCorentin Chary 2860999342a0SCorentin Chary rect->freq = res.tv_sec + res.tv_usec / 1000000.; 2861999342a0SCorentin Chary rect->freq /= count; 2862999342a0SCorentin Chary rect->freq = 1. / rect->freq; 2863999342a0SCorentin Chary } 2864999342a0SCorentin Chary } 28657d964c9dSCorentin Chary return has_dirty; 2866999342a0SCorentin Chary } 2867999342a0SCorentin Chary 2868999342a0SCorentin Chary double vnc_update_freq(VncState *vs, int x, int y, int w, int h) 2869999342a0SCorentin Chary { 2870999342a0SCorentin Chary int i, j; 2871999342a0SCorentin Chary double total = 0; 2872999342a0SCorentin Chary int num = 0; 2873999342a0SCorentin Chary 28745a3804dbSMarc-André Lureau x = QEMU_ALIGN_DOWN(x, VNC_STAT_RECT); 28755a3804dbSMarc-André Lureau y = QEMU_ALIGN_DOWN(y, VNC_STAT_RECT); 2876999342a0SCorentin Chary 2877999342a0SCorentin Chary for (j = y; j <= y + h; j += VNC_STAT_RECT) { 2878999342a0SCorentin Chary for (i = x; i <= x + w; i += VNC_STAT_RECT) { 2879999342a0SCorentin Chary total += vnc_stat_rect(vs->vd, i, j)->freq; 2880999342a0SCorentin Chary num++; 2881999342a0SCorentin Chary } 2882999342a0SCorentin Chary } 2883999342a0SCorentin Chary 2884999342a0SCorentin Chary if (num) { 2885999342a0SCorentin Chary return total / num; 2886999342a0SCorentin Chary } else { 2887999342a0SCorentin Chary return 0; 2888999342a0SCorentin Chary } 2889999342a0SCorentin Chary } 2890999342a0SCorentin Chary 2891999342a0SCorentin Chary static void vnc_rect_updated(VncDisplay *vd, int x, int y, struct timeval * tv) 2892999342a0SCorentin Chary { 2893999342a0SCorentin Chary VncRectStat *rect; 2894999342a0SCorentin Chary 2895999342a0SCorentin Chary rect = vnc_stat_rect(vd, x, y); 2896999342a0SCorentin Chary if (rect->updated) { 2897999342a0SCorentin Chary return ; 2898999342a0SCorentin Chary } 2899999342a0SCorentin Chary rect->times[rect->idx] = *tv; 2900999342a0SCorentin Chary rect->idx = (rect->idx + 1) % ARRAY_SIZE(rect->times); 2901999342a0SCorentin Chary rect->updated = true; 2902999342a0SCorentin Chary } 2903999342a0SCorentin Chary 29043e230dd2SCorentin Chary static int vnc_refresh_server_surface(VncDisplay *vd) 29053e230dd2SCorentin Chary { 2906bea60dd7SPeter Lieven int width = MIN(pixman_image_get_width(vd->guest.fb), 2907bea60dd7SPeter Lieven pixman_image_get_width(vd->server)); 2908bea60dd7SPeter Lieven int height = MIN(pixman_image_get_height(vd->guest.fb), 2909bea60dd7SPeter Lieven pixman_image_get_height(vd->server)); 2910eb8934b0SGerd Hoffmann int cmp_bytes, server_stride, line_bytes, guest_ll, guest_stride, y = 0; 291112b316d4SPeter Lieven uint8_t *guest_row0 = NULL, *server_row0; 29123e230dd2SCorentin Chary VncState *vs; 29133e230dd2SCorentin Chary int has_dirty = 0; 29149f64916dSGerd Hoffmann pixman_image_t *tmpbuf = NULL; 29153e230dd2SCorentin Chary 291680e0c8c3SCorentin Chary struct timeval tv = { 0, 0 }; 2917999342a0SCorentin Chary 291880e0c8c3SCorentin Chary if (!vd->non_adaptive) { 2919999342a0SCorentin Chary gettimeofday(&tv, NULL); 29207d964c9dSCorentin Chary has_dirty = vnc_update_stats(vd, &tv); 292180e0c8c3SCorentin Chary } 2922999342a0SCorentin Chary 29233e230dd2SCorentin Chary /* 29243e230dd2SCorentin Chary * Walk through the guest dirty map. 29253e230dd2SCorentin Chary * Check and copy modified bits from guest to server surface. 29263e230dd2SCorentin Chary * Update server dirty map. 29273e230dd2SCorentin Chary */ 2928bea60dd7SPeter Lieven server_row0 = (uint8_t *)pixman_image_get_data(vd->server); 2929eb8934b0SGerd Hoffmann server_stride = guest_stride = guest_ll = 2930eb8934b0SGerd Hoffmann pixman_image_get_stride(vd->server); 2931bea60dd7SPeter Lieven cmp_bytes = MIN(VNC_DIRTY_PIXELS_PER_BIT * VNC_SERVER_FB_BYTES, 2932bea60dd7SPeter Lieven server_stride); 29339f64916dSGerd Hoffmann if (vd->guest.format != VNC_SERVER_FB_FORMAT) { 29349f64916dSGerd Hoffmann int width = pixman_image_get_width(vd->server); 29359f64916dSGerd Hoffmann tmpbuf = qemu_pixman_linebuf_create(VNC_SERVER_FB_FORMAT, width); 293612b316d4SPeter Lieven } else { 2937eb8934b0SGerd Hoffmann int guest_bpp = 2938eb8934b0SGerd Hoffmann PIXMAN_FORMAT_BPP(pixman_image_get_format(vd->guest.fb)); 293912b316d4SPeter Lieven guest_row0 = (uint8_t *)pixman_image_get_data(vd->guest.fb); 294012b316d4SPeter Lieven guest_stride = pixman_image_get_stride(vd->guest.fb); 2941949ed4c2SPhilippe Mathieu-Daudé guest_ll = pixman_image_get_width(vd->guest.fb) 2942949ed4c2SPhilippe Mathieu-Daudé * DIV_ROUND_UP(guest_bpp, 8); 29439f64916dSGerd Hoffmann } 2944eb8934b0SGerd Hoffmann line_bytes = MIN(server_stride, guest_ll); 294512b316d4SPeter Lieven 294612b316d4SPeter Lieven for (;;) { 29473e230dd2SCorentin Chary int x; 294812b316d4SPeter Lieven uint8_t *guest_ptr, *server_ptr; 294912b316d4SPeter Lieven unsigned long offset = find_next_bit((unsigned long *) &vd->guest.dirty, 295012b316d4SPeter Lieven height * VNC_DIRTY_BPL(&vd->guest), 295112b316d4SPeter Lieven y * VNC_DIRTY_BPL(&vd->guest)); 295212b316d4SPeter Lieven if (offset == height * VNC_DIRTY_BPL(&vd->guest)) { 295312b316d4SPeter Lieven /* no more dirty bits */ 295412b316d4SPeter Lieven break; 295512b316d4SPeter Lieven } 295612b316d4SPeter Lieven y = offset / VNC_DIRTY_BPL(&vd->guest); 295712b316d4SPeter Lieven x = offset % VNC_DIRTY_BPL(&vd->guest); 295812b316d4SPeter Lieven 295912b316d4SPeter Lieven server_ptr = server_row0 + y * server_stride + x * cmp_bytes; 29603e230dd2SCorentin Chary 29619f64916dSGerd Hoffmann if (vd->guest.format != VNC_SERVER_FB_FORMAT) { 2962bc210eb1SGerd Hoffmann qemu_pixman_linebuf_fill(tmpbuf, vd->guest.fb, width, 0, y); 29639f64916dSGerd Hoffmann guest_ptr = (uint8_t *)pixman_image_get_data(tmpbuf); 29649f64916dSGerd Hoffmann } else { 296512b316d4SPeter Lieven guest_ptr = guest_row0 + y * guest_stride; 29669f64916dSGerd Hoffmann } 296712b316d4SPeter Lieven guest_ptr += x * cmp_bytes; 29683e230dd2SCorentin Chary 296912b316d4SPeter Lieven for (; x < DIV_ROUND_UP(width, VNC_DIRTY_PIXELS_PER_BIT); 297012b316d4SPeter Lieven x++, guest_ptr += cmp_bytes, server_ptr += cmp_bytes) { 2971bea60dd7SPeter Lieven int _cmp_bytes = cmp_bytes; 297212b316d4SPeter Lieven if (!test_and_clear_bit(x, vd->guest.dirty[y])) { 29733e230dd2SCorentin Chary continue; 2974b4c85ddcSPeter Lieven } 2975eb8934b0SGerd Hoffmann if ((x + 1) * cmp_bytes > line_bytes) { 2976eb8934b0SGerd Hoffmann _cmp_bytes = line_bytes - x * cmp_bytes; 2977bea60dd7SPeter Lieven } 2978eb8934b0SGerd Hoffmann assert(_cmp_bytes >= 0); 2979bea60dd7SPeter Lieven if (memcmp(server_ptr, guest_ptr, _cmp_bytes) == 0) { 29803e230dd2SCorentin Chary continue; 2981b4c85ddcSPeter Lieven } 2982bea60dd7SPeter Lieven memcpy(server_ptr, guest_ptr, _cmp_bytes); 298312b316d4SPeter Lieven if (!vd->non_adaptive) { 298412b316d4SPeter Lieven vnc_rect_updated(vd, x * VNC_DIRTY_PIXELS_PER_BIT, 298512b316d4SPeter Lieven y, &tv); 298612b316d4SPeter Lieven } 29873e230dd2SCorentin Chary QTAILQ_FOREACH(vs, &vd->clients, next) { 298812b316d4SPeter Lieven set_bit(x, vs->dirty[y]); 29893e230dd2SCorentin Chary } 29903e230dd2SCorentin Chary has_dirty++; 29913e230dd2SCorentin Chary } 299212b316d4SPeter Lieven 299312b316d4SPeter Lieven y++; 29943e230dd2SCorentin Chary } 29959f64916dSGerd Hoffmann qemu_pixman_image_unref(tmpbuf); 29963e230dd2SCorentin Chary return has_dirty; 29973e230dd2SCorentin Chary } 29983e230dd2SCorentin Chary 29990f7b2864SGerd Hoffmann static void vnc_refresh(DisplayChangeListener *dcl) 30003e230dd2SCorentin Chary { 30010f7b2864SGerd Hoffmann VncDisplay *vd = container_of(dcl, VncDisplay, dcl); 30023e230dd2SCorentin Chary VncState *vs, *vn; 30033e230dd2SCorentin Chary int has_dirty, rects = 0; 30043e230dd2SCorentin Chary 30059d6b2070SChenLiang if (QTAILQ_EMPTY(&vd->clients)) { 30069d6b2070SChenLiang update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_MAX); 30079d6b2070SChenLiang return; 30089d6b2070SChenLiang } 30099d6b2070SChenLiang 30101d0d59feSGerd Hoffmann graphic_hw_update(vd->dcl.con); 30113e230dd2SCorentin Chary 3012bd023f95SCorentin Chary if (vnc_trylock_display(vd)) { 30130f7b2864SGerd Hoffmann update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE); 3014bd023f95SCorentin Chary return; 3015bd023f95SCorentin Chary } 3016bd023f95SCorentin Chary 30173e230dd2SCorentin Chary has_dirty = vnc_refresh_server_surface(vd); 3018bd023f95SCorentin Chary vnc_unlock_display(vd); 30193e230dd2SCorentin Chary 30203e230dd2SCorentin Chary QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) { 30216af998dbSDaniel P. Berrange rects += vnc_update_client(vs, has_dirty); 30223e230dd2SCorentin Chary /* vs might be free()ed here */ 30233e230dd2SCorentin Chary } 3024bd023f95SCorentin Chary 30253e230dd2SCorentin Chary if (has_dirty && rects) { 30260f7b2864SGerd Hoffmann vd->dcl.update_interval /= 2; 30270f7b2864SGerd Hoffmann if (vd->dcl.update_interval < VNC_REFRESH_INTERVAL_BASE) { 30280f7b2864SGerd Hoffmann vd->dcl.update_interval = VNC_REFRESH_INTERVAL_BASE; 30290f7b2864SGerd Hoffmann } 30303e230dd2SCorentin Chary } else { 30310f7b2864SGerd Hoffmann vd->dcl.update_interval += VNC_REFRESH_INTERVAL_INC; 30320f7b2864SGerd Hoffmann if (vd->dcl.update_interval > VNC_REFRESH_INTERVAL_MAX) { 30330f7b2864SGerd Hoffmann vd->dcl.update_interval = VNC_REFRESH_INTERVAL_MAX; 30343e230dd2SCorentin Chary } 30353e230dd2SCorentin Chary } 30363e230dd2SCorentin Chary } 30373e230dd2SCorentin Chary 303804d2529dSDaniel P. Berrange static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc, 30392c8cf549SMichael Tokarev bool skipauth, bool websocket) 30403e230dd2SCorentin Chary { 3041fedf0d35SMarkus Armbruster VncState *vs = g_new0(VncState, 1); 304290cd03a3SDaniel P. Berrange bool first_client = QTAILQ_EMPTY(&vd->clients); 30437d964c9dSCorentin Chary int i; 30447d964c9dSCorentin Chary 3045ad6374c4SDaniel P. Berrange trace_vnc_client_connect(vs, sioc); 30466bf21f3dSLi Qiang vs->zrle = g_new0(VncZrle, 1); 30476bf21f3dSLi Qiang vs->tight = g_new0(VncTight, 1); 3048f31f9c10SGerd Hoffmann vs->magic = VNC_MAGIC; 304904d2529dSDaniel P. Berrange vs->sioc = sioc; 305004d2529dSDaniel P. Berrange object_ref(OBJECT(vs->sioc)); 305104d2529dSDaniel P. Berrange vs->ioc = QIO_CHANNEL(sioc); 305204d2529dSDaniel P. Berrange object_ref(OBJECT(vs->ioc)); 3053d616ccc5SGerd Hoffmann vs->vd = vd; 30547e7e2ebcSDaniel P. Berrange 305504d2529dSDaniel P. Berrange buffer_init(&vs->input, "vnc-input/%p", sioc); 305604d2529dSDaniel P. Berrange buffer_init(&vs->output, "vnc-output/%p", sioc); 305704d2529dSDaniel P. Berrange buffer_init(&vs->jobs_buffer, "vnc-jobs_buffer/%p", sioc); 3058543b9580SGerd Hoffmann 30596bf21f3dSLi Qiang buffer_init(&vs->tight->tight, "vnc-tight/%p", sioc); 30606bf21f3dSLi Qiang buffer_init(&vs->tight->zlib, "vnc-tight-zlib/%p", sioc); 30616bf21f3dSLi Qiang buffer_init(&vs->tight->gradient, "vnc-tight-gradient/%p", sioc); 3062543b9580SGerd Hoffmann #ifdef CONFIG_VNC_JPEG 30636bf21f3dSLi Qiang buffer_init(&vs->tight->jpeg, "vnc-tight-jpeg/%p", sioc); 3064543b9580SGerd Hoffmann #endif 3065543b9580SGerd Hoffmann #ifdef CONFIG_VNC_PNG 30666bf21f3dSLi Qiang buffer_init(&vs->tight->png, "vnc-tight-png/%p", sioc); 3067543b9580SGerd Hoffmann #endif 306804d2529dSDaniel P. Berrange buffer_init(&vs->zlib.zlib, "vnc-zlib/%p", sioc); 30696bf21f3dSLi Qiang buffer_init(&vs->zrle->zrle, "vnc-zrle/%p", sioc); 30706bf21f3dSLi Qiang buffer_init(&vs->zrle->fb, "vnc-zrle-fb/%p", sioc); 30716bf21f3dSLi Qiang buffer_init(&vs->zrle->zlib, "vnc-zrle-zlib/%p", sioc); 3072543b9580SGerd Hoffmann 30737e7e2ebcSDaniel P. Berrange if (skipauth) { 30747e7e2ebcSDaniel P. Berrange vs->auth = VNC_AUTH_NONE; 30757e7e2ebcSDaniel P. Berrange vs->subauth = VNC_AUTH_INVALID; 30767e7e2ebcSDaniel P. Berrange } else { 3077f9148c8aSDaniel P. Berrange if (websocket) { 3078f9148c8aSDaniel P. Berrange vs->auth = vd->ws_auth; 3079f9148c8aSDaniel P. Berrange vs->subauth = VNC_AUTH_INVALID; 3080f9148c8aSDaniel P. Berrange } else { 30817e7e2ebcSDaniel P. Berrange vs->auth = vd->auth; 30827e7e2ebcSDaniel P. Berrange vs->subauth = vd->subauth; 30837e7e2ebcSDaniel P. Berrange } 3084f9148c8aSDaniel P. Berrange } 308504d2529dSDaniel P. Berrange VNC_DEBUG("Client sioc=%p ws=%d auth=%d subauth=%d\n", 308604d2529dSDaniel P. Berrange sioc, websocket, vs->auth, vs->subauth); 30877e7e2ebcSDaniel P. Berrange 30887267c094SAnthony Liguori vs->lossy_rect = g_malloc0(VNC_STAT_ROWS * sizeof (*vs->lossy_rect)); 30897d964c9dSCorentin Chary for (i = 0; i < VNC_STAT_ROWS; ++i) { 3090fedf0d35SMarkus Armbruster vs->lossy_rect[i] = g_new0(uint8_t, VNC_STAT_COLS); 30917d964c9dSCorentin Chary } 30923e230dd2SCorentin Chary 309304d2529dSDaniel P. Berrange VNC_DEBUG("New client on socket %p\n", vs->sioc); 30940f7b2864SGerd Hoffmann update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE); 309504d2529dSDaniel P. Berrange qio_channel_set_blocking(vs->ioc, false, NULL); 3096a75d6f07SBrandon Carpenter if (vs->ioc_tag) { 3097a75d6f07SBrandon Carpenter g_source_remove(vs->ioc_tag); 3098a75d6f07SBrandon Carpenter } 30997536ee4bSTim Hardeck if (websocket) { 31007536ee4bSTim Hardeck vs->websocket = 1; 310138e5756aSDaniel P. Berrange if (vd->tlscreds) { 310204d2529dSDaniel P. Berrange vs->ioc_tag = qio_channel_add_watch( 31032ddafce7SDing Hui vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR, 31042ddafce7SDing Hui vncws_tls_handshake_io, vs, NULL); 31053e305e4aSDaniel P. Berrange } else { 310604d2529dSDaniel P. Berrange vs->ioc_tag = qio_channel_add_watch( 31072ddafce7SDing Hui vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR, 31082ddafce7SDing Hui vncws_handshake_io, vs, NULL); 31090057a0d5STim Hardeck } 311004d2529dSDaniel P. Berrange } else { 311104d2529dSDaniel P. Berrange vs->ioc_tag = qio_channel_add_watch( 31122ddafce7SDing Hui vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR, 31132ddafce7SDing Hui vnc_client_io, vs, NULL); 31147536ee4bSTim Hardeck } 31153e230dd2SCorentin Chary 31163e230dd2SCorentin Chary vnc_client_cache_addr(vs); 3117fb6ba0d5SWenchao Xia vnc_qmp_event(vs, QAPI_EVENT_VNC_CONNECTED); 31188cf36489SGerd Hoffmann vnc_set_share_mode(vs, VNC_SHARE_MODE_CONNECTING); 31193e230dd2SCorentin Chary 31203e230dd2SCorentin Chary vs->last_x = -1; 31213e230dd2SCorentin Chary vs->last_y = -1; 31223e230dd2SCorentin Chary 31233e230dd2SCorentin Chary vs->as.freq = 44100; 31243e230dd2SCorentin Chary vs->as.nchannels = 2; 312585bc5852SKővágó, Zoltán vs->as.fmt = AUDIO_FORMAT_S16; 31263e230dd2SCorentin Chary vs->as.endianness = 0; 31273e230dd2SCorentin Chary 3128bd023f95SCorentin Chary qemu_mutex_init(&vs->output_mutex); 3129175b2a6eSCorentin Chary vs->bh = qemu_bh_new(vnc_jobs_bh, vs); 3130bd023f95SCorentin Chary 3131e5f34cddSGerd Hoffmann QTAILQ_INSERT_TAIL(&vd->clients, vs, next); 3132c7628bffSGerd Hoffmann if (first_client) { 3133c7628bffSGerd Hoffmann vnc_update_server_surface(vd); 3134c7628bffSGerd Hoffmann } 31353e230dd2SCorentin Chary 31361d0d59feSGerd Hoffmann graphic_hw_update(vd->dcl.con); 31373e230dd2SCorentin Chary 313890cd03a3SDaniel P. Berrange if (!vs->websocket) { 3139dbee9897SDaniel P. Berrange vnc_start_protocol(vs); 314090cd03a3SDaniel P. Berrange } 314190cd03a3SDaniel P. Berrange 314290cd03a3SDaniel P. Berrange if (vd->num_connecting > vd->connections_limit) { 314390cd03a3SDaniel P. Berrange QTAILQ_FOREACH(vs, &vd->clients, next) { 314490cd03a3SDaniel P. Berrange if (vs->share_mode == VNC_SHARE_MODE_CONNECTING) { 314590cd03a3SDaniel P. Berrange vnc_disconnect_start(vs); 314690cd03a3SDaniel P. Berrange return; 314790cd03a3SDaniel P. Berrange } 314890cd03a3SDaniel P. Berrange } 314990cd03a3SDaniel P. Berrange } 315090cd03a3SDaniel P. Berrange } 315190cd03a3SDaniel P. Berrange 3152dbee9897SDaniel P. Berrange void vnc_start_protocol(VncState *vs) 315390cd03a3SDaniel P. Berrange { 31543e230dd2SCorentin Chary vnc_write(vs, "RFB 003.008\n", 12); 31553e230dd2SCorentin Chary vnc_flush(vs); 31563e230dd2SCorentin Chary vnc_read_when(vs, protocol_version, 12); 31573e230dd2SCorentin Chary 31583e230dd2SCorentin Chary vs->mouse_mode_notifier.notify = check_pointer_type_change; 31593e230dd2SCorentin Chary qemu_add_mouse_mode_change_notifier(&vs->mouse_mode_notifier); 31603e230dd2SCorentin Chary } 31613e230dd2SCorentin Chary 316213e1d0e7SDaniel P. Berrange static void vnc_listen_io(QIONetListener *listener, 316313e1d0e7SDaniel P. Berrange QIOChannelSocket *cioc, 316404d2529dSDaniel P. Berrange void *opaque) 31653e230dd2SCorentin Chary { 3166bf01c179SDaniel P. Berrange VncDisplay *vd = opaque; 316713e1d0e7SDaniel P. Berrange bool isWebsock = listener == vd->wslistener; 31684ee74fa7SDaniel P. Berrange 316913e1d0e7SDaniel P. Berrange qio_channel_set_name(QIO_CHANNEL(cioc), 31704ee74fa7SDaniel P. Berrange isWebsock ? "vnc-ws-server" : "vnc-server"); 317113e1d0e7SDaniel P. Berrange qio_channel_set_delay(QIO_CHANNEL(cioc), false); 317213e1d0e7SDaniel P. Berrange vnc_connect(vd, cioc, false, isWebsock); 31737536ee4bSTim Hardeck } 31747536ee4bSTim Hardeck 31757c20b4a3SGerd Hoffmann static const DisplayChangeListenerOps dcl_ops = { 31767c20b4a3SGerd Hoffmann .dpy_name = "vnc", 31770f7b2864SGerd Hoffmann .dpy_refresh = vnc_refresh, 31787c20b4a3SGerd Hoffmann .dpy_gfx_update = vnc_dpy_update, 3179c12aeb86SGerd Hoffmann .dpy_gfx_switch = vnc_dpy_switch, 318034da30afSBenjamin Herrenschmidt .dpy_gfx_check_format = qemu_pixman_check_format, 31817c20b4a3SGerd Hoffmann .dpy_mouse_set = vnc_mouse_set, 31827c20b4a3SGerd Hoffmann .dpy_cursor_define = vnc_dpy_cursor_define, 31837c20b4a3SGerd Hoffmann }; 31847c20b4a3SGerd Hoffmann 3185ab4f931eSFei Li void vnc_display_init(const char *id, Error **errp) 31863e230dd2SCorentin Chary { 3187bf01c179SDaniel P. Berrange VncDisplay *vd; 31884db14629SGerd Hoffmann 31894db14629SGerd Hoffmann if (vnc_display_find(id) != NULL) { 31904db14629SGerd Hoffmann return; 31914db14629SGerd Hoffmann } 3192bf01c179SDaniel P. Berrange vd = g_malloc0(sizeof(*vd)); 31933e230dd2SCorentin Chary 3194bf01c179SDaniel P. Berrange vd->id = strdup(id); 3195bf01c179SDaniel P. Berrange QTAILQ_INSERT_TAIL(&vnc_displays, vd, next); 31963e230dd2SCorentin Chary 3197bf01c179SDaniel P. Berrange QTAILQ_INIT(&vd->clients); 3198bf01c179SDaniel P. Berrange vd->expires = TIME_MAX; 31993e230dd2SCorentin Chary 320040066175SGerd Hoffmann if (keyboard_layout) { 320140066175SGerd Hoffmann trace_vnc_key_map_init(keyboard_layout); 3202ab4f931eSFei Li vd->kbd_layout = init_keyboard_layout(name2keysym, 3203ab4f931eSFei Li keyboard_layout, errp); 320440066175SGerd Hoffmann } else { 3205ab4f931eSFei Li vd->kbd_layout = init_keyboard_layout(name2keysym, "en-us", errp); 320640066175SGerd Hoffmann } 32073e230dd2SCorentin Chary 3208bf01c179SDaniel P. Berrange if (!vd->kbd_layout) { 3209ab4f931eSFei Li return; 3210bf01c179SDaniel P. Berrange } 32113e230dd2SCorentin Chary 3212bf01c179SDaniel P. Berrange vd->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE; 3213bf01c179SDaniel P. Berrange vd->connections_limit = 32; 321412e29b16SDaniel P. Berrange 3215bf01c179SDaniel P. Berrange qemu_mutex_init(&vd->mutex); 3216bd023f95SCorentin Chary vnc_start_worker_thread(); 3217bd023f95SCorentin Chary 3218bf01c179SDaniel P. Berrange vd->dcl.ops = &dcl_ops; 3219bf01c179SDaniel P. Berrange register_displaychangelistener(&vd->dcl); 3220c2f2ba49SGerd Hoffmann vd->kbd = qkbd_state_init(vd->dcl.con); 32213e230dd2SCorentin Chary } 32223e230dd2SCorentin Chary 32233e230dd2SCorentin Chary 3224bf01c179SDaniel P. Berrange static void vnc_display_close(VncDisplay *vd) 32253e230dd2SCorentin Chary { 3226bf01c179SDaniel P. Berrange if (!vd) { 32273e230dd2SCorentin Chary return; 322804d2529dSDaniel P. Berrange } 3229bf01c179SDaniel P. Berrange vd->is_unix = false; 32304ee74fa7SDaniel P. Berrange 323113e1d0e7SDaniel P. Berrange if (vd->listener) { 323213e1d0e7SDaniel P. Berrange qio_net_listener_disconnect(vd->listener); 323313e1d0e7SDaniel P. Berrange object_unref(OBJECT(vd->listener)); 323404d2529dSDaniel P. Berrange } 323513e1d0e7SDaniel P. Berrange vd->listener = NULL; 323613e1d0e7SDaniel P. Berrange 323713e1d0e7SDaniel P. Berrange if (vd->wslistener) { 323813e1d0e7SDaniel P. Berrange qio_net_listener_disconnect(vd->wslistener); 323913e1d0e7SDaniel P. Berrange object_unref(OBJECT(vd->wslistener)); 32407536ee4bSTim Hardeck } 324113e1d0e7SDaniel P. Berrange vd->wslistener = NULL; 32424ee74fa7SDaniel P. Berrange 3243bf01c179SDaniel P. Berrange vd->auth = VNC_AUTH_INVALID; 3244bf01c179SDaniel P. Berrange vd->subauth = VNC_AUTH_INVALID; 3245bf01c179SDaniel P. Berrange if (vd->tlscreds) { 3246bf01c179SDaniel P. Berrange object_unparent(OBJECT(vd->tlscreds)); 3247bf01c179SDaniel P. Berrange vd->tlscreds = NULL; 3248bf01c179SDaniel P. Berrange } 3249b76806d4SDaniel P. Berrange if (vd->tlsauthz) { 3250b76806d4SDaniel P. Berrange object_unparent(OBJECT(vd->tlsauthz)); 3251b76806d4SDaniel P. Berrange vd->tlsauthz = NULL; 3252b76806d4SDaniel P. Berrange } 3253b76806d4SDaniel P. Berrange g_free(vd->tlsauthzid); 3254b76806d4SDaniel P. Berrange vd->tlsauthzid = NULL; 3255a54f0d2bSPierre Ossman if (vd->lock_key_sync) { 3256a54f0d2bSPierre Ossman qemu_remove_led_event_handler(vd->led); 32572dc120beSGerd Hoffmann vd->led = NULL; 3258a54f0d2bSPierre Ossman } 3259b76806d4SDaniel P. Berrange #ifdef CONFIG_VNC_SASL 3260b76806d4SDaniel P. Berrange if (vd->sasl.authz) { 3261b76806d4SDaniel P. Berrange object_unparent(OBJECT(vd->sasl.authz)); 3262b76806d4SDaniel P. Berrange vd->sasl.authz = NULL; 3263b76806d4SDaniel P. Berrange } 3264b76806d4SDaniel P. Berrange g_free(vd->sasl.authzid); 3265b76806d4SDaniel P. Berrange vd->sasl.authzid = NULL; 3266b76806d4SDaniel P. Berrange #endif 32673e230dd2SCorentin Chary } 32683e230dd2SCorentin Chary 326914f7143eSGerd Hoffmann int vnc_display_password(const char *id, const char *password) 32701cd20f8bSAnthony Liguori { 3271bf01c179SDaniel P. Berrange VncDisplay *vd = vnc_display_find(id); 32721cd20f8bSAnthony Liguori 3273bf01c179SDaniel P. Berrange if (!vd) { 3274a6aa9d3eSLuiz Capitulino return -EINVAL; 32751cd20f8bSAnthony Liguori } 3276bf01c179SDaniel P. Berrange if (vd->auth == VNC_AUTH_NONE) { 3277cf864569SGerd Hoffmann error_printf_unless_qmp("If you want use passwords please enable " 32787ea7d36eSMarkus Armbruster "password auth using '-vnc ${dpy},password'.\n"); 3279cf864569SGerd Hoffmann return -EINVAL; 32801cd20f8bSAnthony Liguori } 32811cd20f8bSAnthony Liguori 3282bf01c179SDaniel P. Berrange g_free(vd->password); 3283bf01c179SDaniel P. Berrange vd->password = g_strdup(password); 3284a6aa9d3eSLuiz Capitulino 3285a6aa9d3eSLuiz Capitulino return 0; 32863e230dd2SCorentin Chary } 32873e230dd2SCorentin Chary 328814f7143eSGerd Hoffmann int vnc_display_pw_expire(const char *id, time_t expires) 32893c9405a0SGerd Hoffmann { 3290bf01c179SDaniel P. Berrange VncDisplay *vd = vnc_display_find(id); 32913c9405a0SGerd Hoffmann 3292bf01c179SDaniel P. Berrange if (!vd) { 32931643f2b2SGerd Hoffmann return -EINVAL; 32941643f2b2SGerd Hoffmann } 32951643f2b2SGerd Hoffmann 3296bf01c179SDaniel P. Berrange vd->expires = expires; 32973c9405a0SGerd Hoffmann return 0; 32983c9405a0SGerd Hoffmann } 32993c9405a0SGerd Hoffmann 3300bf01c179SDaniel P. Berrange static void vnc_display_print_local_addr(VncDisplay *vd) 33013e230dd2SCorentin Chary { 3302bd269ebcSMarkus Armbruster SocketAddress *addr; 33033e230dd2SCorentin Chary 330413e1d0e7SDaniel P. Berrange if (!vd->listener || !vd->listener->nsioc) { 33054ee74fa7SDaniel P. Berrange return; 33064ee74fa7SDaniel P. Berrange } 33074ee74fa7SDaniel P. Berrange 3308b94b3c02SMarkus Armbruster addr = qio_channel_socket_get_local_address(vd->listener->sioc[0], NULL); 330904d2529dSDaniel P. Berrange if (!addr) { 331033df7bf3SPaolo Bonzini return; 331104d2529dSDaniel P. Berrange } 331204d2529dSDaniel P. Berrange 3313bd269ebcSMarkus Armbruster if (addr->type != SOCKET_ADDRESS_TYPE_INET) { 3314bd269ebcSMarkus Armbruster qapi_free_SocketAddress(addr); 331533df7bf3SPaolo Bonzini return; 331604d2529dSDaniel P. Berrange } 331733df7bf3SPaolo Bonzini error_printf_unless_qmp("VNC server running on %s:%s\n", 3318bd269ebcSMarkus Armbruster addr->u.inet.host, 3319bd269ebcSMarkus Armbruster addr->u.inet.port); 3320bd269ebcSMarkus Armbruster qapi_free_SocketAddress(addr); 33213e230dd2SCorentin Chary } 33223e230dd2SCorentin Chary 33234db14629SGerd Hoffmann static QemuOptsList qemu_vnc_opts = { 33244db14629SGerd Hoffmann .name = "vnc", 33254db14629SGerd Hoffmann .head = QTAILQ_HEAD_INITIALIZER(qemu_vnc_opts.head), 33264db14629SGerd Hoffmann .implied_opt_name = "vnc", 33274db14629SGerd Hoffmann .desc = { 33284db14629SGerd Hoffmann { 33294db14629SGerd Hoffmann .name = "vnc", 33304db14629SGerd Hoffmann .type = QEMU_OPT_STRING, 33314db14629SGerd Hoffmann },{ 33324db14629SGerd Hoffmann .name = "websocket", 33334db14629SGerd Hoffmann .type = QEMU_OPT_STRING, 33344db14629SGerd Hoffmann },{ 33353e305e4aSDaniel P. Berrange .name = "tls-creds", 33363e305e4aSDaniel P. Berrange .type = QEMU_OPT_STRING, 33373e305e4aSDaniel P. Berrange },{ 33384db14629SGerd Hoffmann .name = "share", 33394db14629SGerd Hoffmann .type = QEMU_OPT_STRING, 33404db14629SGerd Hoffmann },{ 33411d0d59feSGerd Hoffmann .name = "display", 33421d0d59feSGerd Hoffmann .type = QEMU_OPT_STRING, 33431d0d59feSGerd Hoffmann },{ 33441d0d59feSGerd Hoffmann .name = "head", 33451d0d59feSGerd Hoffmann .type = QEMU_OPT_NUMBER, 33461d0d59feSGerd Hoffmann },{ 3347e5f34cddSGerd Hoffmann .name = "connections", 3348e5f34cddSGerd Hoffmann .type = QEMU_OPT_NUMBER, 3349e5f34cddSGerd Hoffmann },{ 335088428b7aSGonglei .name = "to", 335188428b7aSGonglei .type = QEMU_OPT_NUMBER, 335288428b7aSGonglei },{ 335388428b7aSGonglei .name = "ipv4", 335488428b7aSGonglei .type = QEMU_OPT_BOOL, 335588428b7aSGonglei },{ 335688428b7aSGonglei .name = "ipv6", 335788428b7aSGonglei .type = QEMU_OPT_BOOL, 335888428b7aSGonglei },{ 33594db14629SGerd Hoffmann .name = "password", 33604db14629SGerd Hoffmann .type = QEMU_OPT_BOOL, 33614db14629SGerd Hoffmann },{ 33624db14629SGerd Hoffmann .name = "reverse", 33634db14629SGerd Hoffmann .type = QEMU_OPT_BOOL, 33644db14629SGerd Hoffmann },{ 33654db14629SGerd Hoffmann .name = "lock-key-sync", 33664db14629SGerd Hoffmann .type = QEMU_OPT_BOOL, 33674db14629SGerd Hoffmann },{ 3368c5ce8333SGerd Hoffmann .name = "key-delay-ms", 3369c5ce8333SGerd Hoffmann .type = QEMU_OPT_NUMBER, 3370c5ce8333SGerd Hoffmann },{ 33714db14629SGerd Hoffmann .name = "sasl", 33724db14629SGerd Hoffmann .type = QEMU_OPT_BOOL, 33734db14629SGerd Hoffmann },{ 33744db14629SGerd Hoffmann .name = "acl", 33754db14629SGerd Hoffmann .type = QEMU_OPT_BOOL, 33764db14629SGerd Hoffmann },{ 337755cf09a0SDaniel P. Berrange .name = "tls-authz", 337855cf09a0SDaniel P. Berrange .type = QEMU_OPT_STRING, 337955cf09a0SDaniel P. Berrange },{ 338055cf09a0SDaniel P. Berrange .name = "sasl-authz", 338155cf09a0SDaniel P. Berrange .type = QEMU_OPT_STRING, 338255cf09a0SDaniel P. Berrange },{ 33834db14629SGerd Hoffmann .name = "lossy", 33844db14629SGerd Hoffmann .type = QEMU_OPT_BOOL, 33854db14629SGerd Hoffmann },{ 33864db14629SGerd Hoffmann .name = "non-adaptive", 33874db14629SGerd Hoffmann .type = QEMU_OPT_BOOL, 3388f0b9f36dSKővágó, Zoltán },{ 3389f0b9f36dSKővágó, Zoltán .name = "audiodev", 3390f0b9f36dSKővágó, Zoltán .type = QEMU_OPT_STRING, 33914db14629SGerd Hoffmann }, 33924db14629SGerd Hoffmann { /* end of list */ } 33934db14629SGerd Hoffmann }, 33944db14629SGerd Hoffmann }; 33954db14629SGerd Hoffmann 33960dd72e15SDaniel P. Berrange 33973e305e4aSDaniel P. Berrange static int 3398eda24e18SDaniel P. Berrange vnc_display_setup_auth(int *auth, 3399eda24e18SDaniel P. Berrange int *subauth, 3400eda24e18SDaniel P. Berrange QCryptoTLSCreds *tlscreds, 34010dd72e15SDaniel P. Berrange bool password, 34020dd72e15SDaniel P. Berrange bool sasl, 34033e305e4aSDaniel P. Berrange bool websocket, 34043e305e4aSDaniel P. Berrange Error **errp) 34050dd72e15SDaniel P. Berrange { 34060dd72e15SDaniel P. Berrange /* 34070dd72e15SDaniel P. Berrange * We have a choice of 3 authentication options 34080dd72e15SDaniel P. Berrange * 34090dd72e15SDaniel P. Berrange * 1. none 34100dd72e15SDaniel P. Berrange * 2. vnc 34110dd72e15SDaniel P. Berrange * 3. sasl 34120dd72e15SDaniel P. Berrange * 34130dd72e15SDaniel P. Berrange * The channel can be run in 2 modes 34140dd72e15SDaniel P. Berrange * 34150dd72e15SDaniel P. Berrange * 1. clear 34160dd72e15SDaniel P. Berrange * 2. tls 34170dd72e15SDaniel P. Berrange * 34180dd72e15SDaniel P. Berrange * And TLS can use 2 types of credentials 34190dd72e15SDaniel P. Berrange * 34200dd72e15SDaniel P. Berrange * 1. anon 34210dd72e15SDaniel P. Berrange * 2. x509 34220dd72e15SDaniel P. Berrange * 34230dd72e15SDaniel P. Berrange * We thus have 9 possible logical combinations 34240dd72e15SDaniel P. Berrange * 34250dd72e15SDaniel P. Berrange * 1. clear + none 34260dd72e15SDaniel P. Berrange * 2. clear + vnc 34270dd72e15SDaniel P. Berrange * 3. clear + sasl 34280dd72e15SDaniel P. Berrange * 4. tls + anon + none 34290dd72e15SDaniel P. Berrange * 5. tls + anon + vnc 34300dd72e15SDaniel P. Berrange * 6. tls + anon + sasl 34310dd72e15SDaniel P. Berrange * 7. tls + x509 + none 34320dd72e15SDaniel P. Berrange * 8. tls + x509 + vnc 34330dd72e15SDaniel P. Berrange * 9. tls + x509 + sasl 34340dd72e15SDaniel P. Berrange * 34350dd72e15SDaniel P. Berrange * These need to be mapped into the VNC auth schemes 34360dd72e15SDaniel P. Berrange * in an appropriate manner. In regular VNC, all the 34370dd72e15SDaniel P. Berrange * TLS options get mapped into VNC_AUTH_VENCRYPT 34380dd72e15SDaniel P. Berrange * sub-auth types. 3439f9148c8aSDaniel P. Berrange * 3440f9148c8aSDaniel P. Berrange * In websockets, the https:// protocol already provides 3441f9148c8aSDaniel P. Berrange * TLS support, so there is no need to make use of the 3442f9148c8aSDaniel P. Berrange * VeNCrypt extension. Furthermore, websockets browser 3443f9148c8aSDaniel P. Berrange * clients could not use VeNCrypt even if they wanted to, 3444f9148c8aSDaniel P. Berrange * as they cannot control when the TLS handshake takes 3445f9148c8aSDaniel P. Berrange * place. Thus there is no option but to rely on https://, 3446f9148c8aSDaniel P. Berrange * meaning combinations 4->6 and 7->9 will be mapped to 3447f9148c8aSDaniel P. Berrange * VNC auth schemes in the same way as combos 1->3. 3448f9148c8aSDaniel P. Berrange * 3449f9148c8aSDaniel P. Berrange * Regardless of fact that we have a different mapping to 3450f9148c8aSDaniel P. Berrange * VNC auth mechs for plain VNC vs websockets VNC, the end 3451f9148c8aSDaniel P. Berrange * result has the same security characteristics. 34520dd72e15SDaniel P. Berrange */ 3453eda24e18SDaniel P. Berrange if (websocket || !tlscreds) { 34540dd72e15SDaniel P. Berrange if (password) { 34550dd72e15SDaniel P. Berrange VNC_DEBUG("Initializing VNC server with password auth\n"); 3456eda24e18SDaniel P. Berrange *auth = VNC_AUTH_VNC; 34570dd72e15SDaniel P. Berrange } else if (sasl) { 34580dd72e15SDaniel P. Berrange VNC_DEBUG("Initializing VNC server with SASL auth\n"); 3459eda24e18SDaniel P. Berrange *auth = VNC_AUTH_SASL; 34600dd72e15SDaniel P. Berrange } else { 34610dd72e15SDaniel P. Berrange VNC_DEBUG("Initializing VNC server with no auth\n"); 3462eda24e18SDaniel P. Berrange *auth = VNC_AUTH_NONE; 34630dd72e15SDaniel P. Berrange } 3464eda24e18SDaniel P. Berrange *subauth = VNC_AUTH_INVALID; 3465f9148c8aSDaniel P. Berrange } else { 3466eda24e18SDaniel P. Berrange bool is_x509 = object_dynamic_cast(OBJECT(tlscreds), 3467eda24e18SDaniel P. Berrange TYPE_QCRYPTO_TLS_CREDS_X509) != NULL; 3468eda24e18SDaniel P. Berrange bool is_anon = object_dynamic_cast(OBJECT(tlscreds), 3469eda24e18SDaniel P. Berrange TYPE_QCRYPTO_TLS_CREDS_ANON) != NULL; 3470eda24e18SDaniel P. Berrange 3471eda24e18SDaniel P. Berrange if (!is_x509 && !is_anon) { 3472eda24e18SDaniel P. Berrange error_setg(errp, 3473eda24e18SDaniel P. Berrange "Unsupported TLS cred type %s", 3474eda24e18SDaniel P. Berrange object_get_typename(OBJECT(tlscreds))); 3475eda24e18SDaniel P. Berrange return -1; 3476eda24e18SDaniel P. Berrange } 3477eda24e18SDaniel P. Berrange *auth = VNC_AUTH_VENCRYPT; 3478eda24e18SDaniel P. Berrange if (password) { 3479eda24e18SDaniel P. Berrange if (is_x509) { 3480eda24e18SDaniel P. Berrange VNC_DEBUG("Initializing VNC server with x509 password auth\n"); 3481eda24e18SDaniel P. Berrange *subauth = VNC_AUTH_VENCRYPT_X509VNC; 3482eda24e18SDaniel P. Berrange } else { 3483eda24e18SDaniel P. Berrange VNC_DEBUG("Initializing VNC server with TLS password auth\n"); 3484eda24e18SDaniel P. Berrange *subauth = VNC_AUTH_VENCRYPT_TLSVNC; 3485eda24e18SDaniel P. Berrange } 3486eda24e18SDaniel P. Berrange 3487eda24e18SDaniel P. Berrange } else if (sasl) { 3488eda24e18SDaniel P. Berrange if (is_x509) { 3489eda24e18SDaniel P. Berrange VNC_DEBUG("Initializing VNC server with x509 SASL auth\n"); 3490eda24e18SDaniel P. Berrange *subauth = VNC_AUTH_VENCRYPT_X509SASL; 3491eda24e18SDaniel P. Berrange } else { 3492eda24e18SDaniel P. Berrange VNC_DEBUG("Initializing VNC server with TLS SASL auth\n"); 3493eda24e18SDaniel P. Berrange *subauth = VNC_AUTH_VENCRYPT_TLSSASL; 3494eda24e18SDaniel P. Berrange } 3495eda24e18SDaniel P. Berrange } else { 3496eda24e18SDaniel P. Berrange if (is_x509) { 3497eda24e18SDaniel P. Berrange VNC_DEBUG("Initializing VNC server with x509 no auth\n"); 3498eda24e18SDaniel P. Berrange *subauth = VNC_AUTH_VENCRYPT_X509NONE; 3499eda24e18SDaniel P. Berrange } else { 3500eda24e18SDaniel P. Berrange VNC_DEBUG("Initializing VNC server with TLS no auth\n"); 3501eda24e18SDaniel P. Berrange *subauth = VNC_AUTH_VENCRYPT_TLSNONE; 3502eda24e18SDaniel P. Berrange } 3503f9148c8aSDaniel P. Berrange } 35040dd72e15SDaniel P. Berrange } 35053e305e4aSDaniel P. Berrange return 0; 35060dd72e15SDaniel P. Berrange } 35070dd72e15SDaniel P. Berrange 35083e305e4aSDaniel P. Berrange 3509275e0d61SDaniel P. Berrange static int vnc_display_get_address(const char *addrstr, 3510275e0d61SDaniel P. Berrange bool websocket, 3511e5766eb4SGerd Hoffmann bool reverse, 3512275e0d61SDaniel P. Berrange int displaynum, 3513275e0d61SDaniel P. Berrange int to, 3514275e0d61SDaniel P. Berrange bool has_ipv4, 3515275e0d61SDaniel P. Berrange bool has_ipv6, 3516275e0d61SDaniel P. Berrange bool ipv4, 3517275e0d61SDaniel P. Berrange bool ipv6, 3518bd269ebcSMarkus Armbruster SocketAddress **retaddr, 3519275e0d61SDaniel P. Berrange Error **errp) 3520275e0d61SDaniel P. Berrange { 3521275e0d61SDaniel P. Berrange int ret = -1; 3522bd269ebcSMarkus Armbruster SocketAddress *addr = NULL; 3523275e0d61SDaniel P. Berrange 3524bd269ebcSMarkus Armbruster addr = g_new0(SocketAddress, 1); 3525275e0d61SDaniel P. Berrange 3526275e0d61SDaniel P. Berrange if (strncmp(addrstr, "unix:", 5) == 0) { 3527bd269ebcSMarkus Armbruster addr->type = SOCKET_ADDRESS_TYPE_UNIX; 3528bd269ebcSMarkus Armbruster addr->u.q_unix.path = g_strdup(addrstr + 5); 3529275e0d61SDaniel P. Berrange 3530275e0d61SDaniel P. Berrange if (websocket) { 3531275e0d61SDaniel P. Berrange error_setg(errp, "UNIX sockets not supported with websock"); 3532275e0d61SDaniel P. Berrange goto cleanup; 3533275e0d61SDaniel P. Berrange } 3534275e0d61SDaniel P. Berrange 3535275e0d61SDaniel P. Berrange if (to) { 3536275e0d61SDaniel P. Berrange error_setg(errp, "Port range not support with UNIX socket"); 3537275e0d61SDaniel P. Berrange goto cleanup; 3538275e0d61SDaniel P. Berrange } 3539275e0d61SDaniel P. Berrange ret = 0; 3540275e0d61SDaniel P. Berrange } else { 3541275e0d61SDaniel P. Berrange const char *port; 3542275e0d61SDaniel P. Berrange size_t hostlen; 3543275e0d61SDaniel P. Berrange unsigned long long baseport = 0; 3544275e0d61SDaniel P. Berrange InetSocketAddress *inet; 3545275e0d61SDaniel P. Berrange 3546275e0d61SDaniel P. Berrange port = strrchr(addrstr, ':'); 3547275e0d61SDaniel P. Berrange if (!port) { 3548275e0d61SDaniel P. Berrange if (websocket) { 3549275e0d61SDaniel P. Berrange hostlen = 0; 3550275e0d61SDaniel P. Berrange port = addrstr; 3551275e0d61SDaniel P. Berrange } else { 3552275e0d61SDaniel P. Berrange error_setg(errp, "no vnc port specified"); 3553275e0d61SDaniel P. Berrange goto cleanup; 3554275e0d61SDaniel P. Berrange } 3555275e0d61SDaniel P. Berrange } else { 3556275e0d61SDaniel P. Berrange hostlen = port - addrstr; 3557275e0d61SDaniel P. Berrange port++; 3558275e0d61SDaniel P. Berrange if (*port == '\0') { 3559275e0d61SDaniel P. Berrange error_setg(errp, "vnc port cannot be empty"); 3560275e0d61SDaniel P. Berrange goto cleanup; 3561275e0d61SDaniel P. Berrange } 3562275e0d61SDaniel P. Berrange } 3563275e0d61SDaniel P. Berrange 3564bd269ebcSMarkus Armbruster addr->type = SOCKET_ADDRESS_TYPE_INET; 3565bd269ebcSMarkus Armbruster inet = &addr->u.inet; 3566275e0d61SDaniel P. Berrange if (addrstr[0] == '[' && addrstr[hostlen - 1] == ']') { 3567275e0d61SDaniel P. Berrange inet->host = g_strndup(addrstr + 1, hostlen - 2); 3568275e0d61SDaniel P. Berrange } else { 3569275e0d61SDaniel P. Berrange inet->host = g_strndup(addrstr, hostlen); 3570275e0d61SDaniel P. Berrange } 3571275e0d61SDaniel P. Berrange /* plain VNC port is just an offset, for websocket 3572275e0d61SDaniel P. Berrange * port is absolute */ 3573275e0d61SDaniel P. Berrange if (websocket) { 3574275e0d61SDaniel P. Berrange if (g_str_equal(addrstr, "") || 3575275e0d61SDaniel P. Berrange g_str_equal(addrstr, "on")) { 3576396f935aSDaniel P. Berrange if (displaynum == -1) { 3577396f935aSDaniel P. Berrange error_setg(errp, "explicit websocket port is required"); 3578396f935aSDaniel P. Berrange goto cleanup; 3579396f935aSDaniel P. Berrange } 3580275e0d61SDaniel P. Berrange inet->port = g_strdup_printf( 3581275e0d61SDaniel P. Berrange "%d", displaynum + 5700); 3582275e0d61SDaniel P. Berrange if (to) { 3583275e0d61SDaniel P. Berrange inet->has_to = true; 3584275e0d61SDaniel P. Berrange inet->to = to + 5700; 3585275e0d61SDaniel P. Berrange } 3586275e0d61SDaniel P. Berrange } else { 3587275e0d61SDaniel P. Berrange inet->port = g_strdup(port); 3588275e0d61SDaniel P. Berrange } 3589275e0d61SDaniel P. Berrange } else { 3590e5766eb4SGerd Hoffmann int offset = reverse ? 0 : 5900; 3591275e0d61SDaniel P. Berrange if (parse_uint_full(port, &baseport, 10) < 0) { 3592275e0d61SDaniel P. Berrange error_setg(errp, "can't convert to a number: %s", port); 3593275e0d61SDaniel P. Berrange goto cleanup; 3594275e0d61SDaniel P. Berrange } 3595275e0d61SDaniel P. Berrange if (baseport > 65535 || 3596e5766eb4SGerd Hoffmann baseport + offset > 65535) { 3597275e0d61SDaniel P. Berrange error_setg(errp, "port %s out of range", port); 3598275e0d61SDaniel P. Berrange goto cleanup; 3599275e0d61SDaniel P. Berrange } 3600275e0d61SDaniel P. Berrange inet->port = g_strdup_printf( 3601e5766eb4SGerd Hoffmann "%d", (int)baseport + offset); 3602275e0d61SDaniel P. Berrange 3603275e0d61SDaniel P. Berrange if (to) { 3604275e0d61SDaniel P. Berrange inet->has_to = true; 3605e5766eb4SGerd Hoffmann inet->to = to + offset; 3606275e0d61SDaniel P. Berrange } 3607275e0d61SDaniel P. Berrange } 3608275e0d61SDaniel P. Berrange 3609275e0d61SDaniel P. Berrange inet->ipv4 = ipv4; 3610275e0d61SDaniel P. Berrange inet->has_ipv4 = has_ipv4; 3611275e0d61SDaniel P. Berrange inet->ipv6 = ipv6; 3612275e0d61SDaniel P. Berrange inet->has_ipv6 = has_ipv6; 3613275e0d61SDaniel P. Berrange 3614275e0d61SDaniel P. Berrange ret = baseport; 3615275e0d61SDaniel P. Berrange } 3616275e0d61SDaniel P. Berrange 3617275e0d61SDaniel P. Berrange *retaddr = addr; 3618275e0d61SDaniel P. Berrange 3619275e0d61SDaniel P. Berrange cleanup: 3620275e0d61SDaniel P. Berrange if (ret < 0) { 3621bd269ebcSMarkus Armbruster qapi_free_SocketAddress(addr); 3622275e0d61SDaniel P. Berrange } 3623275e0d61SDaniel P. Berrange return ret; 3624275e0d61SDaniel P. Berrange } 3625275e0d61SDaniel P. Berrange 36269f26f325SPhilippe Mathieu-Daudé static void vnc_free_addresses(SocketAddress ***retsaddr, 36279f26f325SPhilippe Mathieu-Daudé size_t *retnsaddr) 36289f26f325SPhilippe Mathieu-Daudé { 36299f26f325SPhilippe Mathieu-Daudé size_t i; 36309f26f325SPhilippe Mathieu-Daudé 36319f26f325SPhilippe Mathieu-Daudé for (i = 0; i < *retnsaddr; i++) { 36329f26f325SPhilippe Mathieu-Daudé qapi_free_SocketAddress((*retsaddr)[i]); 36339f26f325SPhilippe Mathieu-Daudé } 36349f26f325SPhilippe Mathieu-Daudé g_free(*retsaddr); 36359f26f325SPhilippe Mathieu-Daudé 36369f26f325SPhilippe Mathieu-Daudé *retsaddr = NULL; 36379f26f325SPhilippe Mathieu-Daudé *retnsaddr = 0; 36389f26f325SPhilippe Mathieu-Daudé } 36399f26f325SPhilippe Mathieu-Daudé 3640275e0d61SDaniel P. Berrange static int vnc_display_get_addresses(QemuOpts *opts, 3641e5766eb4SGerd Hoffmann bool reverse, 3642bd269ebcSMarkus Armbruster SocketAddress ***retsaddr, 3643396f935aSDaniel P. Berrange size_t *retnsaddr, 3644bd269ebcSMarkus Armbruster SocketAddress ***retwsaddr, 3645396f935aSDaniel P. Berrange size_t *retnwsaddr, 3646275e0d61SDaniel P. Berrange Error **errp) 3647275e0d61SDaniel P. Berrange { 3648bd269ebcSMarkus Armbruster SocketAddress *saddr = NULL; 3649bd269ebcSMarkus Armbruster SocketAddress *wsaddr = NULL; 3650396f935aSDaniel P. Berrange QemuOptsIter addriter; 3651396f935aSDaniel P. Berrange const char *addr; 3652275e0d61SDaniel P. Berrange int to = qemu_opt_get_number(opts, "to", 0); 3653275e0d61SDaniel P. Berrange bool has_ipv4 = qemu_opt_get(opts, "ipv4"); 3654275e0d61SDaniel P. Berrange bool has_ipv6 = qemu_opt_get(opts, "ipv6"); 3655275e0d61SDaniel P. Berrange bool ipv4 = qemu_opt_get_bool(opts, "ipv4", false); 3656275e0d61SDaniel P. Berrange bool ipv6 = qemu_opt_get_bool(opts, "ipv6", false); 3657396f935aSDaniel P. Berrange int displaynum = -1; 3658396f935aSDaniel P. Berrange int ret = -1; 3659275e0d61SDaniel P. Berrange 3660275e0d61SDaniel P. Berrange *retsaddr = NULL; 3661396f935aSDaniel P. Berrange *retnsaddr = 0; 3662275e0d61SDaniel P. Berrange *retwsaddr = NULL; 3663396f935aSDaniel P. Berrange *retnwsaddr = 0; 3664275e0d61SDaniel P. Berrange 3665396f935aSDaniel P. Berrange addr = qemu_opt_get(opts, "vnc"); 3666396f935aSDaniel P. Berrange if (addr == NULL || g_str_equal(addr, "none")) { 3667396f935aSDaniel P. Berrange ret = 0; 3668396f935aSDaniel P. Berrange goto cleanup; 3669396f935aSDaniel P. Berrange } 3670396f935aSDaniel P. Berrange if (qemu_opt_get(opts, "websocket") && 3671275e0d61SDaniel P. Berrange !qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA1)) { 3672275e0d61SDaniel P. Berrange error_setg(errp, 3673275e0d61SDaniel P. Berrange "SHA1 hash support is required for websockets"); 3674396f935aSDaniel P. Berrange goto cleanup; 3675275e0d61SDaniel P. Berrange } 3676275e0d61SDaniel P. Berrange 3677396f935aSDaniel P. Berrange qemu_opt_iter_init(&addriter, opts, "vnc"); 3678396f935aSDaniel P. Berrange while ((addr = qemu_opt_iter_next(&addriter)) != NULL) { 3679396f935aSDaniel P. Berrange int rv; 3680e5766eb4SGerd Hoffmann rv = vnc_display_get_address(addr, false, reverse, 0, to, 3681275e0d61SDaniel P. Berrange has_ipv4, has_ipv6, 3682275e0d61SDaniel P. Berrange ipv4, ipv6, 3683275e0d61SDaniel P. Berrange &saddr, errp); 3684396f935aSDaniel P. Berrange if (rv < 0) { 3685396f935aSDaniel P. Berrange goto cleanup; 3686275e0d61SDaniel P. Berrange } 3687396f935aSDaniel P. Berrange /* Historical compat - first listen address can be used 3688396f935aSDaniel P. Berrange * to set the default websocket port 3689396f935aSDaniel P. Berrange */ 3690396f935aSDaniel P. Berrange if (displaynum == -1) { 3691396f935aSDaniel P. Berrange displaynum = rv; 3692396f935aSDaniel P. Berrange } 3693bd269ebcSMarkus Armbruster *retsaddr = g_renew(SocketAddress *, *retsaddr, *retnsaddr + 1); 3694396f935aSDaniel P. Berrange (*retsaddr)[(*retnsaddr)++] = saddr; 3695396f935aSDaniel P. Berrange } 3696396f935aSDaniel P. Berrange 3697396f935aSDaniel P. Berrange /* If we had multiple primary displays, we don't do defaults 3698396f935aSDaniel P. Berrange * for websocket, and require explicit config instead. */ 3699396f935aSDaniel P. Berrange if (*retnsaddr > 1) { 3700396f935aSDaniel P. Berrange displaynum = -1; 3701396f935aSDaniel P. Berrange } 3702396f935aSDaniel P. Berrange 3703396f935aSDaniel P. Berrange qemu_opt_iter_init(&addriter, opts, "websocket"); 3704396f935aSDaniel P. Berrange while ((addr = qemu_opt_iter_next(&addriter)) != NULL) { 3705e5766eb4SGerd Hoffmann if (vnc_display_get_address(addr, true, reverse, displaynum, to, 3706275e0d61SDaniel P. Berrange has_ipv4, has_ipv6, 3707275e0d61SDaniel P. Berrange ipv4, ipv6, 3708275e0d61SDaniel P. Berrange &wsaddr, errp) < 0) { 3709396f935aSDaniel P. Berrange goto cleanup; 3710275e0d61SDaniel P. Berrange } 3711396f935aSDaniel P. Berrange 3712396f935aSDaniel P. Berrange /* Historical compat - if only a single listen address was 3713396f935aSDaniel P. Berrange * provided, then this is used to set the default listen 3714396f935aSDaniel P. Berrange * address for websocket too 3715396f935aSDaniel P. Berrange */ 3716396f935aSDaniel P. Berrange if (*retnsaddr == 1 && 3717bd269ebcSMarkus Armbruster (*retsaddr)[0]->type == SOCKET_ADDRESS_TYPE_INET && 3718bd269ebcSMarkus Armbruster wsaddr->type == SOCKET_ADDRESS_TYPE_INET && 3719bd269ebcSMarkus Armbruster g_str_equal(wsaddr->u.inet.host, "") && 3720bd269ebcSMarkus Armbruster !g_str_equal((*retsaddr)[0]->u.inet.host, "")) { 3721bd269ebcSMarkus Armbruster g_free(wsaddr->u.inet.host); 3722bd269ebcSMarkus Armbruster wsaddr->u.inet.host = g_strdup((*retsaddr)[0]->u.inet.host); 3723275e0d61SDaniel P. Berrange } 3724275e0d61SDaniel P. Berrange 3725bd269ebcSMarkus Armbruster *retwsaddr = g_renew(SocketAddress *, *retwsaddr, *retnwsaddr + 1); 3726396f935aSDaniel P. Berrange (*retwsaddr)[(*retnwsaddr)++] = wsaddr; 3727396f935aSDaniel P. Berrange } 3728396f935aSDaniel P. Berrange 3729396f935aSDaniel P. Berrange ret = 0; 3730396f935aSDaniel P. Berrange cleanup: 3731396f935aSDaniel P. Berrange if (ret < 0) { 37329f26f325SPhilippe Mathieu-Daudé vnc_free_addresses(retsaddr, retnsaddr); 37339f26f325SPhilippe Mathieu-Daudé vnc_free_addresses(retwsaddr, retnwsaddr); 3734396f935aSDaniel P. Berrange } 3735396f935aSDaniel P. Berrange return ret; 3736275e0d61SDaniel P. Berrange } 3737275e0d61SDaniel P. Berrange 37388bd22f47SDaniel P. Berrange static int vnc_display_connect(VncDisplay *vd, 3739bd269ebcSMarkus Armbruster SocketAddress **saddr, 3740396f935aSDaniel P. Berrange size_t nsaddr, 3741bd269ebcSMarkus Armbruster SocketAddress **wsaddr, 3742396f935aSDaniel P. Berrange size_t nwsaddr, 37438bd22f47SDaniel P. Berrange Error **errp) 37448bd22f47SDaniel P. Berrange { 37458bd22f47SDaniel P. Berrange /* connect to viewer */ 37468bd22f47SDaniel P. Berrange QIOChannelSocket *sioc = NULL; 3747396f935aSDaniel P. Berrange if (nwsaddr != 0) { 37488bd22f47SDaniel P. Berrange error_setg(errp, "Cannot use websockets in reverse mode"); 37498bd22f47SDaniel P. Berrange return -1; 37508bd22f47SDaniel P. Berrange } 3751396f935aSDaniel P. Berrange if (nsaddr != 1) { 3752396f935aSDaniel P. Berrange error_setg(errp, "Expected a single address in reverse mode"); 3753396f935aSDaniel P. Berrange return -1; 3754396f935aSDaniel P. Berrange } 3755bd269ebcSMarkus Armbruster /* TODO SOCKET_ADDRESS_TYPE_FD when fd has AF_UNIX */ 3756bd269ebcSMarkus Armbruster vd->is_unix = saddr[0]->type == SOCKET_ADDRESS_TYPE_UNIX; 37578bd22f47SDaniel P. Berrange sioc = qio_channel_socket_new(); 37588bd22f47SDaniel P. Berrange qio_channel_set_name(QIO_CHANNEL(sioc), "vnc-reverse"); 3759396f935aSDaniel P. Berrange if (qio_channel_socket_connect_sync(sioc, saddr[0], errp) < 0) { 37608bd22f47SDaniel P. Berrange return -1; 37618bd22f47SDaniel P. Berrange } 37628bd22f47SDaniel P. Berrange vnc_connect(vd, sioc, false, false); 37638bd22f47SDaniel P. Berrange object_unref(OBJECT(sioc)); 37648bd22f47SDaniel P. Berrange return 0; 37658bd22f47SDaniel P. Berrange } 37668bd22f47SDaniel P. Berrange 37678bd22f47SDaniel P. Berrange 37688bd22f47SDaniel P. Berrange static int vnc_display_listen(VncDisplay *vd, 3769bd269ebcSMarkus Armbruster SocketAddress **saddr, 3770396f935aSDaniel P. Berrange size_t nsaddr, 3771bd269ebcSMarkus Armbruster SocketAddress **wsaddr, 3772396f935aSDaniel P. Berrange size_t nwsaddr, 37738bd22f47SDaniel P. Berrange Error **errp) 37748bd22f47SDaniel P. Berrange { 3775396f935aSDaniel P. Berrange size_t i; 37768bd22f47SDaniel P. Berrange 377713e1d0e7SDaniel P. Berrange if (nsaddr) { 377813e1d0e7SDaniel P. Berrange vd->listener = qio_net_listener_new(); 377913e1d0e7SDaniel P. Berrange qio_net_listener_set_name(vd->listener, "vnc-listen"); 3780396f935aSDaniel P. Berrange for (i = 0; i < nsaddr; i++) { 378113e1d0e7SDaniel P. Berrange if (qio_net_listener_open_sync(vd->listener, 3782fc8135c6SJuan Quintela saddr[i], 1, 37838bd22f47SDaniel P. Berrange errp) < 0) { 37848bd22f47SDaniel P. Berrange return -1; 37858bd22f47SDaniel P. Berrange } 3786396f935aSDaniel P. Berrange } 378713e1d0e7SDaniel P. Berrange 378813e1d0e7SDaniel P. Berrange qio_net_listener_set_client_func(vd->listener, 378913e1d0e7SDaniel P. Berrange vnc_listen_io, vd, NULL); 379013e1d0e7SDaniel P. Berrange } 379113e1d0e7SDaniel P. Berrange 379213e1d0e7SDaniel P. Berrange if (nwsaddr) { 379313e1d0e7SDaniel P. Berrange vd->wslistener = qio_net_listener_new(); 379413e1d0e7SDaniel P. Berrange qio_net_listener_set_name(vd->wslistener, "vnc-ws-listen"); 3795396f935aSDaniel P. Berrange for (i = 0; i < nwsaddr; i++) { 379613e1d0e7SDaniel P. Berrange if (qio_net_listener_open_sync(vd->wslistener, 3797fc8135c6SJuan Quintela wsaddr[i], 1, 37988bd22f47SDaniel P. Berrange errp) < 0) { 37998bd22f47SDaniel P. Berrange return -1; 38008bd22f47SDaniel P. Berrange } 3801396f935aSDaniel P. Berrange } 38028bd22f47SDaniel P. Berrange 380313e1d0e7SDaniel P. Berrange qio_net_listener_set_client_func(vd->wslistener, 380413e1d0e7SDaniel P. Berrange vnc_listen_io, vd, NULL); 380513e1d0e7SDaniel P. Berrange } 380613e1d0e7SDaniel P. Berrange 38078bd22f47SDaniel P. Berrange return 0; 38088bd22f47SDaniel P. Berrange } 38098bd22f47SDaniel P. Berrange 38108bd22f47SDaniel P. Berrange 38114db14629SGerd Hoffmann void vnc_display_open(const char *id, Error **errp) 38123e230dd2SCorentin Chary { 3813bf01c179SDaniel P. Berrange VncDisplay *vd = vnc_display_find(id); 38144db14629SGerd Hoffmann QemuOpts *opts = qemu_opts_find(&qemu_vnc_opts, id); 3815bd269ebcSMarkus Armbruster SocketAddress **saddr = NULL, **wsaddr = NULL; 3816396f935aSDaniel P. Berrange size_t nsaddr, nwsaddr; 3817e2a11d9dSGonglei const char *share, *device_id; 38181d0d59feSGerd Hoffmann QemuConsole *con; 3819a2c72de0SGonglei bool password = false; 3820a2c72de0SGonglei bool reverse = false; 38213e305e4aSDaniel P. Berrange const char *credid; 3822a2c72de0SGonglei bool sasl = false; 38233e230dd2SCorentin Chary int acl = 0; 382455cf09a0SDaniel P. Berrange const char *tlsauthz; 382555cf09a0SDaniel P. Berrange const char *saslauthz; 38263e230dd2SCorentin Chary int lock_key_sync = 1; 3827c5ce8333SGerd Hoffmann int key_delay_ms; 3828f0b9f36dSKővágó, Zoltán const char *audiodev; 38293e230dd2SCorentin Chary 3830bf01c179SDaniel P. Berrange if (!vd) { 38312d55f0e8SPaolo Bonzini error_setg(errp, "VNC display not active"); 38322d55f0e8SPaolo Bonzini return; 38332d55f0e8SPaolo Bonzini } 3834bf01c179SDaniel P. Berrange vnc_display_close(vd); 38354db14629SGerd Hoffmann 38364db14629SGerd Hoffmann if (!opts) { 38372d55f0e8SPaolo Bonzini return; 38384db14629SGerd Hoffmann } 3839275e0d61SDaniel P. Berrange 3840e5766eb4SGerd Hoffmann reverse = qemu_opt_get_bool(opts, "reverse", false); 3841e5766eb4SGerd Hoffmann if (vnc_display_get_addresses(opts, reverse, &saddr, &nsaddr, 3842396f935aSDaniel P. Berrange &wsaddr, &nwsaddr, errp) < 0) { 3843275e0d61SDaniel P. Berrange goto fail; 3844275e0d61SDaniel P. Berrange } 3845275e0d61SDaniel P. Berrange 38464db14629SGerd Hoffmann password = qemu_opt_get_bool(opts, "password", false); 3847800567a6SDaniel P. Berrange if (password) { 3848800567a6SDaniel P. Berrange if (fips_get_state()) { 38492d55f0e8SPaolo Bonzini error_setg(errp, 38500f66998fSPaul Moore "VNC password auth disabled due to FIPS mode, " 38510f66998fSPaul Moore "consider using the VeNCrypt or SASL authentication " 38522d55f0e8SPaolo Bonzini "methods as an alternative"); 38531ce52c78SPaolo Bonzini goto fail; 38540f66998fSPaul Moore } 3855800567a6SDaniel P. Berrange if (!qcrypto_cipher_supports( 3856f844836dSGonglei QCRYPTO_CIPHER_ALG_DES_RFB, QCRYPTO_CIPHER_MODE_ECB)) { 3857800567a6SDaniel P. Berrange error_setg(errp, 3858800567a6SDaniel P. Berrange "Cipher backend does not support DES RFB algorithm"); 3859800567a6SDaniel P. Berrange goto fail; 3860800567a6SDaniel P. Berrange } 3861800567a6SDaniel P. Berrange } 38624db14629SGerd Hoffmann 38634db14629SGerd Hoffmann lock_key_sync = qemu_opt_get_bool(opts, "lock-key-sync", true); 3864d3b0db6dSAlexander Graf key_delay_ms = qemu_opt_get_number(opts, "key-delay-ms", 10); 38654db14629SGerd Hoffmann sasl = qemu_opt_get_bool(opts, "sasl", false); 3866d169f04bSDaniel P. Berrange #ifndef CONFIG_VNC_SASL 3867d169f04bSDaniel P. Berrange if (sasl) { 3868d169f04bSDaniel P. Berrange error_setg(errp, "VNC SASL auth requires cyrus-sasl support"); 3869d169f04bSDaniel P. Berrange goto fail; 3870d169f04bSDaniel P. Berrange } 3871d169f04bSDaniel P. Berrange #endif /* CONFIG_VNC_SASL */ 38723e305e4aSDaniel P. Berrange credid = qemu_opt_get(opts, "tls-creds"); 38733e305e4aSDaniel P. Berrange if (credid) { 38743e305e4aSDaniel P. Berrange Object *creds; 38753e305e4aSDaniel P. Berrange creds = object_resolve_path_component( 38763e305e4aSDaniel P. Berrange object_get_objects_root(), credid); 38773e305e4aSDaniel P. Berrange if (!creds) { 38783e305e4aSDaniel P. Berrange error_setg(errp, "No TLS credentials with id '%s'", 38793e305e4aSDaniel P. Berrange credid); 38803e305e4aSDaniel P. Berrange goto fail; 38813e305e4aSDaniel P. Berrange } 3882bf01c179SDaniel P. Berrange vd->tlscreds = (QCryptoTLSCreds *) 38833e305e4aSDaniel P. Berrange object_dynamic_cast(creds, 38843e305e4aSDaniel P. Berrange TYPE_QCRYPTO_TLS_CREDS); 3885bf01c179SDaniel P. Berrange if (!vd->tlscreds) { 38863e305e4aSDaniel P. Berrange error_setg(errp, "Object with id '%s' is not TLS credentials", 38873e305e4aSDaniel P. Berrange credid); 38883e305e4aSDaniel P. Berrange goto fail; 38893e305e4aSDaniel P. Berrange } 3890bf01c179SDaniel P. Berrange object_ref(OBJECT(vd->tlscreds)); 38913e305e4aSDaniel P. Berrange 3892bf01c179SDaniel P. Berrange if (vd->tlscreds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { 38933e305e4aSDaniel P. Berrange error_setg(errp, 38943e305e4aSDaniel P. Berrange "Expecting TLS credentials with a server endpoint"); 38953e305e4aSDaniel P. Berrange goto fail; 38963e305e4aSDaniel P. Berrange } 3897d169f04bSDaniel P. Berrange } 389855cf09a0SDaniel P. Berrange if (qemu_opt_get(opts, "acl")) { 389955cf09a0SDaniel P. Berrange error_report("The 'acl' option to -vnc is deprecated. " 390055cf09a0SDaniel P. Berrange "Please use the 'tls-authz' and 'sasl-authz' " 390155cf09a0SDaniel P. Berrange "options instead"); 390255cf09a0SDaniel P. Berrange } 39034db14629SGerd Hoffmann acl = qemu_opt_get_bool(opts, "acl", false); 390455cf09a0SDaniel P. Berrange tlsauthz = qemu_opt_get(opts, "tls-authz"); 390555cf09a0SDaniel P. Berrange if (acl && tlsauthz) { 390655cf09a0SDaniel P. Berrange error_setg(errp, "'acl' option is mutually exclusive with the " 390755cf09a0SDaniel P. Berrange "'tls-authz' option"); 390855cf09a0SDaniel P. Berrange goto fail; 390955cf09a0SDaniel P. Berrange } 391055cf09a0SDaniel P. Berrange if (tlsauthz && !vd->tlscreds) { 391155cf09a0SDaniel P. Berrange error_setg(errp, "'tls-authz' provided but TLS is not enabled"); 391255cf09a0SDaniel P. Berrange goto fail; 391355cf09a0SDaniel P. Berrange } 391455cf09a0SDaniel P. Berrange 391555cf09a0SDaniel P. Berrange saslauthz = qemu_opt_get(opts, "sasl-authz"); 391655cf09a0SDaniel P. Berrange if (acl && saslauthz) { 391755cf09a0SDaniel P. Berrange error_setg(errp, "'acl' option is mutually exclusive with the " 391855cf09a0SDaniel P. Berrange "'sasl-authz' option"); 391955cf09a0SDaniel P. Berrange goto fail; 392055cf09a0SDaniel P. Berrange } 392155cf09a0SDaniel P. Berrange if (saslauthz && !sasl) { 392255cf09a0SDaniel P. Berrange error_setg(errp, "'sasl-authz' provided but SASL auth is not enabled"); 392355cf09a0SDaniel P. Berrange goto fail; 392455cf09a0SDaniel P. Berrange } 39254db14629SGerd Hoffmann 39264db14629SGerd Hoffmann share = qemu_opt_get(opts, "share"); 39274db14629SGerd Hoffmann if (share) { 39284db14629SGerd Hoffmann if (strcmp(share, "ignore") == 0) { 3929bf01c179SDaniel P. Berrange vd->share_policy = VNC_SHARE_POLICY_IGNORE; 39304db14629SGerd Hoffmann } else if (strcmp(share, "allow-exclusive") == 0) { 3931bf01c179SDaniel P. Berrange vd->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE; 39324db14629SGerd Hoffmann } else if (strcmp(share, "force-shared") == 0) { 3933bf01c179SDaniel P. Berrange vd->share_policy = VNC_SHARE_POLICY_FORCE_SHARED; 39348cf36489SGerd Hoffmann } else { 39352d55f0e8SPaolo Bonzini error_setg(errp, "unknown vnc share= option"); 39361ce52c78SPaolo Bonzini goto fail; 39378cf36489SGerd Hoffmann } 39384db14629SGerd Hoffmann } else { 3939bf01c179SDaniel P. Berrange vd->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE; 39403e230dd2SCorentin Chary } 3941bf01c179SDaniel P. Berrange vd->connections_limit = qemu_opt_get_number(opts, "connections", 32); 39423e230dd2SCorentin Chary 39434db14629SGerd Hoffmann #ifdef CONFIG_VNC_JPEG 3944bf01c179SDaniel P. Berrange vd->lossy = qemu_opt_get_bool(opts, "lossy", false); 39454db14629SGerd Hoffmann #endif 3946bf01c179SDaniel P. Berrange vd->non_adaptive = qemu_opt_get_bool(opts, "non-adaptive", false); 3947e22492d3SPeter Lieven /* adaptive updates are only used with tight encoding and 3948e22492d3SPeter Lieven * if lossy updates are enabled so we can disable all the 3949e22492d3SPeter Lieven * calculations otherwise */ 3950bf01c179SDaniel P. Berrange if (!vd->lossy) { 3951bf01c179SDaniel P. Berrange vd->non_adaptive = true; 3952e22492d3SPeter Lieven } 3953e22492d3SPeter Lieven 395455cf09a0SDaniel P. Berrange if (tlsauthz) { 395555cf09a0SDaniel P. Berrange vd->tlsauthzid = g_strdup(tlsauthz); 395655cf09a0SDaniel P. Berrange } else if (acl) { 3957bf01c179SDaniel P. Berrange if (strcmp(vd->id, "default") == 0) { 3958b76806d4SDaniel P. Berrange vd->tlsauthzid = g_strdup("vnc.x509dname"); 3959c8496408SGerd Hoffmann } else { 3960b76806d4SDaniel P. Berrange vd->tlsauthzid = g_strdup_printf("vnc.%s.x509dname", vd->id); 3961c8496408SGerd Hoffmann } 3962b76806d4SDaniel P. Berrange vd->tlsauthz = QAUTHZ(qauthz_list_new(vd->tlsauthzid, 3963b76806d4SDaniel P. Berrange QAUTHZ_LIST_POLICY_DENY, 3964b76806d4SDaniel P. Berrange &error_abort)); 39653e230dd2SCorentin Chary } 39663e230dd2SCorentin Chary #ifdef CONFIG_VNC_SASL 396755cf09a0SDaniel P. Berrange if (sasl) { 396855cf09a0SDaniel P. Berrange if (saslauthz) { 396955cf09a0SDaniel P. Berrange vd->sasl.authzid = g_strdup(saslauthz); 397055cf09a0SDaniel P. Berrange } else if (acl) { 3971bf01c179SDaniel P. Berrange if (strcmp(vd->id, "default") == 0) { 3972b76806d4SDaniel P. Berrange vd->sasl.authzid = g_strdup("vnc.username"); 3973c8496408SGerd Hoffmann } else { 3974b76806d4SDaniel P. Berrange vd->sasl.authzid = g_strdup_printf("vnc.%s.username", vd->id); 3975c8496408SGerd Hoffmann } 3976b76806d4SDaniel P. Berrange vd->sasl.authz = QAUTHZ(qauthz_list_new(vd->sasl.authzid, 3977b76806d4SDaniel P. Berrange QAUTHZ_LIST_POLICY_DENY, 3978b76806d4SDaniel P. Berrange &error_abort)); 39793e230dd2SCorentin Chary } 398055cf09a0SDaniel P. Berrange } 39813e230dd2SCorentin Chary #endif 39823e230dd2SCorentin Chary 3983eda24e18SDaniel P. Berrange if (vnc_display_setup_auth(&vd->auth, &vd->subauth, 3984eda24e18SDaniel P. Berrange vd->tlscreds, password, 3985eda24e18SDaniel P. Berrange sasl, false, errp) < 0) { 3986eda24e18SDaniel P. Berrange goto fail; 3987eda24e18SDaniel P. Berrange } 39887364dbdaSDaniel P. Berrange trace_vnc_auth_init(vd, 0, vd->auth, vd->subauth); 3989eda24e18SDaniel P. Berrange 3990eda24e18SDaniel P. Berrange if (vnc_display_setup_auth(&vd->ws_auth, &vd->ws_subauth, 3991eda24e18SDaniel P. Berrange vd->tlscreds, password, 3992eda24e18SDaniel P. Berrange sasl, true, errp) < 0) { 39933e305e4aSDaniel P. Berrange goto fail; 39943e305e4aSDaniel P. Berrange } 39957364dbdaSDaniel P. Berrange trace_vnc_auth_init(vd, 1, vd->ws_auth, vd->ws_subauth); 39963e230dd2SCorentin Chary 39973e230dd2SCorentin Chary #ifdef CONFIG_VNC_SASL 3998b5dc0d7dSMarc-André Lureau if (sasl) { 3999b5dc0d7dSMarc-André Lureau int saslErr = sasl_server_init(NULL, "qemu"); 4000b5dc0d7dSMarc-André Lureau 4001b5dc0d7dSMarc-André Lureau if (saslErr != SASL_OK) { 40022d55f0e8SPaolo Bonzini error_setg(errp, "Failed to initialize SASL auth: %s", 40033e230dd2SCorentin Chary sasl_errstring(saslErr, NULL, NULL)); 40041ce52c78SPaolo Bonzini goto fail; 40053e230dd2SCorentin Chary } 4006b5dc0d7dSMarc-André Lureau } 40073e230dd2SCorentin Chary #endif 4008bf01c179SDaniel P. Berrange vd->lock_key_sync = lock_key_sync; 4009a54f0d2bSPierre Ossman if (lock_key_sync) { 4010a54f0d2bSPierre Ossman vd->led = qemu_add_led_event_handler(kbd_leds, vd); 4011a54f0d2bSPierre Ossman } 4012a54f0d2bSPierre Ossman vd->ledstate = 0; 40133e230dd2SCorentin Chary 4014f0b9f36dSKővágó, Zoltán audiodev = qemu_opt_get(opts, "audiodev"); 4015f0b9f36dSKővágó, Zoltán if (audiodev) { 4016f0b9f36dSKővágó, Zoltán vd->audio_state = audio_state_by_name(audiodev); 4017f0b9f36dSKővágó, Zoltán if (!vd->audio_state) { 4018f0b9f36dSKővágó, Zoltán error_setg(errp, "Audiodev '%s' not found", audiodev); 4019f0b9f36dSKővágó, Zoltán goto fail; 4020f0b9f36dSKővágó, Zoltán } 4021f0b9f36dSKővágó, Zoltán } 4022f0b9f36dSKővágó, Zoltán 40231d0d59feSGerd Hoffmann device_id = qemu_opt_get(opts, "display"); 40241d0d59feSGerd Hoffmann if (device_id) { 40251d0d59feSGerd Hoffmann int head = qemu_opt_get_number(opts, "head", 0); 4026f2c1d54cSGerd Hoffmann Error *err = NULL; 40271d0d59feSGerd Hoffmann 4028f2c1d54cSGerd Hoffmann con = qemu_console_lookup_by_device_name(device_id, head, &err); 4029f2c1d54cSGerd Hoffmann if (err) { 4030f2c1d54cSGerd Hoffmann error_propagate(errp, err); 40311d0d59feSGerd Hoffmann goto fail; 40321d0d59feSGerd Hoffmann } 40331d0d59feSGerd Hoffmann } else { 40341d0d59feSGerd Hoffmann con = NULL; 40351d0d59feSGerd Hoffmann } 40361d0d59feSGerd Hoffmann 4037bf01c179SDaniel P. Berrange if (con != vd->dcl.con) { 4038c2f2ba49SGerd Hoffmann qkbd_state_free(vd->kbd); 4039bf01c179SDaniel P. Berrange unregister_displaychangelistener(&vd->dcl); 4040bf01c179SDaniel P. Berrange vd->dcl.con = con; 4041bf01c179SDaniel P. Berrange register_displaychangelistener(&vd->dcl); 4042c2f2ba49SGerd Hoffmann vd->kbd = qkbd_state_init(vd->dcl.con); 40431d0d59feSGerd Hoffmann } 4044c2f2ba49SGerd Hoffmann qkbd_state_set_delay(vd->kbd, key_delay_ms); 40451d0d59feSGerd Hoffmann 4046fa03cb7fSMarc-André Lureau if (saddr == NULL) { 4047fa03cb7fSMarc-André Lureau goto cleanup; 4048fa03cb7fSMarc-André Lureau } 4049fa03cb7fSMarc-André Lureau 40503e230dd2SCorentin Chary if (reverse) { 4051396f935aSDaniel P. Berrange if (vnc_display_connect(vd, saddr, nsaddr, wsaddr, nwsaddr, errp) < 0) { 4052e0d03b8cSDaniel P. Berrange goto fail; 40533e230dd2SCorentin Chary } 40543e230dd2SCorentin Chary } else { 4055396f935aSDaniel P. Berrange if (vnc_display_listen(vd, saddr, nsaddr, wsaddr, nwsaddr, errp) < 0) { 40563d00ac1aSCole Robinson goto fail; 40573d00ac1aSCole Robinson } 40583e230dd2SCorentin Chary } 4059e0d03b8cSDaniel P. Berrange 4060275e0d61SDaniel P. Berrange if (qemu_opt_get(opts, "to")) { 4061bf01c179SDaniel P. Berrange vnc_display_print_local_addr(vd); 406233df7bf3SPaolo Bonzini } 406333df7bf3SPaolo Bonzini 4064396f935aSDaniel P. Berrange cleanup: 40659f26f325SPhilippe Mathieu-Daudé vnc_free_addresses(&saddr, &nsaddr); 40669f26f325SPhilippe Mathieu-Daudé vnc_free_addresses(&wsaddr, &nwsaddr); 40672d55f0e8SPaolo Bonzini return; 40681ce52c78SPaolo Bonzini 40691ce52c78SPaolo Bonzini fail: 40704ee74fa7SDaniel P. Berrange vnc_display_close(vd); 4071396f935aSDaniel P. Berrange goto cleanup; 40723e230dd2SCorentin Chary } 407313661089SDaniel P. Berrange 407414f7143eSGerd Hoffmann void vnc_display_add_client(const char *id, int csock, bool skipauth) 407513661089SDaniel P. Berrange { 4076bf01c179SDaniel P. Berrange VncDisplay *vd = vnc_display_find(id); 407704d2529dSDaniel P. Berrange QIOChannelSocket *sioc; 407813661089SDaniel P. Berrange 4079bf01c179SDaniel P. Berrange if (!vd) { 4080d616ccc5SGerd Hoffmann return; 4081d616ccc5SGerd Hoffmann } 408204d2529dSDaniel P. Berrange 408304d2529dSDaniel P. Berrange sioc = qio_channel_socket_new_fd(csock, NULL); 408404d2529dSDaniel P. Berrange if (sioc) { 408510bcfe58SDaniel P. Berrange qio_channel_set_name(QIO_CHANNEL(sioc), "vnc-server"); 4086bf01c179SDaniel P. Berrange vnc_connect(vd, sioc, skipauth, false); 408704d2529dSDaniel P. Berrange object_unref(OBJECT(sioc)); 408804d2529dSDaniel P. Berrange } 408913661089SDaniel P. Berrange } 40904db14629SGerd Hoffmann 40919634f4e3SGerd Hoffmann static void vnc_auto_assign_id(QemuOptsList *olist, QemuOpts *opts) 40922779672fSGonglei { 40932779672fSGonglei int i = 2; 40942779672fSGonglei char *id; 40952779672fSGonglei 40962779672fSGonglei id = g_strdup("default"); 40972779672fSGonglei while (qemu_opts_find(olist, id)) { 40982779672fSGonglei g_free(id); 40992779672fSGonglei id = g_strdup_printf("vnc%d", i++); 41002779672fSGonglei } 41012779672fSGonglei qemu_opts_set_id(opts, id); 41022779672fSGonglei } 41032779672fSGonglei 410470b94331SMarkus Armbruster QemuOpts *vnc_parse(const char *str, Error **errp) 41054db14629SGerd Hoffmann { 41064db14629SGerd Hoffmann QemuOptsList *olist = qemu_find_opts("vnc"); 410770b94331SMarkus Armbruster QemuOpts *opts = qemu_opts_parse(olist, str, true, errp); 410881607cbfSGonglei const char *id; 41094db14629SGerd Hoffmann 411081607cbfSGonglei if (!opts) { 411181607cbfSGonglei return NULL; 411281607cbfSGonglei } 411381607cbfSGonglei 411481607cbfSGonglei id = qemu_opts_id(opts); 41154db14629SGerd Hoffmann if (!id) { 41164db14629SGerd Hoffmann /* auto-assign id if not present */ 41172779672fSGonglei vnc_auto_assign_id(olist, opts); 41189634f4e3SGerd Hoffmann } 41199634f4e3SGerd Hoffmann return opts; 41204db14629SGerd Hoffmann } 41214db14629SGerd Hoffmann 412228d0de7aSMarkus Armbruster int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp) 41239634f4e3SGerd Hoffmann { 41249634f4e3SGerd Hoffmann Error *local_err = NULL; 41259634f4e3SGerd Hoffmann char *id = (char *)qemu_opts_id(opts); 41269634f4e3SGerd Hoffmann 41279634f4e3SGerd Hoffmann assert(id); 4128ab4f931eSFei Li vnc_display_init(id, &local_err); 4129ab4f931eSFei Li if (local_err) { 4130612aea20SMarkus Armbruster error_propagate(errp, local_err); 4131612aea20SMarkus Armbruster return -1; 4132ab4f931eSFei Li } 41334db14629SGerd Hoffmann vnc_display_open(id, &local_err); 41344db14629SGerd Hoffmann if (local_err != NULL) { 4135612aea20SMarkus Armbruster error_propagate(errp, local_err); 4136612aea20SMarkus Armbruster return -1; 41374db14629SGerd Hoffmann } 41384db14629SGerd Hoffmann return 0; 41394db14629SGerd Hoffmann } 41404db14629SGerd Hoffmann 41414db14629SGerd Hoffmann static void vnc_register_config(void) 41424db14629SGerd Hoffmann { 41434db14629SGerd Hoffmann qemu_add_opts(&qemu_vnc_opts); 41444db14629SGerd Hoffmann } 414534294e2fSEduardo Habkost opts_init(vnc_register_config); 4146