blob: acbb88c4ca0f00d792e3881ba6610a367ba5af9f [file] [log] [blame]
bellard7d510b82006-05-01 10:38:19 +00001/*
2 * QEMU VNC display driver
ths5fafdf22007-09-16 21:08:06 +00003 *
bellard7d510b82006-05-01 10:38:19 +00004 * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
5 * Copyright (C) 2006 Fabrice Bellard
aliguori19a490b2009-03-06 20:27:13 +00006 * Copyright (C) 2009 Red Hat, Inc
ths5fafdf22007-09-16 21:08:06 +00007 *
bellard7d510b82006-05-01 10:38:19 +00008 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 * THE SOFTWARE.
25 */
26
Peter Maydelle16f4c82016-01-29 17:49:51 +000027#include "qemu/osdep.h"
aliguori19a490b2009-03-06 20:27:13 +000028#include "vnc.h"
Corentin Charybd023f92010-07-07 20:58:02 +020029#include "vnc-jobs.h"
Gerd Hoffmann40066172014-05-21 13:18:20 +020030#include "trace.h"
Markus Armbruster13d4ff02019-08-12 07:23:37 +020031#include "hw/qdev-core.h"
Paolo Bonzini9c17d612012-12-17 18:20:04 +010032#include "sysemu/sysemu.h"
Daniel P. Berrangé7b5fa0b2020-12-11 16:08:25 +000033#include "sysemu/runstate.h"
Markus Armbrusterd49b6832015-03-17 18:29:20 +010034#include "qemu/error-report.h"
Markus Armbrusterdb725812019-08-12 07:23:50 +020035#include "qemu/main-loop.h"
Markus Armbruster0b8fa322019-05-23 16:35:07 +020036#include "qemu/module.h"
Markus Armbruster922a01a2018-02-01 12:18:46 +010037#include "qemu/option.h"
Paolo Bonzini1de7afc2012-12-17 18:20:00 +010038#include "qemu/sockets.h"
39#include "qemu/timer.h"
Daniel P. Berrangeb76806d2016-02-18 18:40:24 +000040#include "authz/list.h"
Gerd Hoffmann4db14622014-09-16 12:33:03 +020041#include "qemu/config-file.h"
Markus Armbruster5d756482019-02-14 16:22:38 +010042#include "qapi/qapi-emit-events.h"
43#include "qapi/qapi-events-ui.h"
Markus Armbrustere688df62018-02-01 12:18:31 +010044#include "qapi/error.h"
Markus Armbruster9af23982018-02-11 10:36:01 +010045#include "qapi/qapi-commands-ui.h"
Gerd Hoffmann8d447d12013-12-02 14:27:18 +010046#include "ui/input.h"
Daniel P. Berrange8e9b0d22015-07-01 18:10:36 +010047#include "crypto/hash.h"
Philippe Mathieu-Daudé3c52bf02021-06-28 18:09:13 +020048#include "crypto/tlscreds.h"
Daniel P. Berrange3e305e42015-08-06 14:39:32 +010049#include "crypto/tlscredsanon.h"
50#include "crypto/tlscredsx509.h"
Richard Hendersonf7b25022019-03-14 15:37:43 -070051#include "crypto/random.h"
Daniel P. Berrangé6c6840e2021-03-11 11:43:41 +000052#include "crypto/secret_common.h"
Daniel P. Berrange3e305e42015-08-06 14:39:32 +010053#include "qom/object_interfaces.h"
Veronia Bahaaf348b6d2016-03-20 19:16:19 +020054#include "qemu/cutils.h"
Paolo Bonzini653c9742021-01-20 15:42:35 +010055#include "qemu/help_option.h"
Daniel P. Berrange57a6d6d2017-02-03 12:06:47 +000056#include "io/dns-resolver.h"
Marc-André Lureau756a98d2022-04-20 17:26:13 +040057#include "monitor/monitor.h"
bellard24236862006-04-30 21:28:36 +000058
Gerd Hoffmann0f7b2862013-03-14 11:56:16 +010059#define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT
Stefano Stabellini2430ffe2009-08-03 10:56:01 +010060#define VNC_REFRESH_INTERVAL_INC 50
Gerd Hoffmann0f7b2862013-03-14 11:56:16 +010061#define VNC_REFRESH_INTERVAL_MAX GUI_REFRESH_INTERVAL_IDLE
Corentin Chary999342a2011-02-04 09:05:55 +010062static const struct timeval VNC_REFRESH_STATS = { 0, 500000 };
63static const struct timeval VNC_REFRESH_LOSSY = { 2, 0 };
bellard24236862006-04-30 21:28:36 +000064
65#include "vnc_keysym.h"
Daniel P. Berrange800567a2015-07-01 18:10:38 +010066#include "crypto/cipher.h"
ths70848512007-08-25 01:37:05 +000067
Gerd Hoffmannd616ccc2014-07-29 12:14:08 +020068static QTAILQ_HEAD(, VncDisplay) vnc_displays =
69 QTAILQ_HEAD_INITIALIZER(vnc_displays);
bellarda9ce8592007-02-05 20:20:30 +000070
Gerd Hoffmannd467b672010-05-21 11:54:34 +020071static int vnc_cursor_define(VncState *vs);
Daniel P. Berrangee2b72cb2017-12-18 19:12:24 +000072static void vnc_update_throttle_offset(VncState *vs);
Gerd Hoffmannd467b672010-05-21 11:54:34 +020073
Gerd Hoffmann8cf36482011-11-24 18:10:49 +010074static void vnc_set_share_mode(VncState *vs, VncShareMode mode)
75{
76#ifdef _VNC_DEBUG
77 static const char *mn[] = {
78 [0] = "undefined",
79 [VNC_SHARE_MODE_CONNECTING] = "connecting",
80 [VNC_SHARE_MODE_SHARED] = "shared",
81 [VNC_SHARE_MODE_EXCLUSIVE] = "exclusive",
82 [VNC_SHARE_MODE_DISCONNECTED] = "disconnected",
83 };
Daniel P. Berrange04d25292015-02-27 16:20:57 +000084 fprintf(stderr, "%s/%p: %s -> %s\n", __func__,
85 vs->ioc, mn[vs->share_mode], mn[mode]);
Gerd Hoffmann8cf36482011-11-24 18:10:49 +010086#endif
87
Gerd Hoffmanne5f34cd2014-10-02 12:09:34 +020088 switch (vs->share_mode) {
89 case VNC_SHARE_MODE_CONNECTING:
90 vs->vd->num_connecting--;
91 break;
92 case VNC_SHARE_MODE_SHARED:
93 vs->vd->num_shared--;
94 break;
95 case VNC_SHARE_MODE_EXCLUSIVE:
Gerd Hoffmann8cf36482011-11-24 18:10:49 +010096 vs->vd->num_exclusive--;
Gerd Hoffmanne5f34cd2014-10-02 12:09:34 +020097 break;
98 default:
99 break;
Gerd Hoffmann8cf36482011-11-24 18:10:49 +0100100 }
Gerd Hoffmanne5f34cd2014-10-02 12:09:34 +0200101
Gerd Hoffmann8cf36482011-11-24 18:10:49 +0100102 vs->share_mode = mode;
Gerd Hoffmanne5f34cd2014-10-02 12:09:34 +0200103
104 switch (vs->share_mode) {
105 case VNC_SHARE_MODE_CONNECTING:
106 vs->vd->num_connecting++;
107 break;
108 case VNC_SHARE_MODE_SHARED:
109 vs->vd->num_shared++;
110 break;
111 case VNC_SHARE_MODE_EXCLUSIVE:
Gerd Hoffmann8cf36482011-11-24 18:10:49 +0100112 vs->vd->num_exclusive++;
Gerd Hoffmanne5f34cd2014-10-02 12:09:34 +0200113 break;
114 default:
115 break;
Gerd Hoffmann8cf36482011-11-24 18:10:49 +0100116 }
117}
118
aliguori1ff7df12009-03-06 20:27:05 +0000119
Markus Armbrusterbd269eb2017-04-26 09:36:41 +0200120static void vnc_init_basic_info(SocketAddress *addr,
Eric Blake98481bf2015-10-26 16:34:45 -0600121 VncBasicInfo *info,
122 Error **errp)
Luiz Capitulinod96fd292009-12-10 17:16:10 -0200123{
Daniel P. Berrange04d25292015-02-27 16:20:57 +0000124 switch (addr->type) {
Markus Armbrusterbd269eb2017-04-26 09:36:41 +0200125 case SOCKET_ADDRESS_TYPE_INET:
126 info->host = g_strdup(addr->u.inet.host);
127 info->service = g_strdup(addr->u.inet.port);
128 if (addr->u.inet.ipv6) {
Daniel P. Berrange04d25292015-02-27 16:20:57 +0000129 info->family = NETWORK_ADDRESS_FAMILY_IPV6;
130 } else {
131 info->family = NETWORK_ADDRESS_FAMILY_IPV4;
132 }
133 break;
Luiz Capitulinod96fd292009-12-10 17:16:10 -0200134
Markus Armbrusterbd269eb2017-04-26 09:36:41 +0200135 case SOCKET_ADDRESS_TYPE_UNIX:
Daniel P. Berrange04d25292015-02-27 16:20:57 +0000136 info->host = g_strdup("");
Markus Armbrusterbd269eb2017-04-26 09:36:41 +0200137 info->service = g_strdup(addr->u.q_unix.path);
Daniel P. Berrange04d25292015-02-27 16:20:57 +0000138 info->family = NETWORK_ADDRESS_FAMILY_UNIX;
139 break;
140
Markus Armbrusterbd269eb2017-04-26 09:36:41 +0200141 case SOCKET_ADDRESS_TYPE_VSOCK:
142 case SOCKET_ADDRESS_TYPE_FD:
Markus Armbrustera6c76282017-03-30 19:43:11 +0200143 error_setg(errp, "Unsupported socket address type %s",
Markus Armbruster977c7362017-08-24 10:46:08 +0200144 SocketAddressType_str(addr->type));
Daniel P. Berrange04d25292015-02-27 16:20:57 +0000145 break;
Markus Armbrustera6c76282017-03-30 19:43:11 +0200146 default:
147 abort();
Luiz Capitulinod96fd292009-12-10 17:16:10 -0200148 }
149
Daniel P. Berrange04d25292015-02-27 16:20:57 +0000150 return;
Luiz Capitulinod96fd292009-12-10 17:16:10 -0200151}
152
Daniel P. Berrange04d25292015-02-27 16:20:57 +0000153static void vnc_init_basic_info_from_server_addr(QIOChannelSocket *ioc,
154 VncBasicInfo *info,
Eric Blake98481bf2015-10-26 16:34:45 -0600155 Error **errp)
Luiz Capitulinod96fd292009-12-10 17:16:10 -0200156{
Markus Armbrusterbd269eb2017-04-26 09:36:41 +0200157 SocketAddress *addr = NULL;
Luiz Capitulinod96fd292009-12-10 17:16:10 -0200158
Daniel P. Berrange624cdd42016-08-02 11:45:24 +0100159 if (!ioc) {
160 error_setg(errp, "No listener socket available");
161 return;
162 }
163
Daniel P. Berrange04d25292015-02-27 16:20:57 +0000164 addr = qio_channel_socket_get_local_address(ioc, errp);
165 if (!addr) {
Eric Blake98481bf2015-10-26 16:34:45 -0600166 return;
Luiz Capitulinod96fd292009-12-10 17:16:10 -0200167 }
168
Daniel P. Berrange04d25292015-02-27 16:20:57 +0000169 vnc_init_basic_info(addr, info, errp);
Markus Armbrusterbd269eb2017-04-26 09:36:41 +0200170 qapi_free_SocketAddress(addr);
Luiz Capitulinod96fd292009-12-10 17:16:10 -0200171}
172
Daniel P. Berrange04d25292015-02-27 16:20:57 +0000173static void vnc_init_basic_info_from_remote_addr(QIOChannelSocket *ioc,
174 VncBasicInfo *info,
Eric Blake98481bf2015-10-26 16:34:45 -0600175 Error **errp)
Luiz Capitulinod96fd292009-12-10 17:16:10 -0200176{
Markus Armbrusterbd269eb2017-04-26 09:36:41 +0200177 SocketAddress *addr = NULL;
Luiz Capitulinod96fd292009-12-10 17:16:10 -0200178
Daniel P. Berrange04d25292015-02-27 16:20:57 +0000179 addr = qio_channel_socket_get_remote_address(ioc, errp);
180 if (!addr) {
Eric Blake98481bf2015-10-26 16:34:45 -0600181 return;
Luiz Capitulinod96fd292009-12-10 17:16:10 -0200182 }
183
Daniel P. Berrange04d25292015-02-27 16:20:57 +0000184 vnc_init_basic_info(addr, info, errp);
Markus Armbrusterbd269eb2017-04-26 09:36:41 +0200185 qapi_free_SocketAddress(addr);
Luiz Capitulinod96fd292009-12-10 17:16:10 -0200186}
187
aliguori1ff7df12009-03-06 20:27:05 +0000188static const char *vnc_auth_name(VncDisplay *vd) {
189 switch (vd->auth) {
190 case VNC_AUTH_INVALID:
191 return "invalid";
192 case VNC_AUTH_NONE:
193 return "none";
194 case VNC_AUTH_VNC:
195 return "vnc";
196 case VNC_AUTH_RA2:
197 return "ra2";
198 case VNC_AUTH_RA2NE:
199 return "ra2ne";
200 case VNC_AUTH_TIGHT:
201 return "tight";
202 case VNC_AUTH_ULTRA:
203 return "ultra";
204 case VNC_AUTH_TLS:
205 return "tls";
206 case VNC_AUTH_VENCRYPT:
aliguori1ff7df12009-03-06 20:27:05 +0000207 switch (vd->subauth) {
208 case VNC_AUTH_VENCRYPT_PLAIN:
209 return "vencrypt+plain";
210 case VNC_AUTH_VENCRYPT_TLSNONE:
211 return "vencrypt+tls+none";
212 case VNC_AUTH_VENCRYPT_TLSVNC:
213 return "vencrypt+tls+vnc";
214 case VNC_AUTH_VENCRYPT_TLSPLAIN:
215 return "vencrypt+tls+plain";
216 case VNC_AUTH_VENCRYPT_X509NONE:
217 return "vencrypt+x509+none";
218 case VNC_AUTH_VENCRYPT_X509VNC:
219 return "vencrypt+x509+vnc";
220 case VNC_AUTH_VENCRYPT_X509PLAIN:
221 return "vencrypt+x509+plain";
aliguori28a76be2009-03-06 20:27:40 +0000222 case VNC_AUTH_VENCRYPT_TLSSASL:
223 return "vencrypt+tls+sasl";
224 case VNC_AUTH_VENCRYPT_X509SASL:
225 return "vencrypt+x509+sasl";
aliguori1ff7df12009-03-06 20:27:05 +0000226 default:
227 return "vencrypt";
228 }
aliguori2f9606b2009-03-06 20:27:28 +0000229 case VNC_AUTH_SASL:
aliguori28a76be2009-03-06 20:27:40 +0000230 return "sasl";
aliguori1ff7df12009-03-06 20:27:05 +0000231 }
232 return "unknown";
233}
234
Gerd Hoffmannd616ccc2014-07-29 12:14:08 +0200235static VncServerInfo *vnc_server_info_get(VncDisplay *vd)
Luiz Capitulinoa7789382010-01-14 14:50:53 -0200236{
Wenchao Xiafb6ba0d2014-06-18 08:43:49 +0200237 VncServerInfo *info;
Eric Blake98481bf2015-10-26 16:34:45 -0600238 Error *err = NULL;
Luiz Capitulinoa7789382010-01-14 14:50:53 -0200239
Daniel P. Berrange13e1d0e2018-02-01 16:45:14 +0000240 if (!vd->listener || !vd->listener->nsioc) {
Daniel P. Berrange4ee74fa2017-02-03 12:06:44 +0000241 return NULL;
242 }
243
Daniel P. Berrange3e7f1362016-08-02 11:45:25 +0100244 info = g_malloc0(sizeof(*info));
Daniel P. Berrange13e1d0e2018-02-01 16:45:14 +0000245 vnc_init_basic_info_from_server_addr(vd->listener->sioc[0],
Eric Blakeddf21902015-10-26 16:34:49 -0600246 qapi_VncServerInfo_base(info), &err);
Gerd Hoffmannd616ccc2014-07-29 12:14:08 +0200247 info->auth = g_strdup(vnc_auth_name(vd));
Eric Blake98481bf2015-10-26 16:34:45 -0600248 if (err) {
249 qapi_free_VncServerInfo(info);
250 info = NULL;
251 error_free(err);
252 }
Wenchao Xiafb6ba0d2014-06-18 08:43:49 +0200253 return info;
Luiz Capitulinoa7789382010-01-14 14:50:53 -0200254}
255
Luiz Capitulino4a80dba2010-01-14 14:50:56 -0200256static void vnc_client_cache_auth(VncState *client)
aliguori1ff7df12009-03-06 20:27:05 +0000257{
Luiz Capitulino4a80dba2010-01-14 14:50:56 -0200258 if (!client->info) {
259 return;
Luiz Capitulinod96fd292009-12-10 17:16:10 -0200260 }
aliguori1263b7d2009-03-06 20:27:32 +0000261
Daniel P. Berrange3e305e42015-08-06 14:39:32 +0100262 if (client->tls) {
263 client->info->x509_dname =
264 qcrypto_tls_session_get_peer_name(client->tls);
Luiz Capitulinod96fd292009-12-10 17:16:10 -0200265 }
aliguori1263b7d2009-03-06 20:27:32 +0000266#ifdef CONFIG_VNC_SASL
267 if (client->sasl.conn &&
Luiz Capitulinod96fd292009-12-10 17:16:10 -0200268 client->sasl.username) {
Wenchao Xiafb6ba0d2014-06-18 08:43:49 +0200269 client->info->sasl_username = g_strdup(client->sasl.username);
Luiz Capitulinod96fd292009-12-10 17:16:10 -0200270 }
271#endif
Luiz Capitulino4a80dba2010-01-14 14:50:56 -0200272}
Luiz Capitulinod96fd292009-12-10 17:16:10 -0200273
Luiz Capitulino4a80dba2010-01-14 14:50:56 -0200274static void vnc_client_cache_addr(VncState *client)
275{
Eric Blake98481bf2015-10-26 16:34:45 -0600276 Error *err = NULL;
Luiz Capitulino4a80dba2010-01-14 14:50:56 -0200277
Eric Blake98481bf2015-10-26 16:34:45 -0600278 client->info = g_malloc0(sizeof(*client->info));
Daniel P. Berrange04d25292015-02-27 16:20:57 +0000279 vnc_init_basic_info_from_remote_addr(client->sioc,
Eric Blakeddf21902015-10-26 16:34:49 -0600280 qapi_VncClientInfo_base(client->info),
Eric Blake98481bf2015-10-26 16:34:45 -0600281 &err);
Gerd Hoffmanne1b3d472019-09-04 07:52:50 +0200282 client->info->websocket = client->websocket;
Eric Blake98481bf2015-10-26 16:34:45 -0600283 if (err) {
284 qapi_free_VncClientInfo(client->info);
285 client->info = NULL;
286 error_free(err);
Luiz Capitulino4a80dba2010-01-14 14:50:56 -0200287 }
Luiz Capitulinod96fd292009-12-10 17:16:10 -0200288}
289
Wenchao Xiafb6ba0d2014-06-18 08:43:49 +0200290static void vnc_qmp_event(VncState *vs, QAPIEvent event)
Luiz Capitulino586153d2010-01-14 14:50:57 -0200291{
Wenchao Xiafb6ba0d2014-06-18 08:43:49 +0200292 VncServerInfo *si;
Luiz Capitulino586153d2010-01-14 14:50:57 -0200293
294 if (!vs->info) {
295 return;
296 }
297
Gerd Hoffmannd616ccc2014-07-29 12:14:08 +0200298 si = vnc_server_info_get(vs->vd);
Wenchao Xiafb6ba0d2014-06-18 08:43:49 +0200299 if (!si) {
Luiz Capitulino586153d2010-01-14 14:50:57 -0200300 return;
301 }
302
Wenchao Xiafb6ba0d2014-06-18 08:43:49 +0200303 switch (event) {
304 case QAPI_EVENT_VNC_CONNECTED:
Peter Xu3ab72382018-08-15 21:37:37 +0800305 qapi_event_send_vnc_connected(si, qapi_VncClientInfo_base(vs->info));
Wenchao Xiafb6ba0d2014-06-18 08:43:49 +0200306 break;
307 case QAPI_EVENT_VNC_INITIALIZED:
Peter Xu3ab72382018-08-15 21:37:37 +0800308 qapi_event_send_vnc_initialized(si, vs->info);
Wenchao Xiafb6ba0d2014-06-18 08:43:49 +0200309 break;
310 case QAPI_EVENT_VNC_DISCONNECTED:
Peter Xu3ab72382018-08-15 21:37:37 +0800311 qapi_event_send_vnc_disconnected(si, vs->info);
Wenchao Xiafb6ba0d2014-06-18 08:43:49 +0200312 break;
313 default:
314 break;
315 }
Luiz Capitulino586153d2010-01-14 14:50:57 -0200316
Wenchao Xiafb6ba0d2014-06-18 08:43:49 +0200317 qapi_free_VncServerInfo(si);
Luiz Capitulino586153d2010-01-14 14:50:57 -0200318}
319
Luiz Capitulino2b54aa82011-10-17 16:41:22 -0200320static VncClientInfo *qmp_query_vnc_client(const VncState *client)
Luiz Capitulinod96fd292009-12-10 17:16:10 -0200321{
Luiz Capitulino2b54aa82011-10-17 16:41:22 -0200322 VncClientInfo *info;
Daniel P. Berrange04d25292015-02-27 16:20:57 +0000323 Error *err = NULL;
Luiz Capitulino2b54aa82011-10-17 16:41:22 -0200324
325 info = g_malloc0(sizeof(*info));
Daniel P. Berrange04d25292015-02-27 16:20:57 +0000326
327 vnc_init_basic_info_from_remote_addr(client->sioc,
328 qapi_VncClientInfo_base(info),
329 &err);
330 if (err) {
331 error_free(err);
332 qapi_free_VncClientInfo(info);
333 return NULL;
334 }
335
Eric Blakeddf21902015-10-26 16:34:49 -0600336 info->websocket = client->websocket;
Luiz Capitulinod96fd292009-12-10 17:16:10 -0200337
Daniel P. Berrange3e305e42015-08-06 14:39:32 +0100338 if (client->tls) {
339 info->x509_dname = qcrypto_tls_session_get_peer_name(client->tls);
Luiz Capitulino2b54aa82011-10-17 16:41:22 -0200340 }
Luiz Capitulinod96fd292009-12-10 17:16:10 -0200341#ifdef CONFIG_VNC_SASL
Luiz Capitulino2b54aa82011-10-17 16:41:22 -0200342 if (client->sasl.conn && client->sasl.username) {
Luiz Capitulino2b54aa82011-10-17 16:41:22 -0200343 info->sasl_username = g_strdup(client->sasl.username);
344 }
aliguori1263b7d2009-03-06 20:27:32 +0000345#endif
Luiz Capitulino2b54aa82011-10-17 16:41:22 -0200346
347 return info;
aliguori1ff7df12009-03-06 20:27:05 +0000348}
349
Gerd Hoffmannd616ccc2014-07-29 12:14:08 +0200350static VncDisplay *vnc_display_find(const char *id)
351{
352 VncDisplay *vd;
353
354 if (id == NULL) {
355 return QTAILQ_FIRST(&vnc_displays);
356 }
357 QTAILQ_FOREACH(vd, &vnc_displays, next) {
358 if (strcmp(id, vd->id) == 0) {
359 return vd;
360 }
361 }
362 return NULL;
363}
364
Gerd Hoffmann2d29a432014-12-09 15:27:39 +0100365static VncClientInfoList *qmp_query_client_list(VncDisplay *vd)
366{
Eric Blake54aa3de2020-11-12 19:13:37 -0600367 VncClientInfoList *prev = NULL;
Gerd Hoffmann2d29a432014-12-09 15:27:39 +0100368 VncState *client;
369
370 QTAILQ_FOREACH(client, &vd->clients, next) {
Eric Blake54aa3de2020-11-12 19:13:37 -0600371 QAPI_LIST_PREPEND(prev, qmp_query_vnc_client(client));
Gerd Hoffmann2d29a432014-12-09 15:27:39 +0100372 }
373 return prev;
374}
375
Luiz Capitulino2b54aa82011-10-17 16:41:22 -0200376VncInfo *qmp_query_vnc(Error **errp)
Luiz Capitulinod96fd292009-12-10 17:16:10 -0200377{
Luiz Capitulino2b54aa82011-10-17 16:41:22 -0200378 VncInfo *info = g_malloc0(sizeof(*info));
Gerd Hoffmannd616ccc2014-07-29 12:14:08 +0200379 VncDisplay *vd = vnc_display_find(NULL);
Markus Armbrusterbd269eb2017-04-26 09:36:41 +0200380 SocketAddress *addr = NULL;
Luiz Capitulinod96fd292009-12-10 17:16:10 -0200381
Daniel P. Berrange13e1d0e2018-02-01 16:45:14 +0000382 if (vd == NULL || !vd->listener || !vd->listener->nsioc) {
Luiz Capitulino2b54aa82011-10-17 16:41:22 -0200383 info->enabled = false;
aliguori1ff7df12009-03-06 20:27:05 +0000384 } else {
Luiz Capitulino2b54aa82011-10-17 16:41:22 -0200385 info->enabled = true;
386
387 /* for compatibility with the original command */
388 info->has_clients = true;
Gerd Hoffmann2d29a432014-12-09 15:27:39 +0100389 info->clients = qmp_query_client_list(vd);
Luiz Capitulinod96fd292009-12-10 17:16:10 -0200390
Daniel P. Berrange13e1d0e2018-02-01 16:45:14 +0000391 addr = qio_channel_socket_get_local_address(vd->listener->sioc[0],
392 errp);
Daniel P. Berrange04d25292015-02-27 16:20:57 +0000393 if (!addr) {
Luiz Capitulino2b54aa82011-10-17 16:41:22 -0200394 goto out_error;
aliguori1ff7df12009-03-06 20:27:05 +0000395 }
Luiz Capitulino2b54aa82011-10-17 16:41:22 -0200396
Daniel P. Berrange04d25292015-02-27 16:20:57 +0000397 switch (addr->type) {
Markus Armbrusterbd269eb2017-04-26 09:36:41 +0200398 case SOCKET_ADDRESS_TYPE_INET:
399 info->host = g_strdup(addr->u.inet.host);
400 info->service = g_strdup(addr->u.inet.port);
401 if (addr->u.inet.ipv6) {
Daniel P. Berrange04d25292015-02-27 16:20:57 +0000402 info->family = NETWORK_ADDRESS_FAMILY_IPV6;
403 } else {
404 info->family = NETWORK_ADDRESS_FAMILY_IPV4;
405 }
406 break;
407
Markus Armbrusterbd269eb2017-04-26 09:36:41 +0200408 case SOCKET_ADDRESS_TYPE_UNIX:
Daniel P. Berrange04d25292015-02-27 16:20:57 +0000409 info->host = g_strdup("");
Markus Armbrusterbd269eb2017-04-26 09:36:41 +0200410 info->service = g_strdup(addr->u.q_unix.path);
Daniel P. Berrange04d25292015-02-27 16:20:57 +0000411 info->family = NETWORK_ADDRESS_FAMILY_UNIX;
412 break;
413
Markus Armbrusterbd269eb2017-04-26 09:36:41 +0200414 case SOCKET_ADDRESS_TYPE_VSOCK:
415 case SOCKET_ADDRESS_TYPE_FD:
Markus Armbrustera6c76282017-03-30 19:43:11 +0200416 error_setg(errp, "Unsupported socket address type %s",
Markus Armbruster977c7362017-08-24 10:46:08 +0200417 SocketAddressType_str(addr->type));
Luiz Capitulino2b54aa82011-10-17 16:41:22 -0200418 goto out_error;
Markus Armbrustera6c76282017-03-30 19:43:11 +0200419 default:
420 abort();
Luiz Capitulino2b54aa82011-10-17 16:41:22 -0200421 }
422
Luiz Capitulino2b54aa82011-10-17 16:41:22 -0200423 info->has_family = true;
Luiz Capitulino2b54aa82011-10-17 16:41:22 -0200424
Gerd Hoffmannd616ccc2014-07-29 12:14:08 +0200425 info->auth = g_strdup(vnc_auth_name(vd));
bellarda9ce8592007-02-05 20:20:30 +0000426 }
Luiz Capitulino2b54aa82011-10-17 16:41:22 -0200427
Markus Armbrusterbd269eb2017-04-26 09:36:41 +0200428 qapi_free_SocketAddress(addr);
Luiz Capitulino2b54aa82011-10-17 16:41:22 -0200429 return info;
430
431out_error:
Markus Armbrusterbd269eb2017-04-26 09:36:41 +0200432 qapi_free_SocketAddress(addr);
Luiz Capitulino2b54aa82011-10-17 16:41:22 -0200433 qapi_free_VncInfo(info);
434 return NULL;
bellarda9ce8592007-02-05 20:20:30 +0000435}
436
Daniel P. Berrange2a7e6852017-02-03 12:06:43 +0000437
438static void qmp_query_auth(int auth, int subauth,
439 VncPrimaryAuth *qmp_auth,
440 VncVencryptSubAuth *qmp_vencrypt,
441 bool *qmp_has_vencrypt);
442
443static VncServerInfo2List *qmp_query_server_entry(QIOChannelSocket *ioc,
444 bool websocket,
445 int auth,
446 int subauth,
447 VncServerInfo2List *prev)
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100448{
Daniel P. Berrange2a7e6852017-02-03 12:06:43 +0000449 VncServerInfo2 *info;
Daniel P. Berrange04d25292015-02-27 16:20:57 +0000450 Error *err = NULL;
Markus Armbrusterbd269eb2017-04-26 09:36:41 +0200451 SocketAddress *addr;
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100452
Markus Armbruster9261ef52020-06-30 11:03:28 +0200453 addr = qio_channel_socket_get_local_address(ioc, NULL);
Daniel P. Berrange04d25292015-02-27 16:20:57 +0000454 if (!addr) {
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100455 return prev;
456 }
457
Daniel P. Berrange2a7e6852017-02-03 12:06:43 +0000458 info = g_new0(VncServerInfo2, 1);
459 vnc_init_basic_info(addr, qapi_VncServerInfo2_base(info), &err);
Markus Armbrusterbd269eb2017-04-26 09:36:41 +0200460 qapi_free_SocketAddress(addr);
Daniel P. Berrange04d25292015-02-27 16:20:57 +0000461 if (err) {
Daniel P. Berrange2a7e6852017-02-03 12:06:43 +0000462 qapi_free_VncServerInfo2(info);
Daniel P. Berrange04d25292015-02-27 16:20:57 +0000463 error_free(err);
464 return prev;
465 }
Gerd Hoffmann4478aa72014-12-10 09:49:39 +0100466 info->websocket = websocket;
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100467
Daniel P. Berrange2a7e6852017-02-03 12:06:43 +0000468 qmp_query_auth(auth, subauth, &info->auth,
469 &info->vencrypt, &info->has_vencrypt);
470
Eric Blake54aa3de2020-11-12 19:13:37 -0600471 QAPI_LIST_PREPEND(prev, info);
472 return prev;
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100473}
474
Daniel P. Berrange2a7e6852017-02-03 12:06:43 +0000475static void qmp_query_auth(int auth, int subauth,
476 VncPrimaryAuth *qmp_auth,
477 VncVencryptSubAuth *qmp_vencrypt,
478 bool *qmp_has_vencrypt)
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100479{
Daniel P. Berrange2a7e6852017-02-03 12:06:43 +0000480 switch (auth) {
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100481 case VNC_AUTH_VNC:
Daniel P. Berrange2a7e6852017-02-03 12:06:43 +0000482 *qmp_auth = VNC_PRIMARY_AUTH_VNC;
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100483 break;
484 case VNC_AUTH_RA2:
Daniel P. Berrange2a7e6852017-02-03 12:06:43 +0000485 *qmp_auth = VNC_PRIMARY_AUTH_RA2;
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100486 break;
487 case VNC_AUTH_RA2NE:
Daniel P. Berrange2a7e6852017-02-03 12:06:43 +0000488 *qmp_auth = VNC_PRIMARY_AUTH_RA2NE;
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100489 break;
490 case VNC_AUTH_TIGHT:
Daniel P. Berrange2a7e6852017-02-03 12:06:43 +0000491 *qmp_auth = VNC_PRIMARY_AUTH_TIGHT;
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100492 break;
493 case VNC_AUTH_ULTRA:
Daniel P. Berrange2a7e6852017-02-03 12:06:43 +0000494 *qmp_auth = VNC_PRIMARY_AUTH_ULTRA;
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100495 break;
496 case VNC_AUTH_TLS:
Daniel P. Berrange2a7e6852017-02-03 12:06:43 +0000497 *qmp_auth = VNC_PRIMARY_AUTH_TLS;
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100498 break;
499 case VNC_AUTH_VENCRYPT:
Daniel P. Berrange2a7e6852017-02-03 12:06:43 +0000500 *qmp_auth = VNC_PRIMARY_AUTH_VENCRYPT;
501 *qmp_has_vencrypt = true;
502 switch (subauth) {
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100503 case VNC_AUTH_VENCRYPT_PLAIN:
Daniel P. Berrange2a7e6852017-02-03 12:06:43 +0000504 *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_PLAIN;
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100505 break;
506 case VNC_AUTH_VENCRYPT_TLSNONE:
Daniel P. Berrange2a7e6852017-02-03 12:06:43 +0000507 *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_NONE;
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100508 break;
509 case VNC_AUTH_VENCRYPT_TLSVNC:
Daniel P. Berrange2a7e6852017-02-03 12:06:43 +0000510 *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_VNC;
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100511 break;
512 case VNC_AUTH_VENCRYPT_TLSPLAIN:
Daniel P. Berrange2a7e6852017-02-03 12:06:43 +0000513 *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_PLAIN;
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100514 break;
515 case VNC_AUTH_VENCRYPT_X509NONE:
Daniel P. Berrange2a7e6852017-02-03 12:06:43 +0000516 *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_NONE;
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100517 break;
518 case VNC_AUTH_VENCRYPT_X509VNC:
Daniel P. Berrange2a7e6852017-02-03 12:06:43 +0000519 *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_VNC;
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100520 break;
521 case VNC_AUTH_VENCRYPT_X509PLAIN:
Daniel P. Berrange2a7e6852017-02-03 12:06:43 +0000522 *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_PLAIN;
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100523 break;
524 case VNC_AUTH_VENCRYPT_TLSSASL:
Daniel P. Berrange2a7e6852017-02-03 12:06:43 +0000525 *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_SASL;
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100526 break;
527 case VNC_AUTH_VENCRYPT_X509SASL:
Daniel P. Berrange2a7e6852017-02-03 12:06:43 +0000528 *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_SASL;
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100529 break;
530 default:
Daniel P. Berrange2a7e6852017-02-03 12:06:43 +0000531 *qmp_has_vencrypt = false;
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100532 break;
533 }
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100534 break;
535 case VNC_AUTH_SASL:
Daniel P. Berrange2a7e6852017-02-03 12:06:43 +0000536 *qmp_auth = VNC_PRIMARY_AUTH_SASL;
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100537 break;
538 case VNC_AUTH_NONE:
539 default:
Daniel P. Berrange2a7e6852017-02-03 12:06:43 +0000540 *qmp_auth = VNC_PRIMARY_AUTH_NONE;
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100541 break;
542 }
543}
544
545VncInfo2List *qmp_query_vnc_servers(Error **errp)
546{
Eric Blake54aa3de2020-11-12 19:13:37 -0600547 VncInfo2List *prev = NULL;
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100548 VncInfo2 *info;
549 VncDisplay *vd;
550 DeviceState *dev;
Daniel P. Berrange4ee74fa2017-02-03 12:06:44 +0000551 size_t i;
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100552
553 QTAILQ_FOREACH(vd, &vnc_displays, next) {
554 info = g_new0(VncInfo2, 1);
555 info->id = g_strdup(vd->id);
556 info->clients = qmp_query_client_list(vd);
Daniel P. Berrange2a7e6852017-02-03 12:06:43 +0000557 qmp_query_auth(vd->auth, vd->subauth, &info->auth,
558 &info->vencrypt, &info->has_vencrypt);
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100559 if (vd->dcl.con) {
560 dev = DEVICE(object_property_get_link(OBJECT(vd->dcl.con),
Markus Armbruster552d7f42020-07-07 18:05:51 +0200561 "device", &error_abort));
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100562 info->display = g_strdup(dev->id);
563 }
Daniel P. Berrange13e1d0e2018-02-01 16:45:14 +0000564 for (i = 0; vd->listener != NULL && i < vd->listener->nsioc; i++) {
Daniel P. Berrange04d25292015-02-27 16:20:57 +0000565 info->server = qmp_query_server_entry(
Daniel P. Berrange13e1d0e2018-02-01 16:45:14 +0000566 vd->listener->sioc[i], false, vd->auth, vd->subauth,
567 info->server);
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100568 }
Daniel P. Berrange13e1d0e2018-02-01 16:45:14 +0000569 for (i = 0; vd->wslistener != NULL && i < vd->wslistener->nsioc; i++) {
Daniel P. Berrange04d25292015-02-27 16:20:57 +0000570 info->server = qmp_query_server_entry(
Daniel P. Berrange13e1d0e2018-02-01 16:45:14 +0000571 vd->wslistener->sioc[i], true, vd->ws_auth,
Daniel P. Berrange4ee74fa2017-02-03 12:06:44 +0000572 vd->ws_subauth, info->server);
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100573 }
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100574
Eric Blake54aa3de2020-11-12 19:13:37 -0600575 QAPI_LIST_PREPEND(prev, info);
Gerd Hoffmanndf887682014-12-17 15:49:44 +0100576 }
577 return prev;
578}
579
Zihao Chang1f08e342021-03-16 15:58:44 +0800580bool vnc_display_reload_certs(const char *id, Error **errp)
581{
582 VncDisplay *vd = vnc_display_find(id);
583 QCryptoTLSCredsClass *creds = NULL;
584
585 if (!vd) {
586 error_setg(errp, "Can not find vnc display");
587 return false;
588 }
589
590 if (!vd->tlscreds) {
Michael Tokarev4087ecb2021-05-08 12:25:58 +0300591 error_setg(errp, "vnc tls is not enabled");
Zihao Chang1f08e342021-03-16 15:58:44 +0800592 return false;
593 }
594
595 creds = QCRYPTO_TLS_CREDS_GET_CLASS(OBJECT(vd->tlscreds));
596 if (creds->reload == NULL) {
597 error_setg(errp, "%s doesn't support to reload TLS credential",
598 object_get_typename(OBJECT(vd->tlscreds)));
599 return false;
600 }
601 if (!creds->reload(vd->tlscreds, errp)) {
602 return false;
603 }
604
605 return true;
606}
607
bellard24236862006-04-30 21:28:36 +0000608/* TODO
609 1) Get the queue working for IO.
610 2) there is some weirdness when using the -S option (the screen is grey
611 and not totally invalidated
612 3) resolutions > 1024
613*/
614
Daniel P. Berrange6af998d2017-12-18 19:12:16 +0000615static int vnc_update_client(VncState *vs, int has_dirty);
Gerd Hoffmann198a0032009-06-16 14:19:48 +0200616static void vnc_disconnect_start(VncState *vs);
bellard24236862006-04-30 21:28:36 +0000617
aliguori753b4052009-02-16 14:59:30 +0000618static void vnc_colordepth(VncState *vs);
Stefano Stabellini1fc62412009-08-03 10:54:32 +0100619static void framebuffer_update_request(VncState *vs, int incremental,
620 int x_position, int y_position,
621 int w, int h);
Gerd Hoffmann0f7b2862013-03-14 11:56:16 +0100622static void vnc_refresh(DisplayChangeListener *dcl);
Stefano Stabellini1fc62412009-08-03 10:54:32 +0100623static int vnc_refresh_server_surface(VncDisplay *vd);
aliguori7eac3a82008-09-15 16:03:41 +0000624
Gerd Hoffmannd05959c2015-10-30 12:10:06 +0100625static int vnc_width(VncDisplay *vd)
626{
627 return MIN(VNC_MAX_WIDTH, ROUND_UP(surface_width(vd->ds),
628 VNC_DIRTY_PIXELS_PER_BIT));
629}
630
Daniel P. Berrangé69cc8db2021-03-11 18:29:57 +0000631static int vnc_true_width(VncDisplay *vd)
632{
633 return MIN(VNC_MAX_WIDTH, surface_width(vd->ds));
634}
635
Gerd Hoffmannd05959c2015-10-30 12:10:06 +0100636static int vnc_height(VncDisplay *vd)
637{
638 return MIN(VNC_MAX_HEIGHT, surface_height(vd->ds));
639}
640
Peter Lievenbea60dd2014-06-30 10:57:51 +0200641static void vnc_set_area_dirty(DECLARE_BITMAP(dirty[VNC_MAX_HEIGHT],
642 VNC_MAX_WIDTH / VNC_DIRTY_PIXELS_PER_BIT),
Gerd Hoffmannf7b3d682015-10-30 12:10:08 +0100643 VncDisplay *vd,
644 int x, int y, int w, int h)
645{
646 int width = vnc_width(vd);
647 int height = vnc_height(vd);
648
Peter Lieven91937222014-01-08 10:08:37 +0100649 /* this is needed this to ensure we updated all affected
650 * blocks if x % VNC_DIRTY_PIXELS_PER_BIT != 0 */
Peter Lievenb4c85dd2014-01-08 10:08:33 +0100651 w += (x % VNC_DIRTY_PIXELS_PER_BIT);
652 x -= (x % VNC_DIRTY_PIXELS_PER_BIT);
balrog0486e8a2007-12-11 22:31:32 +0000653
Gerd Hoffmann9f649162012-10-10 13:29:43 +0200654 x = MIN(x, width);
655 y = MIN(y, height);
656 w = MIN(x + w, width) - x;
Peter Lieven91937222014-01-08 10:08:37 +0100657 h = MIN(y + h, height);
balrog788abf82008-05-20 00:07:58 +0000658
Peter Lievenb4c85dd2014-01-08 10:08:33 +0100659 for (; y < h; y++) {
Peter Lievenbea60dd2014-06-30 10:57:51 +0200660 bitmap_set(dirty[y], x / VNC_DIRTY_PIXELS_PER_BIT,
Peter Lieven91937222014-01-08 10:08:37 +0100661 DIV_ROUND_UP(w, VNC_DIRTY_PIXELS_PER_BIT));
Peter Lievenb4c85dd2014-01-08 10:08:33 +0100662 }
bellard24236862006-04-30 21:28:36 +0000663}
664
Peter Lievenbea60dd2014-06-30 10:57:51 +0200665static void vnc_dpy_update(DisplayChangeListener *dcl,
666 int x, int y, int w, int h)
667{
668 VncDisplay *vd = container_of(dcl, VncDisplay, dcl);
669 struct VncSurface *s = &vd->guest;
Peter Lievenbea60dd2014-06-30 10:57:51 +0200670
Gerd Hoffmannf7b3d682015-10-30 12:10:08 +0100671 vnc_set_area_dirty(s->dirty, vd, x, y, w, h);
Peter Lievenbea60dd2014-06-30 10:57:51 +0200672}
673
Corentin Chary70a45682010-05-03 14:31:34 +0200674void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
675 int32_t encoding)
bellard24236862006-04-30 21:28:36 +0000676{
677 vnc_write_u16(vs, x);
678 vnc_write_u16(vs, y);
679 vnc_write_u16(vs, w);
680 vnc_write_u16(vs, h);
681
682 vnc_write_s32(vs, encoding);
683}
684
Gerd Hoffmann763deea2021-01-12 14:41:20 +0100685static void vnc_desktop_resize_ext(VncState *vs, int reject_reason)
686{
Daniel P. Berrangéadc8fce2021-03-11 18:29:54 +0000687 trace_vnc_msg_server_ext_desktop_resize(
688 vs, vs->ioc, vs->client_width, vs->client_height, reject_reason);
689
Gerd Hoffmann763deea2021-01-12 14:41:20 +0100690 vnc_lock_output(vs);
691 vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
692 vnc_write_u8(vs, 0);
693 vnc_write_u16(vs, 1); /* number of rects */
694 vnc_framebuffer_update(vs,
695 reject_reason ? 1 : 0,
696 reject_reason,
697 vs->client_width, vs->client_height,
698 VNC_ENCODING_DESKTOP_RESIZE_EXT);
699 vnc_write_u8(vs, 1); /* number of screens */
700 vnc_write_u8(vs, 0); /* padding */
701 vnc_write_u8(vs, 0); /* padding */
702 vnc_write_u8(vs, 0); /* padding */
703 vnc_write_u32(vs, 0); /* screen id */
704 vnc_write_u16(vs, 0); /* screen x-pos */
705 vnc_write_u16(vs, 0); /* screen y-pos */
706 vnc_write_u16(vs, vs->client_width);
707 vnc_write_u16(vs, vs->client_height);
708 vnc_write_u32(vs, 0); /* screen flags */
709 vnc_unlock_output(vs);
710 vnc_flush(vs);
711}
Tim Hardeck32ed2682013-01-21 11:04:43 +0100712
Gerd Hoffmann621aaeb2010-05-25 18:25:16 +0200713static void vnc_desktop_resize(VncState *vs)
714{
Gerd Hoffmann763deea2021-01-12 14:41:20 +0100715 if (vs->ioc == NULL || (!vnc_has_feature(vs, VNC_FEATURE_RESIZE) &&
716 !vnc_has_feature(vs, VNC_FEATURE_RESIZE_EXT))) {
Gerd Hoffmann621aaeb2010-05-25 18:25:16 +0200717 return;
718 }
Daniel P. Berrangé69cc8db2021-03-11 18:29:57 +0000719 if (vs->client_width == vs->vd->true_width &&
Gerd Hoffmannd2397262021-01-25 11:40:40 +0100720 vs->client_height == pixman_image_get_height(vs->vd->server)) {
721 return;
722 }
Daniel P. Berrange4c956bd2018-01-18 15:52:54 +0000723
Daniel P. Berrangé69cc8db2021-03-11 18:29:57 +0000724 assert(vs->vd->true_width < 65536 &&
725 vs->vd->true_width >= 0);
Daniel P. Berrange4c956bd2018-01-18 15:52:54 +0000726 assert(pixman_image_get_height(vs->vd->server) < 65536 &&
727 pixman_image_get_height(vs->vd->server) >= 0);
Daniel P. Berrangé69cc8db2021-03-11 18:29:57 +0000728 vs->client_width = vs->vd->true_width;
Peter Lievenbea60dd2014-06-30 10:57:51 +0200729 vs->client_height = pixman_image_get_height(vs->vd->server);
Gerd Hoffmann763deea2021-01-12 14:41:20 +0100730
731 if (vnc_has_feature(vs, VNC_FEATURE_RESIZE_EXT)) {
732 vnc_desktop_resize_ext(vs, 0);
733 return;
734 }
735
Daniel P. Berrangéadc8fce2021-03-11 18:29:54 +0000736 trace_vnc_msg_server_desktop_resize(
737 vs, vs->ioc, vs->client_width, vs->client_height);
738
Corentin Charybd023f92010-07-07 20:58:02 +0200739 vnc_lock_output(vs);
Gerd Hoffmann621aaeb2010-05-25 18:25:16 +0200740 vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
741 vnc_write_u8(vs, 0);
742 vnc_write_u16(vs, 1); /* number of rects */
Gerd Hoffmann5862d192010-05-25 18:25:18 +0200743 vnc_framebuffer_update(vs, 0, 0, vs->client_width, vs->client_height,
Gerd Hoffmann621aaeb2010-05-25 18:25:16 +0200744 VNC_ENCODING_DESKTOPRESIZE);
Corentin Charybd023f92010-07-07 20:58:02 +0200745 vnc_unlock_output(vs);
Gerd Hoffmann621aaeb2010-05-25 18:25:16 +0200746 vnc_flush(vs);
747}
748
Corentin Charybd023f92010-07-07 20:58:02 +0200749static void vnc_abort_display_jobs(VncDisplay *vd)
750{
751 VncState *vs;
752
753 QTAILQ_FOREACH(vs, &vd->clients, next) {
754 vnc_lock_output(vs);
755 vs->abort = true;
756 vnc_unlock_output(vs);
757 }
758 QTAILQ_FOREACH(vs, &vd->clients, next) {
759 vnc_jobs_join(vs);
760 }
761 QTAILQ_FOREACH(vs, &vd->clients, next) {
762 vnc_lock_output(vs);
Gerd Hoffmannbbcdeb62019-03-05 14:09:30 +0100763 if (vs->update == VNC_STATE_UPDATE_NONE &&
764 vs->job_update != VNC_STATE_UPDATE_NONE) {
765 /* job aborted before completion */
766 vs->update = vs->job_update;
767 vs->job_update = VNC_STATE_UPDATE_NONE;
768 }
Corentin Charybd023f92010-07-07 20:58:02 +0200769 vs->abort = false;
770 vnc_unlock_output(vs);
771 }
772}
Corentin Charybd023f92010-07-07 20:58:02 +0200773
Gerd Hoffmann9f649162012-10-10 13:29:43 +0200774int vnc_server_fb_stride(VncDisplay *vd)
775{
776 return pixman_image_get_stride(vd->server);
777}
778
779void *vnc_server_fb_ptr(VncDisplay *vd, int x, int y)
780{
781 uint8_t *ptr;
782
783 ptr = (uint8_t *)pixman_image_get_data(vd->server);
784 ptr += y * vnc_server_fb_stride(vd);
785 ptr += x * VNC_SERVER_FB_BYTES;
786 return ptr;
787}
788
Gerd Hoffmann453f8422015-10-30 12:10:07 +0100789static void vnc_update_server_surface(VncDisplay *vd)
790{
Daniel P. Berrangeb69a5532016-08-16 17:30:32 +0100791 int width, height;
792
Gerd Hoffmann453f8422015-10-30 12:10:07 +0100793 qemu_pixman_image_unref(vd->server);
794 vd->server = NULL;
795
Gerd Hoffmannc7628bf2015-10-30 12:10:09 +0100796 if (QTAILQ_EMPTY(&vd->clients)) {
797 return;
798 }
799
Daniel P. Berrangeb69a5532016-08-16 17:30:32 +0100800 width = vnc_width(vd);
801 height = vnc_height(vd);
Daniel P. Berrangé69cc8db2021-03-11 18:29:57 +0000802 vd->true_width = vnc_true_width(vd);
Gerd Hoffmann453f8422015-10-30 12:10:07 +0100803 vd->server = pixman_image_create_bits(VNC_SERVER_FB_FORMAT,
Daniel P. Berrangeb69a5532016-08-16 17:30:32 +0100804 width, height,
Gerd Hoffmann453f8422015-10-30 12:10:07 +0100805 NULL, 0);
Daniel P. Berrangeb69a5532016-08-16 17:30:32 +0100806
807 memset(vd->guest.dirty, 0x00, sizeof(vd->guest.dirty));
808 vnc_set_area_dirty(vd->guest.dirty, vd, 0, 0,
809 width, height);
Gerd Hoffmann453f8422015-10-30 12:10:07 +0100810}
811
Gerd Hoffmann61e77a52019-01-16 11:10:49 +0100812static bool vnc_check_pageflip(DisplaySurface *s1,
813 DisplaySurface *s2)
814{
815 return (s1 != NULL &&
816 s2 != NULL &&
817 surface_width(s1) == surface_width(s2) &&
818 surface_height(s1) == surface_height(s2) &&
819 surface_format(s1) == surface_format(s2));
820
821}
822
Gerd Hoffmannc12aeb82013-02-28 15:03:04 +0100823static void vnc_dpy_switch(DisplayChangeListener *dcl,
Gerd Hoffmannc12aeb82013-02-28 15:03:04 +0100824 DisplaySurface *surface)
aliguori753b4052009-02-16 14:59:30 +0000825{
Gerd Hoffmann21ef45d2013-02-28 11:34:31 +0100826 VncDisplay *vd = container_of(dcl, VncDisplay, dcl);
Gerd Hoffmann61e77a52019-01-16 11:10:49 +0100827 bool pageflip = vnc_check_pageflip(vd->ds, surface);
Amit Shah41b4bef2010-02-05 16:34:05 +0530828 VncState *vs;
Stefano Stabellini1fc62412009-08-03 10:54:32 +0100829
Corentin Charybd023f92010-07-07 20:58:02 +0200830 vnc_abort_display_jobs(vd);
Gerd Hoffmann453f8422015-10-30 12:10:07 +0100831 vd->ds = surface;
Corentin Charybd023f92010-07-07 20:58:02 +0200832
Stefano Stabellini1fc62412009-08-03 10:54:32 +0100833 /* guest surface */
Gerd Hoffmann9f649162012-10-10 13:29:43 +0200834 qemu_pixman_image_unref(vd->guest.fb);
Gerd Hoffmannd39fa6d2013-02-28 17:16:48 +0100835 vd->guest.fb = pixman_image_ref(surface->image);
Marc-André Lureauff174c62023-08-30 13:38:21 +0400836 vd->guest.format = surface_format(surface);
Stefano Stabellini1fc62412009-08-03 10:54:32 +0100837
Daniel P. Berrangé69cc8db2021-03-11 18:29:57 +0000838
Gerd Hoffmann61e77a52019-01-16 11:10:49 +0100839 if (pageflip) {
Daniel P. Berrangé69cc8db2021-03-11 18:29:57 +0000840 trace_vnc_server_dpy_pageflip(vd,
841 surface_width(surface),
842 surface_height(surface),
843 surface_format(surface));
Gerd Hoffmann61e77a52019-01-16 11:10:49 +0100844 vnc_set_area_dirty(vd->guest.dirty, vd, 0, 0,
845 surface_width(surface),
846 surface_height(surface));
847 return;
848 }
849
Daniel P. Berrangé69cc8db2021-03-11 18:29:57 +0000850 trace_vnc_server_dpy_recreate(vd,
851 surface_width(surface),
852 surface_height(surface),
853 surface_format(surface));
Gerd Hoffmann61e77a52019-01-16 11:10:49 +0100854 /* server surface */
855 vnc_update_server_surface(vd);
856
Amit Shah41b4bef2010-02-05 16:34:05 +0530857 QTAILQ_FOREACH(vs, &vd->clients, next) {
Stefano Stabellini1fc62412009-08-03 10:54:32 +0100858 vnc_colordepth(vs);
Gerd Hoffmann1d4b6382010-05-25 18:25:20 +0200859 vnc_desktop_resize(vs);
Gerd Hoffmannb3c2de92021-01-12 14:41:18 +0100860 vnc_cursor_define(vs);
Peter Lievenbea60dd2014-06-30 10:57:51 +0200861 memset(vs->dirty, 0x00, sizeof(vs->dirty));
Gerd Hoffmannf7b3d682015-10-30 12:10:08 +0100862 vnc_set_area_dirty(vs->dirty, vd, 0, 0,
Daniel P. Berrangeb69a5532016-08-16 17:30:32 +0100863 vnc_width(vd),
864 vnc_height(vd));
Daniel P. Berrangee2b72cb2017-12-18 19:12:24 +0000865 vnc_update_throttle_offset(vs);
aliguori753b4052009-02-16 14:59:30 +0000866 }
867}
868
bellard35127792006-05-14 18:11:49 +0000869/* fastest code */
Gerd Hoffmann9f649162012-10-10 13:29:43 +0200870static void vnc_write_pixels_copy(VncState *vs,
Gerd Hoffmannd467b672010-05-21 11:54:34 +0200871 void *pixels, int size)
bellard35127792006-05-14 18:11:49 +0000872{
873 vnc_write(vs, pixels, size);
874}
875
876/* slowest but generic code. */
Corentin Chary70a45682010-05-03 14:31:34 +0200877void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v)
bellard35127792006-05-14 18:11:49 +0000878{
aliguori7eac3a82008-09-15 16:03:41 +0000879 uint8_t r, g, b;
bellard35127792006-05-14 18:11:49 +0000880
Gerd Hoffmann9f649162012-10-10 13:29:43 +0200881#if VNC_SERVER_FB_FORMAT == PIXMAN_FORMAT(32, PIXMAN_TYPE_ARGB, 0, 8, 8, 8)
882 r = (((v & 0x00ff0000) >> 16) << vs->client_pf.rbits) >> 8;
883 g = (((v & 0x0000ff00) >> 8) << vs->client_pf.gbits) >> 8;
884 b = (((v & 0x000000ff) >> 0) << vs->client_pf.bbits) >> 8;
885#else
886# error need some bits here if you change VNC_SERVER_FB_FORMAT
887#endif
888 v = (r << vs->client_pf.rshift) |
889 (g << vs->client_pf.gshift) |
890 (b << vs->client_pf.bshift);
891 switch (vs->client_pf.bytes_per_pixel) {
bellard35127792006-05-14 18:11:49 +0000892 case 1:
893 buf[0] = v;
894 break;
895 case 2:
Gerd Hoffmann9f649162012-10-10 13:29:43 +0200896 if (vs->client_be) {
bellard35127792006-05-14 18:11:49 +0000897 buf[0] = v >> 8;
898 buf[1] = v;
899 } else {
900 buf[1] = v >> 8;
901 buf[0] = v;
902 }
903 break;
904 default:
905 case 4:
Gerd Hoffmann9f649162012-10-10 13:29:43 +0200906 if (vs->client_be) {
bellard35127792006-05-14 18:11:49 +0000907 buf[0] = v >> 24;
908 buf[1] = v >> 16;
909 buf[2] = v >> 8;
910 buf[3] = v;
911 } else {
912 buf[3] = v >> 24;
913 buf[2] = v >> 16;
914 buf[1] = v >> 8;
915 buf[0] = v;
916 }
917 break;
918 }
919}
920
Gerd Hoffmann9f649162012-10-10 13:29:43 +0200921static void vnc_write_pixels_generic(VncState *vs,
Gerd Hoffmannd467b672010-05-21 11:54:34 +0200922 void *pixels1, int size)
bellard35127792006-05-14 18:11:49 +0000923{
bellard35127792006-05-14 18:11:49 +0000924 uint8_t buf[4];
bellard35127792006-05-14 18:11:49 +0000925
Gerd Hoffmann9f649162012-10-10 13:29:43 +0200926 if (VNC_SERVER_FB_BYTES == 4) {
aliguori7eac3a82008-09-15 16:03:41 +0000927 uint32_t *pixels = pixels1;
928 int n, i;
929 n = size >> 2;
Gerd Hoffmann9f649162012-10-10 13:29:43 +0200930 for (i = 0; i < n; i++) {
aliguori7eac3a82008-09-15 16:03:41 +0000931 vnc_convert_pixel(vs, buf, pixels[i]);
Gerd Hoffmann9f649162012-10-10 13:29:43 +0200932 vnc_write(vs, buf, vs->client_pf.bytes_per_pixel);
aliguori7eac3a82008-09-15 16:03:41 +0000933 }
bellard35127792006-05-14 18:11:49 +0000934 }
935}
936
Corentin Charya8852112010-05-19 09:24:09 +0200937int vnc_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
bellard24236862006-04-30 21:28:36 +0000938{
939 int i;
ths60fe76f2007-12-16 03:02:09 +0000940 uint8_t *row;
Stefano Stabellini1fc62412009-08-03 10:54:32 +0100941 VncDisplay *vd = vs->vd;
bellard24236862006-04-30 21:28:36 +0000942
Gerd Hoffmann9f649162012-10-10 13:29:43 +0200943 row = vnc_server_fb_ptr(vd, x, y);
bellard24236862006-04-30 21:28:36 +0000944 for (i = 0; i < h; i++) {
Gerd Hoffmann9f649162012-10-10 13:29:43 +0200945 vs->write_pixels(vs, row, w * VNC_SERVER_FB_BYTES);
946 row += vnc_server_fb_stride(vd);
bellard24236862006-04-30 21:28:36 +0000947 }
Corentin Charya8852112010-05-19 09:24:09 +0200948 return 1;
bellard24236862006-04-30 21:28:36 +0000949}
950
Corentin Charybd023f92010-07-07 20:58:02 +0200951int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
bellard24236862006-04-30 21:28:36 +0000952{
Corentin Charya8852112010-05-19 09:24:09 +0200953 int n = 0;
954
aliguorifb437312009-02-02 15:58:43 +0000955 switch(vs->vnc_encoding) {
aliguori28a76be2009-03-06 20:27:40 +0000956 case VNC_ENCODING_ZLIB:
Corentin Charya8852112010-05-19 09:24:09 +0200957 n = vnc_zlib_send_framebuffer_update(vs, x, y, w, h);
aliguori28a76be2009-03-06 20:27:40 +0000958 break;
959 case VNC_ENCODING_HEXTILE:
960 vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_HEXTILE);
Corentin Charya8852112010-05-19 09:24:09 +0200961 n = vnc_hextile_send_framebuffer_update(vs, x, y, w, h);
aliguori28a76be2009-03-06 20:27:40 +0000962 break;
Corentin Chary380282b2010-05-19 09:24:10 +0200963 case VNC_ENCODING_TIGHT:
964 n = vnc_tight_send_framebuffer_update(vs, x, y, w, h);
965 break;
Corentin Charyefe556a2010-07-07 20:57:56 +0200966 case VNC_ENCODING_TIGHT_PNG:
967 n = vnc_tight_png_send_framebuffer_update(vs, x, y, w, h);
968 break;
Corentin Chary148954f2011-02-04 09:06:01 +0100969 case VNC_ENCODING_ZRLE:
970 n = vnc_zrle_send_framebuffer_update(vs, x, y, w, h);
971 break;
972 case VNC_ENCODING_ZYWRLE:
973 n = vnc_zywrle_send_framebuffer_update(vs, x, y, w, h);
974 break;
aliguori28a76be2009-03-06 20:27:40 +0000975 default:
Gerd Hoffmann0780ec72020-01-21 07:02:10 +0100976 vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_RAW);
977 n = vnc_raw_send_framebuffer_update(vs, x, y, w, h);
aliguori28a76be2009-03-06 20:27:40 +0000978 break;
aliguorifb437312009-02-02 15:58:43 +0000979 }
Corentin Charya8852112010-05-19 09:24:09 +0200980 return n;
bellard24236862006-04-30 21:28:36 +0000981}
982
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +0100983static void vnc_mouse_set(DisplayChangeListener *dcl,
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +0100984 int x, int y, int visible)
Gerd Hoffmannd467b672010-05-21 11:54:34 +0200985{
986 /* can we ask the client(s) to move the pointer ??? */
987}
988
989static int vnc_cursor_define(VncState *vs)
990{
Marc-André Lureau385ac972023-01-17 15:24:40 +0400991 QEMUCursor *c = qemu_console_get_cursor(vs->vd->dcl.con);
Gerd Hoffmannd467b672010-05-21 11:54:34 +0200992 int isize;
993
Marc-André Lureau385ac972023-01-17 15:24:40 +0400994 if (!c) {
Gerd Hoffmannb3c2de92021-01-12 14:41:18 +0100995 return -1;
996 }
997
Gerd Hoffmann074a86d2020-12-08 12:57:34 +0100998 if (vnc_has_feature(vs, VNC_FEATURE_ALPHA_CURSOR)) {
999 vnc_lock_output(vs);
1000 vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
1001 vnc_write_u8(vs, 0); /* padding */
1002 vnc_write_u16(vs, 1); /* # of rects */
1003 vnc_framebuffer_update(vs, c->hot_x, c->hot_y, c->width, c->height,
1004 VNC_ENCODING_ALPHA_CURSOR);
1005 vnc_write_s32(vs, VNC_ENCODING_RAW);
1006 vnc_write(vs, c->data, c->width * c->height * 4);
1007 vnc_unlock_output(vs);
1008 return 0;
1009 }
Gerd Hoffmannd467b672010-05-21 11:54:34 +02001010 if (vnc_has_feature(vs, VNC_FEATURE_RICH_CURSOR)) {
Corentin Charyd01f9592010-07-07 20:58:03 +02001011 vnc_lock_output(vs);
Gerd Hoffmannd467b672010-05-21 11:54:34 +02001012 vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
1013 vnc_write_u8(vs, 0); /* padding */
1014 vnc_write_u16(vs, 1); /* # of rects */
1015 vnc_framebuffer_update(vs, c->hot_x, c->hot_y, c->width, c->height,
1016 VNC_ENCODING_RICH_CURSOR);
Gerd Hoffmann9f649162012-10-10 13:29:43 +02001017 isize = c->width * c->height * vs->client_pf.bytes_per_pixel;
1018 vnc_write_pixels_generic(vs, c->data, isize);
Gerd Hoffmannd467b672010-05-21 11:54:34 +02001019 vnc_write(vs, vs->vd->cursor_mask, vs->vd->cursor_msize);
Corentin Charyd01f9592010-07-07 20:58:03 +02001020 vnc_unlock_output(vs);
Gerd Hoffmannd467b672010-05-21 11:54:34 +02001021 return 0;
1022 }
1023 return -1;
1024}
1025
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001026static void vnc_dpy_cursor_define(DisplayChangeListener *dcl,
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001027 QEMUCursor *c)
Gerd Hoffmannd467b672010-05-21 11:54:34 +02001028{
Gerd Hoffmannd616ccc2014-07-29 12:14:08 +02001029 VncDisplay *vd = container_of(dcl, VncDisplay, dcl);
Gerd Hoffmannd467b672010-05-21 11:54:34 +02001030 VncState *vs;
1031
Anthony Liguori7267c092011-08-20 22:09:37 -05001032 g_free(vd->cursor_mask);
Gerd Hoffmannd467b672010-05-21 11:54:34 +02001033 vd->cursor_msize = cursor_get_mono_bpl(c) * c->height;
Anthony Liguori7267c092011-08-20 22:09:37 -05001034 vd->cursor_mask = g_malloc0(vd->cursor_msize);
Gerd Hoffmannd467b672010-05-21 11:54:34 +02001035 cursor_get_mono_mask(c, 0, vd->cursor_mask);
1036
1037 QTAILQ_FOREACH(vs, &vd->clients, next) {
1038 vnc_cursor_define(vs);
1039 }
1040}
1041
Chih-Min Chao4769a882015-04-09 02:04:12 +08001042static int find_and_clear_dirty_height(VncState *vs,
Corentin Chary6c71a532011-02-04 09:06:06 +01001043 int y, int last_x, int x, int height)
bellard24236862006-04-30 21:28:36 +00001044{
1045 int h;
1046
Corentin Chary6c71a532011-02-04 09:06:06 +01001047 for (h = 1; h < (height - y); h++) {
Corentin Charybc2429b2011-02-04 09:06:05 +01001048 if (!test_bit(last_x, vs->dirty[y + h])) {
aliguori28a76be2009-03-06 20:27:40 +00001049 break;
Corentin Charybc2429b2011-02-04 09:06:05 +01001050 }
Peter Lieven863d7c92014-01-08 10:08:36 +01001051 bitmap_clear(vs->dirty[y + h], last_x, x - last_x);
bellard24236862006-04-30 21:28:36 +00001052 }
1053
1054 return h;
1055}
1056
Daniel P. Berrangee2b72cb2017-12-18 19:12:24 +00001057/*
1058 * Figure out how much pending data we should allow in the output
1059 * buffer before we throttle incremental display updates, and/or
1060 * drop audio samples.
1061 *
1062 * We allow for equiv of 1 full display's worth of FB updates,
1063 * and 1 second of audio samples. If audio backlog was larger
1064 * than that the client would already suffering awful audio
1065 * glitches, so dropping samples is no worse really).
1066 */
1067static void vnc_update_throttle_offset(VncState *vs)
1068{
1069 size_t offset =
1070 vs->client_width * vs->client_height * vs->client_pf.bytes_per_pixel;
1071
1072 if (vs->audio_cap) {
Daniel P. Berrangee2b72cb2017-12-18 19:12:24 +00001073 int bps;
Daniel P. Berrangee2b72cb2017-12-18 19:12:24 +00001074 switch (vs->as.fmt) {
1075 default:
Kővágó, Zoltán85bc5852019-03-08 23:34:13 +01001076 case AUDIO_FORMAT_U8:
1077 case AUDIO_FORMAT_S8:
Daniel P. Berrangee2b72cb2017-12-18 19:12:24 +00001078 bps = 1;
1079 break;
Kővágó, Zoltán85bc5852019-03-08 23:34:13 +01001080 case AUDIO_FORMAT_U16:
1081 case AUDIO_FORMAT_S16:
Daniel P. Berrangee2b72cb2017-12-18 19:12:24 +00001082 bps = 2;
1083 break;
Kővágó, Zoltán85bc5852019-03-08 23:34:13 +01001084 case AUDIO_FORMAT_U32:
1085 case AUDIO_FORMAT_S32:
Daniel P. Berrangee2b72cb2017-12-18 19:12:24 +00001086 bps = 4;
1087 break;
1088 }
Daniel P. Berrangécf070652018-02-05 11:49:37 +00001089 offset += vs->as.freq * bps * vs->as.nchannels;
Daniel P. Berrangee2b72cb2017-12-18 19:12:24 +00001090 }
1091
1092 /* Put a floor of 1MB on offset, so that if we have a large pending
1093 * buffer and the display is resized to a small size & back again
1094 * we don't suddenly apply a tiny send limit
1095 */
1096 offset = MAX(offset, 1024 * 1024);
1097
Daniel P. Berrange6aa22a22017-12-18 19:12:27 +00001098 if (vs->throttle_output_offset != offset) {
1099 trace_vnc_client_throttle_threshold(
1100 vs, vs->ioc, vs->throttle_output_offset, offset, vs->client_width,
1101 vs->client_height, vs->client_pf.bytes_per_pixel, vs->audio_cap);
1102 }
1103
Daniel P. Berrangee2b72cb2017-12-18 19:12:24 +00001104 vs->throttle_output_offset = offset;
1105}
1106
Daniel P. Berrange0bad8342017-12-18 19:12:23 +00001107static bool vnc_should_update(VncState *vs)
1108{
1109 switch (vs->update) {
1110 case VNC_STATE_UPDATE_NONE:
1111 break;
1112 case VNC_STATE_UPDATE_INCREMENTAL:
Daniel P. Berrangee2b72cb2017-12-18 19:12:24 +00001113 /* Only allow incremental updates if the pending send queue
Daniel P. Berrangeada8d2e2017-12-18 19:12:25 +00001114 * is less than the permitted threshold, and the job worker
1115 * is completely idle.
Daniel P. Berrange0bad8342017-12-18 19:12:23 +00001116 */
Daniel P. Berrangeada8d2e2017-12-18 19:12:25 +00001117 if (vs->output.offset < vs->throttle_output_offset &&
1118 vs->job_update == VNC_STATE_UPDATE_NONE) {
Daniel P. Berrange0bad8342017-12-18 19:12:23 +00001119 return true;
1120 }
Daniel P. Berrange6aa22a22017-12-18 19:12:27 +00001121 trace_vnc_client_throttle_incremental(
1122 vs, vs->ioc, vs->job_update, vs->output.offset);
Daniel P. Berrange0bad8342017-12-18 19:12:23 +00001123 break;
1124 case VNC_STATE_UPDATE_FORCE:
Daniel P. Berrangeada8d2e2017-12-18 19:12:25 +00001125 /* Only allow forced updates if the pending send queue
1126 * does not contain a previous forced update, and the
1127 * job worker is completely idle.
1128 *
1129 * Note this means we'll queue a forced update, even if
1130 * the output buffer size is otherwise over the throttle
1131 * output limit.
1132 */
1133 if (vs->force_update_offset == 0 &&
1134 vs->job_update == VNC_STATE_UPDATE_NONE) {
1135 return true;
1136 }
Daniel P. Berrange6aa22a22017-12-18 19:12:27 +00001137 trace_vnc_client_throttle_forced(
1138 vs, vs->ioc, vs->job_update, vs->force_update_offset);
Daniel P. Berrangeada8d2e2017-12-18 19:12:25 +00001139 break;
Daniel P. Berrange0bad8342017-12-18 19:12:23 +00001140 }
1141 return false;
1142}
1143
Daniel P. Berrange6af998d2017-12-18 19:12:16 +00001144static int vnc_update_client(VncState *vs, int has_dirty)
bellard24236862006-04-30 21:28:36 +00001145{
Daniel P. Berrangeb939eb82017-12-18 19:12:18 +00001146 VncDisplay *vd = vs->vd;
1147 VncJob *job;
1148 int y;
1149 int height, width;
1150 int n = 0;
1151
Gerd Hoffmann5a8be0f2016-07-13 12:21:20 +02001152 if (vs->disconnecting) {
1153 vnc_disconnect_finish(vs);
1154 return 0;
1155 }
1156
Gerd Hoffmann63658282014-07-23 11:52:02 +02001157 vs->has_dirty += has_dirty;
Daniel P. Berrange0bad8342017-12-18 19:12:23 +00001158 if (!vnc_should_update(vs)) {
Daniel P. Berrangeb939eb82017-12-18 19:12:18 +00001159 return 0;
1160 }
1161
Daniel P. Berrangefef1bba2017-12-18 19:12:21 +00001162 if (!vs->has_dirty && vs->update != VNC_STATE_UPDATE_FORCE) {
Daniel P. Berrangeb939eb82017-12-18 19:12:18 +00001163 return 0;
1164 }
1165
1166 /*
1167 * Send screen updates to the vnc client using the server
1168 * surface and server dirty map. guest surface updates
1169 * happening in parallel don't disturb us, the next pass will
1170 * send them to the client.
1171 */
1172 job = vnc_job_new(vs);
1173
1174 height = pixman_image_get_height(vd->server);
1175 width = pixman_image_get_width(vd->server);
1176
1177 y = 0;
1178 for (;;) {
1179 int x, h;
1180 unsigned long x2;
1181 unsigned long offset = find_next_bit((unsigned long *) &vs->dirty,
1182 height * VNC_DIRTY_BPL(vs),
1183 y * VNC_DIRTY_BPL(vs));
1184 if (offset == height * VNC_DIRTY_BPL(vs)) {
1185 /* no more dirty bits */
1186 break;
1187 }
1188 y = offset / VNC_DIRTY_BPL(vs);
1189 x = offset % VNC_DIRTY_BPL(vs);
1190 x2 = find_next_zero_bit((unsigned long *) &vs->dirty[y],
1191 VNC_DIRTY_BPL(vs), x);
1192 bitmap_clear(vs->dirty[y], x, x2 - x);
1193 h = find_and_clear_dirty_height(vs, y, x, x2, height);
1194 x2 = MIN(x2, width / VNC_DIRTY_PIXELS_PER_BIT);
1195 if (x2 > x) {
1196 n += vnc_job_add_rect(job, x * VNC_DIRTY_PIXELS_PER_BIT, y,
1197 (x2 - x) * VNC_DIRTY_PIXELS_PER_BIT, h);
1198 }
1199 if (!x && x2 == width / VNC_DIRTY_PIXELS_PER_BIT) {
1200 y += h;
1201 if (y == height) {
1202 break;
1203 }
1204 }
1205 }
1206
Daniel P. Berrangeada8d2e2017-12-18 19:12:25 +00001207 vs->job_update = vs->update;
Daniel P. Berrange728a7ac2017-12-18 19:12:22 +00001208 vs->update = VNC_STATE_UPDATE_NONE;
Daniel P. Berrangeada8d2e2017-12-18 19:12:25 +00001209 vnc_job_push(job);
Daniel P. Berrangeb939eb82017-12-18 19:12:18 +00001210 vs->has_dirty = 0;
1211 return n;
bellard24236862006-04-30 21:28:36 +00001212}
1213
malc429a8ed2008-12-01 20:57:48 +00001214/* audio */
1215static void audio_capture_notify(void *opaque, audcnotification_e cmd)
1216{
1217 VncState *vs = opaque;
1218
Gerd Hoffmannf31f9c12018-05-07 12:22:54 +02001219 assert(vs->magic == VNC_MAGIC);
malc429a8ed2008-12-01 20:57:48 +00001220 switch (cmd) {
1221 case AUD_CNOTIFY_DISABLE:
Daniel P. Berrangéadc8fce2021-03-11 18:29:54 +00001222 trace_vnc_msg_server_audio_end(vs, vs->ioc);
Corentin Charybd023f92010-07-07 20:58:02 +02001223 vnc_lock_output(vs);
Daniel P. Berrange46a183d2010-03-31 18:20:43 +01001224 vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
1225 vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
1226 vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_END);
Corentin Charybd023f92010-07-07 20:58:02 +02001227 vnc_unlock_output(vs);
malc429a8ed2008-12-01 20:57:48 +00001228 vnc_flush(vs);
1229 break;
1230
1231 case AUD_CNOTIFY_ENABLE:
Daniel P. Berrangéadc8fce2021-03-11 18:29:54 +00001232 trace_vnc_msg_server_audio_begin(vs, vs->ioc);
Corentin Charybd023f92010-07-07 20:58:02 +02001233 vnc_lock_output(vs);
Daniel P. Berrange46a183d2010-03-31 18:20:43 +01001234 vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
1235 vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
1236 vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_BEGIN);
Corentin Charybd023f92010-07-07 20:58:02 +02001237 vnc_unlock_output(vs);
malc429a8ed2008-12-01 20:57:48 +00001238 vnc_flush(vs);
1239 break;
1240 }
1241}
1242
1243static void audio_capture_destroy(void *opaque)
1244{
1245}
1246
Philippe Mathieu-Daudé57a878e2020-05-05 15:25:58 +02001247static void audio_capture(void *opaque, const void *buf, int size)
malc429a8ed2008-12-01 20:57:48 +00001248{
1249 VncState *vs = opaque;
1250
Gerd Hoffmannf31f9c12018-05-07 12:22:54 +02001251 assert(vs->magic == VNC_MAGIC);
Daniel P. Berrangéadc8fce2021-03-11 18:29:54 +00001252 trace_vnc_msg_server_audio_data(vs, vs->ioc, buf, size);
Corentin Charybd023f92010-07-07 20:58:02 +02001253 vnc_lock_output(vs);
Daniel P. Berrangee2b72cb2017-12-18 19:12:24 +00001254 if (vs->output.offset < vs->throttle_output_offset) {
1255 vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
1256 vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
1257 vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_DATA);
1258 vnc_write_u32(vs, size);
1259 vnc_write(vs, buf, size);
Daniel P. Berrange6aa22a22017-12-18 19:12:27 +00001260 } else {
1261 trace_vnc_client_throttle_audio(vs, vs->ioc, vs->output.offset);
Daniel P. Berrangee2b72cb2017-12-18 19:12:24 +00001262 }
Corentin Charybd023f92010-07-07 20:58:02 +02001263 vnc_unlock_output(vs);
malc429a8ed2008-12-01 20:57:48 +00001264 vnc_flush(vs);
1265}
1266
1267static void audio_add(VncState *vs)
1268{
1269 struct audio_capture_ops ops;
1270
1271 if (vs->audio_cap) {
Cole Robinson027a79c2014-03-21 19:42:21 -04001272 error_report("audio already running");
malc429a8ed2008-12-01 20:57:48 +00001273 return;
1274 }
1275
1276 ops.notify = audio_capture_notify;
1277 ops.destroy = audio_capture_destroy;
1278 ops.capture = audio_capture;
1279
Kővágó, Zoltánf0b9f362019-08-19 01:06:48 +02001280 vs->audio_cap = AUD_add_capture(vs->vd->audio_state, &vs->as, &ops, vs);
malc429a8ed2008-12-01 20:57:48 +00001281 if (!vs->audio_cap) {
Cole Robinson027a79c2014-03-21 19:42:21 -04001282 error_report("Failed to add audio capture");
malc429a8ed2008-12-01 20:57:48 +00001283 }
1284}
1285
1286static void audio_del(VncState *vs)
1287{
1288 if (vs->audio_cap) {
1289 AUD_del_capture(vs->audio_cap, vs);
1290 vs->audio_cap = NULL;
1291 }
1292}
1293
Gerd Hoffmann198a0032009-06-16 14:19:48 +02001294static void vnc_disconnect_start(VncState *vs)
1295{
Daniel P. Berrange04d25292015-02-27 16:20:57 +00001296 if (vs->disconnecting) {
Gerd Hoffmann198a0032009-06-16 14:19:48 +02001297 return;
Daniel P. Berrange04d25292015-02-27 16:20:57 +00001298 }
Daniel P. Berrangead6374c2017-09-21 13:15:27 +01001299 trace_vnc_client_disconnect_start(vs, vs->ioc);
Gerd Hoffmann8cf36482011-11-24 18:10:49 +01001300 vnc_set_share_mode(vs, VNC_SHARE_MODE_DISCONNECTED);
Daniel P. Berrange04d25292015-02-27 16:20:57 +00001301 if (vs->ioc_tag) {
1302 g_source_remove(vs->ioc_tag);
Brandon Carpentera75d6f02017-09-12 08:21:47 -07001303 vs->ioc_tag = 0;
Daniel P. Berrange04d25292015-02-27 16:20:57 +00001304 }
1305 qio_channel_close(vs->ioc, NULL);
1306 vs->disconnecting = TRUE;
Gerd Hoffmann198a0032009-06-16 14:19:48 +02001307}
1308
Tim Hardeck7536ee42013-01-21 11:04:44 +01001309void vnc_disconnect_finish(VncState *vs)
Gerd Hoffmann198a0032009-06-16 14:19:48 +02001310{
Corentin Chary7d964c92011-02-04 09:05:56 +01001311 int i;
1312
Daniel P. Berrangead6374c2017-09-21 13:15:27 +01001313 trace_vnc_client_disconnect_finish(vs, vs->ioc);
1314
Corentin Charybd023f92010-07-07 20:58:02 +02001315 vnc_jobs_join(vs); /* Wait encoding jobs */
1316
1317 vnc_lock_output(vs);
Wenchao Xiafb6ba0d2014-06-18 08:43:49 +02001318 vnc_qmp_event(vs, QAPI_EVENT_VNC_DISCONNECTED);
Luiz Capitulino0d72f3d2010-01-14 14:50:58 -02001319
Corentin Chary5d418e32010-05-19 09:24:07 +02001320 buffer_free(&vs->input);
1321 buffer_free(&vs->output);
Luiz Capitulino4a80dba2010-01-14 14:50:56 -02001322
Wenchao Xiafb6ba0d2014-06-18 08:43:49 +02001323 qapi_free_VncClientInfo(vs->info);
Luiz Capitulino4a80dba2010-01-14 14:50:56 -02001324
Corentin Chary161c4f22010-05-19 09:24:08 +02001325 vnc_zlib_clear(vs);
Corentin Chary380282b2010-05-19 09:24:10 +02001326 vnc_tight_clear(vs);
Corentin Chary148954f2011-02-04 09:06:01 +01001327 vnc_zrle_clear(vs);
Corentin Chary161c4f22010-05-19 09:24:08 +02001328
Gerd Hoffmann198a0032009-06-16 14:19:48 +02001329#ifdef CONFIG_VNC_SASL
1330 vnc_sasl_client_cleanup(vs);
1331#endif /* CONFIG_VNC_SASL */
1332 audio_del(vs);
Gerd Hoffmannc2f2ba42019-01-22 10:28:12 +01001333 qkbd_state_lift_all_keys(vs->vd->kbd);
Gerd Hoffmann198a0032009-06-16 14:19:48 +02001334
Daniel P. Berrange90cd03a2016-09-29 16:45:39 +01001335 if (vs->mouse_mode_notifier.notify != NULL) {
Tim Hardeck6fd8e792013-01-21 11:04:45 +01001336 qemu_remove_mouse_mode_change_notifier(&vs->mouse_mode_notifier);
Daniel P. Berrange90cd03a2016-09-29 16:45:39 +01001337 }
1338 QTAILQ_REMOVE(&vs->vd->clients, vs, next);
1339 if (QTAILQ_EMPTY(&vs->vd->clients)) {
1340 /* last client gone */
1341 vnc_update_server_surface(vs->vd);
Tim Hardeck6fd8e792013-01-21 11:04:45 +01001342 }
Rao Lei1dbbe6f2022-01-05 10:08:08 +08001343 vnc_unlock_output(vs);
1344
Marc-André Lureau1b17f1e2021-07-19 19:42:15 +04001345 if (vs->cbpeer.notifier.notify) {
Gerd Hoffmann0bf41ca2021-05-19 07:39:38 +02001346 qemu_clipboard_peer_unregister(&vs->cbpeer);
1347 }
Amit Shah41b4bef2010-02-05 16:34:05 +05301348
Corentin Charybd023f92010-07-07 20:58:02 +02001349 qemu_mutex_destroy(&vs->output_mutex);
Tim Hardeck6fd8e792013-01-21 11:04:45 +01001350 if (vs->bh != NULL) {
1351 qemu_bh_delete(vs->bh);
1352 }
Corentin Chary175b2a62012-03-14 07:58:47 +01001353 buffer_free(&vs->jobs_buffer);
Corentin Chary175b2a62012-03-14 07:58:47 +01001354
Corentin Chary7d964c92011-02-04 09:05:56 +01001355 for (i = 0; i < VNC_STAT_ROWS; ++i) {
Anthony Liguori7267c092011-08-20 22:09:37 -05001356 g_free(vs->lossy_rect[i]);
Corentin Chary7d964c92011-02-04 09:05:56 +01001357 }
Anthony Liguori7267c092011-08-20 22:09:37 -05001358 g_free(vs->lossy_rect);
Daniel P. Berrange04d25292015-02-27 16:20:57 +00001359
1360 object_unref(OBJECT(vs->ioc));
1361 vs->ioc = NULL;
1362 object_unref(OBJECT(vs->sioc));
1363 vs->sioc = NULL;
Gerd Hoffmannf31f9c12018-05-07 12:22:54 +02001364 vs->magic = 0;
Li Qiang6bf21f32019-08-31 08:39:22 -07001365 g_free(vs->zrle);
1366 g_free(vs->tight);
Anthony Liguori7267c092011-08-20 22:09:37 -05001367 g_free(vs);
Gerd Hoffmann198a0032009-06-16 14:19:48 +02001368}
aliguori2f9606b2009-03-06 20:27:28 +00001369
Vladimir Sementsov-Ogievskiy34ab29c2019-12-05 20:46:19 +03001370size_t vnc_client_io_error(VncState *vs, ssize_t ret, Error *err)
bellard24236862006-04-30 21:28:36 +00001371{
Daniel P. Berrange04d25292015-02-27 16:20:57 +00001372 if (ret <= 0) {
1373 if (ret == 0) {
Daniel P. Berrangead6374c2017-09-21 13:15:27 +01001374 trace_vnc_client_eof(vs, vs->ioc);
Michael Tokarev537848e2017-02-03 12:52:29 +03001375 vnc_disconnect_start(vs);
Daniel P. Berrange04d25292015-02-27 16:20:57 +00001376 } else if (ret != QIO_CHANNEL_ERR_BLOCK) {
Daniel P. Berrangead6374c2017-09-21 13:15:27 +01001377 trace_vnc_client_io_error(vs, vs->ioc,
Vladimir Sementsov-Ogievskiy34ab29c2019-12-05 20:46:19 +03001378 err ? error_get_pretty(err) : "Unknown");
Michael Tokarev537848e2017-02-03 12:52:29 +03001379 vnc_disconnect_start(vs);
balrogea01e5f2008-04-24 23:40:55 +00001380 }
bellard24236862006-04-30 21:28:36 +00001381
Vladimir Sementsov-Ogievskiy34ab29c2019-12-05 20:46:19 +03001382 error_free(err);
aliguori28a76be2009-03-06 20:27:40 +00001383 return 0;
bellard24236862006-04-30 21:28:36 +00001384 }
1385 return ret;
1386}
1387
aliguori5fb6c7a2009-03-06 20:27:23 +00001388
1389void vnc_client_error(VncState *vs)
bellard24236862006-04-30 21:28:36 +00001390{
Gerd Hoffmann198a0032009-06-16 14:19:48 +02001391 VNC_DEBUG("Closing down client sock: protocol error\n");
1392 vnc_disconnect_start(vs);
bellard24236862006-04-30 21:28:36 +00001393}
1394
Daniel P. Berrange3e305e42015-08-06 14:39:32 +01001395
aliguori2f9606b2009-03-06 20:27:28 +00001396/*
1397 * Called to write a chunk of data to the client socket. The data may
1398 * be the raw data, or may have already been encoded by SASL.
1399 * The data will be written either straight onto the socket, or
1400 * written via the GNUTLS wrappers, if TLS/SSL encryption is enabled
1401 *
1402 * NB, it is theoretically possible to have 2 layers of encryption,
1403 * both SASL, and this TLS layer. It is highly unlikely in practice
1404 * though, since SASL encryption will typically be a no-op if TLS
1405 * is active
1406 *
1407 * Returns the number of bytes written, which may be less than
1408 * the requested 'datalen' if the socket would block. Returns
Daniel P. Berrange30b80fd2017-12-18 19:12:28 +00001409 * 0 on I/O error, and disconnects the client socket.
aliguori2f9606b2009-03-06 20:27:28 +00001410 */
Daniel P. Berrange30b80fd2017-12-18 19:12:28 +00001411size_t vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen)
bellard24236862006-04-30 21:28:36 +00001412{
Daniel P. Berrange04d25292015-02-27 16:20:57 +00001413 Error *err = NULL;
Daniel P. Berrangefdd1ab62015-08-06 15:35:55 +01001414 ssize_t ret;
Vladimir Sementsov-Ogievskiy34ab29c2019-12-05 20:46:19 +03001415 ret = qio_channel_write(vs->ioc, (const char *)data, datalen, &err);
aliguori23decc82009-03-20 15:59:18 +00001416 VNC_DEBUG("Wrote wire %p %zd -> %ld\n", data, datalen, ret);
Vladimir Sementsov-Ogievskiy34ab29c2019-12-05 20:46:19 +03001417 return vnc_client_io_error(vs, ret, err);
aliguori2f9606b2009-03-06 20:27:28 +00001418}
1419
1420
1421/*
1422 * Called to write buffered data to the client socket, when not
1423 * using any SASL SSF encryption layers. Will write as much data
1424 * as possible without blocking. If all buffered data is written,
1425 * will switch the FD poll() handler back to read monitoring.
1426 *
1427 * Returns the number of bytes written, which may be less than
Daniel P. Berrange30b80fd2017-12-18 19:12:28 +00001428 * the buffered output data if the socket would block. Returns
1429 * 0 on I/O error, and disconnects the client socket.
aliguori2f9606b2009-03-06 20:27:28 +00001430 */
Daniel P. Berrange30b80fd2017-12-18 19:12:28 +00001431static size_t vnc_client_write_plain(VncState *vs)
aliguori2f9606b2009-03-06 20:27:28 +00001432{
Daniel P. Berrange6aa22a22017-12-18 19:12:27 +00001433 size_t offset;
Daniel P. Berrange30b80fd2017-12-18 19:12:28 +00001434 size_t ret;
aliguori2f9606b2009-03-06 20:27:28 +00001435
1436#ifdef CONFIG_VNC_SASL
aliguori23decc82009-03-20 15:59:18 +00001437 VNC_DEBUG("Write Plain: Pending output %p size %zd offset %zd. Wait SSF %d\n",
aliguori2f9606b2009-03-06 20:27:28 +00001438 vs->output.buffer, vs->output.capacity, vs->output.offset,
1439 vs->sasl.waitWriteSSF);
1440
1441 if (vs->sasl.conn &&
1442 vs->sasl.runSSF &&
1443 vs->sasl.waitWriteSSF) {
1444 ret = vnc_client_write_buf(vs, vs->output.buffer, vs->sasl.waitWriteSSF);
1445 if (ret)
1446 vs->sasl.waitWriteSSF -= ret;
1447 } else
1448#endif /* CONFIG_VNC_SASL */
1449 ret = vnc_client_write_buf(vs, vs->output.buffer, vs->output.offset);
bellard24236862006-04-30 21:28:36 +00001450 if (!ret)
aliguori2f9606b2009-03-06 20:27:28 +00001451 return 0;
bellard24236862006-04-30 21:28:36 +00001452
Daniel P. Berrangeada8d2e2017-12-18 19:12:25 +00001453 if (ret >= vs->force_update_offset) {
Daniel P. Berrange6aa22a22017-12-18 19:12:27 +00001454 if (vs->force_update_offset != 0) {
1455 trace_vnc_client_unthrottle_forced(vs, vs->ioc);
1456 }
Daniel P. Berrangeada8d2e2017-12-18 19:12:25 +00001457 vs->force_update_offset = 0;
1458 } else {
1459 vs->force_update_offset -= ret;
1460 }
Daniel P. Berrange6aa22a22017-12-18 19:12:27 +00001461 offset = vs->output.offset;
Tim Hardeck32ed2682013-01-21 11:04:43 +01001462 buffer_advance(&vs->output, ret);
Daniel P. Berrange6aa22a22017-12-18 19:12:27 +00001463 if (offset >= vs->throttle_output_offset &&
1464 vs->output.offset < vs->throttle_output_offset) {
1465 trace_vnc_client_unthrottle_incremental(vs, vs->ioc, vs->output.offset);
1466 }
bellard24236862006-04-30 21:28:36 +00001467
1468 if (vs->output.offset == 0) {
Daniel P. Berrange04d25292015-02-27 16:20:57 +00001469 if (vs->ioc_tag) {
1470 g_source_remove(vs->ioc_tag);
1471 }
1472 vs->ioc_tag = qio_channel_add_watch(
Ding Hui2ddafce2020-10-29 11:22:41 +08001473 vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR,
1474 vnc_client_io, vs, NULL);
bellard24236862006-04-30 21:28:36 +00001475 }
aliguori2f9606b2009-03-06 20:27:28 +00001476
1477 return ret;
1478}
1479
1480
1481/*
1482 * First function called whenever there is data to be written to
1483 * the client socket. Will delegate actual work according to whether
1484 * SASL SSF layers are enabled (thus requiring encryption calls)
1485 */
Daniel P. Berrange04d25292015-02-27 16:20:57 +00001486static void vnc_client_write_locked(VncState *vs)
aliguori2f9606b2009-03-06 20:27:28 +00001487{
aliguori2f9606b2009-03-06 20:27:28 +00001488#ifdef CONFIG_VNC_SASL
1489 if (vs->sasl.conn &&
1490 vs->sasl.runSSF &&
Blue Swirl9678d952010-04-25 18:35:52 +00001491 !vs->sasl.waitWriteSSF) {
1492 vnc_client_write_sasl(vs);
1493 } else
aliguori2f9606b2009-03-06 20:27:28 +00001494#endif /* CONFIG_VNC_SASL */
Tim Hardeck7536ee42013-01-21 11:04:44 +01001495 {
Daniel P. Berranged5f04222015-03-11 15:53:49 +00001496 vnc_client_write_plain(vs);
Tim Hardeck7536ee42013-01-21 11:04:44 +01001497 }
bellard24236862006-04-30 21:28:36 +00001498}
1499
Daniel P. Berrange04d25292015-02-27 16:20:57 +00001500static void vnc_client_write(VncState *vs)
Corentin Charybd023f92010-07-07 20:58:02 +02001501{
Gerd Hoffmannf31f9c12018-05-07 12:22:54 +02001502 assert(vs->magic == VNC_MAGIC);
Corentin Charybd023f92010-07-07 20:58:02 +02001503 vnc_lock_output(vs);
Daniel P. Berranged5f04222015-03-11 15:53:49 +00001504 if (vs->output.offset) {
Daniel P. Berrange04d25292015-02-27 16:20:57 +00001505 vnc_client_write_locked(vs);
1506 } else if (vs->ioc != NULL) {
1507 if (vs->ioc_tag) {
1508 g_source_remove(vs->ioc_tag);
1509 }
1510 vs->ioc_tag = qio_channel_add_watch(
Ding Hui2ddafce2020-10-29 11:22:41 +08001511 vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR,
1512 vnc_client_io, vs, NULL);
Corentin Charybd023f92010-07-07 20:58:02 +02001513 }
1514 vnc_unlock_output(vs);
1515}
1516
aliguori5fb6c7a2009-03-06 20:27:23 +00001517void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting)
bellard24236862006-04-30 21:28:36 +00001518{
1519 vs->read_handler = func;
1520 vs->read_handler_expect = expecting;
1521}
1522
aliguori2f9606b2009-03-06 20:27:28 +00001523
1524/*
1525 * Called to read a chunk of data from the client socket. The data may
1526 * be the raw data, or may need to be further decoded by SASL.
1527 * The data will be read either straight from to the socket, or
1528 * read via the GNUTLS wrappers, if TLS/SSL encryption is enabled
1529 *
1530 * NB, it is theoretically possible to have 2 layers of encryption,
1531 * both SASL, and this TLS layer. It is highly unlikely in practice
1532 * though, since SASL encryption will typically be a no-op if TLS
1533 * is active
1534 *
1535 * Returns the number of bytes read, which may be less than
1536 * the requested 'datalen' if the socket would block. Returns
Daniel P. Berrange30b80fd2017-12-18 19:12:28 +00001537 * 0 on I/O error or EOF, and disconnects the client socket.
aliguori2f9606b2009-03-06 20:27:28 +00001538 */
Daniel P. Berrange30b80fd2017-12-18 19:12:28 +00001539size_t vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen)
bellard24236862006-04-30 21:28:36 +00001540{
Daniel P. Berrangefdd1ab62015-08-06 15:35:55 +01001541 ssize_t ret;
Daniel P. Berrange04d25292015-02-27 16:20:57 +00001542 Error *err = NULL;
Vladimir Sementsov-Ogievskiy34ab29c2019-12-05 20:46:19 +03001543 ret = qio_channel_read(vs->ioc, (char *)data, datalen, &err);
aliguori23decc82009-03-20 15:59:18 +00001544 VNC_DEBUG("Read wire %p %zd -> %ld\n", data, datalen, ret);
Vladimir Sementsov-Ogievskiy34ab29c2019-12-05 20:46:19 +03001545 return vnc_client_io_error(vs, ret, err);
aliguori2f9606b2009-03-06 20:27:28 +00001546}
1547
1548
1549/*
1550 * Called to read data from the client socket to the input buffer,
1551 * when not using any SASL SSF encryption layers. Will read as much
1552 * data as possible without blocking.
1553 *
Daniel P. Berrange30b80fd2017-12-18 19:12:28 +00001554 * Returns the number of bytes read, which may be less than
1555 * the requested 'datalen' if the socket would block. Returns
1556 * 0 on I/O error or EOF, and disconnects the client socket.
aliguori2f9606b2009-03-06 20:27:28 +00001557 */
Daniel P. Berrange30b80fd2017-12-18 19:12:28 +00001558static size_t vnc_client_read_plain(VncState *vs)
aliguori2f9606b2009-03-06 20:27:28 +00001559{
Daniel P. Berrange30b80fd2017-12-18 19:12:28 +00001560 size_t ret;
aliguori23decc82009-03-20 15:59:18 +00001561 VNC_DEBUG("Read plain %p size %zd offset %zd\n",
aliguori2f9606b2009-03-06 20:27:28 +00001562 vs->input.buffer, vs->input.capacity, vs->input.offset);
1563 buffer_reserve(&vs->input, 4096);
1564 ret = vnc_client_read_buf(vs, buffer_end(&vs->input), 4096);
1565 if (!ret)
1566 return 0;
1567 vs->input.offset += ret;
1568 return ret;
1569}
1570
Corentin Chary175b2a62012-03-14 07:58:47 +01001571static void vnc_jobs_bh(void *opaque)
1572{
1573 VncState *vs = opaque;
1574
Gerd Hoffmannf31f9c12018-05-07 12:22:54 +02001575 assert(vs->magic == VNC_MAGIC);
Corentin Chary175b2a62012-03-14 07:58:47 +01001576 vnc_jobs_consume_buffer(vs);
1577}
aliguori2f9606b2009-03-06 20:27:28 +00001578
1579/*
1580 * First function called whenever there is more data to be read from
1581 * the client socket. Will delegate actual work according to whether
1582 * SASL SSF layers are enabled (thus requiring decryption calls)
Daniel P. Berrangeea697442016-06-27 16:48:49 +01001583 * Returns 0 on success, -1 if client disconnected
aliguori2f9606b2009-03-06 20:27:28 +00001584 */
Daniel P. Berrangeea697442016-06-27 16:48:49 +01001585static int vnc_client_read(VncState *vs)
aliguori2f9606b2009-03-06 20:27:28 +00001586{
Markus Armbrustere33e66b2023-09-21 14:13:08 +02001587 size_t sz;
aliguori2f9606b2009-03-06 20:27:28 +00001588
1589#ifdef CONFIG_VNC_SASL
1590 if (vs->sasl.conn && vs->sasl.runSSF)
Markus Armbrustere33e66b2023-09-21 14:13:08 +02001591 sz = vnc_client_read_sasl(vs);
aliguori2f9606b2009-03-06 20:27:28 +00001592 else
1593#endif /* CONFIG_VNC_SASL */
Markus Armbrustere33e66b2023-09-21 14:13:08 +02001594 sz = vnc_client_read_plain(vs);
1595 if (!sz) {
Daniel P. Berrange04d25292015-02-27 16:20:57 +00001596 if (vs->disconnecting) {
Gerd Hoffmann198a0032009-06-16 14:19:48 +02001597 vnc_disconnect_finish(vs);
Daniel P. Berrangeea697442016-06-27 16:48:49 +01001598 return -1;
Daniel P. Berrange04d25292015-02-27 16:20:57 +00001599 }
Daniel P. Berrangeea697442016-06-27 16:48:49 +01001600 return 0;
Gerd Hoffmann198a0032009-06-16 14:19:48 +02001601 }
bellard24236862006-04-30 21:28:36 +00001602
bellard24236862006-04-30 21:28:36 +00001603 while (vs->read_handler && vs->input.offset >= vs->read_handler_expect) {
aliguori28a76be2009-03-06 20:27:40 +00001604 size_t len = vs->read_handler_expect;
1605 int ret;
bellard24236862006-04-30 21:28:36 +00001606
aliguori28a76be2009-03-06 20:27:40 +00001607 ret = vs->read_handler(vs, vs->input.buffer, len);
Daniel P. Berrange04d25292015-02-27 16:20:57 +00001608 if (vs->disconnecting) {
Gerd Hoffmann198a0032009-06-16 14:19:48 +02001609 vnc_disconnect_finish(vs);
Daniel P. Berrangeea697442016-06-27 16:48:49 +01001610 return -1;
Gerd Hoffmann198a0032009-06-16 14:19:48 +02001611 }
bellard24236862006-04-30 21:28:36 +00001612
aliguori28a76be2009-03-06 20:27:40 +00001613 if (!ret) {
Tim Hardeck32ed2682013-01-21 11:04:43 +01001614 buffer_advance(&vs->input, len);
aliguori28a76be2009-03-06 20:27:40 +00001615 } else {
1616 vs->read_handler_expect = ret;
1617 }
bellard24236862006-04-30 21:28:36 +00001618 }
Daniel P. Berrangeea697442016-06-27 16:48:49 +01001619 return 0;
bellard24236862006-04-30 21:28:36 +00001620}
1621
Daniel P. Berrange04d25292015-02-27 16:20:57 +00001622gboolean vnc_client_io(QIOChannel *ioc G_GNUC_UNUSED,
1623 GIOCondition condition, void *opaque)
1624{
1625 VncState *vs = opaque;
Gerd Hoffmannf31f9c12018-05-07 12:22:54 +02001626
1627 assert(vs->magic == VNC_MAGIC);
Ding Hui2ddafce2020-10-29 11:22:41 +08001628
1629 if (condition & (G_IO_HUP | G_IO_ERR)) {
1630 vnc_disconnect_start(vs);
1631 return TRUE;
1632 }
1633
Daniel P. Berrange04d25292015-02-27 16:20:57 +00001634 if (condition & G_IO_IN) {
Daniel P. Berrangeea697442016-06-27 16:48:49 +01001635 if (vnc_client_read(vs) < 0) {
Gerd Hoffmann1bc31172018-04-20 10:48:19 +02001636 /* vs is free()ed here */
1637 return TRUE;
Daniel P. Berrangeea697442016-06-27 16:48:49 +01001638 }
Daniel P. Berrange04d25292015-02-27 16:20:57 +00001639 }
1640 if (condition & G_IO_OUT) {
1641 vnc_client_write(vs);
1642 }
Gerd Hoffmann1bc31172018-04-20 10:48:19 +02001643
Klim Kireevd49b87f2018-02-07 12:48:44 +03001644 if (vs->disconnecting) {
1645 if (vs->ioc_tag != 0) {
1646 g_source_remove(vs->ioc_tag);
1647 }
1648 vs->ioc_tag = 0;
1649 }
Daniel P. Berrange04d25292015-02-27 16:20:57 +00001650 return TRUE;
1651}
1652
1653
Daniel P. Berrangef887cf12017-12-18 19:12:26 +00001654/*
1655 * Scale factor to apply to vs->throttle_output_offset when checking for
1656 * hard limit. Worst case normal usage could be x2, if we have a complete
1657 * incremental update and complete forced update in the output buffer.
1658 * So x3 should be good enough, but we pick x5 to be conservative and thus
1659 * (hopefully) never trigger incorrectly.
1660 */
1661#define VNC_THROTTLE_OUTPUT_LIMIT_SCALE 5
1662
aliguori5fb6c7a2009-03-06 20:27:23 +00001663void vnc_write(VncState *vs, const void *data, size_t len)
bellard24236862006-04-30 21:28:36 +00001664{
Gerd Hoffmannf31f9c12018-05-07 12:22:54 +02001665 assert(vs->magic == VNC_MAGIC);
Daniel P. Berrangef887cf12017-12-18 19:12:26 +00001666 if (vs->disconnecting) {
1667 return;
1668 }
1669 /* Protection against malicious client/guest to prevent our output
1670 * buffer growing without bound if client stops reading data. This
1671 * should rarely trigger, because we have earlier throttling code
1672 * which stops issuing framebuffer updates and drops audio data
1673 * if the throttle_output_offset value is exceeded. So we only reach
1674 * this higher level if a huge number of pseudo-encodings get
1675 * triggered while data can't be sent on the socket.
1676 *
1677 * NB throttle_output_offset can be zero during early protocol
1678 * handshake, or from the job thread's VncState clone
1679 */
1680 if (vs->throttle_output_offset != 0 &&
Daniel P. Berrangédffa1de2018-02-05 11:49:35 +00001681 (vs->output.offset / VNC_THROTTLE_OUTPUT_LIMIT_SCALE) >
1682 vs->throttle_output_offset) {
Daniel P. Berrange6aa22a22017-12-18 19:12:27 +00001683 trace_vnc_client_output_limit(vs, vs->ioc, vs->output.offset,
1684 vs->throttle_output_offset);
Daniel P. Berrangef887cf12017-12-18 19:12:26 +00001685 vnc_disconnect_start(vs);
1686 return;
1687 }
bellard24236862006-04-30 21:28:36 +00001688 buffer_reserve(&vs->output, len);
1689
Daniel P. Berrange04d25292015-02-27 16:20:57 +00001690 if (vs->ioc != NULL && buffer_empty(&vs->output)) {
1691 if (vs->ioc_tag) {
1692 g_source_remove(vs->ioc_tag);
1693 }
1694 vs->ioc_tag = qio_channel_add_watch(
Ding Hui2ddafce2020-10-29 11:22:41 +08001695 vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_OUT,
1696 vnc_client_io, vs, NULL);
bellard24236862006-04-30 21:28:36 +00001697 }
1698
1699 buffer_append(&vs->output, data, len);
1700}
1701
aliguori5fb6c7a2009-03-06 20:27:23 +00001702void vnc_write_s32(VncState *vs, int32_t value)
bellard24236862006-04-30 21:28:36 +00001703{
1704 vnc_write_u32(vs, *(uint32_t *)&value);
1705}
1706
aliguori5fb6c7a2009-03-06 20:27:23 +00001707void vnc_write_u32(VncState *vs, uint32_t value)
bellard24236862006-04-30 21:28:36 +00001708{
1709 uint8_t buf[4];
1710
1711 buf[0] = (value >> 24) & 0xFF;
1712 buf[1] = (value >> 16) & 0xFF;
1713 buf[2] = (value >> 8) & 0xFF;
1714 buf[3] = value & 0xFF;
1715
1716 vnc_write(vs, buf, 4);
1717}
1718
aliguori5fb6c7a2009-03-06 20:27:23 +00001719void vnc_write_u16(VncState *vs, uint16_t value)
bellard24236862006-04-30 21:28:36 +00001720{
bellard64f5a132006-08-24 20:36:44 +00001721 uint8_t buf[2];
bellard24236862006-04-30 21:28:36 +00001722
1723 buf[0] = (value >> 8) & 0xFF;
1724 buf[1] = value & 0xFF;
1725
1726 vnc_write(vs, buf, 2);
1727}
1728
aliguori5fb6c7a2009-03-06 20:27:23 +00001729void vnc_write_u8(VncState *vs, uint8_t value)
bellard24236862006-04-30 21:28:36 +00001730{
1731 vnc_write(vs, (char *)&value, 1);
1732}
1733
aliguori5fb6c7a2009-03-06 20:27:23 +00001734void vnc_flush(VncState *vs)
bellard24236862006-04-30 21:28:36 +00001735{
Corentin Charybd023f92010-07-07 20:58:02 +02001736 vnc_lock_output(vs);
Daniel P. Berranged5f04222015-03-11 15:53:49 +00001737 if (vs->ioc != NULL && vs->output.offset) {
Corentin Charybd023f92010-07-07 20:58:02 +02001738 vnc_client_write_locked(vs);
1739 }
Klim Kireevd49b87f2018-02-07 12:48:44 +03001740 if (vs->disconnecting) {
1741 if (vs->ioc_tag != 0) {
1742 g_source_remove(vs->ioc_tag);
1743 }
1744 vs->ioc_tag = 0;
1745 }
Corentin Charybd023f92010-07-07 20:58:02 +02001746 vnc_unlock_output(vs);
bellard24236862006-04-30 21:28:36 +00001747}
1748
Blue Swirl71a8cde2012-10-28 11:04:48 +00001749static uint8_t read_u8(uint8_t *data, size_t offset)
bellard24236862006-04-30 21:28:36 +00001750{
1751 return data[offset];
1752}
1753
Blue Swirl71a8cde2012-10-28 11:04:48 +00001754static uint16_t read_u16(uint8_t *data, size_t offset)
bellard24236862006-04-30 21:28:36 +00001755{
1756 return ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF);
1757}
1758
Blue Swirl71a8cde2012-10-28 11:04:48 +00001759static int32_t read_s32(uint8_t *data, size_t offset)
bellard24236862006-04-30 21:28:36 +00001760{
1761 return (int32_t)((data[offset] << 24) | (data[offset + 1] << 16) |
aliguori28a76be2009-03-06 20:27:40 +00001762 (data[offset + 2] << 8) | data[offset + 3]);
bellard24236862006-04-30 21:28:36 +00001763}
1764
aliguori5fb6c7a2009-03-06 20:27:23 +00001765uint32_t read_u32(uint8_t *data, size_t offset)
bellard24236862006-04-30 21:28:36 +00001766{
1767 return ((data[offset] << 24) | (data[offset + 1] << 16) |
aliguori28a76be2009-03-06 20:27:40 +00001768 (data[offset + 2] << 8) | data[offset + 3]);
bellard24236862006-04-30 21:28:36 +00001769}
1770
Jan Kiszka9e8dd452011-06-20 14:06:26 +02001771static void check_pointer_type_change(Notifier *notifier, void *data)
bellard564c3372007-02-05 20:14:10 +00001772{
Anthony Liguori37c34d92010-03-10 09:38:29 -06001773 VncState *vs = container_of(notifier, VncState, mouse_mode_notifier);
Gerd Hoffmann14768eb2013-12-02 15:17:45 +01001774 int absolute = qemu_input_is_absolute();
Anthony Liguori37c34d92010-03-10 09:38:29 -06001775
aliguori29fa4ed2009-02-02 15:58:29 +00001776 if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE) && vs->absolute != absolute) {
Corentin Charybd023f92010-07-07 20:58:02 +02001777 vnc_lock_output(vs);
Daniel P. Berrange46a183d2010-03-31 18:20:43 +01001778 vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
aliguori28a76be2009-03-06 20:27:40 +00001779 vnc_write_u8(vs, 0);
1780 vnc_write_u16(vs, 1);
1781 vnc_framebuffer_update(vs, absolute, 0,
Peter Lievenbea60dd2014-06-30 10:57:51 +02001782 pixman_image_get_width(vs->vd->server),
1783 pixman_image_get_height(vs->vd->server),
aliguori29fa4ed2009-02-02 15:58:29 +00001784 VNC_ENCODING_POINTER_TYPE_CHANGE);
Corentin Charybd023f92010-07-07 20:58:02 +02001785 vnc_unlock_output(vs);
aliguori28a76be2009-03-06 20:27:40 +00001786 vnc_flush(vs);
bellard564c3372007-02-05 20:14:10 +00001787 }
1788 vs->absolute = absolute;
1789}
1790
bellard24236862006-04-30 21:28:36 +00001791static void pointer_event(VncState *vs, int button_mask, int x, int y)
1792{
Eric Blake7fb1cf12015-11-18 01:52:57 -07001793 static uint32_t bmap[INPUT_BUTTON__MAX] = {
Gerd Hoffmann14768eb2013-12-02 15:17:45 +01001794 [INPUT_BUTTON_LEFT] = 0x01,
1795 [INPUT_BUTTON_MIDDLE] = 0x02,
1796 [INPUT_BUTTON_RIGHT] = 0x04,
Gerd Hoffmannf22d0af2016-01-12 12:14:12 +01001797 [INPUT_BUTTON_WHEEL_UP] = 0x08,
1798 [INPUT_BUTTON_WHEEL_DOWN] = 0x10,
Gerd Hoffmann14768eb2013-12-02 15:17:45 +01001799 };
1800 QemuConsole *con = vs->vd->dcl.con;
Peter Lievenbea60dd2014-06-30 10:57:51 +02001801 int width = pixman_image_get_width(vs->vd->server);
1802 int height = pixman_image_get_height(vs->vd->server);
bellard24236862006-04-30 21:28:36 +00001803
Gerd Hoffmann14768eb2013-12-02 15:17:45 +01001804 if (vs->last_bmask != button_mask) {
1805 qemu_input_update_buttons(con, bmap, vs->last_bmask, button_mask);
1806 vs->last_bmask = button_mask;
1807 }
bellard564c3372007-02-05 20:14:10 +00001808
1809 if (vs->absolute) {
Philippe Voinov9cfa7ab2017-05-05 15:39:52 +02001810 qemu_input_queue_abs(con, INPUT_AXIS_X, x, 0, width);
1811 qemu_input_queue_abs(con, INPUT_AXIS_Y, y, 0, height);
aliguori29fa4ed2009-02-02 15:58:29 +00001812 } else if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE)) {
Gerd Hoffmann14768eb2013-12-02 15:17:45 +01001813 qemu_input_queue_rel(con, INPUT_AXIS_X, x - 0x7FFF);
1814 qemu_input_queue_rel(con, INPUT_AXIS_Y, y - 0x7FFF);
bellard24236862006-04-30 21:28:36 +00001815 } else {
Gerd Hoffmann14768eb2013-12-02 15:17:45 +01001816 if (vs->last_x != -1) {
1817 qemu_input_queue_rel(con, INPUT_AXIS_X, x - vs->last_x);
1818 qemu_input_queue_rel(con, INPUT_AXIS_Y, y - vs->last_y);
1819 }
aliguori28a76be2009-03-06 20:27:40 +00001820 vs->last_x = x;
1821 vs->last_y = y;
bellard24236862006-04-30 21:28:36 +00001822 }
Gerd Hoffmann14768eb2013-12-02 15:17:45 +01001823 qemu_input_event_sync();
bellard24236862006-04-30 21:28:36 +00001824}
1825
Gerd Hoffmannc2f2ba42019-01-22 10:28:12 +01001826static void press_key(VncState *vs, QKeyCode qcode)
bellard64f5a132006-08-24 20:36:44 +00001827{
Gerd Hoffmannc2f2ba42019-01-22 10:28:12 +01001828 qkbd_state_key_event(vs->vd->kbd, qcode, true);
1829 qkbd_state_key_event(vs->vd->kbd, qcode, false);
balroga528b802007-10-30 22:38:53 +00001830}
1831
Lei Liab99e5c2013-04-25 13:29:10 +08001832static void vnc_led_state_change(VncState *vs)
1833{
Lei Liab99e5c2013-04-25 13:29:10 +08001834 if (!vnc_has_feature(vs, VNC_FEATURE_LED_STATE)) {
1835 return;
1836 }
1837
Lei Liab99e5c2013-04-25 13:29:10 +08001838 vnc_lock_output(vs);
1839 vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
1840 vnc_write_u8(vs, 0);
1841 vnc_write_u16(vs, 1);
1842 vnc_framebuffer_update(vs, 0, 0, 1, 1, VNC_ENCODING_LED_STATE);
Pierre Ossmana54f0d22017-01-09 17:14:02 +01001843 vnc_write_u8(vs, vs->vd->ledstate);
Lei Liab99e5c2013-04-25 13:29:10 +08001844 vnc_unlock_output(vs);
1845 vnc_flush(vs);
1846}
1847
Gerd Hoffmann7ffb82c2010-02-26 17:17:39 +01001848static void kbd_leds(void *opaque, int ledstate)
1849{
Pierre Ossmana54f0d22017-01-09 17:14:02 +01001850 VncDisplay *vd = opaque;
1851 VncState *client;
Gerd Hoffmann7ffb82c2010-02-26 17:17:39 +01001852
Gerd Hoffmann40066172014-05-21 13:18:20 +02001853 trace_vnc_key_guest_leds((ledstate & QEMU_CAPS_LOCK_LED),
1854 (ledstate & QEMU_NUM_LOCK_LED),
1855 (ledstate & QEMU_SCROLL_LOCK_LED));
1856
Pierre Ossmana54f0d22017-01-09 17:14:02 +01001857 if (ledstate == vd->ledstate) {
1858 return;
Lei Li96f3d172013-04-25 13:29:09 +08001859 }
Lei Liab99e5c2013-04-25 13:29:10 +08001860
Pierre Ossmana54f0d22017-01-09 17:14:02 +01001861 vd->ledstate = ledstate;
1862
1863 QTAILQ_FOREACH(client, &vd->clients, next) {
1864 vnc_led_state_change(client);
Lei Liab99e5c2013-04-25 13:29:10 +08001865 }
Gerd Hoffmann7ffb82c2010-02-26 17:17:39 +01001866}
1867
aliguori9ca313a2008-08-23 23:27:37 +00001868static void do_key_event(VncState *vs, int down, int keycode, int sym)
bellard24236862006-04-30 21:28:36 +00001869{
Gerd Hoffmannc2f2ba42019-01-22 10:28:12 +01001870 QKeyCode qcode = qemu_input_key_number_to_qcode(keycode);
1871
bellard64f5a132006-08-24 20:36:44 +00001872 /* QEMU console switch */
Gerd Hoffmannc2f2ba42019-01-22 10:28:12 +01001873 switch (qcode) {
1874 case Q_KEY_CODE_1 ... Q_KEY_CODE_9: /* '1' to '9' keys */
1875 if (vs->vd->dcl.con == NULL && down &&
1876 qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_CTRL) &&
1877 qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_ALT)) {
bellard64f5a132006-08-24 20:36:44 +00001878 /* Reset the modifiers sent to the current console */
Gerd Hoffmannc2f2ba42019-01-22 10:28:12 +01001879 qkbd_state_lift_all_keys(vs->vd->kbd);
1880 console_select(qcode - Q_KEY_CODE_1);
bellard64f5a132006-08-24 20:36:44 +00001881 return;
1882 }
Gerd Hoffmannc2f2ba42019-01-22 10:28:12 +01001883 default:
balroga528b802007-10-30 22:38:53 +00001884 break;
1885 }
1886
Lei Lie7b2aac2013-04-25 13:29:11 +08001887 /* Turn off the lock state sync logic if the client support the led
1888 state extension.
1889 */
Gerd Hoffmann98920882011-01-14 10:56:54 +01001890 if (down && vs->vd->lock_key_sync &&
Lei Lie7b2aac2013-04-25 13:29:11 +08001891 !vnc_has_feature(vs, VNC_FEATURE_LED_STATE) &&
Gerd Hoffmann3a0558b2010-03-10 17:12:02 +01001892 keycode_is_keypad(vs->vd->kbd_layout, keycode)) {
balroga528b802007-10-30 22:38:53 +00001893 /* If the numlock state needs to change then simulate an additional
1894 keypress before sending this one. This will happen if the user
1895 toggles numlock away from the VNC window.
1896 */
aliguori753b4052009-02-16 14:59:30 +00001897 if (keysym_is_numlock(vs->vd->kbd_layout, sym & 0xFFFF)) {
Gerd Hoffmannc2f2ba42019-01-22 10:28:12 +01001898 if (!qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_NUMLOCK)) {
Gerd Hoffmann40066172014-05-21 13:18:20 +02001899 trace_vnc_key_sync_numlock(true);
Gerd Hoffmannc2f2ba42019-01-22 10:28:12 +01001900 press_key(vs, Q_KEY_CODE_NUM_LOCK);
balroga528b802007-10-30 22:38:53 +00001901 }
1902 } else {
Gerd Hoffmannc2f2ba42019-01-22 10:28:12 +01001903 if (qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_NUMLOCK)) {
Gerd Hoffmann40066172014-05-21 13:18:20 +02001904 trace_vnc_key_sync_numlock(false);
Gerd Hoffmannc2f2ba42019-01-22 10:28:12 +01001905 press_key(vs, Q_KEY_CODE_NUM_LOCK);
balroga528b802007-10-30 22:38:53 +00001906 }
1907 }
bellard64f5a132006-08-24 20:36:44 +00001908 }
bellard24236862006-04-30 21:28:36 +00001909
Gerd Hoffmann98920882011-01-14 10:56:54 +01001910 if (down && vs->vd->lock_key_sync &&
Lei Lie7b2aac2013-04-25 13:29:11 +08001911 !vnc_has_feature(vs, VNC_FEATURE_LED_STATE) &&
Gerd Hoffmann3a0558b2010-03-10 17:12:02 +01001912 ((sym >= 'A' && sym <= 'Z') || (sym >= 'a' && sym <= 'z'))) {
Gerd Hoffmann6b132502009-11-02 12:47:06 +01001913 /* If the capslock state needs to change then simulate an additional
1914 keypress before sending this one. This will happen if the user
1915 toggles capslock away from the VNC window.
1916 */
1917 int uppercase = !!(sym >= 'A' && sym <= 'Z');
Gerd Hoffmannc2f2ba42019-01-22 10:28:12 +01001918 bool shift = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_SHIFT);
1919 bool capslock = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_CAPSLOCK);
Gerd Hoffmann6b132502009-11-02 12:47:06 +01001920 if (capslock) {
1921 if (uppercase == shift) {
Gerd Hoffmann40066172014-05-21 13:18:20 +02001922 trace_vnc_key_sync_capslock(false);
Gerd Hoffmannc2f2ba42019-01-22 10:28:12 +01001923 press_key(vs, Q_KEY_CODE_CAPS_LOCK);
Gerd Hoffmann6b132502009-11-02 12:47:06 +01001924 }
1925 } else {
1926 if (uppercase != shift) {
Gerd Hoffmann40066172014-05-21 13:18:20 +02001927 trace_vnc_key_sync_capslock(true);
Gerd Hoffmannc2f2ba42019-01-22 10:28:12 +01001928 press_key(vs, Q_KEY_CODE_CAPS_LOCK);
Gerd Hoffmann6b132502009-11-02 12:47:06 +01001929 }
1930 }
1931 }
1932
Gerd Hoffmannc2f2ba42019-01-22 10:28:12 +01001933 qkbd_state_key_event(vs->vd->kbd, qcode, down);
1934 if (!qemu_console_is_graphic(NULL)) {
1935 bool numlock = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_NUMLOCK);
1936 bool control = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_CTRL);
bellard64f5a132006-08-24 20:36:44 +00001937 /* QEMU console emulation */
1938 if (down) {
1939 switch (keycode) {
1940 case 0x2a: /* Left Shift */
1941 case 0x36: /* Right Shift */
1942 case 0x1d: /* Left CTRL */
1943 case 0x9d: /* Right CTRL */
1944 case 0x38: /* Left ALT */
1945 case 0xb8: /* Right ALT */
1946 break;
1947 case 0xc8:
Marc-André Lureaucc6ba2c2023-08-30 13:38:20 +04001948 qemu_text_console_put_keysym(NULL, QEMU_KEY_UP);
bellard64f5a132006-08-24 20:36:44 +00001949 break;
1950 case 0xd0:
Marc-André Lureaucc6ba2c2023-08-30 13:38:20 +04001951 qemu_text_console_put_keysym(NULL, QEMU_KEY_DOWN);
bellard64f5a132006-08-24 20:36:44 +00001952 break;
1953 case 0xcb:
Marc-André Lureaucc6ba2c2023-08-30 13:38:20 +04001954 qemu_text_console_put_keysym(NULL, QEMU_KEY_LEFT);
bellard64f5a132006-08-24 20:36:44 +00001955 break;
1956 case 0xcd:
Marc-André Lureaucc6ba2c2023-08-30 13:38:20 +04001957 qemu_text_console_put_keysym(NULL, QEMU_KEY_RIGHT);
bellard64f5a132006-08-24 20:36:44 +00001958 break;
1959 case 0xd3:
Marc-André Lureaucc6ba2c2023-08-30 13:38:20 +04001960 qemu_text_console_put_keysym(NULL, QEMU_KEY_DELETE);
bellard64f5a132006-08-24 20:36:44 +00001961 break;
1962 case 0xc7:
Marc-André Lureaucc6ba2c2023-08-30 13:38:20 +04001963 qemu_text_console_put_keysym(NULL, QEMU_KEY_HOME);
bellard64f5a132006-08-24 20:36:44 +00001964 break;
1965 case 0xcf:
Marc-André Lureaucc6ba2c2023-08-30 13:38:20 +04001966 qemu_text_console_put_keysym(NULL, QEMU_KEY_END);
bellard64f5a132006-08-24 20:36:44 +00001967 break;
1968 case 0xc9:
Marc-André Lureaucc6ba2c2023-08-30 13:38:20 +04001969 qemu_text_console_put_keysym(NULL, QEMU_KEY_PAGEUP);
bellard64f5a132006-08-24 20:36:44 +00001970 break;
1971 case 0xd1:
Marc-André Lureaucc6ba2c2023-08-30 13:38:20 +04001972 qemu_text_console_put_keysym(NULL, QEMU_KEY_PAGEDOWN);
bellard64f5a132006-08-24 20:36:44 +00001973 break;
Gerd Hoffmannbb0a18e2009-06-11 11:32:14 +02001974
1975 case 0x47:
Marc-André Lureaucc6ba2c2023-08-30 13:38:20 +04001976 qemu_text_console_put_keysym(NULL, numlock ? '7' : QEMU_KEY_HOME);
Gerd Hoffmannbb0a18e2009-06-11 11:32:14 +02001977 break;
1978 case 0x48:
Marc-André Lureaucc6ba2c2023-08-30 13:38:20 +04001979 qemu_text_console_put_keysym(NULL, numlock ? '8' : QEMU_KEY_UP);
Gerd Hoffmannbb0a18e2009-06-11 11:32:14 +02001980 break;
1981 case 0x49:
Marc-André Lureaucc6ba2c2023-08-30 13:38:20 +04001982 qemu_text_console_put_keysym(NULL, numlock ? '9' : QEMU_KEY_PAGEUP);
Gerd Hoffmannbb0a18e2009-06-11 11:32:14 +02001983 break;
1984 case 0x4b:
Marc-André Lureaucc6ba2c2023-08-30 13:38:20 +04001985 qemu_text_console_put_keysym(NULL, numlock ? '4' : QEMU_KEY_LEFT);
Gerd Hoffmannbb0a18e2009-06-11 11:32:14 +02001986 break;
1987 case 0x4c:
Marc-André Lureaucc6ba2c2023-08-30 13:38:20 +04001988 qemu_text_console_put_keysym(NULL, '5');
Gerd Hoffmannbb0a18e2009-06-11 11:32:14 +02001989 break;
1990 case 0x4d:
Marc-André Lureaucc6ba2c2023-08-30 13:38:20 +04001991 qemu_text_console_put_keysym(NULL, numlock ? '6' : QEMU_KEY_RIGHT);
Gerd Hoffmannbb0a18e2009-06-11 11:32:14 +02001992 break;
1993 case 0x4f:
Marc-André Lureaucc6ba2c2023-08-30 13:38:20 +04001994 qemu_text_console_put_keysym(NULL, numlock ? '1' : QEMU_KEY_END);
Gerd Hoffmannbb0a18e2009-06-11 11:32:14 +02001995 break;
1996 case 0x50:
Marc-André Lureaucc6ba2c2023-08-30 13:38:20 +04001997 qemu_text_console_put_keysym(NULL, numlock ? '2' : QEMU_KEY_DOWN);
Gerd Hoffmannbb0a18e2009-06-11 11:32:14 +02001998 break;
1999 case 0x51:
Marc-André Lureaucc6ba2c2023-08-30 13:38:20 +04002000 qemu_text_console_put_keysym(NULL, numlock ? '3' : QEMU_KEY_PAGEDOWN);
Gerd Hoffmannbb0a18e2009-06-11 11:32:14 +02002001 break;
2002 case 0x52:
Marc-André Lureaucc6ba2c2023-08-30 13:38:20 +04002003 qemu_text_console_put_keysym(NULL, '0');
Gerd Hoffmannbb0a18e2009-06-11 11:32:14 +02002004 break;
2005 case 0x53:
Marc-André Lureaucc6ba2c2023-08-30 13:38:20 +04002006 qemu_text_console_put_keysym(NULL, numlock ? '.' : QEMU_KEY_DELETE);
Gerd Hoffmannbb0a18e2009-06-11 11:32:14 +02002007 break;
2008
2009 case 0xb5:
Marc-André Lureaucc6ba2c2023-08-30 13:38:20 +04002010 qemu_text_console_put_keysym(NULL, '/');
Gerd Hoffmannbb0a18e2009-06-11 11:32:14 +02002011 break;
2012 case 0x37:
Marc-André Lureaucc6ba2c2023-08-30 13:38:20 +04002013 qemu_text_console_put_keysym(NULL, '*');
Gerd Hoffmannbb0a18e2009-06-11 11:32:14 +02002014 break;
2015 case 0x4a:
Marc-André Lureaucc6ba2c2023-08-30 13:38:20 +04002016 qemu_text_console_put_keysym(NULL, '-');
Gerd Hoffmannbb0a18e2009-06-11 11:32:14 +02002017 break;
2018 case 0x4e:
Marc-André Lureaucc6ba2c2023-08-30 13:38:20 +04002019 qemu_text_console_put_keysym(NULL, '+');
Gerd Hoffmannbb0a18e2009-06-11 11:32:14 +02002020 break;
2021 case 0x9c:
Marc-André Lureaucc6ba2c2023-08-30 13:38:20 +04002022 qemu_text_console_put_keysym(NULL, '\n');
Gerd Hoffmannbb0a18e2009-06-11 11:32:14 +02002023 break;
2024
bellard64f5a132006-08-24 20:36:44 +00002025 default:
Gerd Hoffmanne26437c2011-11-08 10:02:16 +01002026 if (control) {
Marc-André Lureaucc6ba2c2023-08-30 13:38:20 +04002027 qemu_text_console_put_keysym(NULL, sym & 0x1f);
Gerd Hoffmanne26437c2011-11-08 10:02:16 +01002028 } else {
Marc-André Lureaucc6ba2c2023-08-30 13:38:20 +04002029 qemu_text_console_put_keysym(NULL, sym);
Gerd Hoffmanne26437c2011-11-08 10:02:16 +01002030 }
bellard64f5a132006-08-24 20:36:44 +00002031 break;
2032 }
2033 }
2034 }
bellard24236862006-04-30 21:28:36 +00002035}
2036
Gerd Hoffmann40066172014-05-21 13:18:20 +02002037static const char *code2name(int keycode)
2038{
Markus Armbruster977c7362017-08-24 10:46:08 +02002039 return QKeyCode_str(qemu_input_key_number_to_qcode(keycode));
Gerd Hoffmann40066172014-05-21 13:18:20 +02002040}
2041
bellardbdbd7672006-05-01 21:44:22 +00002042static void key_event(VncState *vs, int down, uint32_t sym)
2043{
aliguori9ca313a2008-08-23 23:27:37 +00002044 int keycode;
Gerd Hoffmann4a93fe12009-12-11 11:25:07 +01002045 int lsym = sym;
aliguori9ca313a2008-08-23 23:27:37 +00002046
Gerd Hoffmann81c0d5a2013-03-14 14:27:08 +01002047 if (lsym >= 'A' && lsym <= 'Z' && qemu_console_is_graphic(NULL)) {
Gerd Hoffmann4a93fe12009-12-11 11:25:07 +01002048 lsym = lsym - 'A' + 'a';
2049 }
aliguori9ca313a2008-08-23 23:27:37 +00002050
Gerd Hoffmannabb4f2c2018-02-22 08:05:13 +01002051 keycode = keysym2scancode(vs->vd->kbd_layout, lsym & 0xFFFF,
Gerd Hoffmann19c1b9f2019-01-22 10:28:14 +01002052 vs->vd->kbd, down) & SCANCODE_KEYMASK;
Gerd Hoffmann40066172014-05-21 13:18:20 +02002053 trace_vnc_key_event_map(down, sym, keycode, code2name(keycode));
aliguori9ca313a2008-08-23 23:27:37 +00002054 do_key_event(vs, down, keycode, sym);
2055}
2056
2057static void ext_key_event(VncState *vs, int down,
2058 uint32_t sym, uint16_t keycode)
2059{
2060 /* if the user specifies a keyboard layout, always use it */
Gerd Hoffmann40066172014-05-21 13:18:20 +02002061 if (keyboard_layout) {
aliguori9ca313a2008-08-23 23:27:37 +00002062 key_event(vs, down, sym);
Gerd Hoffmann40066172014-05-21 13:18:20 +02002063 } else {
2064 trace_vnc_key_event_ext(down, sym, keycode, code2name(keycode));
aliguori9ca313a2008-08-23 23:27:37 +00002065 do_key_event(vs, down, keycode, sym);
Gerd Hoffmann40066172014-05-21 13:18:20 +02002066 }
bellardbdbd7672006-05-01 21:44:22 +00002067}
2068
bellard24236862006-04-30 21:28:36 +00002069static void framebuffer_update_request(VncState *vs, int incremental,
Peter Lievenbea60dd2014-06-30 10:57:51 +02002070 int x, int y, int w, int h)
bellard24236862006-04-30 21:28:36 +00002071{
Peter Lievenbea60dd2014-06-30 10:57:51 +02002072 if (incremental) {
Daniel P. Berrangefef1bba2017-12-18 19:12:21 +00002073 if (vs->update != VNC_STATE_UPDATE_FORCE) {
2074 vs->update = VNC_STATE_UPDATE_INCREMENTAL;
2075 }
2076 } else {
2077 vs->update = VNC_STATE_UPDATE_FORCE;
2078 vnc_set_area_dirty(vs->dirty, vs->vd, x, y, w, h);
Gerd Hoffmann104b8d12021-01-25 11:40:41 +01002079 if (vnc_has_feature(vs, VNC_FEATURE_RESIZE_EXT)) {
2080 vnc_desktop_resize_ext(vs, 0);
2081 }
bellard24236862006-04-30 21:28:36 +00002082 }
2083}
2084
aliguori9ca313a2008-08-23 23:27:37 +00002085static void send_ext_key_event_ack(VncState *vs)
2086{
Corentin Charybd023f92010-07-07 20:58:02 +02002087 vnc_lock_output(vs);
Daniel P. Berrange46a183d2010-03-31 18:20:43 +01002088 vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
aliguori9ca313a2008-08-23 23:27:37 +00002089 vnc_write_u8(vs, 0);
2090 vnc_write_u16(vs, 1);
Gerd Hoffmannd39fa6d2013-02-28 17:16:48 +01002091 vnc_framebuffer_update(vs, 0, 0,
Peter Lievenbea60dd2014-06-30 10:57:51 +02002092 pixman_image_get_width(vs->vd->server),
2093 pixman_image_get_height(vs->vd->server),
aliguori29fa4ed2009-02-02 15:58:29 +00002094 VNC_ENCODING_EXT_KEY_EVENT);
Corentin Charybd023f92010-07-07 20:58:02 +02002095 vnc_unlock_output(vs);
aliguori9ca313a2008-08-23 23:27:37 +00002096 vnc_flush(vs);
2097}
2098
malc429a8ed2008-12-01 20:57:48 +00002099static void send_ext_audio_ack(VncState *vs)
2100{
Corentin Charybd023f92010-07-07 20:58:02 +02002101 vnc_lock_output(vs);
Daniel P. Berrange46a183d2010-03-31 18:20:43 +01002102 vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
malc429a8ed2008-12-01 20:57:48 +00002103 vnc_write_u8(vs, 0);
2104 vnc_write_u16(vs, 1);
Gerd Hoffmannd39fa6d2013-02-28 17:16:48 +01002105 vnc_framebuffer_update(vs, 0, 0,
Peter Lievenbea60dd2014-06-30 10:57:51 +02002106 pixman_image_get_width(vs->vd->server),
2107 pixman_image_get_height(vs->vd->server),
aliguori29fa4ed2009-02-02 15:58:29 +00002108 VNC_ENCODING_AUDIO);
Corentin Charybd023f92010-07-07 20:58:02 +02002109 vnc_unlock_output(vs);
malc429a8ed2008-12-01 20:57:48 +00002110 vnc_flush(vs);
2111}
2112
Daniel P. Berrangé7b5fa0b2020-12-11 16:08:25 +00002113static void send_xvp_message(VncState *vs, int code)
2114{
2115 vnc_lock_output(vs);
2116 vnc_write_u8(vs, VNC_MSG_SERVER_XVP);
2117 vnc_write_u8(vs, 0); /* pad */
2118 vnc_write_u8(vs, 1); /* version */
2119 vnc_write_u8(vs, code);
2120 vnc_unlock_output(vs);
2121 vnc_flush(vs);
2122}
2123
bellard24236862006-04-30 21:28:36 +00002124static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
2125{
2126 int i;
aliguori29fa4ed2009-02-02 15:58:29 +00002127 unsigned int enc = 0;
bellard24236862006-04-30 21:28:36 +00002128
aliguori29fa4ed2009-02-02 15:58:29 +00002129 vs->features = 0;
Corentin Charya9f20d32010-05-19 09:24:01 +02002130 vs->vnc_encoding = 0;
Li Qiang6bf21f32019-08-31 08:39:22 -07002131 vs->tight->compression = 9;
2132 vs->tight->quality = -1; /* Lossless by default */
bellard564c3372007-02-05 20:14:10 +00002133 vs->absolute = -1;
bellard24236862006-04-30 21:28:36 +00002134
Corentin Chary8a0f0d02010-05-19 09:24:02 +02002135 /*
2136 * Start from the end because the encodings are sent in order of preference.
Dong Xu Wange5bed752011-11-22 18:06:24 +08002137 * This way the preferred encoding (first encoding defined in the array)
Corentin Chary8a0f0d02010-05-19 09:24:02 +02002138 * will be set at the end of the loop.
2139 */
bellard24236862006-04-30 21:28:36 +00002140 for (i = n_encodings - 1; i >= 0; i--) {
aliguori29fa4ed2009-02-02 15:58:29 +00002141 enc = encodings[i];
2142 switch (enc) {
2143 case VNC_ENCODING_RAW:
Corentin Charya9f20d32010-05-19 09:24:01 +02002144 vs->vnc_encoding = enc;
aliguori29fa4ed2009-02-02 15:58:29 +00002145 break;
aliguori29fa4ed2009-02-02 15:58:29 +00002146 case VNC_ENCODING_HEXTILE:
2147 vs->features |= VNC_FEATURE_HEXTILE_MASK;
Corentin Charya9f20d32010-05-19 09:24:01 +02002148 vs->vnc_encoding = enc;
aliguori29fa4ed2009-02-02 15:58:29 +00002149 break;
Corentin Chary380282b2010-05-19 09:24:10 +02002150 case VNC_ENCODING_TIGHT:
2151 vs->features |= VNC_FEATURE_TIGHT_MASK;
2152 vs->vnc_encoding = enc;
2153 break;
Kshitij Suri95f85102022-04-08 07:13:34 +00002154#ifdef CONFIG_PNG
Corentin Charyefe556a2010-07-07 20:57:56 +02002155 case VNC_ENCODING_TIGHT_PNG:
2156 vs->features |= VNC_FEATURE_TIGHT_PNG_MASK;
2157 vs->vnc_encoding = enc;
2158 break;
Joel Martinfe3e7f22012-05-16 12:54:25 +00002159#endif
aliguori059cef42009-02-02 15:58:54 +00002160 case VNC_ENCODING_ZLIB:
Cameron Esfahani557ba0e2020-01-20 21:00:52 -08002161 /*
2162 * VNC_ENCODING_ZRLE compresses better than VNC_ENCODING_ZLIB.
2163 * So prioritize ZRLE, even if the client hints that it prefers
2164 * ZLIB.
2165 */
2166 if ((vs->features & VNC_FEATURE_ZRLE_MASK) == 0) {
2167 vs->features |= VNC_FEATURE_ZLIB_MASK;
2168 vs->vnc_encoding = enc;
2169 }
aliguori059cef42009-02-02 15:58:54 +00002170 break;
Corentin Chary148954f2011-02-04 09:06:01 +01002171 case VNC_ENCODING_ZRLE:
2172 vs->features |= VNC_FEATURE_ZRLE_MASK;
2173 vs->vnc_encoding = enc;
2174 break;
2175 case VNC_ENCODING_ZYWRLE:
2176 vs->features |= VNC_FEATURE_ZYWRLE_MASK;
2177 vs->vnc_encoding = enc;
2178 break;
aliguori29fa4ed2009-02-02 15:58:29 +00002179 case VNC_ENCODING_DESKTOPRESIZE:
2180 vs->features |= VNC_FEATURE_RESIZE_MASK;
2181 break;
Gerd Hoffmann763deea2021-01-12 14:41:20 +01002182 case VNC_ENCODING_DESKTOP_RESIZE_EXT:
2183 vs->features |= VNC_FEATURE_RESIZE_EXT_MASK;
2184 break;
aliguori29fa4ed2009-02-02 15:58:29 +00002185 case VNC_ENCODING_POINTER_TYPE_CHANGE:
2186 vs->features |= VNC_FEATURE_POINTER_TYPE_CHANGE_MASK;
2187 break;
Gerd Hoffmannd467b672010-05-21 11:54:34 +02002188 case VNC_ENCODING_RICH_CURSOR:
2189 vs->features |= VNC_FEATURE_RICH_CURSOR_MASK;
Gerd Hoffmann074a86d2020-12-08 12:57:34 +01002190 break;
2191 case VNC_ENCODING_ALPHA_CURSOR:
2192 vs->features |= VNC_FEATURE_ALPHA_CURSOR_MASK;
Gerd Hoffmannd467b672010-05-21 11:54:34 +02002193 break;
aliguori29fa4ed2009-02-02 15:58:29 +00002194 case VNC_ENCODING_EXT_KEY_EVENT:
aliguori9ca313a2008-08-23 23:27:37 +00002195 send_ext_key_event_ack(vs);
2196 break;
aliguori29fa4ed2009-02-02 15:58:29 +00002197 case VNC_ENCODING_AUDIO:
Paolo Bonzini9e58d7a2023-09-25 13:08:27 +02002198 if (vs->vd->audio_state) {
2199 vs->features |= VNC_FEATURE_AUDIO_MASK;
2200 send_ext_audio_ack(vs);
2201 }
malc429a8ed2008-12-01 20:57:48 +00002202 break;
aliguori29fa4ed2009-02-02 15:58:29 +00002203 case VNC_ENCODING_WMVi:
2204 vs->features |= VNC_FEATURE_WMVI_MASK;
aliguorica4cca42008-09-15 16:05:16 +00002205 break;
Lei Liab99e5c2013-04-25 13:29:10 +08002206 case VNC_ENCODING_LED_STATE:
2207 vs->features |= VNC_FEATURE_LED_STATE_MASK;
2208 break;
Daniel P. Berrangé7b5fa0b2020-12-11 16:08:25 +00002209 case VNC_ENCODING_XVP:
2210 if (vs->vd->power_control) {
Paolo Bonzini477b3012023-09-25 13:05:58 +02002211 vs->features |= VNC_FEATURE_XVP_MASK;
Daniel P. Berrangé7b5fa0b2020-12-11 16:08:25 +00002212 send_xvp_message(vs, VNC_XVP_CODE_INIT);
2213 }
2214 break;
Gerd Hoffmann0bf41ca2021-05-19 07:39:38 +02002215 case VNC_ENCODING_CLIPBOARD_EXT:
2216 vs->features |= VNC_FEATURE_CLIPBOARD_EXT_MASK;
2217 vnc_server_cut_text_caps(vs);
2218 break;
aliguorifb437312009-02-02 15:58:43 +00002219 case VNC_ENCODING_COMPRESSLEVEL0 ... VNC_ENCODING_COMPRESSLEVEL0 + 9:
Li Qiang6bf21f32019-08-31 08:39:22 -07002220 vs->tight->compression = (enc & 0x0F);
aliguorifb437312009-02-02 15:58:43 +00002221 break;
2222 case VNC_ENCODING_QUALITYLEVEL0 ... VNC_ENCODING_QUALITYLEVEL0 + 9:
Corentin Charyb31f5192011-02-04 09:05:54 +01002223 if (vs->vd->lossy) {
Li Qiang6bf21f32019-08-31 08:39:22 -07002224 vs->tight->quality = (enc & 0x0F);
Corentin Charyb31f5192011-02-04 09:05:54 +01002225 }
aliguorifb437312009-02-02 15:58:43 +00002226 break;
aliguori29fa4ed2009-02-02 15:58:29 +00002227 default:
2228 VNC_DEBUG("Unknown encoding: %d (0x%.8x): %d\n", i, enc, enc);
2229 break;
2230 }
bellard24236862006-04-30 21:28:36 +00002231 }
Gerd Hoffmannd2397262021-01-25 11:40:40 +01002232 vnc_desktop_resize(vs);
Jan Kiszka9e8dd452011-06-20 14:06:26 +02002233 check_pointer_type_change(&vs->mouse_mode_notifier, NULL);
Gerd Hoffmannd2397262021-01-25 11:40:40 +01002234 vnc_led_state_change(vs);
2235 vnc_cursor_define(vs);
bellard24236862006-04-30 21:28:36 +00002236}
2237
aliguori6cec5482009-01-15 22:17:38 +00002238static void set_pixel_conversion(VncState *vs)
2239{
Gerd Hoffmann9f649162012-10-10 13:29:43 +02002240 pixman_format_code_t fmt = qemu_pixman_get_format(&vs->client_pf);
2241
2242 if (fmt == VNC_SERVER_FB_FORMAT) {
aliguori6cec5482009-01-15 22:17:38 +00002243 vs->write_pixels = vnc_write_pixels_copy;
Corentin Chary70a45682010-05-03 14:31:34 +02002244 vnc_hextile_set_pixel_conversion(vs, 0);
aliguori6cec5482009-01-15 22:17:38 +00002245 } else {
2246 vs->write_pixels = vnc_write_pixels_generic;
Corentin Chary70a45682010-05-03 14:31:34 +02002247 vnc_hextile_set_pixel_conversion(vs, 1);
aliguori6cec5482009-01-15 22:17:38 +00002248 }
2249}
2250
Alexander Graf0c426e42016-05-24 17:19:19 +03002251static void send_color_map(VncState *vs)
2252{
2253 int i;
2254
Peng Liang947191b2020-11-16 22:13:38 +08002255 vnc_lock_output(vs);
Alexander Graf0c426e42016-05-24 17:19:19 +03002256 vnc_write_u8(vs, VNC_MSG_SERVER_SET_COLOUR_MAP_ENTRIES);
2257 vnc_write_u8(vs, 0); /* padding */
2258 vnc_write_u16(vs, 0); /* first color */
2259 vnc_write_u16(vs, 256); /* # of colors */
2260
2261 for (i = 0; i < 256; i++) {
2262 PixelFormat *pf = &vs->client_pf;
2263
2264 vnc_write_u16(vs, (((i >> pf->rshift) & pf->rmax) << (16 - pf->rbits)));
2265 vnc_write_u16(vs, (((i >> pf->gshift) & pf->gmax) << (16 - pf->gbits)));
2266 vnc_write_u16(vs, (((i >> pf->bshift) & pf->bmax) << (16 - pf->bbits)));
2267 }
Peng Liang947191b2020-11-16 22:13:38 +08002268 vnc_unlock_output(vs);
Alexander Graf0c426e42016-05-24 17:19:19 +03002269}
2270
Gerd Hoffmannec9fb412016-06-06 11:18:45 +02002271static void set_pixel_format(VncState *vs, int bits_per_pixel,
aliguori28a76be2009-03-06 20:27:40 +00002272 int big_endian_flag, int true_color_flag,
2273 int red_max, int green_max, int blue_max,
2274 int red_shift, int green_shift, int blue_shift)
bellard24236862006-04-30 21:28:36 +00002275{
bellard35127792006-05-14 18:11:49 +00002276 if (!true_color_flag) {
Alexander Graf0c426e42016-05-24 17:19:19 +03002277 /* Expose a reasonable default 256 color map */
2278 bits_per_pixel = 8;
Alexander Graf0c426e42016-05-24 17:19:19 +03002279 red_max = 7;
2280 green_max = 7;
2281 blue_max = 3;
2282 red_shift = 0;
2283 green_shift = 3;
2284 blue_shift = 6;
bellard35127792006-05-14 18:11:49 +00002285 }
aliguori7eac3a82008-09-15 16:03:41 +00002286
Petr Matouseke6908bf2014-10-27 12:41:44 +01002287 switch (bits_per_pixel) {
2288 case 8:
2289 case 16:
2290 case 32:
2291 break;
2292 default:
2293 vnc_client_error(vs);
2294 return;
2295 }
2296
Prasad J Pandit4c65fed2015-12-03 18:54:17 +05302297 vs->client_pf.rmax = red_max ? red_max : 0xFF;
Cédric Le Goater7c9209e2017-03-13 15:33:25 +01002298 vs->client_pf.rbits = ctpopl(red_max);
Gerd Hoffmann9f649162012-10-10 13:29:43 +02002299 vs->client_pf.rshift = red_shift;
2300 vs->client_pf.rmask = red_max << red_shift;
Prasad J Pandit4c65fed2015-12-03 18:54:17 +05302301 vs->client_pf.gmax = green_max ? green_max : 0xFF;
Cédric Le Goater7c9209e2017-03-13 15:33:25 +01002302 vs->client_pf.gbits = ctpopl(green_max);
Gerd Hoffmann9f649162012-10-10 13:29:43 +02002303 vs->client_pf.gshift = green_shift;
2304 vs->client_pf.gmask = green_max << green_shift;
Prasad J Pandit4c65fed2015-12-03 18:54:17 +05302305 vs->client_pf.bmax = blue_max ? blue_max : 0xFF;
Cédric Le Goater7c9209e2017-03-13 15:33:25 +01002306 vs->client_pf.bbits = ctpopl(blue_max);
Gerd Hoffmann9f649162012-10-10 13:29:43 +02002307 vs->client_pf.bshift = blue_shift;
2308 vs->client_pf.bmask = blue_max << blue_shift;
2309 vs->client_pf.bits_per_pixel = bits_per_pixel;
2310 vs->client_pf.bytes_per_pixel = bits_per_pixel / 8;
2311 vs->client_pf.depth = bits_per_pixel == 32 ? 24 : bits_per_pixel;
2312 vs->client_be = big_endian_flag;
bellard24236862006-04-30 21:28:36 +00002313
Alexander Graf0c426e42016-05-24 17:19:19 +03002314 if (!true_color_flag) {
2315 send_color_map(vs);
2316 }
2317
aliguori6cec5482009-01-15 22:17:38 +00002318 set_pixel_conversion(vs);
bellard24236862006-04-30 21:28:36 +00002319
Gerd Hoffmann1d0d59f2014-09-18 12:54:49 +02002320 graphic_hw_invalidate(vs->vd->dcl.con);
2321 graphic_hw_update(vs->vd->dcl.con);
bellard24236862006-04-30 21:28:36 +00002322}
2323
aliguorica4cca42008-09-15 16:05:16 +00002324static void pixel_format_message (VncState *vs) {
2325 char pad[3] = { 0, 0, 0 };
2326
Gerd Hoffmann9f649162012-10-10 13:29:43 +02002327 vs->client_pf = qemu_default_pixelformat(32);
2328
2329 vnc_write_u8(vs, vs->client_pf.bits_per_pixel); /* bits-per-pixel */
2330 vnc_write_u8(vs, vs->client_pf.depth); /* depth */
aliguorica4cca42008-09-15 16:05:16 +00002331
Marc-André Lureaue03b5682022-03-23 19:57:17 +04002332#if HOST_BIG_ENDIAN
aliguorica4cca42008-09-15 16:05:16 +00002333 vnc_write_u8(vs, 1); /* big-endian-flag */
2334#else
2335 vnc_write_u8(vs, 0); /* big-endian-flag */
2336#endif
2337 vnc_write_u8(vs, 1); /* true-color-flag */
Gerd Hoffmann9f649162012-10-10 13:29:43 +02002338 vnc_write_u16(vs, vs->client_pf.rmax); /* red-max */
2339 vnc_write_u16(vs, vs->client_pf.gmax); /* green-max */
2340 vnc_write_u16(vs, vs->client_pf.bmax); /* blue-max */
2341 vnc_write_u8(vs, vs->client_pf.rshift); /* red-shift */
2342 vnc_write_u8(vs, vs->client_pf.gshift); /* green-shift */
2343 vnc_write_u8(vs, vs->client_pf.bshift); /* blue-shift */
2344 vnc_write(vs, pad, 3); /* padding */
Corentin Chary70a45682010-05-03 14:31:34 +02002345
2346 vnc_hextile_set_pixel_conversion(vs, 0);
aliguorica4cca42008-09-15 16:05:16 +00002347 vs->write_pixels = vnc_write_pixels_copy;
aliguorica4cca42008-09-15 16:05:16 +00002348}
2349
aliguori753b4052009-02-16 14:59:30 +00002350static void vnc_colordepth(VncState *vs)
aliguori7eac3a82008-09-15 16:03:41 +00002351{
aliguori753b4052009-02-16 14:59:30 +00002352 if (vnc_has_feature(vs, VNC_FEATURE_WMVI)) {
aliguorica4cca42008-09-15 16:05:16 +00002353 /* Sending a WMVi message to notify the client*/
Corentin Charybd023f92010-07-07 20:58:02 +02002354 vnc_lock_output(vs);
Daniel P. Berrange46a183d2010-03-31 18:20:43 +01002355 vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
aliguorica4cca42008-09-15 16:05:16 +00002356 vnc_write_u8(vs, 0);
2357 vnc_write_u16(vs, 1); /* number of rects */
Gerd Hoffmannd39fa6d2013-02-28 17:16:48 +01002358 vnc_framebuffer_update(vs, 0, 0,
Daniel P. Berrangé3d3a5282021-03-11 18:29:56 +00002359 vs->client_width,
2360 vs->client_height,
Gerd Hoffmannd39fa6d2013-02-28 17:16:48 +01002361 VNC_ENCODING_WMVi);
aliguorica4cca42008-09-15 16:05:16 +00002362 pixel_format_message(vs);
Corentin Charybd023f92010-07-07 20:58:02 +02002363 vnc_unlock_output(vs);
aliguorica4cca42008-09-15 16:05:16 +00002364 vnc_flush(vs);
aliguori7eac3a82008-09-15 16:03:41 +00002365 } else {
aliguori6cec5482009-01-15 22:17:38 +00002366 set_pixel_conversion(vs);
aliguori7eac3a82008-09-15 16:03:41 +00002367 }
2368}
2369
ths60fe76f2007-12-16 03:02:09 +00002370static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
bellard24236862006-04-30 21:28:36 +00002371{
2372 int i;
2373 uint16_t limit;
Daniel P. Berrangécf070652018-02-05 11:49:37 +00002374 uint32_t freq;
Stefano Stabellini2430ffe2009-08-03 10:56:01 +01002375 VncDisplay *vd = vs->vd;
2376
2377 if (data[0] > 3) {
Gerd Hoffmann0f7b2862013-03-14 11:56:16 +01002378 update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
Stefano Stabellini2430ffe2009-08-03 10:56:01 +01002379 }
bellard24236862006-04-30 21:28:36 +00002380
2381 switch (data[0]) {
Daniel P. Berrange46a183d2010-03-31 18:20:43 +01002382 case VNC_MSG_CLIENT_SET_PIXEL_FORMAT:
aliguori28a76be2009-03-06 20:27:40 +00002383 if (len == 1)
2384 return 20;
bellard24236862006-04-30 21:28:36 +00002385
Gerd Hoffmannec9fb412016-06-06 11:18:45 +02002386 set_pixel_format(vs, read_u8(data, 4),
aliguori28a76be2009-03-06 20:27:40 +00002387 read_u8(data, 6), read_u8(data, 7),
2388 read_u16(data, 8), read_u16(data, 10),
2389 read_u16(data, 12), read_u8(data, 14),
2390 read_u8(data, 15), read_u8(data, 16));
2391 break;
Daniel P. Berrange46a183d2010-03-31 18:20:43 +01002392 case VNC_MSG_CLIENT_SET_ENCODINGS:
aliguori28a76be2009-03-06 20:27:40 +00002393 if (len == 1)
2394 return 4;
bellard24236862006-04-30 21:28:36 +00002395
aliguori28a76be2009-03-06 20:27:40 +00002396 if (len == 4) {
aliguori69dd5c92008-12-22 21:06:23 +00002397 limit = read_u16(data, 2);
2398 if (limit > 0)
2399 return 4 + (limit * 4);
2400 } else
2401 limit = read_u16(data, 2);
bellard24236862006-04-30 21:28:36 +00002402
aliguori28a76be2009-03-06 20:27:40 +00002403 for (i = 0; i < limit; i++) {
2404 int32_t val = read_s32(data, 4 + (i * 4));
2405 memcpy(data + 4 + (i * 4), &val, sizeof(val));
2406 }
bellard24236862006-04-30 21:28:36 +00002407
aliguori28a76be2009-03-06 20:27:40 +00002408 set_encodings(vs, (int32_t *)(data + 4), limit);
2409 break;
Daniel P. Berrange46a183d2010-03-31 18:20:43 +01002410 case VNC_MSG_CLIENT_FRAMEBUFFER_UPDATE_REQUEST:
aliguori28a76be2009-03-06 20:27:40 +00002411 if (len == 1)
2412 return 10;
bellard24236862006-04-30 21:28:36 +00002413
aliguori28a76be2009-03-06 20:27:40 +00002414 framebuffer_update_request(vs,
2415 read_u8(data, 1), read_u16(data, 2), read_u16(data, 4),
2416 read_u16(data, 6), read_u16(data, 8));
2417 break;
Daniel P. Berrange46a183d2010-03-31 18:20:43 +01002418 case VNC_MSG_CLIENT_KEY_EVENT:
aliguori28a76be2009-03-06 20:27:40 +00002419 if (len == 1)
2420 return 8;
bellard24236862006-04-30 21:28:36 +00002421
aliguori28a76be2009-03-06 20:27:40 +00002422 key_event(vs, read_u8(data, 1), read_u32(data, 4));
2423 break;
Daniel P. Berrange46a183d2010-03-31 18:20:43 +01002424 case VNC_MSG_CLIENT_POINTER_EVENT:
aliguori28a76be2009-03-06 20:27:40 +00002425 if (len == 1)
2426 return 6;
bellard24236862006-04-30 21:28:36 +00002427
aliguori28a76be2009-03-06 20:27:40 +00002428 pointer_event(vs, read_u8(data, 1), read_u16(data, 2), read_u16(data, 4));
2429 break;
Daniel P. Berrange46a183d2010-03-31 18:20:43 +01002430 case VNC_MSG_CLIENT_CUT_TEXT:
Peter Lievenf9a70e72014-06-30 10:07:54 +02002431 if (len == 1) {
aliguori28a76be2009-03-06 20:27:40 +00002432 return 8;
Peter Lievenf9a70e72014-06-30 10:07:54 +02002433 }
Mauro Matteo Cascellad3070402022-09-25 22:45:11 +02002434 uint32_t dlen = abs(read_s32(data, 4));
aliguori28a76be2009-03-06 20:27:40 +00002435 if (len == 8) {
Peter Lievenf9a70e72014-06-30 10:07:54 +02002436 if (dlen > (1 << 20)) {
2437 error_report("vnc: client_cut_text msg payload has %u bytes"
2438 " which exceeds our limit of 1MB.", dlen);
2439 vnc_client_error(vs);
2440 break;
2441 }
2442 if (dlen > 0) {
thsbaa76662007-09-13 12:41:42 +00002443 return 8 + dlen;
Peter Lievenf9a70e72014-06-30 10:07:54 +02002444 }
thsbaa76662007-09-13 12:41:42 +00002445 }
bellard24236862006-04-30 21:28:36 +00002446
Gerd Hoffmann0bf41ca2021-05-19 07:39:38 +02002447 if (read_s32(data, 4) < 0) {
Mauro Matteo Cascellad3070402022-09-25 22:45:11 +02002448 if (dlen < 4) {
2449 error_report("vnc: malformed payload (header less than 4 bytes)"
2450 " in extended clipboard pseudo-encoding.");
2451 vnc_client_error(vs);
2452 break;
2453 }
2454 vnc_client_cut_text_ext(vs, dlen, read_u32(data, 8), data + 12);
Gerd Hoffmann0bf41ca2021-05-19 07:39:38 +02002455 break;
2456 }
2457 vnc_client_cut_text(vs, read_u32(data, 4), data + 8);
aliguori28a76be2009-03-06 20:27:40 +00002458 break;
Daniel P. Berrangé7b5fa0b2020-12-11 16:08:25 +00002459 case VNC_MSG_CLIENT_XVP:
Paolo Bonzini477b3012023-09-25 13:05:58 +02002460 if (!vnc_has_feature(vs, VNC_FEATURE_XVP)) {
Daniel P. Berrangé7b5fa0b2020-12-11 16:08:25 +00002461 error_report("vnc: xvp client message while disabled");
2462 vnc_client_error(vs);
2463 break;
2464 }
2465 if (len == 1) {
2466 return 4;
2467 }
2468 if (len == 4) {
2469 uint8_t version = read_u8(data, 2);
2470 uint8_t action = read_u8(data, 3);
2471
2472 if (version != 1) {
2473 error_report("vnc: xvp client message version %d != 1",
2474 version);
2475 vnc_client_error(vs);
2476 break;
2477 }
2478
2479 switch (action) {
2480 case VNC_XVP_ACTION_SHUTDOWN:
2481 qemu_system_powerdown_request();
2482 break;
2483 case VNC_XVP_ACTION_REBOOT:
2484 send_xvp_message(vs, VNC_XVP_CODE_FAIL);
2485 break;
2486 case VNC_XVP_ACTION_RESET:
2487 qemu_system_reset_request(SHUTDOWN_CAUSE_HOST_QMP_SYSTEM_RESET);
2488 break;
2489 default:
2490 send_xvp_message(vs, VNC_XVP_CODE_FAIL);
2491 break;
2492 }
2493 }
2494 break;
Daniel P. Berrange46a183d2010-03-31 18:20:43 +01002495 case VNC_MSG_CLIENT_QEMU:
aliguori9ca313a2008-08-23 23:27:37 +00002496 if (len == 1)
2497 return 2;
2498
2499 switch (read_u8(data, 1)) {
Daniel P. Berrange46a183d2010-03-31 18:20:43 +01002500 case VNC_MSG_CLIENT_QEMU_EXT_KEY_EVENT:
aliguori9ca313a2008-08-23 23:27:37 +00002501 if (len == 2)
2502 return 12;
2503
2504 ext_key_event(vs, read_u16(data, 2),
2505 read_u32(data, 4), read_u32(data, 8));
2506 break;
Daniel P. Berrange46a183d2010-03-31 18:20:43 +01002507 case VNC_MSG_CLIENT_QEMU_AUDIO:
Paolo Bonzini9e58d7a2023-09-25 13:08:27 +02002508 if (!vnc_has_feature(vs, VNC_FEATURE_AUDIO)) {
2509 error_report("Audio message %d with audio disabled", read_u8(data, 2));
2510 vnc_client_error(vs);
2511 break;
2512 }
2513
malc429a8ed2008-12-01 20:57:48 +00002514 if (len == 2)
2515 return 4;
2516
2517 switch (read_u16 (data, 2)) {
Daniel P. Berrange46a183d2010-03-31 18:20:43 +01002518 case VNC_MSG_CLIENT_QEMU_AUDIO_ENABLE:
Daniel P. Berrangéadc8fce2021-03-11 18:29:54 +00002519 trace_vnc_msg_client_audio_enable(vs, vs->ioc);
malc429a8ed2008-12-01 20:57:48 +00002520 audio_add(vs);
2521 break;
Daniel P. Berrange46a183d2010-03-31 18:20:43 +01002522 case VNC_MSG_CLIENT_QEMU_AUDIO_DISABLE:
Daniel P. Berrangéadc8fce2021-03-11 18:29:54 +00002523 trace_vnc_msg_client_audio_disable(vs, vs->ioc);
malc429a8ed2008-12-01 20:57:48 +00002524 audio_del(vs);
2525 break;
Daniel P. Berrange46a183d2010-03-31 18:20:43 +01002526 case VNC_MSG_CLIENT_QEMU_AUDIO_SET_FORMAT:
malc429a8ed2008-12-01 20:57:48 +00002527 if (len == 4)
2528 return 10;
2529 switch (read_u8(data, 4)) {
Kővágó, Zoltán85bc5852019-03-08 23:34:13 +01002530 case 0: vs->as.fmt = AUDIO_FORMAT_U8; break;
2531 case 1: vs->as.fmt = AUDIO_FORMAT_S8; break;
2532 case 2: vs->as.fmt = AUDIO_FORMAT_U16; break;
2533 case 3: vs->as.fmt = AUDIO_FORMAT_S16; break;
2534 case 4: vs->as.fmt = AUDIO_FORMAT_U32; break;
2535 case 5: vs->as.fmt = AUDIO_FORMAT_S32; break;
malc429a8ed2008-12-01 20:57:48 +00002536 default:
Daniel P. Berrange153130c2015-03-17 13:42:54 +00002537 VNC_DEBUG("Invalid audio format %d\n", read_u8(data, 4));
malc429a8ed2008-12-01 20:57:48 +00002538 vnc_client_error(vs);
2539 break;
2540 }
2541 vs->as.nchannels = read_u8(data, 5);
2542 if (vs->as.nchannels != 1 && vs->as.nchannels != 2) {
Marc-André Lureau090fdc82017-12-20 15:06:18 +01002543 VNC_DEBUG("Invalid audio channel count %d\n",
Daniel P. Berrange153130c2015-03-17 13:42:54 +00002544 read_u8(data, 5));
malc429a8ed2008-12-01 20:57:48 +00002545 vnc_client_error(vs);
2546 break;
2547 }
Daniel P. Berrangécf070652018-02-05 11:49:37 +00002548 freq = read_u32(data, 6);
2549 /* No official limit for protocol, but 48khz is a sensible
2550 * upper bound for trustworthy clients, and this limit
2551 * protects calculations involving 'vs->as.freq' later.
2552 */
2553 if (freq > 48000) {
2554 VNC_DEBUG("Invalid audio frequency %u > 48000", freq);
2555 vnc_client_error(vs);
2556 break;
2557 }
2558 vs->as.freq = freq;
Daniel P. Berrangéadc8fce2021-03-11 18:29:54 +00002559 trace_vnc_msg_client_audio_format(
2560 vs, vs->ioc, vs->as.fmt, vs->as.nchannels, vs->as.freq);
malc429a8ed2008-12-01 20:57:48 +00002561 break;
2562 default:
Paolo Bonzini0cb9c582023-09-25 13:06:46 +02002563 VNC_DEBUG("Invalid audio message %d\n", read_u8(data, 2));
malc429a8ed2008-12-01 20:57:48 +00002564 vnc_client_error(vs);
2565 break;
2566 }
2567 break;
2568
aliguori9ca313a2008-08-23 23:27:37 +00002569 default:
Daniel P. Berrange153130c2015-03-17 13:42:54 +00002570 VNC_DEBUG("Msg: %d\n", read_u16(data, 0));
aliguori9ca313a2008-08-23 23:27:37 +00002571 vnc_client_error(vs);
2572 break;
2573 }
2574 break;
Gerd Hoffmann763deea2021-01-12 14:41:20 +01002575 case VNC_MSG_CLIENT_SET_DESKTOP_SIZE:
2576 {
2577 size_t size;
2578 uint8_t screens;
Daniel P. Berrangéadc8fce2021-03-11 18:29:54 +00002579 int w, h;
Gerd Hoffmann763deea2021-01-12 14:41:20 +01002580
2581 if (len < 8) {
2582 return 8;
2583 }
2584
2585 screens = read_u8(data, 6);
2586 size = 8 + screens * 16;
2587 if (len < size) {
2588 return size;
2589 }
Daniel P. Berrangéadc8fce2021-03-11 18:29:54 +00002590 w = read_u16(data, 2);
2591 h = read_u16(data, 4);
Gerd Hoffmann763deea2021-01-12 14:41:20 +01002592
Daniel P. Berrangéadc8fce2021-03-11 18:29:54 +00002593 trace_vnc_msg_client_set_desktop_size(vs, vs->ioc, w, h, screens);
Gerd Hoffmann763deea2021-01-12 14:41:20 +01002594 if (dpy_ui_info_supported(vs->vd->dcl.con)) {
2595 QemuUIInfo info;
2596 memset(&info, 0, sizeof(info));
Daniel P. Berrangéadc8fce2021-03-11 18:29:54 +00002597 info.width = w;
2598 info.height = h;
Marc-André Lureauca19ef52021-04-13 20:39:11 +04002599 dpy_set_ui_info(vs->vd->dcl.con, &info, false);
Gerd Hoffmann763deea2021-01-12 14:41:20 +01002600 vnc_desktop_resize_ext(vs, 4 /* Request forwarded */);
2601 } else {
2602 vnc_desktop_resize_ext(vs, 3 /* Invalid screen layout */);
2603 }
2604
2605 break;
2606 }
bellard24236862006-04-30 21:28:36 +00002607 default:
Daniel P. Berrange153130c2015-03-17 13:42:54 +00002608 VNC_DEBUG("Msg: %d\n", data[0]);
aliguori28a76be2009-03-06 20:27:40 +00002609 vnc_client_error(vs);
2610 break;
bellard24236862006-04-30 21:28:36 +00002611 }
ths5fafdf22007-09-16 21:08:06 +00002612
Daniel P. Berrangee2b72cb2017-12-18 19:12:24 +00002613 vnc_update_throttle_offset(vs);
bellard24236862006-04-30 21:28:36 +00002614 vnc_read_when(vs, protocol_client_msg, 1);
2615 return 0;
2616}
2617
ths60fe76f2007-12-16 03:02:09 +00002618static int protocol_client_init(VncState *vs, uint8_t *data, size_t len)
bellard24236862006-04-30 21:28:36 +00002619{
thsc35734b2007-03-19 15:17:08 +00002620 char buf[1024];
Gerd Hoffmann8cf36482011-11-24 18:10:49 +01002621 VncShareMode mode;
thsc35734b2007-03-19 15:17:08 +00002622 int size;
bellard24236862006-04-30 21:28:36 +00002623
Gerd Hoffmann8cf36482011-11-24 18:10:49 +01002624 mode = data[0] ? VNC_SHARE_MODE_SHARED : VNC_SHARE_MODE_EXCLUSIVE;
2625 switch (vs->vd->share_policy) {
2626 case VNC_SHARE_POLICY_IGNORE:
2627 /*
2628 * Ignore the shared flag. Nothing to do here.
2629 *
2630 * Doesn't conform to the rfb spec but is traditional qemu
2631 * behavior, thus left here as option for compatibility
2632 * reasons.
2633 */
2634 break;
2635 case VNC_SHARE_POLICY_ALLOW_EXCLUSIVE:
2636 /*
2637 * Policy: Allow clients ask for exclusive access.
2638 *
2639 * Implementation: When a client asks for exclusive access,
2640 * disconnect all others. Shared connects are allowed as long
2641 * as no exclusive connection exists.
2642 *
2643 * This is how the rfb spec suggests to handle the shared flag.
2644 */
2645 if (mode == VNC_SHARE_MODE_EXCLUSIVE) {
2646 VncState *client;
2647 QTAILQ_FOREACH(client, &vs->vd->clients, next) {
2648 if (vs == client) {
2649 continue;
2650 }
2651 if (client->share_mode != VNC_SHARE_MODE_EXCLUSIVE &&
2652 client->share_mode != VNC_SHARE_MODE_SHARED) {
2653 continue;
2654 }
2655 vnc_disconnect_start(client);
2656 }
2657 }
2658 if (mode == VNC_SHARE_MODE_SHARED) {
2659 if (vs->vd->num_exclusive > 0) {
2660 vnc_disconnect_start(vs);
2661 return 0;
2662 }
2663 }
2664 break;
2665 case VNC_SHARE_POLICY_FORCE_SHARED:
2666 /*
2667 * Policy: Shared connects only.
2668 * Implementation: Disallow clients asking for exclusive access.
2669 *
2670 * Useful for shared desktop sessions where you don't want
2671 * someone forgetting to say -shared when running the vnc
2672 * client disconnect everybody else.
2673 */
2674 if (mode == VNC_SHARE_MODE_EXCLUSIVE) {
2675 vnc_disconnect_start(vs);
2676 return 0;
2677 }
2678 break;
2679 }
2680 vnc_set_share_mode(vs, mode);
2681
Gerd Hoffmanne5f34cd2014-10-02 12:09:34 +02002682 if (vs->vd->num_shared > vs->vd->connections_limit) {
2683 vnc_disconnect_start(vs);
2684 return 0;
2685 }
2686
Daniel P. Berrange4c956bd2018-01-18 15:52:54 +00002687 assert(pixman_image_get_width(vs->vd->server) < 65536 &&
2688 pixman_image_get_width(vs->vd->server) >= 0);
2689 assert(pixman_image_get_height(vs->vd->server) < 65536 &&
2690 pixman_image_get_height(vs->vd->server) >= 0);
Peter Lievenbea60dd2014-06-30 10:57:51 +02002691 vs->client_width = pixman_image_get_width(vs->vd->server);
2692 vs->client_height = pixman_image_get_height(vs->vd->server);
Gerd Hoffmann5862d192010-05-25 18:25:18 +02002693 vnc_write_u16(vs, vs->client_width);
2694 vnc_write_u16(vs, vs->client_height);
bellard24236862006-04-30 21:28:36 +00002695
aliguorica4cca42008-09-15 16:05:16 +00002696 pixel_format_message(vs);
bellard24236862006-04-30 21:28:36 +00002697
Thomas Huth97efe4f2016-11-21 18:25:15 +01002698 if (qemu_name) {
thsc35734b2007-03-19 15:17:08 +00002699 size = snprintf(buf, sizeof(buf), "QEMU (%s)", qemu_name);
Thomas Huth97efe4f2016-11-21 18:25:15 +01002700 if (size > sizeof(buf)) {
2701 size = sizeof(buf);
2702 }
2703 } else {
thsc35734b2007-03-19 15:17:08 +00002704 size = snprintf(buf, sizeof(buf), "QEMU");
Thomas Huth97efe4f2016-11-21 18:25:15 +01002705 }
thsc35734b2007-03-19 15:17:08 +00002706
2707 vnc_write_u32(vs, size);
2708 vnc_write(vs, buf, size);
bellard24236862006-04-30 21:28:36 +00002709 vnc_flush(vs);
2710
Luiz Capitulino4a80dba2010-01-14 14:50:56 -02002711 vnc_client_cache_auth(vs);
Wenchao Xiafb6ba0d2014-06-18 08:43:49 +02002712 vnc_qmp_event(vs, QAPI_EVENT_VNC_INITIALIZED);
Luiz Capitulino4a80dba2010-01-14 14:50:56 -02002713
bellard24236862006-04-30 21:28:36 +00002714 vnc_read_when(vs, protocol_client_msg, 1);
2715
2716 return 0;
2717}
2718
aliguori5fb6c7a2009-03-06 20:27:23 +00002719void start_client_init(VncState *vs)
2720{
2721 vnc_read_when(vs, protocol_client_init, 1);
2722}
2723
Richard Henderson4347e632019-03-14 15:33:08 -07002724static void authentication_failed(VncState *vs)
2725{
2726 vnc_write_u32(vs, 1); /* Reject auth */
2727 if (vs->minor >= 8) {
2728 static const char err[] = "Authentication failed";
2729 vnc_write_u32(vs, sizeof(err));
2730 vnc_write(vs, err, sizeof(err));
2731 }
2732 vnc_flush(vs);
2733 vnc_client_error(vs);
2734}
2735
Daniel P. Berrangé83bee4b2021-06-29 14:25:32 +01002736static void
2737vnc_munge_des_rfb_key(unsigned char *key, size_t nkey)
2738{
2739 size_t i;
2740 for (i = 0; i < nkey; i++) {
2741 uint8_t r = key[i];
2742 r = (r & 0xf0) >> 4 | (r & 0x0f) << 4;
2743 r = (r & 0xcc) >> 2 | (r & 0x33) << 2;
2744 r = (r & 0xaa) >> 1 | (r & 0x55) << 1;
2745 key[i] = r;
2746 }
2747}
2748
ths60fe76f2007-12-16 03:02:09 +00002749static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len)
ths70848512007-08-25 01:37:05 +00002750{
ths60fe76f2007-12-16 03:02:09 +00002751 unsigned char response[VNC_AUTH_CHALLENGE_SIZE];
Daniel P. Berrange800567a2015-07-01 18:10:38 +01002752 size_t i, pwlen;
ths60fe76f2007-12-16 03:02:09 +00002753 unsigned char key[8];
Gerd Hoffmann3c9405a2010-10-07 11:50:45 +02002754 time_t now = time(NULL);
Gonglei60928452015-07-22 17:08:53 +08002755 QCryptoCipher *cipher = NULL;
Daniel P. Berrange800567a2015-07-01 18:10:38 +01002756 Error *err = NULL;
ths70848512007-08-25 01:37:05 +00002757
Anthony Liguori1cd20f82011-01-31 14:27:36 -06002758 if (!vs->vd->password) {
Daniel P. Berrange7364dbd2017-09-21 13:15:28 +01002759 trace_vnc_auth_fail(vs, vs->auth, "password is not set", "");
Gerd Hoffmann6bffdf02010-10-07 11:50:24 +02002760 goto reject;
bellard24236862006-04-30 21:28:36 +00002761 }
Gerd Hoffmann3c9405a2010-10-07 11:50:45 +02002762 if (vs->vd->expires < now) {
Daniel P. Berrange7364dbd2017-09-21 13:15:28 +01002763 trace_vnc_auth_fail(vs, vs->auth, "password is expired", "");
Gerd Hoffmann3c9405a2010-10-07 11:50:45 +02002764 goto reject;
2765 }
bellard24236862006-04-30 21:28:36 +00002766
ths70848512007-08-25 01:37:05 +00002767 memcpy(response, vs->challenge, VNC_AUTH_CHALLENGE_SIZE);
2768
2769 /* Calculate the expected challenge response */
aliguori753b4052009-02-16 14:59:30 +00002770 pwlen = strlen(vs->vd->password);
ths70848512007-08-25 01:37:05 +00002771 for (i=0; i<sizeof(key); i++)
aliguori753b4052009-02-16 14:59:30 +00002772 key[i] = i<pwlen ? vs->vd->password[i] : 0;
Daniel P. Berrangé83bee4b2021-06-29 14:25:32 +01002773 vnc_munge_des_rfb_key(key, sizeof(key));
Daniel P. Berrange800567a2015-07-01 18:10:38 +01002774
2775 cipher = qcrypto_cipher_new(
Daniel P. Berrangé83bee4b2021-06-29 14:25:32 +01002776 QCRYPTO_CIPHER_ALG_DES,
Daniel P. Berrange800567a2015-07-01 18:10:38 +01002777 QCRYPTO_CIPHER_MODE_ECB,
2778 key, G_N_ELEMENTS(key),
2779 &err);
2780 if (!cipher) {
Daniel P. Berrange7364dbd2017-09-21 13:15:28 +01002781 trace_vnc_auth_fail(vs, vs->auth, "cannot create cipher",
2782 error_get_pretty(err));
Daniel P. Berrange800567a2015-07-01 18:10:38 +01002783 error_free(err);
2784 goto reject;
2785 }
2786
Wolfgang Bumillera1695132015-07-14 14:51:40 +02002787 if (qcrypto_cipher_encrypt(cipher,
Daniel P. Berrange800567a2015-07-01 18:10:38 +01002788 vs->challenge,
2789 response,
2790 VNC_AUTH_CHALLENGE_SIZE,
2791 &err) < 0) {
Daniel P. Berrange7364dbd2017-09-21 13:15:28 +01002792 trace_vnc_auth_fail(vs, vs->auth, "cannot encrypt challenge response",
2793 error_get_pretty(err));
Daniel P. Berrange800567a2015-07-01 18:10:38 +01002794 error_free(err);
2795 goto reject;
2796 }
ths70848512007-08-25 01:37:05 +00002797
2798 /* Compare expected vs actual challenge response */
2799 if (memcmp(response, data, VNC_AUTH_CHALLENGE_SIZE) != 0) {
Daniel P. Berrange7364dbd2017-09-21 13:15:28 +01002800 trace_vnc_auth_fail(vs, vs->auth, "mis-matched challenge response", "");
Gerd Hoffmann6bffdf02010-10-07 11:50:24 +02002801 goto reject;
ths70848512007-08-25 01:37:05 +00002802 } else {
Daniel P. Berrange7364dbd2017-09-21 13:15:28 +01002803 trace_vnc_auth_pass(vs, vs->auth);
aliguori28a76be2009-03-06 20:27:40 +00002804 vnc_write_u32(vs, 0); /* Accept auth */
2805 vnc_flush(vs);
ths70848512007-08-25 01:37:05 +00002806
aliguori5fb6c7a2009-03-06 20:27:23 +00002807 start_client_init(vs);
ths70848512007-08-25 01:37:05 +00002808 }
Gonglei60928452015-07-22 17:08:53 +08002809
2810 qcrypto_cipher_free(cipher);
ths70848512007-08-25 01:37:05 +00002811 return 0;
Gerd Hoffmann6bffdf02010-10-07 11:50:24 +02002812
2813reject:
Richard Henderson4347e632019-03-14 15:33:08 -07002814 authentication_failed(vs);
Gonglei60928452015-07-22 17:08:53 +08002815 qcrypto_cipher_free(cipher);
Gerd Hoffmann6bffdf02010-10-07 11:50:24 +02002816 return 0;
ths70848512007-08-25 01:37:05 +00002817}
2818
aliguori5fb6c7a2009-03-06 20:27:23 +00002819void start_auth_vnc(VncState *vs)
ths70848512007-08-25 01:37:05 +00002820{
Richard Hendersonf7b25022019-03-14 15:37:43 -07002821 Error *err = NULL;
2822
2823 if (qcrypto_random_bytes(vs->challenge, sizeof(vs->challenge), &err)) {
2824 trace_vnc_auth_fail(vs, vs->auth, "cannot get random bytes",
2825 error_get_pretty(err));
2826 error_free(err);
2827 authentication_failed(vs);
2828 return;
2829 }
2830
ths70848512007-08-25 01:37:05 +00002831 /* Send client a 'random' challenge */
2832 vnc_write(vs, vs->challenge, sizeof(vs->challenge));
bellard24236862006-04-30 21:28:36 +00002833 vnc_flush(vs);
2834
ths70848512007-08-25 01:37:05 +00002835 vnc_read_when(vs, protocol_client_auth_vnc, sizeof(vs->challenge));
ths70848512007-08-25 01:37:05 +00002836}
2837
ths8d5d2d42007-08-25 01:37:51 +00002838
ths60fe76f2007-12-16 03:02:09 +00002839static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len)
ths70848512007-08-25 01:37:05 +00002840{
2841 /* We only advertise 1 auth scheme at a time, so client
2842 * must pick the one we sent. Verify this */
Daniel P. Berrange7e7e2eb2011-06-23 13:31:41 +01002843 if (data[0] != vs->auth) { /* Reject auth */
Daniel P. Berrange7364dbd2017-09-21 13:15:28 +01002844 trace_vnc_auth_reject(vs, vs->auth, (int)data[0]);
Richard Henderson4347e632019-03-14 15:33:08 -07002845 authentication_failed(vs);
ths70848512007-08-25 01:37:05 +00002846 } else { /* Accept requested auth */
Daniel P. Berrange7364dbd2017-09-21 13:15:28 +01002847 trace_vnc_auth_start(vs, vs->auth);
Daniel P. Berrange7e7e2eb2011-06-23 13:31:41 +01002848 switch (vs->auth) {
ths70848512007-08-25 01:37:05 +00002849 case VNC_AUTH_NONE:
balroga26c97a2007-10-31 01:58:56 +00002850 if (vs->minor >= 8) {
2851 vnc_write_u32(vs, 0); /* Accept auth completion */
2852 vnc_flush(vs);
2853 }
Daniel P. Berrange7364dbd2017-09-21 13:15:28 +01002854 trace_vnc_auth_pass(vs, vs->auth);
aliguori5fb6c7a2009-03-06 20:27:23 +00002855 start_client_init(vs);
ths70848512007-08-25 01:37:05 +00002856 break;
2857
2858 case VNC_AUTH_VNC:
aliguori5fb6c7a2009-03-06 20:27:23 +00002859 start_auth_vnc(vs);
2860 break;
ths70848512007-08-25 01:37:05 +00002861
ths8d5d2d42007-08-25 01:37:51 +00002862 case VNC_AUTH_VENCRYPT:
aliguori5fb6c7a2009-03-06 20:27:23 +00002863 start_auth_vencrypt(vs);
2864 break;
ths8d5d2d42007-08-25 01:37:51 +00002865
aliguori2f9606b2009-03-06 20:27:28 +00002866#ifdef CONFIG_VNC_SASL
2867 case VNC_AUTH_SASL:
aliguori2f9606b2009-03-06 20:27:28 +00002868 start_auth_sasl(vs);
2869 break;
2870#endif /* CONFIG_VNC_SASL */
2871
ths70848512007-08-25 01:37:05 +00002872 default: /* Should not be possible, but just in case */
Daniel P. Berrange7364dbd2017-09-21 13:15:28 +01002873 trace_vnc_auth_fail(vs, vs->auth, "Unhandled auth method", "");
Richard Henderson4347e632019-03-14 15:33:08 -07002874 authentication_failed(vs);
ths70848512007-08-25 01:37:05 +00002875 }
2876 }
2877 return 0;
2878}
2879
ths60fe76f2007-12-16 03:02:09 +00002880static int protocol_version(VncState *vs, uint8_t *version, size_t len)
ths70848512007-08-25 01:37:05 +00002881{
2882 char local[13];
2883
2884 memcpy(local, version, 12);
2885 local[12] = 0;
2886
2887 if (sscanf(local, "RFB %03d.%03d\n", &vs->major, &vs->minor) != 2) {
aliguori28a76be2009-03-06 20:27:40 +00002888 VNC_DEBUG("Malformed protocol version %s\n", local);
2889 vnc_client_error(vs);
2890 return 0;
ths70848512007-08-25 01:37:05 +00002891 }
2892 VNC_DEBUG("Client request protocol version %d.%d\n", vs->major, vs->minor);
2893 if (vs->major != 3 ||
aliguori28a76be2009-03-06 20:27:40 +00002894 (vs->minor != 3 &&
2895 vs->minor != 4 &&
2896 vs->minor != 5 &&
2897 vs->minor != 7 &&
2898 vs->minor != 8)) {
2899 VNC_DEBUG("Unsupported client version\n");
2900 vnc_write_u32(vs, VNC_AUTH_INVALID);
2901 vnc_flush(vs);
2902 vnc_client_error(vs);
2903 return 0;
ths70848512007-08-25 01:37:05 +00002904 }
thsb0566f42007-09-30 13:01:15 +00002905 /* Some broken clients report v3.4 or v3.5, which spec requires to be treated
ths70848512007-08-25 01:37:05 +00002906 * as equivalent to v3.3 by servers
2907 */
thsb0566f42007-09-30 13:01:15 +00002908 if (vs->minor == 4 || vs->minor == 5)
aliguori28a76be2009-03-06 20:27:40 +00002909 vs->minor = 3;
ths70848512007-08-25 01:37:05 +00002910
2911 if (vs->minor == 3) {
Daniel P. Berrange7364dbd2017-09-21 13:15:28 +01002912 trace_vnc_auth_start(vs, vs->auth);
Daniel P. Berrange7e7e2eb2011-06-23 13:31:41 +01002913 if (vs->auth == VNC_AUTH_NONE) {
Daniel P. Berrange7e7e2eb2011-06-23 13:31:41 +01002914 vnc_write_u32(vs, vs->auth);
ths70848512007-08-25 01:37:05 +00002915 vnc_flush(vs);
Daniel P. Berrange7364dbd2017-09-21 13:15:28 +01002916 trace_vnc_auth_pass(vs, vs->auth);
aliguori28a76be2009-03-06 20:27:40 +00002917 start_client_init(vs);
Daniel P. Berrange7e7e2eb2011-06-23 13:31:41 +01002918 } else if (vs->auth == VNC_AUTH_VNC) {
ths70848512007-08-25 01:37:05 +00002919 VNC_DEBUG("Tell client VNC auth\n");
Daniel P. Berrange7e7e2eb2011-06-23 13:31:41 +01002920 vnc_write_u32(vs, vs->auth);
ths70848512007-08-25 01:37:05 +00002921 vnc_flush(vs);
2922 start_auth_vnc(vs);
2923 } else {
Daniel P. Berrange7364dbd2017-09-21 13:15:28 +01002924 trace_vnc_auth_fail(vs, vs->auth,
2925 "Unsupported auth method for v3.3", "");
ths70848512007-08-25 01:37:05 +00002926 vnc_write_u32(vs, VNC_AUTH_INVALID);
2927 vnc_flush(vs);
2928 vnc_client_error(vs);
2929 }
2930 } else {
aliguori28a76be2009-03-06 20:27:40 +00002931 vnc_write_u8(vs, 1); /* num auth */
Daniel P. Berrange7e7e2eb2011-06-23 13:31:41 +01002932 vnc_write_u8(vs, vs->auth);
aliguori28a76be2009-03-06 20:27:40 +00002933 vnc_read_when(vs, protocol_client_auth, 1);
2934 vnc_flush(vs);
ths70848512007-08-25 01:37:05 +00002935 }
bellard24236862006-04-30 21:28:36 +00002936
2937 return 0;
2938}
2939
Corentin Chary999342a2011-02-04 09:05:55 +01002940static VncRectStat *vnc_stat_rect(VncDisplay *vd, int x, int y)
2941{
2942 struct VncSurface *vs = &vd->guest;
2943
2944 return &vs->stats[y / VNC_STAT_RECT][x / VNC_STAT_RECT];
2945}
2946
Corentin Chary7d964c92011-02-04 09:05:56 +01002947void vnc_sent_lossy_rect(VncState *vs, int x, int y, int w, int h)
2948{
2949 int i, j;
2950
2951 w = (x + w) / VNC_STAT_RECT;
2952 h = (y + h) / VNC_STAT_RECT;
2953 x /= VNC_STAT_RECT;
2954 y /= VNC_STAT_RECT;
2955
Corentin Chary207f3282011-02-04 09:06:03 +01002956 for (j = y; j <= h; j++) {
2957 for (i = x; i <= w; i++) {
Corentin Chary7d964c92011-02-04 09:05:56 +01002958 vs->lossy_rect[j][i] = 1;
2959 }
2960 }
2961}
2962
2963static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y)
2964{
2965 VncState *vs;
2966 int sty = y / VNC_STAT_RECT;
2967 int stx = x / VNC_STAT_RECT;
2968 int has_dirty = 0;
2969
Marc-André Lureau5a3804d2017-06-22 13:04:16 +02002970 y = QEMU_ALIGN_DOWN(y, VNC_STAT_RECT);
2971 x = QEMU_ALIGN_DOWN(x, VNC_STAT_RECT);
Corentin Chary7d964c92011-02-04 09:05:56 +01002972
2973 QTAILQ_FOREACH(vs, &vd->clients, next) {
Corentin Charybc2429b2011-02-04 09:06:05 +01002974 int j;
Corentin Chary7d964c92011-02-04 09:05:56 +01002975
2976 /* kernel send buffers are full -> refresh later */
2977 if (vs->output.offset) {
2978 continue;
2979 }
2980
2981 if (!vs->lossy_rect[sty][stx]) {
2982 continue;
2983 }
Corentin Chary207f3282011-02-04 09:06:03 +01002984
Corentin Chary7d964c92011-02-04 09:05:56 +01002985 vs->lossy_rect[sty][stx] = 0;
2986 for (j = 0; j < VNC_STAT_RECT; ++j) {
Peter Lievenb4c85dd2014-01-08 10:08:33 +01002987 bitmap_set(vs->dirty[y + j],
2988 x / VNC_DIRTY_PIXELS_PER_BIT,
2989 VNC_STAT_RECT / VNC_DIRTY_PIXELS_PER_BIT);
Corentin Chary7d964c92011-02-04 09:05:56 +01002990 }
2991 has_dirty++;
2992 }
Corentin Chary207f3282011-02-04 09:06:03 +01002993
Corentin Chary7d964c92011-02-04 09:05:56 +01002994 return has_dirty;
2995}
2996
2997static int vnc_update_stats(VncDisplay *vd, struct timeval * tv)
Corentin Chary999342a2011-02-04 09:05:55 +01002998{
Gerd Hoffmanneebe0b72017-01-24 10:00:28 +01002999 int width = MIN(pixman_image_get_width(vd->guest.fb),
3000 pixman_image_get_width(vd->server));
3001 int height = MIN(pixman_image_get_height(vd->guest.fb),
3002 pixman_image_get_height(vd->server));
Corentin Chary999342a2011-02-04 09:05:55 +01003003 int x, y;
3004 struct timeval res;
Corentin Chary7d964c92011-02-04 09:05:56 +01003005 int has_dirty = 0;
Corentin Chary999342a2011-02-04 09:05:55 +01003006
Gerd Hoffmann9f649162012-10-10 13:29:43 +02003007 for (y = 0; y < height; y += VNC_STAT_RECT) {
3008 for (x = 0; x < width; x += VNC_STAT_RECT) {
Corentin Chary999342a2011-02-04 09:05:55 +01003009 VncRectStat *rect = vnc_stat_rect(vd, x, y);
3010
3011 rect->updated = false;
3012 }
3013 }
3014
Blue Swirlad620c22011-03-13 10:30:52 +00003015 qemu_timersub(tv, &VNC_REFRESH_STATS, &res);
Corentin Chary999342a2011-02-04 09:05:55 +01003016
3017 if (timercmp(&vd->guest.last_freq_check, &res, >)) {
Corentin Chary7d964c92011-02-04 09:05:56 +01003018 return has_dirty;
Corentin Chary999342a2011-02-04 09:05:55 +01003019 }
3020 vd->guest.last_freq_check = *tv;
3021
Gerd Hoffmann9f649162012-10-10 13:29:43 +02003022 for (y = 0; y < height; y += VNC_STAT_RECT) {
3023 for (x = 0; x < width; x += VNC_STAT_RECT) {
Corentin Chary999342a2011-02-04 09:05:55 +01003024 VncRectStat *rect= vnc_stat_rect(vd, x, y);
3025 int count = ARRAY_SIZE(rect->times);
3026 struct timeval min, max;
3027
3028 if (!timerisset(&rect->times[count - 1])) {
3029 continue ;
3030 }
3031
3032 max = rect->times[(rect->idx + count - 1) % count];
Blue Swirlad620c22011-03-13 10:30:52 +00003033 qemu_timersub(tv, &max, &res);
Corentin Chary999342a2011-02-04 09:05:55 +01003034
3035 if (timercmp(&res, &VNC_REFRESH_LOSSY, >)) {
3036 rect->freq = 0;
Corentin Chary7d964c92011-02-04 09:05:56 +01003037 has_dirty += vnc_refresh_lossy_rect(vd, x, y);
Corentin Chary999342a2011-02-04 09:05:55 +01003038 memset(rect->times, 0, sizeof (rect->times));
3039 continue ;
3040 }
3041
3042 min = rect->times[rect->idx];
3043 max = rect->times[(rect->idx + count - 1) % count];
Blue Swirlad620c22011-03-13 10:30:52 +00003044 qemu_timersub(&max, &min, &res);
Corentin Chary999342a2011-02-04 09:05:55 +01003045
3046 rect->freq = res.tv_sec + res.tv_usec / 1000000.;
3047 rect->freq /= count;
3048 rect->freq = 1. / rect->freq;
3049 }
3050 }
Corentin Chary7d964c92011-02-04 09:05:56 +01003051 return has_dirty;
Corentin Chary999342a2011-02-04 09:05:55 +01003052}
3053
3054double vnc_update_freq(VncState *vs, int x, int y, int w, int h)
3055{
3056 int i, j;
3057 double total = 0;
3058 int num = 0;
3059
Marc-André Lureau5a3804d2017-06-22 13:04:16 +02003060 x = QEMU_ALIGN_DOWN(x, VNC_STAT_RECT);
3061 y = QEMU_ALIGN_DOWN(y, VNC_STAT_RECT);
Corentin Chary999342a2011-02-04 09:05:55 +01003062
3063 for (j = y; j <= y + h; j += VNC_STAT_RECT) {
3064 for (i = x; i <= x + w; i += VNC_STAT_RECT) {
3065 total += vnc_stat_rect(vs->vd, i, j)->freq;
3066 num++;
3067 }
3068 }
3069
3070 if (num) {
3071 return total / num;
3072 } else {
3073 return 0;
3074 }
3075}
3076
3077static void vnc_rect_updated(VncDisplay *vd, int x, int y, struct timeval * tv)
3078{
3079 VncRectStat *rect;
3080
3081 rect = vnc_stat_rect(vd, x, y);
3082 if (rect->updated) {
Bin Mengc1dadb82022-10-24 15:28:02 +08003083 return;
Corentin Chary999342a2011-02-04 09:05:55 +01003084 }
3085 rect->times[rect->idx] = *tv;
3086 rect->idx = (rect->idx + 1) % ARRAY_SIZE(rect->times);
3087 rect->updated = true;
3088}
3089
Stefano Stabellini1fc62412009-08-03 10:54:32 +01003090static int vnc_refresh_server_surface(VncDisplay *vd)
3091{
Peter Lievenbea60dd2014-06-30 10:57:51 +02003092 int width = MIN(pixman_image_get_width(vd->guest.fb),
3093 pixman_image_get_width(vd->server));
3094 int height = MIN(pixman_image_get_height(vd->guest.fb),
3095 pixman_image_get_height(vd->server));
Gerd Hoffmanneb8934b2015-08-17 19:56:53 +02003096 int cmp_bytes, server_stride, line_bytes, guest_ll, guest_stride, y = 0;
Peter Lieven12b316d2014-01-08 10:08:35 +01003097 uint8_t *guest_row0 = NULL, *server_row0;
Amit Shah41b4bef2010-02-05 16:34:05 +05303098 VncState *vs;
Stefano Stabellini1fc62412009-08-03 10:54:32 +01003099 int has_dirty = 0;
Gerd Hoffmann9f649162012-10-10 13:29:43 +02003100 pixman_image_t *tmpbuf = NULL;
Wen, Jianxian6c089642022-03-15 06:50:37 +00003101 unsigned long offset;
3102 int x;
3103 uint8_t *guest_ptr, *server_ptr;
Stefano Stabellini1fc62412009-08-03 10:54:32 +01003104
Corentin Chary80e0c8c2011-02-04 09:06:08 +01003105 struct timeval tv = { 0, 0 };
Corentin Chary999342a2011-02-04 09:05:55 +01003106
Corentin Chary80e0c8c2011-02-04 09:06:08 +01003107 if (!vd->non_adaptive) {
3108 gettimeofday(&tv, NULL);
3109 has_dirty = vnc_update_stats(vd, &tv);
3110 }
Corentin Chary999342a2011-02-04 09:05:55 +01003111
Wen, Jianxian6c089642022-03-15 06:50:37 +00003112 offset = find_next_bit((unsigned long *) &vd->guest.dirty,
3113 height * VNC_DIRTY_BPL(&vd->guest), 0);
3114 if (offset == height * VNC_DIRTY_BPL(&vd->guest)) {
3115 /* no dirty bits in guest surface */
3116 return has_dirty;
3117 }
3118
Stefano Stabellini1fc62412009-08-03 10:54:32 +01003119 /*
3120 * Walk through the guest dirty map.
3121 * Check and copy modified bits from guest to server surface.
3122 * Update server dirty map.
3123 */
Peter Lievenbea60dd2014-06-30 10:57:51 +02003124 server_row0 = (uint8_t *)pixman_image_get_data(vd->server);
Gerd Hoffmanneb8934b2015-08-17 19:56:53 +02003125 server_stride = guest_stride = guest_ll =
3126 pixman_image_get_stride(vd->server);
Peter Lievenbea60dd2014-06-30 10:57:51 +02003127 cmp_bytes = MIN(VNC_DIRTY_PIXELS_PER_BIT * VNC_SERVER_FB_BYTES,
3128 server_stride);
Gerd Hoffmann9f649162012-10-10 13:29:43 +02003129 if (vd->guest.format != VNC_SERVER_FB_FORMAT) {
Markus Armbrustere33e66b2023-09-21 14:13:08 +02003130 int w = pixman_image_get_width(vd->server);
3131 tmpbuf = qemu_pixman_linebuf_create(VNC_SERVER_FB_FORMAT, w);
Peter Lieven12b316d2014-01-08 10:08:35 +01003132 } else {
Gerd Hoffmanneb8934b2015-08-17 19:56:53 +02003133 int guest_bpp =
3134 PIXMAN_FORMAT_BPP(pixman_image_get_format(vd->guest.fb));
Peter Lieven12b316d2014-01-08 10:08:35 +01003135 guest_row0 = (uint8_t *)pixman_image_get_data(vd->guest.fb);
3136 guest_stride = pixman_image_get_stride(vd->guest.fb);
Philippe Mathieu-Daudé949ed4c2018-07-04 12:39:17 -03003137 guest_ll = pixman_image_get_width(vd->guest.fb)
3138 * DIV_ROUND_UP(guest_bpp, 8);
Gerd Hoffmann9f649162012-10-10 13:29:43 +02003139 }
Gerd Hoffmanneb8934b2015-08-17 19:56:53 +02003140 line_bytes = MIN(server_stride, guest_ll);
Stefano Stabellini1fc62412009-08-03 10:54:32 +01003141
Peter Lieven12b316d2014-01-08 10:08:35 +01003142 for (;;) {
Peter Lieven12b316d2014-01-08 10:08:35 +01003143 y = offset / VNC_DIRTY_BPL(&vd->guest);
3144 x = offset % VNC_DIRTY_BPL(&vd->guest);
3145
3146 server_ptr = server_row0 + y * server_stride + x * cmp_bytes;
3147
3148 if (vd->guest.format != VNC_SERVER_FB_FORMAT) {
3149 qemu_pixman_linebuf_fill(tmpbuf, vd->guest.fb, width, 0, y);
3150 guest_ptr = (uint8_t *)pixman_image_get_data(tmpbuf);
3151 } else {
3152 guest_ptr = guest_row0 + y * guest_stride;
3153 }
3154 guest_ptr += x * cmp_bytes;
3155
3156 for (; x < DIV_ROUND_UP(width, VNC_DIRTY_PIXELS_PER_BIT);
3157 x++, guest_ptr += cmp_bytes, server_ptr += cmp_bytes) {
Peter Lievenbea60dd2014-06-30 10:57:51 +02003158 int _cmp_bytes = cmp_bytes;
Peter Lieven12b316d2014-01-08 10:08:35 +01003159 if (!test_and_clear_bit(x, vd->guest.dirty[y])) {
3160 continue;
3161 }
Gerd Hoffmanneb8934b2015-08-17 19:56:53 +02003162 if ((x + 1) * cmp_bytes > line_bytes) {
3163 _cmp_bytes = line_bytes - x * cmp_bytes;
Peter Lievenbea60dd2014-06-30 10:57:51 +02003164 }
Gerd Hoffmanneb8934b2015-08-17 19:56:53 +02003165 assert(_cmp_bytes >= 0);
Peter Lievenbea60dd2014-06-30 10:57:51 +02003166 if (memcmp(server_ptr, guest_ptr, _cmp_bytes) == 0) {
Peter Lieven12b316d2014-01-08 10:08:35 +01003167 continue;
3168 }
Peter Lievenbea60dd2014-06-30 10:57:51 +02003169 memcpy(server_ptr, guest_ptr, _cmp_bytes);
Peter Lieven12b316d2014-01-08 10:08:35 +01003170 if (!vd->non_adaptive) {
3171 vnc_rect_updated(vd, x * VNC_DIRTY_PIXELS_PER_BIT,
3172 y, &tv);
3173 }
3174 QTAILQ_FOREACH(vs, &vd->clients, next) {
3175 set_bit(x, vs->dirty[y]);
3176 }
3177 has_dirty++;
3178 }
3179
3180 y++;
Wen, Jianxian6c089642022-03-15 06:50:37 +00003181 offset = find_next_bit((unsigned long *) &vd->guest.dirty,
3182 height * VNC_DIRTY_BPL(&vd->guest),
3183 y * VNC_DIRTY_BPL(&vd->guest));
3184 if (offset == height * VNC_DIRTY_BPL(&vd->guest)) {
3185 /* no more dirty bits */
3186 break;
3187 }
Stefano Stabellini1fc62412009-08-03 10:54:32 +01003188 }
Gerd Hoffmann9f649162012-10-10 13:29:43 +02003189 qemu_pixman_image_unref(tmpbuf);
Stefano Stabellini1fc62412009-08-03 10:54:32 +01003190 return has_dirty;
3191}
3192
Gerd Hoffmann0f7b2862013-03-14 11:56:16 +01003193static void vnc_refresh(DisplayChangeListener *dcl)
Stefano Stabellini703bc682009-08-03 10:54:05 +01003194{
Gerd Hoffmann0f7b2862013-03-14 11:56:16 +01003195 VncDisplay *vd = container_of(dcl, VncDisplay, dcl);
Amit Shah41b4bef2010-02-05 16:34:05 +05303196 VncState *vs, *vn;
3197 int has_dirty, rects = 0;
Stefano Stabellini703bc682009-08-03 10:54:05 +01003198
ChenLiang9d6b2072014-09-29 15:00:40 +08003199 if (QTAILQ_EMPTY(&vd->clients)) {
3200 update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_MAX);
3201 return;
3202 }
3203
Gerd Hoffmann1d0d59f2014-09-18 12:54:49 +02003204 graphic_hw_update(vd->dcl.con);
Stefano Stabellini703bc682009-08-03 10:54:05 +01003205
Corentin Charybd023f92010-07-07 20:58:02 +02003206 if (vnc_trylock_display(vd)) {
Gerd Hoffmann0f7b2862013-03-14 11:56:16 +01003207 update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
Corentin Charybd023f92010-07-07 20:58:02 +02003208 return;
3209 }
3210
Stefano Stabellini1fc62412009-08-03 10:54:32 +01003211 has_dirty = vnc_refresh_server_surface(vd);
Corentin Charybd023f92010-07-07 20:58:02 +02003212 vnc_unlock_display(vd);
Stefano Stabellini1fc62412009-08-03 10:54:32 +01003213
Amit Shah41b4bef2010-02-05 16:34:05 +05303214 QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) {
Daniel P. Berrange6af998d2017-12-18 19:12:16 +00003215 rects += vnc_update_client(vs, has_dirty);
Stefano Stabellini6185c572010-01-25 12:54:57 +00003216 /* vs might be free()ed here */
Stefano Stabellini703bc682009-08-03 10:54:05 +01003217 }
Corentin Charybd023f92010-07-07 20:58:02 +02003218
Stefano Stabellini2430ffe2009-08-03 10:56:01 +01003219 if (has_dirty && rects) {
Gerd Hoffmann0f7b2862013-03-14 11:56:16 +01003220 vd->dcl.update_interval /= 2;
3221 if (vd->dcl.update_interval < VNC_REFRESH_INTERVAL_BASE) {
3222 vd->dcl.update_interval = VNC_REFRESH_INTERVAL_BASE;
3223 }
Stefano Stabellini2430ffe2009-08-03 10:56:01 +01003224 } else {
Gerd Hoffmann0f7b2862013-03-14 11:56:16 +01003225 vd->dcl.update_interval += VNC_REFRESH_INTERVAL_INC;
3226 if (vd->dcl.update_interval > VNC_REFRESH_INTERVAL_MAX) {
3227 vd->dcl.update_interval = VNC_REFRESH_INTERVAL_MAX;
3228 }
Stefano Stabellini703bc682009-08-03 10:54:05 +01003229 }
3230}
3231
Daniel P. Berrange04d25292015-02-27 16:20:57 +00003232static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc,
Michael Tokarev2c8cf542013-06-11 15:42:44 +04003233 bool skipauth, bool websocket)
balrog3aa3eea2008-02-03 02:54:04 +00003234{
Markus Armbrusterfedf0d32015-11-03 17:12:03 +01003235 VncState *vs = g_new0(VncState, 1);
Daniel P. Berrange90cd03a2016-09-29 16:45:39 +01003236 bool first_client = QTAILQ_EMPTY(&vd->clients);
Corentin Chary7d964c92011-02-04 09:05:56 +01003237 int i;
3238
Daniel P. Berrangead6374c2017-09-21 13:15:27 +01003239 trace_vnc_client_connect(vs, sioc);
Li Qiang6bf21f32019-08-31 08:39:22 -07003240 vs->zrle = g_new0(VncZrle, 1);
3241 vs->tight = g_new0(VncTight, 1);
Gerd Hoffmannf31f9c12018-05-07 12:22:54 +02003242 vs->magic = VNC_MAGIC;
Daniel P. Berrange04d25292015-02-27 16:20:57 +00003243 vs->sioc = sioc;
3244 object_ref(OBJECT(vs->sioc));
3245 vs->ioc = QIO_CHANNEL(sioc);
3246 object_ref(OBJECT(vs->ioc));
Gerd Hoffmannd616ccc2014-07-29 12:14:08 +02003247 vs->vd = vd;
Daniel P. Berrange7e7e2eb2011-06-23 13:31:41 +01003248
Daniel P. Berrange04d25292015-02-27 16:20:57 +00003249 buffer_init(&vs->input, "vnc-input/%p", sioc);
3250 buffer_init(&vs->output, "vnc-output/%p", sioc);
Daniel P. Berrange04d25292015-02-27 16:20:57 +00003251 buffer_init(&vs->jobs_buffer, "vnc-jobs_buffer/%p", sioc);
Gerd Hoffmann543b9582015-10-30 12:10:02 +01003252
Li Qiang6bf21f32019-08-31 08:39:22 -07003253 buffer_init(&vs->tight->tight, "vnc-tight/%p", sioc);
3254 buffer_init(&vs->tight->zlib, "vnc-tight-zlib/%p", sioc);
3255 buffer_init(&vs->tight->gradient, "vnc-tight-gradient/%p", sioc);
Gerd Hoffmann543b9582015-10-30 12:10:02 +01003256#ifdef CONFIG_VNC_JPEG
Li Qiang6bf21f32019-08-31 08:39:22 -07003257 buffer_init(&vs->tight->jpeg, "vnc-tight-jpeg/%p", sioc);
Gerd Hoffmann543b9582015-10-30 12:10:02 +01003258#endif
Kshitij Suri95f85102022-04-08 07:13:34 +00003259#ifdef CONFIG_PNG
Li Qiang6bf21f32019-08-31 08:39:22 -07003260 buffer_init(&vs->tight->png, "vnc-tight-png/%p", sioc);
Gerd Hoffmann543b9582015-10-30 12:10:02 +01003261#endif
Daniel P. Berrange04d25292015-02-27 16:20:57 +00003262 buffer_init(&vs->zlib.zlib, "vnc-zlib/%p", sioc);
Li Qiang6bf21f32019-08-31 08:39:22 -07003263 buffer_init(&vs->zrle->zrle, "vnc-zrle/%p", sioc);
3264 buffer_init(&vs->zrle->fb, "vnc-zrle-fb/%p", sioc);
3265 buffer_init(&vs->zrle->zlib, "vnc-zrle-zlib/%p", sioc);
Gerd Hoffmann543b9582015-10-30 12:10:02 +01003266
Daniel P. Berrange7e7e2eb2011-06-23 13:31:41 +01003267 if (skipauth) {
Paolo Bonzini7d374352018-12-13 23:37:37 +01003268 vs->auth = VNC_AUTH_NONE;
3269 vs->subauth = VNC_AUTH_INVALID;
Daniel P. Berrange7e7e2eb2011-06-23 13:31:41 +01003270 } else {
Daniel P. Berrangef9148c82015-03-17 13:42:57 +00003271 if (websocket) {
3272 vs->auth = vd->ws_auth;
3273 vs->subauth = VNC_AUTH_INVALID;
3274 } else {
3275 vs->auth = vd->auth;
3276 vs->subauth = vd->subauth;
3277 }
Daniel P. Berrange7e7e2eb2011-06-23 13:31:41 +01003278 }
Daniel P. Berrange04d25292015-02-27 16:20:57 +00003279 VNC_DEBUG("Client sioc=%p ws=%d auth=%d subauth=%d\n",
3280 sioc, websocket, vs->auth, vs->subauth);
Daniel P. Berrange7e7e2eb2011-06-23 13:31:41 +01003281
Anthony Liguori7267c092011-08-20 22:09:37 -05003282 vs->lossy_rect = g_malloc0(VNC_STAT_ROWS * sizeof (*vs->lossy_rect));
Corentin Chary7d964c92011-02-04 09:05:56 +01003283 for (i = 0; i < VNC_STAT_ROWS; ++i) {
Markus Armbrusterfedf0d32015-11-03 17:12:03 +01003284 vs->lossy_rect[i] = g_new0(uint8_t, VNC_STAT_COLS);
Corentin Chary7d964c92011-02-04 09:05:56 +01003285 }
aliguori753b4052009-02-16 14:59:30 +00003286
Daniel P. Berrange04d25292015-02-27 16:20:57 +00003287 VNC_DEBUG("New client on socket %p\n", vs->sioc);
Gerd Hoffmann0f7b2862013-03-14 11:56:16 +01003288 update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
Daniel P. Berrange04d25292015-02-27 16:20:57 +00003289 qio_channel_set_blocking(vs->ioc, false, NULL);
Brandon Carpentera75d6f02017-09-12 08:21:47 -07003290 if (vs->ioc_tag) {
3291 g_source_remove(vs->ioc_tag);
3292 }
Tim Hardeck7536ee42013-01-21 11:04:44 +01003293 if (websocket) {
3294 vs->websocket = 1;
Daniel P. Berrange38e57562016-09-29 16:45:34 +01003295 if (vd->tlscreds) {
Daniel P. Berrange04d25292015-02-27 16:20:57 +00003296 vs->ioc_tag = qio_channel_add_watch(
Ding Hui2ddafce2020-10-29 11:22:41 +08003297 vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR,
3298 vncws_tls_handshake_io, vs, NULL);
Daniel P. Berrange3e305e42015-08-06 14:39:32 +01003299 } else {
Daniel P. Berrange04d25292015-02-27 16:20:57 +00003300 vs->ioc_tag = qio_channel_add_watch(
Ding Hui2ddafce2020-10-29 11:22:41 +08003301 vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR,
3302 vncws_handshake_io, vs, NULL);
Tim Hardeck0057a0d2013-04-23 16:33:01 +02003303 }
Daniel P. Berrange04d25292015-02-27 16:20:57 +00003304 } else {
3305 vs->ioc_tag = qio_channel_add_watch(
Ding Hui2ddafce2020-10-29 11:22:41 +08003306 vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR,
3307 vnc_client_io, vs, NULL);
Tim Hardeck7536ee42013-01-21 11:04:44 +01003308 }
aliguori753b4052009-02-16 14:59:30 +00003309
Luiz Capitulino4a80dba2010-01-14 14:50:56 -02003310 vnc_client_cache_addr(vs);
Wenchao Xiafb6ba0d2014-06-18 08:43:49 +02003311 vnc_qmp_event(vs, QAPI_EVENT_VNC_CONNECTED);
Gerd Hoffmann8cf36482011-11-24 18:10:49 +01003312 vnc_set_share_mode(vs, VNC_SHARE_MODE_CONNECTING);
Luiz Capitulino4a80dba2010-01-14 14:50:56 -02003313
aliguori753b4052009-02-16 14:59:30 +00003314 vs->last_x = -1;
3315 vs->last_y = -1;
3316
3317 vs->as.freq = 44100;
3318 vs->as.nchannels = 2;
Kővágó, Zoltán85bc5852019-03-08 23:34:13 +01003319 vs->as.fmt = AUDIO_FORMAT_S16;
aliguori753b4052009-02-16 14:59:30 +00003320 vs->as.endianness = 0;
3321
Corentin Charybd023f92010-07-07 20:58:02 +02003322 qemu_mutex_init(&vs->output_mutex);
Corentin Chary175b2a62012-03-14 07:58:47 +01003323 vs->bh = qemu_bh_new(vnc_jobs_bh, vs);
Corentin Charybd023f92010-07-07 20:58:02 +02003324
Gerd Hoffmanne5f34cd2014-10-02 12:09:34 +02003325 QTAILQ_INSERT_TAIL(&vd->clients, vs, next);
Gerd Hoffmannc7628bf2015-10-30 12:10:09 +01003326 if (first_client) {
3327 vnc_update_server_surface(vd);
3328 }
Stefano Stabellini1fc62412009-08-03 10:54:32 +01003329
Gerd Hoffmann1d0d59f2014-09-18 12:54:49 +02003330 graphic_hw_update(vd->dcl.con);
Stefano Stabellini1fc62412009-08-03 10:54:32 +01003331
Daniel P. Berrange90cd03a2016-09-29 16:45:39 +01003332 if (!vs->websocket) {
Daniel P. Berrangedbee9892016-09-29 16:45:40 +01003333 vnc_start_protocol(vs);
Daniel P. Berrange90cd03a2016-09-29 16:45:39 +01003334 }
3335
3336 if (vd->num_connecting > vd->connections_limit) {
3337 QTAILQ_FOREACH(vs, &vd->clients, next) {
3338 if (vs->share_mode == VNC_SHARE_MODE_CONNECTING) {
3339 vnc_disconnect_start(vs);
3340 return;
3341 }
3342 }
3343 }
3344}
3345
Daniel P. Berrangedbee9892016-09-29 16:45:40 +01003346void vnc_start_protocol(VncState *vs)
Daniel P. Berrange90cd03a2016-09-29 16:45:39 +01003347{
balrog3aa3eea2008-02-03 02:54:04 +00003348 vnc_write(vs, "RFB 003.008\n", 12);
3349 vnc_flush(vs);
3350 vnc_read_when(vs, protocol_version, 12);
aliguori753b4052009-02-16 14:59:30 +00003351
Anthony Liguori37c34d92010-03-10 09:38:29 -06003352 vs->mouse_mode_notifier.notify = check_pointer_type_change;
3353 qemu_add_mouse_mode_change_notifier(&vs->mouse_mode_notifier);
balrog3aa3eea2008-02-03 02:54:04 +00003354}
3355
Daniel P. Berrange13e1d0e2018-02-01 16:45:14 +00003356static void vnc_listen_io(QIONetListener *listener,
3357 QIOChannelSocket *cioc,
3358 void *opaque)
bellard24236862006-04-30 21:28:36 +00003359{
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01003360 VncDisplay *vd = opaque;
Daniel P. Berrange13e1d0e2018-02-01 16:45:14 +00003361 bool isWebsock = listener == vd->wslistener;
Daniel P. Berrange4ee74fa2017-02-03 12:06:44 +00003362
Daniel P. Berrange13e1d0e2018-02-01 16:45:14 +00003363 qio_channel_set_name(QIO_CHANNEL(cioc),
3364 isWebsock ? "vnc-ws-server" : "vnc-server");
3365 qio_channel_set_delay(QIO_CHANNEL(cioc), false);
3366 vnc_connect(vd, cioc, false, isWebsock);
Tim Hardeck7536ee42013-01-21 11:04:44 +01003367}
Tim Hardeck7536ee42013-01-21 11:04:44 +01003368
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01003369static const DisplayChangeListenerOps dcl_ops = {
Benjamin Herrenschmidt34da30a2014-07-07 17:18:19 +10003370 .dpy_name = "vnc",
3371 .dpy_refresh = vnc_refresh,
Benjamin Herrenschmidt34da30a2014-07-07 17:18:19 +10003372 .dpy_gfx_update = vnc_dpy_update,
3373 .dpy_gfx_switch = vnc_dpy_switch,
3374 .dpy_gfx_check_format = qemu_pixman_check_format,
3375 .dpy_mouse_set = vnc_mouse_set,
3376 .dpy_cursor_define = vnc_dpy_cursor_define,
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01003377};
3378
Fei Liab4f9312018-10-17 10:26:50 +02003379void vnc_display_init(const char *id, Error **errp)
bellard24236862006-04-30 21:28:36 +00003380{
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01003381 VncDisplay *vd;
Gerd Hoffmann4db14622014-09-16 12:33:03 +02003382
3383 if (vnc_display_find(id) != NULL) {
3384 return;
3385 }
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01003386 vd = g_malloc0(sizeof(*vd));
bellard24236862006-04-30 21:28:36 +00003387
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01003388 vd->id = strdup(id);
3389 QTAILQ_INSERT_TAIL(&vnc_displays, vd, next);
bellard24236862006-04-30 21:28:36 +00003390
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01003391 QTAILQ_INIT(&vd->clients);
3392 vd->expires = TIME_MAX;
bellard24236862006-04-30 21:28:36 +00003393
Gerd Hoffmann40066172014-05-21 13:18:20 +02003394 if (keyboard_layout) {
3395 trace_vnc_key_map_init(keyboard_layout);
Fei Liab4f9312018-10-17 10:26:50 +02003396 vd->kbd_layout = init_keyboard_layout(name2keysym,
3397 keyboard_layout, errp);
Gerd Hoffmann40066172014-05-21 13:18:20 +02003398 } else {
Fei Liab4f9312018-10-17 10:26:50 +02003399 vd->kbd_layout = init_keyboard_layout(name2keysym, "en-us", errp);
Gerd Hoffmann40066172014-05-21 13:18:20 +02003400 }
bellard24236862006-04-30 21:28:36 +00003401
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01003402 if (!vd->kbd_layout) {
Fei Liab4f9312018-10-17 10:26:50 +02003403 return;
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01003404 }
bellard24236862006-04-30 21:28:36 +00003405
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01003406 vd->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE;
3407 vd->connections_limit = 32;
Daniel P. Berrange12e29b12016-08-02 11:45:26 +01003408
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01003409 qemu_mutex_init(&vd->mutex);
Corentin Charybd023f92010-07-07 20:58:02 +02003410 vnc_start_worker_thread();
Corentin Charybd023f92010-07-07 20:58:02 +02003411
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01003412 vd->dcl.ops = &dcl_ops;
3413 register_displaychangelistener(&vd->dcl);
Gerd Hoffmannc2f2ba42019-01-22 10:28:12 +01003414 vd->kbd = qkbd_state_init(vd->dcl.con);
ths71cab5c2007-08-25 01:35:38 +00003415}
ths73fc9742006-12-22 02:09:07 +00003416
ths6f430242007-08-25 01:39:57 +00003417
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01003418static void vnc_display_close(VncDisplay *vd)
ths71cab5c2007-08-25 01:35:38 +00003419{
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01003420 if (!vd) {
aliguori452b4d82009-02-11 21:00:38 +00003421 return;
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01003422 }
3423 vd->is_unix = false;
Daniel P. Berrange4ee74fa2017-02-03 12:06:44 +00003424
Daniel P. Berrange13e1d0e2018-02-01 16:45:14 +00003425 if (vd->listener) {
3426 qio_net_listener_disconnect(vd->listener);
3427 object_unref(OBJECT(vd->listener));
Tim Hardeck7536ee42013-01-21 11:04:44 +01003428 }
Daniel P. Berrange13e1d0e2018-02-01 16:45:14 +00003429 vd->listener = NULL;
3430
3431 if (vd->wslistener) {
3432 qio_net_listener_disconnect(vd->wslistener);
3433 object_unref(OBJECT(vd->wslistener));
3434 }
3435 vd->wslistener = NULL;
Daniel P. Berrange4ee74fa2017-02-03 12:06:44 +00003436
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01003437 vd->auth = VNC_AUTH_INVALID;
3438 vd->subauth = VNC_AUTH_INVALID;
3439 if (vd->tlscreds) {
Zihao Chang521534d2021-01-11 21:19:11 +08003440 object_unref(OBJECT(vd->tlscreds));
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01003441 vd->tlscreds = NULL;
Daniel P. Berrange3e305e42015-08-06 14:39:32 +01003442 }
Daniel P. Berrangeb76806d2016-02-18 18:40:24 +00003443 if (vd->tlsauthz) {
3444 object_unparent(OBJECT(vd->tlsauthz));
3445 vd->tlsauthz = NULL;
3446 }
3447 g_free(vd->tlsauthzid);
3448 vd->tlsauthzid = NULL;
Pierre Ossmana54f0d22017-01-09 17:14:02 +01003449 if (vd->lock_key_sync) {
3450 qemu_remove_led_event_handler(vd->led);
Gerd Hoffmann2dc120b2017-02-21 14:05:32 +01003451 vd->led = NULL;
Pierre Ossmana54f0d22017-01-09 17:14:02 +01003452 }
Daniel P. Berrangeb76806d2016-02-18 18:40:24 +00003453#ifdef CONFIG_VNC_SASL
3454 if (vd->sasl.authz) {
3455 object_unparent(OBJECT(vd->sasl.authz));
3456 vd->sasl.authz = NULL;
3457 }
3458 g_free(vd->sasl.authzid);
3459 vd->sasl.authzid = NULL;
3460#endif
ths71cab5c2007-08-25 01:35:38 +00003461}
3462
Gerd Hoffmann14f71432014-07-29 12:24:55 +02003463int vnc_display_password(const char *id, const char *password)
Anthony Liguori1cd20f82011-01-31 14:27:36 -06003464{
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01003465 VncDisplay *vd = vnc_display_find(id);
Anthony Liguori1cd20f82011-01-31 14:27:36 -06003466
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01003467 if (!vd) {
Luiz Capitulinoa6aa9d32011-12-07 10:19:10 -02003468 return -EINVAL;
Anthony Liguori1cd20f82011-01-31 14:27:36 -06003469 }
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01003470 if (vd->auth == VNC_AUTH_NONE) {
Gerd Hoffmanncf864562013-12-11 13:15:37 +01003471 error_printf_unless_qmp("If you want use passwords please enable "
Markus Armbruster7ea7d362016-08-03 13:37:54 +02003472 "password auth using '-vnc ${dpy},password'.\n");
Gerd Hoffmanncf864562013-12-11 13:15:37 +01003473 return -EINVAL;
Anthony Liguori1cd20f82011-01-31 14:27:36 -06003474 }
3475
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01003476 g_free(vd->password);
3477 vd->password = g_strdup(password);
Luiz Capitulinoa6aa9d32011-12-07 10:19:10 -02003478
3479 return 0;
ths70848512007-08-25 01:37:05 +00003480}
3481
Gerd Hoffmann14f71432014-07-29 12:24:55 +02003482int vnc_display_pw_expire(const char *id, time_t expires)
Gerd Hoffmann3c9405a2010-10-07 11:50:45 +02003483{
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01003484 VncDisplay *vd = vnc_display_find(id);
Gerd Hoffmann3c9405a2010-10-07 11:50:45 +02003485
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01003486 if (!vd) {
Gerd Hoffmann1643f2b2012-05-24 10:55:01 +02003487 return -EINVAL;
3488 }
3489
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01003490 vd->expires = expires;
Gerd Hoffmann3c9405a2010-10-07 11:50:45 +02003491 return 0;
3492}
3493
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01003494static void vnc_display_print_local_addr(VncDisplay *vd)
Anthony Liguorif92f8af2009-05-20 13:01:02 -05003495{
Markus Armbrusterbd269eb2017-04-26 09:36:41 +02003496 SocketAddress *addr;
Gerd Hoffmannd616ccc2014-07-29 12:14:08 +02003497
Daniel P. Berrange13e1d0e2018-02-01 16:45:14 +00003498 if (!vd->listener || !vd->listener->nsioc) {
Daniel P. Berrange4ee74fa2017-02-03 12:06:44 +00003499 return;
3500 }
3501
Markus Armbrusterb94b3c02020-06-30 11:03:36 +02003502 addr = qio_channel_socket_get_local_address(vd->listener->sioc[0], NULL);
Daniel P. Berrange04d25292015-02-27 16:20:57 +00003503 if (!addr) {
Paolo Bonzini33df7bf2016-05-31 14:59:08 +02003504 return;
Daniel P. Berrange04d25292015-02-27 16:20:57 +00003505 }
3506
Markus Armbrusterbd269eb2017-04-26 09:36:41 +02003507 if (addr->type != SOCKET_ADDRESS_TYPE_INET) {
3508 qapi_free_SocketAddress(addr);
Paolo Bonzini33df7bf2016-05-31 14:59:08 +02003509 return;
Daniel P. Berrange04d25292015-02-27 16:20:57 +00003510 }
Paolo Bonzini33df7bf2016-05-31 14:59:08 +02003511 error_printf_unless_qmp("VNC server running on %s:%s\n",
Markus Armbrusterbd269eb2017-04-26 09:36:41 +02003512 addr->u.inet.host,
3513 addr->u.inet.port);
3514 qapi_free_SocketAddress(addr);
Anthony Liguorif92f8af2009-05-20 13:01:02 -05003515}
3516
Gerd Hoffmann4db14622014-09-16 12:33:03 +02003517static QemuOptsList qemu_vnc_opts = {
3518 .name = "vnc",
3519 .head = QTAILQ_HEAD_INITIALIZER(qemu_vnc_opts.head),
3520 .implied_opt_name = "vnc",
3521 .desc = {
3522 {
3523 .name = "vnc",
3524 .type = QEMU_OPT_STRING,
3525 },{
3526 .name = "websocket",
3527 .type = QEMU_OPT_STRING,
3528 },{
Daniel P. Berrange3e305e42015-08-06 14:39:32 +01003529 .name = "tls-creds",
3530 .type = QEMU_OPT_STRING,
3531 },{
Gerd Hoffmann4db14622014-09-16 12:33:03 +02003532 .name = "share",
3533 .type = QEMU_OPT_STRING,
3534 },{
Gerd Hoffmann1d0d59f2014-09-18 12:54:49 +02003535 .name = "display",
3536 .type = QEMU_OPT_STRING,
3537 },{
3538 .name = "head",
3539 .type = QEMU_OPT_NUMBER,
3540 },{
Gerd Hoffmanne5f34cd2014-10-02 12:09:34 +02003541 .name = "connections",
3542 .type = QEMU_OPT_NUMBER,
3543 },{
Gonglei88428b72015-01-30 10:14:34 +08003544 .name = "to",
3545 .type = QEMU_OPT_NUMBER,
3546 },{
3547 .name = "ipv4",
3548 .type = QEMU_OPT_BOOL,
3549 },{
3550 .name = "ipv6",
3551 .type = QEMU_OPT_BOOL,
3552 },{
Gerd Hoffmann4db14622014-09-16 12:33:03 +02003553 .name = "password",
3554 .type = QEMU_OPT_BOOL,
3555 },{
Daniel P. Berrangé6c6840e2021-03-11 11:43:41 +00003556 .name = "password-secret",
3557 .type = QEMU_OPT_STRING,
3558 },{
Gerd Hoffmann4db14622014-09-16 12:33:03 +02003559 .name = "reverse",
3560 .type = QEMU_OPT_BOOL,
3561 },{
3562 .name = "lock-key-sync",
3563 .type = QEMU_OPT_BOOL,
3564 },{
Gerd Hoffmannc5ce8332016-06-01 08:22:30 +02003565 .name = "key-delay-ms",
3566 .type = QEMU_OPT_NUMBER,
3567 },{
Gerd Hoffmann4db14622014-09-16 12:33:03 +02003568 .name = "sasl",
3569 .type = QEMU_OPT_BOOL,
3570 },{
Daniel P. Berrange55cf09a2019-02-27 14:57:54 +00003571 .name = "tls-authz",
3572 .type = QEMU_OPT_STRING,
3573 },{
3574 .name = "sasl-authz",
3575 .type = QEMU_OPT_STRING,
3576 },{
Gerd Hoffmann4db14622014-09-16 12:33:03 +02003577 .name = "lossy",
3578 .type = QEMU_OPT_BOOL,
3579 },{
3580 .name = "non-adaptive",
3581 .type = QEMU_OPT_BOOL,
Kővágó, Zoltánf0b9f362019-08-19 01:06:48 +02003582 },{
3583 .name = "audiodev",
3584 .type = QEMU_OPT_STRING,
Daniel P. Berrangé7b5fa0b2020-12-11 16:08:25 +00003585 },{
3586 .name = "power-control",
3587 .type = QEMU_OPT_BOOL,
Gerd Hoffmann4db14622014-09-16 12:33:03 +02003588 },
3589 { /* end of list */ }
3590 },
3591};
3592
Daniel P. Berrange0dd72e12015-03-17 13:42:56 +00003593
Daniel P. Berrange3e305e42015-08-06 14:39:32 +01003594static int
Daniel P. Berrangeeda24e12016-09-29 16:45:36 +01003595vnc_display_setup_auth(int *auth,
3596 int *subauth,
3597 QCryptoTLSCreds *tlscreds,
Daniel P. Berrange0dd72e12015-03-17 13:42:56 +00003598 bool password,
3599 bool sasl,
Daniel P. Berrange3e305e42015-08-06 14:39:32 +01003600 bool websocket,
3601 Error **errp)
Daniel P. Berrange0dd72e12015-03-17 13:42:56 +00003602{
3603 /*
3604 * We have a choice of 3 authentication options
3605 *
3606 * 1. none
3607 * 2. vnc
3608 * 3. sasl
3609 *
3610 * The channel can be run in 2 modes
3611 *
3612 * 1. clear
3613 * 2. tls
3614 *
3615 * And TLS can use 2 types of credentials
3616 *
3617 * 1. anon
3618 * 2. x509
3619 *
3620 * We thus have 9 possible logical combinations
3621 *
3622 * 1. clear + none
3623 * 2. clear + vnc
3624 * 3. clear + sasl
3625 * 4. tls + anon + none
3626 * 5. tls + anon + vnc
3627 * 6. tls + anon + sasl
3628 * 7. tls + x509 + none
3629 * 8. tls + x509 + vnc
3630 * 9. tls + x509 + sasl
3631 *
3632 * These need to be mapped into the VNC auth schemes
3633 * in an appropriate manner. In regular VNC, all the
3634 * TLS options get mapped into VNC_AUTH_VENCRYPT
3635 * sub-auth types.
Daniel P. Berrangef9148c82015-03-17 13:42:57 +00003636 *
3637 * In websockets, the https:// protocol already provides
3638 * TLS support, so there is no need to make use of the
3639 * VeNCrypt extension. Furthermore, websockets browser
3640 * clients could not use VeNCrypt even if they wanted to,
3641 * as they cannot control when the TLS handshake takes
3642 * place. Thus there is no option but to rely on https://,
3643 * meaning combinations 4->6 and 7->9 will be mapped to
3644 * VNC auth schemes in the same way as combos 1->3.
3645 *
3646 * Regardless of fact that we have a different mapping to
3647 * VNC auth mechs for plain VNC vs websockets VNC, the end
3648 * result has the same security characteristics.
Daniel P. Berrange0dd72e12015-03-17 13:42:56 +00003649 */
Daniel P. Berrangeeda24e12016-09-29 16:45:36 +01003650 if (websocket || !tlscreds) {
3651 if (password) {
Daniel P. Berrange0dd72e12015-03-17 13:42:56 +00003652 VNC_DEBUG("Initializing VNC server with password auth\n");
Daniel P. Berrangeeda24e12016-09-29 16:45:36 +01003653 *auth = VNC_AUTH_VNC;
3654 } else if (sasl) {
Daniel P. Berrange0dd72e12015-03-17 13:42:56 +00003655 VNC_DEBUG("Initializing VNC server with SASL auth\n");
Daniel P. Berrangeeda24e12016-09-29 16:45:36 +01003656 *auth = VNC_AUTH_SASL;
Daniel P. Berrange0dd72e12015-03-17 13:42:56 +00003657 } else {
3658 VNC_DEBUG("Initializing VNC server with no auth\n");
Daniel P. Berrangeeda24e12016-09-29 16:45:36 +01003659 *auth = VNC_AUTH_NONE;
Daniel P. Berrange0dd72e12015-03-17 13:42:56 +00003660 }
Daniel P. Berrangeeda24e12016-09-29 16:45:36 +01003661 *subauth = VNC_AUTH_INVALID;
3662 } else {
3663 bool is_x509 = object_dynamic_cast(OBJECT(tlscreds),
3664 TYPE_QCRYPTO_TLS_CREDS_X509) != NULL;
3665 bool is_anon = object_dynamic_cast(OBJECT(tlscreds),
3666 TYPE_QCRYPTO_TLS_CREDS_ANON) != NULL;
3667
3668 if (!is_x509 && !is_anon) {
3669 error_setg(errp,
3670 "Unsupported TLS cred type %s",
3671 object_get_typename(OBJECT(tlscreds)));
3672 return -1;
3673 }
3674 *auth = VNC_AUTH_VENCRYPT;
3675 if (password) {
3676 if (is_x509) {
3677 VNC_DEBUG("Initializing VNC server with x509 password auth\n");
3678 *subauth = VNC_AUTH_VENCRYPT_X509VNC;
3679 } else {
3680 VNC_DEBUG("Initializing VNC server with TLS password auth\n");
3681 *subauth = VNC_AUTH_VENCRYPT_TLSVNC;
3682 }
3683
3684 } else if (sasl) {
3685 if (is_x509) {
3686 VNC_DEBUG("Initializing VNC server with x509 SASL auth\n");
3687 *subauth = VNC_AUTH_VENCRYPT_X509SASL;
3688 } else {
3689 VNC_DEBUG("Initializing VNC server with TLS SASL auth\n");
3690 *subauth = VNC_AUTH_VENCRYPT_TLSSASL;
3691 }
Daniel P. Berrangef9148c82015-03-17 13:42:57 +00003692 } else {
Daniel P. Berrangeeda24e12016-09-29 16:45:36 +01003693 if (is_x509) {
3694 VNC_DEBUG("Initializing VNC server with x509 no auth\n");
3695 *subauth = VNC_AUTH_VENCRYPT_X509NONE;
3696 } else {
3697 VNC_DEBUG("Initializing VNC server with TLS no auth\n");
3698 *subauth = VNC_AUTH_VENCRYPT_TLSNONE;
3699 }
Daniel P. Berrangef9148c82015-03-17 13:42:57 +00003700 }
Daniel P. Berrange0dd72e12015-03-17 13:42:56 +00003701 }
Daniel P. Berrange3e305e42015-08-06 14:39:32 +01003702 return 0;
Daniel P. Berrange0dd72e12015-03-17 13:42:56 +00003703}
3704
Daniel P. Berrange3e305e42015-08-06 14:39:32 +01003705
Daniel P. Berrange275e0d62017-02-03 12:06:45 +00003706static int vnc_display_get_address(const char *addrstr,
3707 bool websocket,
Gerd Hoffmanne5766eb2017-03-14 09:26:58 +01003708 bool reverse,
Daniel P. Berrange275e0d62017-02-03 12:06:45 +00003709 int displaynum,
3710 int to,
3711 bool has_ipv4,
3712 bool has_ipv6,
3713 bool ipv4,
3714 bool ipv6,
Markus Armbrusterbd269eb2017-04-26 09:36:41 +02003715 SocketAddress **retaddr,
Daniel P. Berrange275e0d62017-02-03 12:06:45 +00003716 Error **errp)
3717{
3718 int ret = -1;
Markus Armbrusterbd269eb2017-04-26 09:36:41 +02003719 SocketAddress *addr = NULL;
Daniel P. Berrange275e0d62017-02-03 12:06:45 +00003720
Markus Armbrusterbd269eb2017-04-26 09:36:41 +02003721 addr = g_new0(SocketAddress, 1);
Daniel P. Berrange275e0d62017-02-03 12:06:45 +00003722
3723 if (strncmp(addrstr, "unix:", 5) == 0) {
Markus Armbrusterbd269eb2017-04-26 09:36:41 +02003724 addr->type = SOCKET_ADDRESS_TYPE_UNIX;
3725 addr->u.q_unix.path = g_strdup(addrstr + 5);
Daniel P. Berrange275e0d62017-02-03 12:06:45 +00003726
3727 if (websocket) {
3728 error_setg(errp, "UNIX sockets not supported with websock");
3729 goto cleanup;
3730 }
3731
3732 if (to) {
3733 error_setg(errp, "Port range not support with UNIX socket");
3734 goto cleanup;
3735 }
3736 ret = 0;
3737 } else {
3738 const char *port;
3739 size_t hostlen;
Eric Blakebd1386c2023-05-22 14:04:29 -05003740 uint64_t baseport = 0;
Daniel P. Berrange275e0d62017-02-03 12:06:45 +00003741 InetSocketAddress *inet;
3742
3743 port = strrchr(addrstr, ':');
3744 if (!port) {
3745 if (websocket) {
3746 hostlen = 0;
3747 port = addrstr;
3748 } else {
3749 error_setg(errp, "no vnc port specified");
3750 goto cleanup;
3751 }
3752 } else {
3753 hostlen = port - addrstr;
3754 port++;
3755 if (*port == '\0') {
3756 error_setg(errp, "vnc port cannot be empty");
3757 goto cleanup;
3758 }
3759 }
3760
Markus Armbrusterbd269eb2017-04-26 09:36:41 +02003761 addr->type = SOCKET_ADDRESS_TYPE_INET;
3762 inet = &addr->u.inet;
Paolo Bonzini3f9c41c2023-03-30 14:23:40 +02003763 if (hostlen && addrstr[0] == '[' && addrstr[hostlen - 1] == ']') {
Daniel P. Berrange275e0d62017-02-03 12:06:45 +00003764 inet->host = g_strndup(addrstr + 1, hostlen - 2);
3765 } else {
3766 inet->host = g_strndup(addrstr, hostlen);
3767 }
3768 /* plain VNC port is just an offset, for websocket
3769 * port is absolute */
3770 if (websocket) {
3771 if (g_str_equal(addrstr, "") ||
3772 g_str_equal(addrstr, "on")) {
Daniel P. Berrange396f9352017-02-03 12:06:49 +00003773 if (displaynum == -1) {
3774 error_setg(errp, "explicit websocket port is required");
3775 goto cleanup;
3776 }
Daniel P. Berrange275e0d62017-02-03 12:06:45 +00003777 inet->port = g_strdup_printf(
3778 "%d", displaynum + 5700);
3779 if (to) {
3780 inet->has_to = true;
3781 inet->to = to + 5700;
3782 }
3783 } else {
3784 inet->port = g_strdup(port);
3785 }
3786 } else {
Gerd Hoffmanne5766eb2017-03-14 09:26:58 +01003787 int offset = reverse ? 0 : 5900;
Eric Blakebd1386c2023-05-22 14:04:29 -05003788 if (parse_uint_full(port, 10, &baseport) < 0) {
Daniel P. Berrange275e0d62017-02-03 12:06:45 +00003789 error_setg(errp, "can't convert to a number: %s", port);
3790 goto cleanup;
3791 }
3792 if (baseport > 65535 ||
Gerd Hoffmanne5766eb2017-03-14 09:26:58 +01003793 baseport + offset > 65535) {
Daniel P. Berrange275e0d62017-02-03 12:06:45 +00003794 error_setg(errp, "port %s out of range", port);
3795 goto cleanup;
3796 }
3797 inet->port = g_strdup_printf(
Gerd Hoffmanne5766eb2017-03-14 09:26:58 +01003798 "%d", (int)baseport + offset);
Daniel P. Berrange275e0d62017-02-03 12:06:45 +00003799
3800 if (to) {
3801 inet->has_to = true;
Gerd Hoffmanne5766eb2017-03-14 09:26:58 +01003802 inet->to = to + offset;
Daniel P. Berrange275e0d62017-02-03 12:06:45 +00003803 }
3804 }
3805
3806 inet->ipv4 = ipv4;
3807 inet->has_ipv4 = has_ipv4;
3808 inet->ipv6 = ipv6;
3809 inet->has_ipv6 = has_ipv6;
3810
3811 ret = baseport;
3812 }
3813
3814 *retaddr = addr;
3815
3816 cleanup:
3817 if (ret < 0) {
Markus Armbrusterbd269eb2017-04-26 09:36:41 +02003818 qapi_free_SocketAddress(addr);
Daniel P. Berrange275e0d62017-02-03 12:06:45 +00003819 }
3820 return ret;
3821}
3822
3823static int vnc_display_get_addresses(QemuOpts *opts,
Gerd Hoffmanne5766eb2017-03-14 09:26:58 +01003824 bool reverse,
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03003825 SocketAddressList **saddr_list_ret,
3826 SocketAddressList **wsaddr_list_ret,
Daniel P. Berrange275e0d62017-02-03 12:06:45 +00003827 Error **errp)
3828{
Markus Armbrusterbd269eb2017-04-26 09:36:41 +02003829 SocketAddress *saddr = NULL;
3830 SocketAddress *wsaddr = NULL;
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03003831 g_autoptr(SocketAddressList) saddr_list = NULL;
3832 SocketAddressList **saddr_tail = &saddr_list;
3833 SocketAddress *single_saddr = NULL;
3834 g_autoptr(SocketAddressList) wsaddr_list = NULL;
3835 SocketAddressList **wsaddr_tail = &wsaddr_list;
Daniel P. Berrange396f9352017-02-03 12:06:49 +00003836 QemuOptsIter addriter;
3837 const char *addr;
Daniel P. Berrange275e0d62017-02-03 12:06:45 +00003838 int to = qemu_opt_get_number(opts, "to", 0);
3839 bool has_ipv4 = qemu_opt_get(opts, "ipv4");
3840 bool has_ipv6 = qemu_opt_get(opts, "ipv6");
3841 bool ipv4 = qemu_opt_get_bool(opts, "ipv4", false);
3842 bool ipv6 = qemu_opt_get_bool(opts, "ipv6", false);
Daniel P. Berrange396f9352017-02-03 12:06:49 +00003843 int displaynum = -1;
Daniel P. Berrange396f9352017-02-03 12:06:49 +00003844
3845 addr = qemu_opt_get(opts, "vnc");
3846 if (addr == NULL || g_str_equal(addr, "none")) {
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03003847 return 0;
Daniel P. Berrange275e0d62017-02-03 12:06:45 +00003848 }
Daniel P. Berrange396f9352017-02-03 12:06:49 +00003849 if (qemu_opt_get(opts, "websocket") &&
Daniel P. Berrange275e0d62017-02-03 12:06:45 +00003850 !qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA1)) {
3851 error_setg(errp,
3852 "SHA1 hash support is required for websockets");
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03003853 return -1;
Daniel P. Berrange275e0d62017-02-03 12:06:45 +00003854 }
3855
Daniel P. Berrange396f9352017-02-03 12:06:49 +00003856 qemu_opt_iter_init(&addriter, opts, "vnc");
3857 while ((addr = qemu_opt_iter_next(&addriter)) != NULL) {
3858 int rv;
Gerd Hoffmanne5766eb2017-03-14 09:26:58 +01003859 rv = vnc_display_get_address(addr, false, reverse, 0, to,
Daniel P. Berrange396f9352017-02-03 12:06:49 +00003860 has_ipv4, has_ipv6,
3861 ipv4, ipv6,
3862 &saddr, errp);
3863 if (rv < 0) {
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03003864 return -1;
Daniel P. Berrange396f9352017-02-03 12:06:49 +00003865 }
3866 /* Historical compat - first listen address can be used
3867 * to set the default websocket port
3868 */
3869 if (displaynum == -1) {
3870 displaynum = rv;
3871 }
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03003872 QAPI_LIST_APPEND(saddr_tail, saddr);
Daniel P. Berrange275e0d62017-02-03 12:06:45 +00003873 }
Daniel P. Berrange396f9352017-02-03 12:06:49 +00003874
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03003875 if (saddr_list && !saddr_list->next) {
3876 single_saddr = saddr_list->value;
3877 } else {
3878 /*
3879 * If we had multiple primary displays, we don't do defaults
3880 * for websocket, and require explicit config instead.
3881 */
Daniel P. Berrange396f9352017-02-03 12:06:49 +00003882 displaynum = -1;
3883 }
3884
3885 qemu_opt_iter_init(&addriter, opts, "websocket");
3886 while ((addr = qemu_opt_iter_next(&addriter)) != NULL) {
Gerd Hoffmanne5766eb2017-03-14 09:26:58 +01003887 if (vnc_display_get_address(addr, true, reverse, displaynum, to,
Daniel P. Berrange275e0d62017-02-03 12:06:45 +00003888 has_ipv4, has_ipv6,
3889 ipv4, ipv6,
3890 &wsaddr, errp) < 0) {
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03003891 return -1;
Daniel P. Berrange275e0d62017-02-03 12:06:45 +00003892 }
Daniel P. Berrange396f9352017-02-03 12:06:49 +00003893
3894 /* Historical compat - if only a single listen address was
3895 * provided, then this is used to set the default listen
3896 * address for websocket too
3897 */
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03003898 if (single_saddr &&
3899 single_saddr->type == SOCKET_ADDRESS_TYPE_INET &&
Markus Armbrusterbd269eb2017-04-26 09:36:41 +02003900 wsaddr->type == SOCKET_ADDRESS_TYPE_INET &&
3901 g_str_equal(wsaddr->u.inet.host, "") &&
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03003902 !g_str_equal(single_saddr->u.inet.host, "")) {
Markus Armbrusterbd269eb2017-04-26 09:36:41 +02003903 g_free(wsaddr->u.inet.host);
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03003904 wsaddr->u.inet.host = g_strdup(single_saddr->u.inet.host);
Daniel P. Berrange275e0d62017-02-03 12:06:45 +00003905 }
Daniel P. Berrange275e0d62017-02-03 12:06:45 +00003906
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03003907 QAPI_LIST_APPEND(wsaddr_tail, wsaddr);
Daniel P. Berrange396f9352017-02-03 12:06:49 +00003908 }
3909
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03003910 *saddr_list_ret = g_steal_pointer(&saddr_list);
3911 *wsaddr_list_ret = g_steal_pointer(&wsaddr_list);
3912 return 0;
Daniel P. Berrange275e0d62017-02-03 12:06:45 +00003913}
3914
Daniel P. Berrange8bd22f42017-02-03 12:06:46 +00003915static int vnc_display_connect(VncDisplay *vd,
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03003916 SocketAddressList *saddr_list,
3917 SocketAddressList *wsaddr_list,
Daniel P. Berrange8bd22f42017-02-03 12:06:46 +00003918 Error **errp)
3919{
3920 /* connect to viewer */
3921 QIOChannelSocket *sioc = NULL;
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03003922 if (wsaddr_list) {
Daniel P. Berrange8bd22f42017-02-03 12:06:46 +00003923 error_setg(errp, "Cannot use websockets in reverse mode");
3924 return -1;
3925 }
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03003926 if (!saddr_list || saddr_list->next) {
Daniel P. Berrange396f9352017-02-03 12:06:49 +00003927 error_setg(errp, "Expected a single address in reverse mode");
3928 return -1;
3929 }
Markus Armbrusterbd269eb2017-04-26 09:36:41 +02003930 /* TODO SOCKET_ADDRESS_TYPE_FD when fd has AF_UNIX */
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03003931 vd->is_unix = saddr_list->value->type == SOCKET_ADDRESS_TYPE_UNIX;
Daniel P. Berrange8bd22f42017-02-03 12:06:46 +00003932 sioc = qio_channel_socket_new();
3933 qio_channel_set_name(QIO_CHANNEL(sioc), "vnc-reverse");
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03003934 if (qio_channel_socket_connect_sync(sioc, saddr_list->value, errp) < 0) {
Alex Chen5f8679f2020-11-26 06:57:02 +00003935 object_unref(OBJECT(sioc));
Daniel P. Berrange8bd22f42017-02-03 12:06:46 +00003936 return -1;
3937 }
3938 vnc_connect(vd, sioc, false, false);
3939 object_unref(OBJECT(sioc));
3940 return 0;
3941}
3942
3943
Daniel P. Berrange8bd22f42017-02-03 12:06:46 +00003944static int vnc_display_listen(VncDisplay *vd,
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03003945 SocketAddressList *saddr_list,
3946 SocketAddressList *wsaddr_list,
Daniel P. Berrange8bd22f42017-02-03 12:06:46 +00003947 Error **errp)
3948{
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03003949 SocketAddressList *el;
Daniel P. Berrange8bd22f42017-02-03 12:06:46 +00003950
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03003951 if (saddr_list) {
Daniel P. Berrange13e1d0e2018-02-01 16:45:14 +00003952 vd->listener = qio_net_listener_new();
3953 qio_net_listener_set_name(vd->listener, "vnc-listen");
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03003954 for (el = saddr_list; el; el = el->next) {
Daniel P. Berrange13e1d0e2018-02-01 16:45:14 +00003955 if (qio_net_listener_open_sync(vd->listener,
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03003956 el->value, 1,
Daniel P. Berrange13e1d0e2018-02-01 16:45:14 +00003957 errp) < 0) {
3958 return -1;
3959 }
Daniel P. Berrange396f9352017-02-03 12:06:49 +00003960 }
Daniel P. Berrange13e1d0e2018-02-01 16:45:14 +00003961
3962 qio_net_listener_set_client_func(vd->listener,
3963 vnc_listen_io, vd, NULL);
Daniel P. Berrange8bd22f42017-02-03 12:06:46 +00003964 }
Daniel P. Berrange13e1d0e2018-02-01 16:45:14 +00003965
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03003966 if (wsaddr_list) {
Daniel P. Berrange13e1d0e2018-02-01 16:45:14 +00003967 vd->wslistener = qio_net_listener_new();
3968 qio_net_listener_set_name(vd->wslistener, "vnc-ws-listen");
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03003969 for (el = wsaddr_list; el; el = el->next) {
Daniel P. Berrange13e1d0e2018-02-01 16:45:14 +00003970 if (qio_net_listener_open_sync(vd->wslistener,
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03003971 el->value, 1,
Daniel P. Berrange13e1d0e2018-02-01 16:45:14 +00003972 errp) < 0) {
3973 return -1;
3974 }
Daniel P. Berrange396f9352017-02-03 12:06:49 +00003975 }
Daniel P. Berrange13e1d0e2018-02-01 16:45:14 +00003976
3977 qio_net_listener_set_client_func(vd->wslistener,
3978 vnc_listen_io, vd, NULL);
Daniel P. Berrange8bd22f42017-02-03 12:06:46 +00003979 }
3980
3981 return 0;
3982}
3983
Vladimir Sementsov-Ogievskiyabea1942022-04-01 17:39:35 +03003984bool vnc_display_update(DisplayUpdateOptionsVNC *arg, Error **errp)
3985{
3986 VncDisplay *vd = vnc_display_find(NULL);
3987
3988 if (!vd) {
3989 error_setg(errp, "Can not find vnc display");
3990 return false;
3991 }
3992
3993 if (arg->has_addresses) {
3994 if (vd->listener) {
3995 qio_net_listener_disconnect(vd->listener);
3996 object_unref(OBJECT(vd->listener));
3997 vd->listener = NULL;
3998 }
3999
4000 if (vnc_display_listen(vd, arg->addresses, NULL, errp) < 0) {
4001 return false;
4002 }
4003 }
4004
4005 return true;
4006}
Daniel P. Berrange8bd22f42017-02-03 12:06:46 +00004007
Gerd Hoffmann4db14622014-09-16 12:33:03 +02004008void vnc_display_open(const char *id, Error **errp)
ths71cab5c2007-08-25 01:35:38 +00004009{
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01004010 VncDisplay *vd = vnc_display_find(id);
Gerd Hoffmann4db14622014-09-16 12:33:03 +02004011 QemuOpts *opts = qemu_opts_find(&qemu_vnc_opts, id);
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03004012 g_autoptr(SocketAddressList) saddr_list = NULL;
4013 g_autoptr(SocketAddressList) wsaddr_list = NULL;
Gongleie2a11d92015-01-30 10:14:35 +08004014 const char *share, *device_id;
Gerd Hoffmann1d0d59f2014-09-18 12:54:49 +02004015 QemuConsole *con;
Gongleia2c72de2015-01-30 10:14:36 +08004016 bool password = false;
4017 bool reverse = false;
Daniel P. Berrange3e305e42015-08-06 14:39:32 +01004018 const char *credid;
Gongleia2c72de2015-01-30 10:14:36 +08004019 bool sasl = false;
Daniel P. Berrange55cf09a2019-02-27 14:57:54 +00004020 const char *tlsauthz;
4021 const char *saslauthz;
Gerd Hoffmann3a0558b2010-03-10 17:12:02 +01004022 int lock_key_sync = 1;
Gerd Hoffmannc5ce8332016-06-01 08:22:30 +02004023 int key_delay_ms;
Kővágó, Zoltánf0b9f362019-08-19 01:06:48 +02004024 const char *audiodev;
Daniel P. Berrangé6c6840e2021-03-11 11:43:41 +00004025 const char *passwordSecret;
ths71cab5c2007-08-25 01:35:38 +00004026
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01004027 if (!vd) {
Paolo Bonzini2d55f0e2012-10-02 10:17:21 +02004028 error_setg(errp, "VNC display not active");
4029 return;
4030 }
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01004031 vnc_display_close(vd);
Gerd Hoffmann4db14622014-09-16 12:33:03 +02004032
4033 if (!opts) {
Paolo Bonzini2d55f0e2012-10-02 10:17:21 +02004034 return;
Gerd Hoffmann4db14622014-09-16 12:33:03 +02004035 }
Daniel P. Berrange275e0d62017-02-03 12:06:45 +00004036
Gerd Hoffmanne5766eb2017-03-14 09:26:58 +01004037 reverse = qemu_opt_get_bool(opts, "reverse", false);
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03004038 if (vnc_display_get_addresses(opts, reverse, &saddr_list, &wsaddr_list,
4039 errp) < 0) {
Daniel P. Berrange275e0d62017-02-03 12:06:45 +00004040 goto fail;
Gerd Hoffmann4db14622014-09-16 12:33:03 +02004041 }
Gongleie2a11d92015-01-30 10:14:35 +08004042
Daniel P. Berrangé6c6840e2021-03-11 11:43:41 +00004043
4044 passwordSecret = qemu_opt_get(opts, "password-secret");
4045 if (passwordSecret) {
4046 if (qemu_opt_get(opts, "password")) {
4047 error_setg(errp,
4048 "'password' flag is redundant with 'password-secret'");
4049 goto fail;
4050 }
4051 vd->password = qcrypto_secret_lookup_as_utf8(passwordSecret,
4052 errp);
4053 if (!vd->password) {
4054 goto fail;
4055 }
4056 password = true;
4057 } else {
4058 password = qemu_opt_get_bool(opts, "password", false);
4059 }
Daniel P. Berrange800567a2015-07-01 18:10:38 +01004060 if (password) {
Daniel P. Berrange800567a2015-07-01 18:10:38 +01004061 if (!qcrypto_cipher_supports(
Daniel P. Berrangé83bee4b2021-06-29 14:25:32 +01004062 QCRYPTO_CIPHER_ALG_DES, QCRYPTO_CIPHER_MODE_ECB)) {
Daniel P. Berrange800567a2015-07-01 18:10:38 +01004063 error_setg(errp,
Daniel P. Berrangé83bee4b2021-06-29 14:25:32 +01004064 "Cipher backend does not support DES algorithm");
Daniel P. Berrange800567a2015-07-01 18:10:38 +01004065 goto fail;
4066 }
ths70848512007-08-25 01:37:05 +00004067 }
4068
Gerd Hoffmann4db14622014-09-16 12:33:03 +02004069 lock_key_sync = qemu_opt_get_bool(opts, "lock-key-sync", true);
Alexander Grafd3b0db62017-07-12 14:43:45 +02004070 key_delay_ms = qemu_opt_get_number(opts, "key-delay-ms", 10);
Gerd Hoffmann4db14622014-09-16 12:33:03 +02004071 sasl = qemu_opt_get_bool(opts, "sasl", false);
Daniel P. Berranged169f042015-03-17 13:42:55 +00004072#ifndef CONFIG_VNC_SASL
4073 if (sasl) {
4074 error_setg(errp, "VNC SASL auth requires cyrus-sasl support");
4075 goto fail;
4076 }
4077#endif /* CONFIG_VNC_SASL */
Daniel P. Berrange3e305e42015-08-06 14:39:32 +01004078 credid = qemu_opt_get(opts, "tls-creds");
4079 if (credid) {
4080 Object *creds;
Daniel P. Berrange3e305e42015-08-06 14:39:32 +01004081 creds = object_resolve_path_component(
4082 object_get_objects_root(), credid);
4083 if (!creds) {
4084 error_setg(errp, "No TLS credentials with id '%s'",
4085 credid);
4086 goto fail;
4087 }
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01004088 vd->tlscreds = (QCryptoTLSCreds *)
Daniel P. Berrange3e305e42015-08-06 14:39:32 +01004089 object_dynamic_cast(creds,
4090 TYPE_QCRYPTO_TLS_CREDS);
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01004091 if (!vd->tlscreds) {
Daniel P. Berrange3e305e42015-08-06 14:39:32 +01004092 error_setg(errp, "Object with id '%s' is not TLS credentials",
4093 credid);
4094 goto fail;
4095 }
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01004096 object_ref(OBJECT(vd->tlscreds));
Daniel P. Berrange3e305e42015-08-06 14:39:32 +01004097
Philippe Mathieu-Daudé3c52bf02021-06-28 18:09:13 +02004098 if (!qcrypto_tls_creds_check_endpoint(vd->tlscreds,
4099 QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
4100 errp)) {
Daniel P. Berrange3e305e42015-08-06 14:39:32 +01004101 goto fail;
4102 }
Gerd Hoffmann4db14622014-09-16 12:33:03 +02004103 }
Daniel P. Berrange55cf09a2019-02-27 14:57:54 +00004104 tlsauthz = qemu_opt_get(opts, "tls-authz");
Daniel P. Berrange55cf09a2019-02-27 14:57:54 +00004105 if (tlsauthz && !vd->tlscreds) {
4106 error_setg(errp, "'tls-authz' provided but TLS is not enabled");
4107 goto fail;
4108 }
4109
4110 saslauthz = qemu_opt_get(opts, "sasl-authz");
Daniel P. Berrange55cf09a2019-02-27 14:57:54 +00004111 if (saslauthz && !sasl) {
4112 error_setg(errp, "'sasl-authz' provided but SASL auth is not enabled");
4113 goto fail;
4114 }
Gerd Hoffmann4db14622014-09-16 12:33:03 +02004115
4116 share = qemu_opt_get(opts, "share");
4117 if (share) {
4118 if (strcmp(share, "ignore") == 0) {
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01004119 vd->share_policy = VNC_SHARE_POLICY_IGNORE;
Gerd Hoffmann4db14622014-09-16 12:33:03 +02004120 } else if (strcmp(share, "allow-exclusive") == 0) {
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01004121 vd->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE;
Gerd Hoffmann4db14622014-09-16 12:33:03 +02004122 } else if (strcmp(share, "force-shared") == 0) {
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01004123 vd->share_policy = VNC_SHARE_POLICY_FORCE_SHARED;
Gerd Hoffmann4db14622014-09-16 12:33:03 +02004124 } else {
4125 error_setg(errp, "unknown vnc share= option");
4126 goto fail;
4127 }
4128 } else {
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01004129 vd->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE;
Gerd Hoffmann4db14622014-09-16 12:33:03 +02004130 }
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01004131 vd->connections_limit = qemu_opt_get_number(opts, "connections", 32);
Gerd Hoffmann4db14622014-09-16 12:33:03 +02004132
Gerd Hoffmann4db14622014-09-16 12:33:03 +02004133#ifdef CONFIG_VNC_JPEG
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01004134 vd->lossy = qemu_opt_get_bool(opts, "lossy", false);
Gerd Hoffmann4db14622014-09-16 12:33:03 +02004135#endif
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01004136 vd->non_adaptive = qemu_opt_get_bool(opts, "non-adaptive", false);
Peter Lievene22492d2014-01-08 10:08:38 +01004137 /* adaptive updates are only used with tight encoding and
4138 * if lossy updates are enabled so we can disable all the
4139 * calculations otherwise */
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01004140 if (!vd->lossy) {
4141 vd->non_adaptive = true;
Peter Lievene22492d2014-01-08 10:08:38 +01004142 }
4143
Daniel P. Berrangé7b5fa0b2020-12-11 16:08:25 +00004144 vd->power_control = qemu_opt_get_bool(opts, "power-control", false);
4145
Daniel P. Berrange55cf09a2019-02-27 14:57:54 +00004146 if (tlsauthz) {
4147 vd->tlsauthzid = g_strdup(tlsauthz);
Daniel P. Berrange2cc45222015-03-02 19:01:05 +00004148 }
aliguori76655d62009-03-06 20:27:37 +00004149#ifdef CONFIG_VNC_SASL
Daniel P. Berrange55cf09a2019-02-27 14:57:54 +00004150 if (sasl) {
4151 if (saslauthz) {
4152 vd->sasl.authzid = g_strdup(saslauthz);
Gerd Hoffmannc8496402014-10-21 14:50:42 +02004153 }
aliguori76655d62009-03-06 20:27:37 +00004154 }
4155#endif
4156
Daniel P. Berrangeeda24e12016-09-29 16:45:36 +01004157 if (vnc_display_setup_auth(&vd->auth, &vd->subauth,
4158 vd->tlscreds, password,
4159 sasl, false, errp) < 0) {
4160 goto fail;
4161 }
Daniel P. Berrange7364dbd2017-09-21 13:15:28 +01004162 trace_vnc_auth_init(vd, 0, vd->auth, vd->subauth);
Daniel P. Berrangeeda24e12016-09-29 16:45:36 +01004163
4164 if (vnc_display_setup_auth(&vd->ws_auth, &vd->ws_subauth,
4165 vd->tlscreds, password,
4166 sasl, true, errp) < 0) {
Daniel P. Berrange3e305e42015-08-06 14:39:32 +01004167 goto fail;
4168 }
Daniel P. Berrange7364dbd2017-09-21 13:15:28 +01004169 trace_vnc_auth_init(vd, 1, vd->ws_auth, vd->ws_subauth);
bellard24236862006-04-30 21:28:36 +00004170
aliguori2f9606b2009-03-06 20:27:28 +00004171#ifdef CONFIG_VNC_SASL
Paolo Bonzinib65310a2021-06-04 14:09:15 +02004172 if (sasl && !vnc_sasl_server_init(errp)) {
4173 goto fail;
aliguori2f9606b2009-03-06 20:27:28 +00004174 }
4175#endif
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01004176 vd->lock_key_sync = lock_key_sync;
Pierre Ossmana54f0d22017-01-09 17:14:02 +01004177 if (lock_key_sync) {
4178 vd->led = qemu_add_led_event_handler(kbd_leds, vd);
4179 }
4180 vd->ledstate = 0;
aliguori2f9606b2009-03-06 20:27:28 +00004181
Kővágó, Zoltánf0b9f362019-08-19 01:06:48 +02004182 audiodev = qemu_opt_get(opts, "audiodev");
4183 if (audiodev) {
Paolo Bonzini176adaf2023-09-22 17:29:19 +02004184 vd->audio_state = audio_state_by_name(audiodev, errp);
Kővágó, Zoltánf0b9f362019-08-19 01:06:48 +02004185 if (!vd->audio_state) {
Kővágó, Zoltánf0b9f362019-08-19 01:06:48 +02004186 goto fail;
4187 }
4188 }
4189
Gerd Hoffmann1d0d59f2014-09-18 12:54:49 +02004190 device_id = qemu_opt_get(opts, "display");
4191 if (device_id) {
Gerd Hoffmann1d0d59f2014-09-18 12:54:49 +02004192 int head = qemu_opt_get_number(opts, "head", 0);
Gerd Hoffmannf2c1d542016-01-12 11:45:43 +01004193 Error *err = NULL;
Gerd Hoffmann1d0d59f2014-09-18 12:54:49 +02004194
Gerd Hoffmannf2c1d542016-01-12 11:45:43 +01004195 con = qemu_console_lookup_by_device_name(device_id, head, &err);
4196 if (err) {
4197 error_propagate(errp, err);
Gerd Hoffmann1d0d59f2014-09-18 12:54:49 +02004198 goto fail;
4199 }
4200 } else {
4201 con = NULL;
4202 }
4203
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01004204 if (con != vd->dcl.con) {
Gerd Hoffmannc2f2ba42019-01-22 10:28:12 +01004205 qkbd_state_free(vd->kbd);
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01004206 unregister_displaychangelistener(&vd->dcl);
4207 vd->dcl.con = con;
4208 register_displaychangelistener(&vd->dcl);
Gerd Hoffmannc2f2ba42019-01-22 10:28:12 +01004209 vd->kbd = qkbd_state_init(vd->dcl.con);
Gerd Hoffmann1d0d59f2014-09-18 12:54:49 +02004210 }
Gerd Hoffmannc2f2ba42019-01-22 10:28:12 +01004211 qkbd_state_set_delay(vd->kbd, key_delay_ms);
Gerd Hoffmann1d0d59f2014-09-18 12:54:49 +02004212
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03004213 if (saddr_list == NULL) {
4214 return;
Marc-André Lureaufa03cb72017-03-28 18:06:46 +02004215 }
4216
balrog3aa3eea2008-02-03 02:54:04 +00004217 if (reverse) {
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03004218 if (vnc_display_connect(vd, saddr_list, wsaddr_list, errp) < 0) {
Daniel P. Berrangee0d03b82015-08-14 18:56:44 +01004219 goto fail;
balrog3aa3eea2008-02-03 02:54:04 +00004220 }
aliguori9712eca2008-11-11 20:51:59 +00004221 } else {
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03004222 if (vnc_display_listen(vd, saddr_list, wsaddr_list, errp) < 0) {
Daniel P. Berrangee0d03b82015-08-14 18:56:44 +01004223 goto fail;
4224 }
bellard24236862006-04-30 21:28:36 +00004225 }
Daniel P. Berrangee0d03b82015-08-14 18:56:44 +01004226
Daniel P. Berrange275e0d62017-02-03 12:06:45 +00004227 if (qemu_opt_get(opts, "to")) {
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01004228 vnc_display_print_local_addr(vd);
Paolo Bonzini33df7bf2016-05-31 14:59:08 +02004229 }
4230
Vladimir Sementsov-Ogievskiyd708f992022-04-01 17:39:34 +03004231 /* Success */
Paolo Bonzini2d55f0e2012-10-02 10:17:21 +02004232 return;
Paolo Bonzini1ce52c72012-10-18 09:07:05 +02004233
4234fail:
Daniel P. Berrange4ee74fa2017-02-03 12:06:44 +00004235 vnc_display_close(vd);
bellard24236862006-04-30 21:28:36 +00004236}
Daniel P. Berrange13661082011-06-23 13:31:42 +01004237
Gerd Hoffmann14f71432014-07-29 12:24:55 +02004238void vnc_display_add_client(const char *id, int csock, bool skipauth)
Daniel P. Berrange13661082011-06-23 13:31:42 +01004239{
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01004240 VncDisplay *vd = vnc_display_find(id);
Daniel P. Berrange04d25292015-02-27 16:20:57 +00004241 QIOChannelSocket *sioc;
Daniel P. Berrange13661082011-06-23 13:31:42 +01004242
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01004243 if (!vd) {
Gerd Hoffmannd616ccc2014-07-29 12:14:08 +02004244 return;
4245 }
Daniel P. Berrange04d25292015-02-27 16:20:57 +00004246
4247 sioc = qio_channel_socket_new_fd(csock, NULL);
4248 if (sioc) {
Daniel P. Berrange10bcfe52016-09-30 11:57:14 +01004249 qio_channel_set_name(QIO_CHANNEL(sioc), "vnc-server");
Daniel P. Berrangebf01c172016-09-29 16:45:35 +01004250 vnc_connect(vd, sioc, skipauth, false);
Daniel P. Berrange04d25292015-02-27 16:20:57 +00004251 object_unref(OBJECT(sioc));
4252 }
Daniel P. Berrange13661082011-06-23 13:31:42 +01004253}
Gerd Hoffmann4db14622014-09-16 12:33:03 +02004254
Gerd Hoffmann9634f4e2015-02-17 09:28:17 +01004255static void vnc_auto_assign_id(QemuOptsList *olist, QemuOpts *opts)
Gonglei27796722015-02-05 17:43:34 +08004256{
4257 int i = 2;
4258 char *id;
4259
4260 id = g_strdup("default");
4261 while (qemu_opts_find(olist, id)) {
4262 g_free(id);
4263 id = g_strdup_printf("vnc%d", i++);
4264 }
4265 qemu_opts_set_id(opts, id);
4266}
4267
Paolo Bonzini653c9742021-01-20 15:42:35 +01004268void vnc_parse(const char *str)
Gerd Hoffmann4db14622014-09-16 12:33:03 +02004269{
Gerd Hoffmann4db14622014-09-16 12:33:03 +02004270 QemuOptsList *olist = qemu_find_opts("vnc");
Paolo Bonzini653c9742021-01-20 15:42:35 +01004271 QemuOpts *opts = qemu_opts_parse_noisily(olist, str, !is_help_option(str));
Gonglei81607cb2015-03-12 15:33:45 +08004272 const char *id;
Gerd Hoffmann4db14622014-09-16 12:33:03 +02004273
Gonglei81607cb2015-03-12 15:33:45 +08004274 if (!opts) {
Paolo Bonzini653c9742021-01-20 15:42:35 +01004275 exit(1);
Gonglei81607cb2015-03-12 15:33:45 +08004276 }
4277
4278 id = qemu_opts_id(opts);
Gerd Hoffmann4db14622014-09-16 12:33:03 +02004279 if (!id) {
4280 /* auto-assign id if not present */
Gonglei27796722015-02-05 17:43:34 +08004281 vnc_auto_assign_id(olist, opts);
Gerd Hoffmann4db14622014-09-16 12:33:03 +02004282 }
Gerd Hoffmann9634f4e2015-02-17 09:28:17 +01004283}
Gerd Hoffmann4db14622014-09-16 12:33:03 +02004284
Markus Armbruster28d0de72015-03-13 13:35:14 +01004285int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp)
Gerd Hoffmann9634f4e2015-02-17 09:28:17 +01004286{
4287 Error *local_err = NULL;
4288 char *id = (char *)qemu_opts_id(opts);
4289
4290 assert(id);
Fei Liab4f9312018-10-17 10:26:50 +02004291 vnc_display_init(id, &local_err);
4292 if (local_err) {
Markus Armbruster612aea22018-10-17 10:26:51 +02004293 error_propagate(errp, local_err);
4294 return -1;
Fei Liab4f9312018-10-17 10:26:50 +02004295 }
Gerd Hoffmann4db14622014-09-16 12:33:03 +02004296 vnc_display_open(id, &local_err);
4297 if (local_err != NULL) {
Markus Armbruster612aea22018-10-17 10:26:51 +02004298 error_propagate(errp, local_err);
4299 return -1;
Gerd Hoffmann4db14622014-09-16 12:33:03 +02004300 }
4301 return 0;
4302}
4303
4304static void vnc_register_config(void)
4305{
4306 qemu_add_opts(&qemu_vnc_opts);
4307}
Eduardo Habkost34294e22016-02-16 18:59:07 -02004308opts_init(vnc_register_config);