#include "qemu/cutils.h"
#include "qemu/help_option.h"
#include "io/dns-resolver.h"
+#include "monitor/monitor.h"
#define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT
#define VNC_REFRESH_INTERVAL_INC 50
info = g_malloc0(sizeof(*info));
vnc_init_basic_info_from_server_addr(vd->listener->sioc[0],
qapi_VncServerInfo_base(info), &err);
- info->has_auth = true;
info->auth = g_strdup(vnc_auth_name(vd));
if (err) {
qapi_free_VncServerInfo(info);
if (client->tls) {
client->info->x509_dname =
qcrypto_tls_session_get_peer_name(client->tls);
- client->info->has_x509_dname =
- client->info->x509_dname != NULL;
}
#ifdef CONFIG_VNC_SASL
if (client->sasl.conn &&
client->sasl.username) {
- client->info->has_sasl_username = true;
client->info->sasl_username = g_strdup(client->sasl.username);
}
#endif
if (client->tls) {
info->x509_dname = qcrypto_tls_session_get_peer_name(client->tls);
- info->has_x509_dname = info->x509_dname != NULL;
}
#ifdef CONFIG_VNC_SASL
if (client->sasl.conn && client->sasl.username) {
- info->has_sasl_username = true;
info->sasl_username = g_strdup(client->sasl.username);
}
#endif
abort();
}
- info->has_host = true;
- info->has_service = true;
info->has_family = true;
- info->has_auth = true;
info->auth = g_strdup(vnc_auth_name(vd));
}
if (vd->dcl.con) {
dev = DEVICE(object_property_get_link(OBJECT(vd->dcl.con),
"device", &error_abort));
- info->has_display = true;
info->display = g_strdup(dev->id);
}
for (i = 0; vd->listener != NULL && i < vd->listener->nsioc; i++) {
/* guest surface */
qemu_pixman_image_unref(vd->guest.fb);
vd->guest.fb = pixman_image_ref(surface->image);
- vd->guest.format = surface->format;
+ vd->guest.format = surface_format(surface);
if (pageflip) {
static int vnc_cursor_define(VncState *vs)
{
- QEMUCursor *c = vs->vd->cursor;
+ QEMUCursor *c = qemu_console_get_cursor(vs->vd->dcl.con);
int isize;
- if (!vs->vd->cursor) {
+ if (!c) {
return -1;
}
VncDisplay *vd = container_of(dcl, VncDisplay, dcl);
VncState *vs;
- cursor_put(vd->cursor);
g_free(vd->cursor_mask);
-
- vd->cursor = c;
- cursor_get(vd->cursor);
vd->cursor_msize = cursor_get_mono_bpl(c) * c->height;
vd->cursor_mask = g_malloc0(vd->cursor_msize);
cursor_get_mono_mask(c, 0, vd->cursor_mask);
*/
static int vnc_client_read(VncState *vs)
{
- size_t ret;
+ size_t sz;
#ifdef CONFIG_VNC_SASL
if (vs->sasl.conn && vs->sasl.runSSF)
- ret = vnc_client_read_sasl(vs);
+ sz = vnc_client_read_sasl(vs);
else
#endif /* CONFIG_VNC_SASL */
- ret = vnc_client_read_plain(vs);
- if (!ret) {
+ sz = vnc_client_read_plain(vs);
+ if (!sz) {
if (vs->disconnecting) {
vnc_disconnect_finish(vs);
return -1;
static void check_pointer_type_change(Notifier *notifier, void *data)
{
VncState *vs = container_of(notifier, VncState, mouse_mode_notifier);
- int absolute = qemu_input_is_absolute();
+ int absolute = qemu_input_is_absolute(vs->vd->dcl.con);
if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE) && vs->absolute != absolute) {
vnc_lock_output(vs);
case 0xb8: /* Right ALT */
break;
case 0xc8:
- kbd_put_keysym(QEMU_KEY_UP);
+ qemu_text_console_put_keysym(NULL, QEMU_KEY_UP);
break;
case 0xd0:
- kbd_put_keysym(QEMU_KEY_DOWN);
+ qemu_text_console_put_keysym(NULL, QEMU_KEY_DOWN);
break;
case 0xcb:
- kbd_put_keysym(QEMU_KEY_LEFT);
+ qemu_text_console_put_keysym(NULL, QEMU_KEY_LEFT);
break;
case 0xcd:
- kbd_put_keysym(QEMU_KEY_RIGHT);
+ qemu_text_console_put_keysym(NULL, QEMU_KEY_RIGHT);
break;
case 0xd3:
- kbd_put_keysym(QEMU_KEY_DELETE);
+ qemu_text_console_put_keysym(NULL, QEMU_KEY_DELETE);
break;
case 0xc7:
- kbd_put_keysym(QEMU_KEY_HOME);
+ qemu_text_console_put_keysym(NULL, QEMU_KEY_HOME);
break;
case 0xcf:
- kbd_put_keysym(QEMU_KEY_END);
+ qemu_text_console_put_keysym(NULL, QEMU_KEY_END);
break;
case 0xc9:
- kbd_put_keysym(QEMU_KEY_PAGEUP);
+ qemu_text_console_put_keysym(NULL, QEMU_KEY_PAGEUP);
break;
case 0xd1:
- kbd_put_keysym(QEMU_KEY_PAGEDOWN);
+ qemu_text_console_put_keysym(NULL, QEMU_KEY_PAGEDOWN);
break;
case 0x47:
- kbd_put_keysym(numlock ? '7' : QEMU_KEY_HOME);
+ qemu_text_console_put_keysym(NULL, numlock ? '7' : QEMU_KEY_HOME);
break;
case 0x48:
- kbd_put_keysym(numlock ? '8' : QEMU_KEY_UP);
+ qemu_text_console_put_keysym(NULL, numlock ? '8' : QEMU_KEY_UP);
break;
case 0x49:
- kbd_put_keysym(numlock ? '9' : QEMU_KEY_PAGEUP);
+ qemu_text_console_put_keysym(NULL, numlock ? '9' : QEMU_KEY_PAGEUP);
break;
case 0x4b:
- kbd_put_keysym(numlock ? '4' : QEMU_KEY_LEFT);
+ qemu_text_console_put_keysym(NULL, numlock ? '4' : QEMU_KEY_LEFT);
break;
case 0x4c:
- kbd_put_keysym('5');
+ qemu_text_console_put_keysym(NULL, '5');
break;
case 0x4d:
- kbd_put_keysym(numlock ? '6' : QEMU_KEY_RIGHT);
+ qemu_text_console_put_keysym(NULL, numlock ? '6' : QEMU_KEY_RIGHT);
break;
case 0x4f:
- kbd_put_keysym(numlock ? '1' : QEMU_KEY_END);
+ qemu_text_console_put_keysym(NULL, numlock ? '1' : QEMU_KEY_END);
break;
case 0x50:
- kbd_put_keysym(numlock ? '2' : QEMU_KEY_DOWN);
+ qemu_text_console_put_keysym(NULL, numlock ? '2' : QEMU_KEY_DOWN);
break;
case 0x51:
- kbd_put_keysym(numlock ? '3' : QEMU_KEY_PAGEDOWN);
+ qemu_text_console_put_keysym(NULL, numlock ? '3' : QEMU_KEY_PAGEDOWN);
break;
case 0x52:
- kbd_put_keysym('0');
+ qemu_text_console_put_keysym(NULL, '0');
break;
case 0x53:
- kbd_put_keysym(numlock ? '.' : QEMU_KEY_DELETE);
+ qemu_text_console_put_keysym(NULL, numlock ? '.' : QEMU_KEY_DELETE);
break;
case 0xb5:
- kbd_put_keysym('/');
+ qemu_text_console_put_keysym(NULL, '/');
break;
case 0x37:
- kbd_put_keysym('*');
+ qemu_text_console_put_keysym(NULL, '*');
break;
case 0x4a:
- kbd_put_keysym('-');
+ qemu_text_console_put_keysym(NULL, '-');
break;
case 0x4e:
- kbd_put_keysym('+');
+ qemu_text_console_put_keysym(NULL, '+');
break;
case 0x9c:
- kbd_put_keysym('\n');
+ qemu_text_console_put_keysym(NULL, '\n');
break;
default:
if (control) {
- kbd_put_keysym(sym & 0x1f);
+ qemu_text_console_put_keysym(NULL, sym & 0x1f);
} else {
- kbd_put_keysym(sym);
+ qemu_text_console_put_keysym(NULL, sym);
}
break;
}
vs->features |= VNC_FEATURE_TIGHT_MASK;
vs->vnc_encoding = enc;
break;
-#ifdef CONFIG_VNC_PNG
+#ifdef CONFIG_PNG
case VNC_ENCODING_TIGHT_PNG:
vs->features |= VNC_FEATURE_TIGHT_PNG_MASK;
vs->vnc_encoding = enc;
send_ext_key_event_ack(vs);
break;
case VNC_ENCODING_AUDIO:
- send_ext_audio_ack(vs);
+ if (vs->vd->audio_state) {
+ vs->features |= VNC_FEATURE_AUDIO_MASK;
+ send_ext_audio_ack(vs);
+ }
break;
case VNC_ENCODING_WMVi:
vs->features |= VNC_FEATURE_WMVI_MASK;
break;
case VNC_ENCODING_XVP:
if (vs->vd->power_control) {
- vs->features |= VNC_FEATURE_XVP;
+ vs->features |= VNC_FEATURE_XVP_MASK;
send_xvp_message(vs, VNC_XVP_CODE_INIT);
}
break;
if (len == 1) {
return 8;
}
+ uint32_t dlen = abs(read_s32(data, 4));
if (len == 8) {
- uint32_t dlen = abs(read_s32(data, 4));
if (dlen > (1 << 20)) {
error_report("vnc: client_cut_text msg payload has %u bytes"
" which exceeds our limit of 1MB.", dlen);
}
if (read_s32(data, 4) < 0) {
- vnc_client_cut_text_ext(vs, abs(read_s32(data, 4)),
- read_u32(data, 8), data + 12);
+ if (dlen < 4) {
+ error_report("vnc: malformed payload (header less than 4 bytes)"
+ " in extended clipboard pseudo-encoding.");
+ vnc_client_error(vs);
+ break;
+ }
+ vnc_client_cut_text_ext(vs, dlen, read_u32(data, 8), data + 12);
break;
}
vnc_client_cut_text(vs, read_u32(data, 4), data + 8);
break;
case VNC_MSG_CLIENT_XVP:
- if (!(vs->features & VNC_FEATURE_XVP)) {
+ if (!vnc_has_feature(vs, VNC_FEATURE_XVP)) {
error_report("vnc: xvp client message while disabled");
vnc_client_error(vs);
break;
read_u32(data, 4), read_u32(data, 8));
break;
case VNC_MSG_CLIENT_QEMU_AUDIO:
+ if (!vnc_has_feature(vs, VNC_FEATURE_AUDIO)) {
+ error_report("Audio message %d with audio disabled", read_u8(data, 2));
+ vnc_client_error(vs);
+ break;
+ }
+
if (len == 2)
return 4;
vs, vs->ioc, vs->as.fmt, vs->as.nchannels, vs->as.freq);
break;
default:
- VNC_DEBUG("Invalid audio message %d\n", read_u8(data, 4));
+ VNC_DEBUG("Invalid audio message %d\n", read_u8(data, 2));
vnc_client_error(vs);
break;
}
rect = vnc_stat_rect(vd, x, y);
if (rect->updated) {
- return ;
+ return;
}
rect->times[rect->idx] = *tv;
rect->idx = (rect->idx + 1) % ARRAY_SIZE(rect->times);
cmp_bytes = MIN(VNC_DIRTY_PIXELS_PER_BIT * VNC_SERVER_FB_BYTES,
server_stride);
if (vd->guest.format != VNC_SERVER_FB_FORMAT) {
- int width = pixman_image_get_width(vd->server);
- tmpbuf = qemu_pixman_linebuf_create(VNC_SERVER_FB_FORMAT, width);
+ int w = pixman_image_get_width(vd->server);
+ tmpbuf = qemu_pixman_linebuf_create(VNC_SERVER_FB_FORMAT, w);
} else {
int guest_bpp =
PIXMAN_FORMAT_BPP(pixman_image_get_format(vd->guest.fb));
#ifdef CONFIG_VNC_JPEG
buffer_init(&vs->tight->jpeg, "vnc-tight-jpeg/%p", sioc);
#endif
-#ifdef CONFIG_VNC_PNG
+#ifdef CONFIG_PNG
buffer_init(&vs->tight->png, "vnc-tight-png/%p", sioc);
#endif
buffer_init(&vs->zlib.zlib, "vnc-zlib/%p", sioc);
} else {
const char *port;
size_t hostlen;
- unsigned long long baseport = 0;
+ uint64_t baseport = 0;
InetSocketAddress *inet;
port = strrchr(addrstr, ':');
addr->type = SOCKET_ADDRESS_TYPE_INET;
inet = &addr->u.inet;
- if (addrstr[0] == '[' && addrstr[hostlen - 1] == ']') {
+ if (hostlen && addrstr[0] == '[' && addrstr[hostlen - 1] == ']') {
inet->host = g_strndup(addrstr + 1, hostlen - 2);
} else {
inet->host = g_strndup(addrstr, hostlen);
}
} else {
int offset = reverse ? 0 : 5900;
- if (parse_uint_full(port, &baseport, 10) < 0) {
+ if (parse_uint_full(port, 10, &baseport) < 0) {
error_setg(errp, "can't convert to a number: %s", port);
goto cleanup;
}
return ret;
}
-static void vnc_free_addresses(SocketAddress ***retsaddr,
- size_t *retnsaddr)
-{
- size_t i;
-
- for (i = 0; i < *retnsaddr; i++) {
- qapi_free_SocketAddress((*retsaddr)[i]);
- }
- g_free(*retsaddr);
-
- *retsaddr = NULL;
- *retnsaddr = 0;
-}
-
static int vnc_display_get_addresses(QemuOpts *opts,
bool reverse,
- SocketAddress ***retsaddr,
- size_t *retnsaddr,
- SocketAddress ***retwsaddr,
- size_t *retnwsaddr,
+ SocketAddressList **saddr_list_ret,
+ SocketAddressList **wsaddr_list_ret,
Error **errp)
{
SocketAddress *saddr = NULL;
SocketAddress *wsaddr = NULL;
+ g_autoptr(SocketAddressList) saddr_list = NULL;
+ SocketAddressList **saddr_tail = &saddr_list;
+ SocketAddress *single_saddr = NULL;
+ g_autoptr(SocketAddressList) wsaddr_list = NULL;
+ SocketAddressList **wsaddr_tail = &wsaddr_list;
QemuOptsIter addriter;
const char *addr;
int to = qemu_opt_get_number(opts, "to", 0);
bool ipv4 = qemu_opt_get_bool(opts, "ipv4", false);
bool ipv6 = qemu_opt_get_bool(opts, "ipv6", false);
int displaynum = -1;
- int ret = -1;
-
- *retsaddr = NULL;
- *retnsaddr = 0;
- *retwsaddr = NULL;
- *retnwsaddr = 0;
addr = qemu_opt_get(opts, "vnc");
if (addr == NULL || g_str_equal(addr, "none")) {
- ret = 0;
- goto cleanup;
+ return 0;
}
if (qemu_opt_get(opts, "websocket") &&
!qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA1)) {
error_setg(errp,
"SHA1 hash support is required for websockets");
- goto cleanup;
+ return -1;
}
qemu_opt_iter_init(&addriter, opts, "vnc");
ipv4, ipv6,
&saddr, errp);
if (rv < 0) {
- goto cleanup;
+ return -1;
}
/* Historical compat - first listen address can be used
* to set the default websocket port
if (displaynum == -1) {
displaynum = rv;
}
- *retsaddr = g_renew(SocketAddress *, *retsaddr, *retnsaddr + 1);
- (*retsaddr)[(*retnsaddr)++] = saddr;
+ QAPI_LIST_APPEND(saddr_tail, saddr);
}
- /* If we had multiple primary displays, we don't do defaults
- * for websocket, and require explicit config instead. */
- if (*retnsaddr > 1) {
+ if (saddr_list && !saddr_list->next) {
+ single_saddr = saddr_list->value;
+ } else {
+ /*
+ * If we had multiple primary displays, we don't do defaults
+ * for websocket, and require explicit config instead.
+ */
displaynum = -1;
}
has_ipv4, has_ipv6,
ipv4, ipv6,
&wsaddr, errp) < 0) {
- goto cleanup;
+ return -1;
}
/* Historical compat - if only a single listen address was
* provided, then this is used to set the default listen
* address for websocket too
*/
- if (*retnsaddr == 1 &&
- (*retsaddr)[0]->type == SOCKET_ADDRESS_TYPE_INET &&
+ if (single_saddr &&
+ single_saddr->type == SOCKET_ADDRESS_TYPE_INET &&
wsaddr->type == SOCKET_ADDRESS_TYPE_INET &&
g_str_equal(wsaddr->u.inet.host, "") &&
- !g_str_equal((*retsaddr)[0]->u.inet.host, "")) {
+ !g_str_equal(single_saddr->u.inet.host, "")) {
g_free(wsaddr->u.inet.host);
- wsaddr->u.inet.host = g_strdup((*retsaddr)[0]->u.inet.host);
+ wsaddr->u.inet.host = g_strdup(single_saddr->u.inet.host);
}
- *retwsaddr = g_renew(SocketAddress *, *retwsaddr, *retnwsaddr + 1);
- (*retwsaddr)[(*retnwsaddr)++] = wsaddr;
+ QAPI_LIST_APPEND(wsaddr_tail, wsaddr);
}
- ret = 0;
- cleanup:
- if (ret < 0) {
- vnc_free_addresses(retsaddr, retnsaddr);
- vnc_free_addresses(retwsaddr, retnwsaddr);
- }
- return ret;
+ *saddr_list_ret = g_steal_pointer(&saddr_list);
+ *wsaddr_list_ret = g_steal_pointer(&wsaddr_list);
+ return 0;
}
static int vnc_display_connect(VncDisplay *vd,
- SocketAddress **saddr,
- size_t nsaddr,
- SocketAddress **wsaddr,
- size_t nwsaddr,
+ SocketAddressList *saddr_list,
+ SocketAddressList *wsaddr_list,
Error **errp)
{
/* connect to viewer */
QIOChannelSocket *sioc = NULL;
- if (nwsaddr != 0) {
+ if (wsaddr_list) {
error_setg(errp, "Cannot use websockets in reverse mode");
return -1;
}
- if (nsaddr != 1) {
+ if (!saddr_list || saddr_list->next) {
error_setg(errp, "Expected a single address in reverse mode");
return -1;
}
/* TODO SOCKET_ADDRESS_TYPE_FD when fd has AF_UNIX */
- vd->is_unix = saddr[0]->type == SOCKET_ADDRESS_TYPE_UNIX;
+ vd->is_unix = saddr_list->value->type == SOCKET_ADDRESS_TYPE_UNIX;
sioc = qio_channel_socket_new();
qio_channel_set_name(QIO_CHANNEL(sioc), "vnc-reverse");
- if (qio_channel_socket_connect_sync(sioc, saddr[0], errp) < 0) {
+ if (qio_channel_socket_connect_sync(sioc, saddr_list->value, errp) < 0) {
object_unref(OBJECT(sioc));
return -1;
}
static int vnc_display_listen(VncDisplay *vd,
- SocketAddress **saddr,
- size_t nsaddr,
- SocketAddress **wsaddr,
- size_t nwsaddr,
+ SocketAddressList *saddr_list,
+ SocketAddressList *wsaddr_list,
Error **errp)
{
- size_t i;
+ SocketAddressList *el;
- if (nsaddr) {
+ if (saddr_list) {
vd->listener = qio_net_listener_new();
qio_net_listener_set_name(vd->listener, "vnc-listen");
- for (i = 0; i < nsaddr; i++) {
+ for (el = saddr_list; el; el = el->next) {
if (qio_net_listener_open_sync(vd->listener,
- saddr[i], 1,
+ el->value, 1,
errp) < 0) {
return -1;
}
vnc_listen_io, vd, NULL);
}
- if (nwsaddr) {
+ if (wsaddr_list) {
vd->wslistener = qio_net_listener_new();
qio_net_listener_set_name(vd->wslistener, "vnc-ws-listen");
- for (i = 0; i < nwsaddr; i++) {
+ for (el = wsaddr_list; el; el = el->next) {
if (qio_net_listener_open_sync(vd->wslistener,
- wsaddr[i], 1,
+ el->value, 1,
errp) < 0) {
return -1;
}
return 0;
}
+bool vnc_display_update(DisplayUpdateOptionsVNC *arg, Error **errp)
+{
+ VncDisplay *vd = vnc_display_find(NULL);
+
+ if (!vd) {
+ error_setg(errp, "Can not find vnc display");
+ return false;
+ }
+
+ if (arg->has_addresses) {
+ if (vd->listener) {
+ qio_net_listener_disconnect(vd->listener);
+ object_unref(OBJECT(vd->listener));
+ vd->listener = NULL;
+ }
+
+ if (vnc_display_listen(vd, arg->addresses, NULL, errp) < 0) {
+ return false;
+ }
+ }
+
+ return true;
+}
void vnc_display_open(const char *id, Error **errp)
{
VncDisplay *vd = vnc_display_find(id);
QemuOpts *opts = qemu_opts_find(&qemu_vnc_opts, id);
- SocketAddress **saddr = NULL, **wsaddr = NULL;
- size_t nsaddr, nwsaddr;
+ g_autoptr(SocketAddressList) saddr_list = NULL;
+ g_autoptr(SocketAddressList) wsaddr_list = NULL;
const char *share, *device_id;
QemuConsole *con;
bool password = false;
}
reverse = qemu_opt_get_bool(opts, "reverse", false);
- if (vnc_display_get_addresses(opts, reverse, &saddr, &nsaddr,
- &wsaddr, &nwsaddr, errp) < 0) {
+ if (vnc_display_get_addresses(opts, reverse, &saddr_list, &wsaddr_list,
+ errp) < 0) {
goto fail;
}
password = qemu_opt_get_bool(opts, "password", false);
}
if (password) {
- if (fips_get_state()) {
- error_setg(errp,
- "VNC password auth disabled due to FIPS mode, "
- "consider using the VeNCrypt or SASL authentication "
- "methods as an alternative");
- goto fail;
- }
if (!qcrypto_cipher_supports(
QCRYPTO_CIPHER_ALG_DES, QCRYPTO_CIPHER_MODE_ECB)) {
error_setg(errp,
audiodev = qemu_opt_get(opts, "audiodev");
if (audiodev) {
- vd->audio_state = audio_state_by_name(audiodev);
+ vd->audio_state = audio_state_by_name(audiodev, errp);
if (!vd->audio_state) {
- error_setg(errp, "Audiodev '%s' not found", audiodev);
goto fail;
}
+ } else {
+ vd->audio_state = audio_get_default_audio_state(NULL);
}
device_id = qemu_opt_get(opts, "display");
}
qkbd_state_set_delay(vd->kbd, key_delay_ms);
- if (saddr == NULL) {
- goto cleanup;
+ if (saddr_list == NULL) {
+ return;
}
if (reverse) {
- if (vnc_display_connect(vd, saddr, nsaddr, wsaddr, nwsaddr, errp) < 0) {
+ if (vnc_display_connect(vd, saddr_list, wsaddr_list, errp) < 0) {
goto fail;
}
} else {
- if (vnc_display_listen(vd, saddr, nsaddr, wsaddr, nwsaddr, errp) < 0) {
+ if (vnc_display_listen(vd, saddr_list, wsaddr_list, errp) < 0) {
goto fail;
}
}
vnc_display_print_local_addr(vd);
}
- cleanup:
- vnc_free_addresses(&saddr, &nsaddr);
- vnc_free_addresses(&wsaddr, &nwsaddr);
+ /* Success */
return;
fail:
vnc_display_close(vd);
- goto cleanup;
}
void vnc_display_add_client(const char *id, int csock, bool skipauth)