#include "crypto/tlscredsx509.h"
#include "qom/object_interfaces.h"
#include "qemu/cutils.h"
+#include "io/dns-resolver.h"
#define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT
#define VNC_REFRESH_INTERVAL_INC 50
VncServerInfo *info;
Error *err = NULL;
+ if (!vd->nlsock) {
+ return NULL;
+ }
+
info = g_malloc0(sizeof(*info));
- vnc_init_basic_info_from_server_addr(vd->lsock,
+ vnc_init_basic_info_from_server_addr(vd->lsock[0],
qapi_VncServerInfo_base(info), &err);
info->has_auth = true;
info->auth = g_strdup(vnc_auth_name(vd));
VncDisplay *vd = vnc_display_find(NULL);
SocketAddress *addr = NULL;
- if (vd == NULL || !vd->lsock) {
+ if (vd == NULL || !vd->nlsock) {
info->enabled = false;
} else {
info->enabled = true;
return info;
}
- addr = qio_channel_socket_get_local_address(vd->lsock, errp);
+ addr = qio_channel_socket_get_local_address(vd->lsock[0], errp);
if (!addr) {
goto out_error;
}
return NULL;
}
-static VncBasicInfoList *qmp_query_server_entry(QIOChannelSocket *ioc,
- bool websocket,
- VncBasicInfoList *prev)
+
+static void qmp_query_auth(int auth, int subauth,
+ VncPrimaryAuth *qmp_auth,
+ VncVencryptSubAuth *qmp_vencrypt,
+ bool *qmp_has_vencrypt);
+
+static VncServerInfo2List *qmp_query_server_entry(QIOChannelSocket *ioc,
+ bool websocket,
+ int auth,
+ int subauth,
+ VncServerInfo2List *prev)
{
- VncBasicInfoList *list;
- VncBasicInfo *info;
+ VncServerInfo2List *list;
+ VncServerInfo2 *info;
Error *err = NULL;
SocketAddress *addr;
return prev;
}
- info = g_new0(VncBasicInfo, 1);
- vnc_init_basic_info(addr, info, &err);
+ info = g_new0(VncServerInfo2, 1);
+ vnc_init_basic_info(addr, qapi_VncServerInfo2_base(info), &err);
qapi_free_SocketAddress(addr);
if (err) {
- qapi_free_VncBasicInfo(info);
+ qapi_free_VncServerInfo2(info);
error_free(err);
return prev;
}
info->websocket = websocket;
- list = g_new0(VncBasicInfoList, 1);
+ qmp_query_auth(auth, subauth, &info->auth,
+ &info->vencrypt, &info->has_vencrypt);
+
+ list = g_new0(VncServerInfo2List, 1);
list->value = info;
list->next = prev;
return list;
}
-static void qmp_query_auth(VncDisplay *vd, VncInfo2 *info)
+static void qmp_query_auth(int auth, int subauth,
+ VncPrimaryAuth *qmp_auth,
+ VncVencryptSubAuth *qmp_vencrypt,
+ bool *qmp_has_vencrypt)
{
- switch (vd->auth) {
+ switch (auth) {
case VNC_AUTH_VNC:
- info->auth = VNC_PRIMARY_AUTH_VNC;
+ *qmp_auth = VNC_PRIMARY_AUTH_VNC;
break;
case VNC_AUTH_RA2:
- info->auth = VNC_PRIMARY_AUTH_RA2;
+ *qmp_auth = VNC_PRIMARY_AUTH_RA2;
break;
case VNC_AUTH_RA2NE:
- info->auth = VNC_PRIMARY_AUTH_RA2NE;
+ *qmp_auth = VNC_PRIMARY_AUTH_RA2NE;
break;
case VNC_AUTH_TIGHT:
- info->auth = VNC_PRIMARY_AUTH_TIGHT;
+ *qmp_auth = VNC_PRIMARY_AUTH_TIGHT;
break;
case VNC_AUTH_ULTRA:
- info->auth = VNC_PRIMARY_AUTH_ULTRA;
+ *qmp_auth = VNC_PRIMARY_AUTH_ULTRA;
break;
case VNC_AUTH_TLS:
- info->auth = VNC_PRIMARY_AUTH_TLS;
+ *qmp_auth = VNC_PRIMARY_AUTH_TLS;
break;
case VNC_AUTH_VENCRYPT:
- info->auth = VNC_PRIMARY_AUTH_VENCRYPT;
- info->has_vencrypt = true;
- switch (vd->subauth) {
+ *qmp_auth = VNC_PRIMARY_AUTH_VENCRYPT;
+ *qmp_has_vencrypt = true;
+ switch (subauth) {
case VNC_AUTH_VENCRYPT_PLAIN:
- info->vencrypt = VNC_VENCRYPT_SUB_AUTH_PLAIN;
+ *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_PLAIN;
break;
case VNC_AUTH_VENCRYPT_TLSNONE:
- info->vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_NONE;
+ *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_NONE;
break;
case VNC_AUTH_VENCRYPT_TLSVNC:
- info->vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_VNC;
+ *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_VNC;
break;
case VNC_AUTH_VENCRYPT_TLSPLAIN:
- info->vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_PLAIN;
+ *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_PLAIN;
break;
case VNC_AUTH_VENCRYPT_X509NONE:
- info->vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_NONE;
+ *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_NONE;
break;
case VNC_AUTH_VENCRYPT_X509VNC:
- info->vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_VNC;
+ *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_VNC;
break;
case VNC_AUTH_VENCRYPT_X509PLAIN:
- info->vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_PLAIN;
+ *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_PLAIN;
break;
case VNC_AUTH_VENCRYPT_TLSSASL:
- info->vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_SASL;
+ *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_SASL;
break;
case VNC_AUTH_VENCRYPT_X509SASL:
- info->vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_SASL;
+ *qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_SASL;
break;
default:
- info->has_vencrypt = false;
+ *qmp_has_vencrypt = false;
break;
}
break;
case VNC_AUTH_SASL:
- info->auth = VNC_PRIMARY_AUTH_SASL;
+ *qmp_auth = VNC_PRIMARY_AUTH_SASL;
break;
case VNC_AUTH_NONE:
default:
- info->auth = VNC_PRIMARY_AUTH_NONE;
+ *qmp_auth = VNC_PRIMARY_AUTH_NONE;
break;
}
}
VncInfo2 *info;
VncDisplay *vd;
DeviceState *dev;
+ size_t i;
QTAILQ_FOREACH(vd, &vnc_displays, next) {
info = g_new0(VncInfo2, 1);
info->id = g_strdup(vd->id);
info->clients = qmp_query_client_list(vd);
- qmp_query_auth(vd, info);
+ qmp_query_auth(vd->auth, vd->subauth, &info->auth,
+ &info->vencrypt, &info->has_vencrypt);
if (vd->dcl.con) {
dev = DEVICE(object_property_get_link(OBJECT(vd->dcl.con),
"device", NULL));
info->has_display = true;
info->display = g_strdup(dev->id);
}
- if (vd->lsock != NULL) {
+ for (i = 0; i < vd->nlsock; i++) {
info->server = qmp_query_server_entry(
- vd->lsock, false, info->server);
+ vd->lsock[i], false, vd->auth, vd->subauth, info->server);
}
- if (vd->lwebsock != NULL) {
+ for (i = 0; i < vd->nlwebsock; i++) {
info->server = qmp_query_server_entry(
- vd->lwebsock, true, info->server);
+ vd->lwebsock[i], true, vd->ws_auth,
+ vd->ws_subauth, info->server);
}
item = g_new0(VncInfo2List, 1);
return n;
}
-static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
-{
- /* send bitblit op to the vnc client */
- vnc_lock_output(vs);
- vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
- vnc_write_u8(vs, 0);
- vnc_write_u16(vs, 1); /* number of rects */
- vnc_framebuffer_update(vs, dst_x, dst_y, w, h, VNC_ENCODING_COPYRECT);
- vnc_write_u16(vs, src_x);
- vnc_write_u16(vs, src_y);
- vnc_unlock_output(vs);
- vnc_flush(vs);
-}
-
-static void vnc_dpy_copy(DisplayChangeListener *dcl,
- int src_x, int src_y,
- int dst_x, int dst_y, int w, int h)
-{
- VncDisplay *vd = container_of(dcl, VncDisplay, dcl);
- VncState *vs, *vn;
- uint8_t *src_row;
- uint8_t *dst_row;
- int i, x, y, pitch, inc, w_lim, s;
- int cmp_bytes;
-
- if (!vd->server) {
- /* no client connected */
- return;
- }
-
- vnc_refresh_server_surface(vd);
- QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) {
- if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
- vs->force_update = 1;
- vnc_update_client(vs, 1, true);
- /* vs might be free()ed here */
- }
- }
-
- if (!vd->server) {
- /* no client connected */
- return;
- }
- /* do bitblit op on the local surface too */
- pitch = vnc_server_fb_stride(vd);
- src_row = vnc_server_fb_ptr(vd, src_x, src_y);
- dst_row = vnc_server_fb_ptr(vd, dst_x, dst_y);
- y = dst_y;
- inc = 1;
- if (dst_y > src_y) {
- /* copy backwards */
- src_row += pitch * (h-1);
- dst_row += pitch * (h-1);
- pitch = -pitch;
- y = dst_y + h - 1;
- inc = -1;
- }
- w_lim = w - (VNC_DIRTY_PIXELS_PER_BIT - (dst_x % VNC_DIRTY_PIXELS_PER_BIT));
- if (w_lim < 0) {
- w_lim = w;
- } else {
- w_lim = w - (w_lim % VNC_DIRTY_PIXELS_PER_BIT);
- }
- for (i = 0; i < h; i++) {
- for (x = 0; x <= w_lim;
- x += s, src_row += cmp_bytes, dst_row += cmp_bytes) {
- if (x == w_lim) {
- if ((s = w - w_lim) == 0)
- break;
- } else if (!x) {
- s = (VNC_DIRTY_PIXELS_PER_BIT -
- (dst_x % VNC_DIRTY_PIXELS_PER_BIT));
- s = MIN(s, w_lim);
- } else {
- s = VNC_DIRTY_PIXELS_PER_BIT;
- }
- cmp_bytes = s * VNC_SERVER_FB_BYTES;
- if (memcmp(src_row, dst_row, cmp_bytes) == 0)
- continue;
- memmove(dst_row, src_row, cmp_bytes);
- QTAILQ_FOREACH(vs, &vd->clients, next) {
- if (!vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
- set_bit(((x + dst_x) / VNC_DIRTY_PIXELS_PER_BIT),
- vs->dirty[y]);
- }
- }
- }
- src_row += pitch - w * VNC_SERVER_FB_BYTES;
- dst_row += pitch - w * VNC_SERVER_FB_BYTES;
- y += inc;
- }
-
- QTAILQ_FOREACH(vs, &vd->clients, next) {
- if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
- vnc_copy(vs, src_x, src_y, dst_x, dst_y, w, h);
- }
- }
-}
-
static void vnc_mouse_set(DisplayChangeListener *dcl,
int x, int y, int visible)
{
audio_del(vs);
vnc_release_modifiers(vs);
- if (vs->initialized) {
- QTAILQ_REMOVE(&vs->vd->clients, vs, next);
+ if (vs->mouse_mode_notifier.notify != NULL) {
qemu_remove_mouse_mode_change_notifier(&vs->mouse_mode_notifier);
- if (QTAILQ_EMPTY(&vs->vd->clients)) {
- /* last client gone */
- vnc_update_server_surface(vs->vd);
- }
+ }
+ QTAILQ_REMOVE(&vs->vd->clients, vs, next);
+ if (QTAILQ_EMPTY(&vs->vd->clients)) {
+ /* last client gone */
+ vnc_update_server_surface(vs->vd);
}
- if (vs->vd->lock_key_sync)
- qemu_remove_led_event_handler(vs->led);
vnc_unlock_output(vs);
qemu_mutex_destroy(&vs->output_mutex);
if (ret <= 0) {
if (ret == 0) {
VNC_DEBUG("Closing down client sock: EOF\n");
+ vnc_disconnect_start(vs);
} else if (ret != QIO_CHANNEL_ERR_BLOCK) {
- VNC_DEBUG("Closing down client sock: ret %d (%s)\n",
+ VNC_DEBUG("Closing down client sock: ret %zd (%s)\n",
ret, errp ? error_get_pretty(*errp) : "Unknown");
+ vnc_disconnect_start(vs);
}
- vnc_disconnect_start(vs);
if (errp) {
error_free(*errp);
*errp = NULL;
qemu_input_event_send_key_delay(vs->vd->key_delay_ms);
}
-static int current_led_state(VncState *vs)
-{
- int ledstate = 0;
-
- if (vs->modifiers_state[0x46]) {
- ledstate |= QEMU_SCROLL_LOCK_LED;
- }
- if (vs->modifiers_state[0x45]) {
- ledstate |= QEMU_NUM_LOCK_LED;
- }
- if (vs->modifiers_state[0x3a]) {
- ledstate |= QEMU_CAPS_LOCK_LED;
- }
-
- return ledstate;
-}
-
static void vnc_led_state_change(VncState *vs)
{
- int ledstate = 0;
-
if (!vnc_has_feature(vs, VNC_FEATURE_LED_STATE)) {
return;
}
- ledstate = current_led_state(vs);
vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1);
vnc_framebuffer_update(vs, 0, 0, 1, 1, VNC_ENCODING_LED_STATE);
- vnc_write_u8(vs, ledstate);
+ vnc_write_u8(vs, vs->vd->ledstate);
vnc_unlock_output(vs);
vnc_flush(vs);
}
static void kbd_leds(void *opaque, int ledstate)
{
- VncState *vs = opaque;
- int caps, num, scr;
- bool has_changed = (ledstate != current_led_state(vs));
+ VncDisplay *vd = opaque;
+ VncState *client;
trace_vnc_key_guest_leds((ledstate & QEMU_CAPS_LOCK_LED),
(ledstate & QEMU_NUM_LOCK_LED),
(ledstate & QEMU_SCROLL_LOCK_LED));
- caps = ledstate & QEMU_CAPS_LOCK_LED ? 1 : 0;
- num = ledstate & QEMU_NUM_LOCK_LED ? 1 : 0;
- scr = ledstate & QEMU_SCROLL_LOCK_LED ? 1 : 0;
-
- if (vs->modifiers_state[0x3a] != caps) {
- vs->modifiers_state[0x3a] = caps;
- }
- if (vs->modifiers_state[0x45] != num) {
- vs->modifiers_state[0x45] = num;
- }
- if (vs->modifiers_state[0x46] != scr) {
- vs->modifiers_state[0x46] = scr;
+ if (ledstate == vd->ledstate) {
+ return;
}
- /* Sending the current led state message to the client */
- if (has_changed) {
- vnc_led_state_change(vs);
+ vd->ledstate = ledstate;
+
+ QTAILQ_FOREACH(client, &vd->clients, next) {
+ vnc_led_state_change(client);
}
}
pixel_format_message(vs);
- if (qemu_name)
+ if (qemu_name) {
size = snprintf(buf, sizeof(buf), "QEMU (%s)", qemu_name);
- else
+ if (size > sizeof(buf)) {
+ size = sizeof(buf);
+ }
+ } else {
size = snprintf(buf, sizeof(buf), "QEMU");
+ }
vnc_write_u32(vs, size);
vnc_write(vs, buf, size);
static int vnc_update_stats(VncDisplay *vd, struct timeval * tv)
{
- int width = pixman_image_get_width(vd->guest.fb);
- int height = pixman_image_get_height(vd->guest.fb);
+ int width = MIN(pixman_image_get_width(vd->guest.fb),
+ pixman_image_get_width(vd->server));
+ int height = MIN(pixman_image_get_height(vd->guest.fb),
+ pixman_image_get_height(vd->server));
int x, y;
struct timeval res;
int has_dirty = 0;
bool skipauth, bool websocket)
{
VncState *vs = g_new0(VncState, 1);
+ bool first_client = QTAILQ_EMPTY(&vd->clients);
int i;
vs->sioc = sioc;
vnc_qmp_event(vs, QAPI_EVENT_VNC_CONNECTED);
vnc_set_share_mode(vs, VNC_SHARE_MODE_CONNECTING);
- if (!vs->websocket) {
- vnc_init_state(vs);
- }
-
- if (vd->num_connecting > vd->connections_limit) {
- QTAILQ_FOREACH(vs, &vd->clients, next) {
- if (vs->share_mode == VNC_SHARE_MODE_CONNECTING) {
- vnc_disconnect_start(vs);
- return;
- }
- }
- }
-}
-
-void vnc_init_state(VncState *vs)
-{
- vs->initialized = true;
- VncDisplay *vd = vs->vd;
- bool first_client = QTAILQ_EMPTY(&vd->clients);
-
vs->last_x = -1;
vs->last_y = -1;
graphic_hw_update(vd->dcl.con);
+ if (!vs->websocket) {
+ vnc_start_protocol(vs);
+ }
+
+ if (vd->num_connecting > vd->connections_limit) {
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ if (vs->share_mode == VNC_SHARE_MODE_CONNECTING) {
+ vnc_disconnect_start(vs);
+ return;
+ }
+ }
+ }
+}
+
+void vnc_start_protocol(VncState *vs)
+{
vnc_write(vs, "RFB 003.008\n", 12);
vnc_flush(vs);
vnc_read_when(vs, protocol_version, 12);
- reset_keys(vs);
- if (vs->vd->lock_key_sync)
- vs->led = qemu_add_led_event_handler(kbd_leds, vs);
vs->mouse_mode_notifier.notify = check_pointer_type_change;
qemu_add_mouse_mode_change_notifier(&vs->mouse_mode_notifier);
VncDisplay *vd = opaque;
QIOChannelSocket *sioc = NULL;
Error *err = NULL;
+ bool isWebsock = false;
+ size_t i;
+
+ for (i = 0; i < vd->nlwebsock; i++) {
+ if (ioc == QIO_CHANNEL(vd->lwebsock[i])) {
+ isWebsock = true;
+ break;
+ }
+ }
sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc), &err);
if (sioc != NULL) {
+ qio_channel_set_name(QIO_CHANNEL(sioc),
+ isWebsock ? "vnc-ws-server" : "vnc-server");
qio_channel_set_delay(QIO_CHANNEL(sioc), false);
- vnc_connect(vd, sioc, false,
- ioc != QIO_CHANNEL(vd->lsock));
+ vnc_connect(vd, sioc, false, isWebsock);
object_unref(OBJECT(sioc));
} else {
/* client probably closed connection before we got there */
static const DisplayChangeListenerOps dcl_ops = {
.dpy_name = "vnc",
.dpy_refresh = vnc_refresh,
- .dpy_gfx_copy = vnc_dpy_copy,
.dpy_gfx_update = vnc_dpy_update,
.dpy_gfx_switch = vnc_dpy_switch,
.dpy_gfx_check_format = qemu_pixman_check_format,
static void vnc_display_close(VncDisplay *vd)
{
+ size_t i;
if (!vd) {
return;
}
vd->is_unix = false;
- if (vd->lsock != NULL) {
- if (vd->lsock_tag) {
- g_source_remove(vd->lsock_tag);
+ for (i = 0; i < vd->nlsock; i++) {
+ if (vd->lsock_tag[i]) {
+ g_source_remove(vd->lsock_tag[i]);
}
- object_unref(OBJECT(vd->lsock));
- vd->lsock = NULL;
+ object_unref(OBJECT(vd->lsock[i]));
}
- if (vd->lwebsock != NULL) {
- if (vd->lwebsock_tag) {
- g_source_remove(vd->lwebsock_tag);
+ g_free(vd->lsock);
+ g_free(vd->lsock_tag);
+ vd->lsock = NULL;
+ vd->lsock_tag = NULL;
+ vd->nlsock = 0;
+
+ for (i = 0; i < vd->nlwebsock; i++) {
+ if (vd->lwebsock_tag[i]) {
+ g_source_remove(vd->lwebsock_tag[i]);
}
- object_unref(OBJECT(vd->lwebsock));
- vd->lwebsock = NULL;
+ object_unref(OBJECT(vd->lwebsock[i]));
}
+ g_free(vd->lwebsock);
+ g_free(vd->lwebsock_tag);
+ vd->lwebsock = NULL;
+ vd->lwebsock_tag = NULL;
+ vd->nlwebsock = 0;
+
vd->auth = VNC_AUTH_INVALID;
vd->subauth = VNC_AUTH_INVALID;
if (vd->tlscreds) {
}
g_free(vd->tlsaclname);
vd->tlsaclname = NULL;
+ if (vd->lock_key_sync) {
+ qemu_remove_led_event_handler(vd->led);
+ vd->led = NULL;
+ }
}
int vnc_display_password(const char *id, const char *password)
SocketAddress *addr;
Error *err = NULL;
- addr = qio_channel_socket_get_local_address(vd->lsock, &err);
+ if (!vd->nlsock) {
+ return;
+ }
+
+ addr = qio_channel_socket_get_local_address(vd->lsock[0], &err);
if (!addr) {
return;
}
}
+static int vnc_display_get_address(const char *addrstr,
+ bool websocket,
+ int displaynum,
+ int to,
+ bool has_ipv4,
+ bool has_ipv6,
+ bool ipv4,
+ bool ipv6,
+ SocketAddress **retaddr,
+ Error **errp)
+{
+ int ret = -1;
+ SocketAddress *addr = NULL;
+
+ addr = g_new0(SocketAddress, 1);
+
+ if (strncmp(addrstr, "unix:", 5) == 0) {
+ addr->type = SOCKET_ADDRESS_KIND_UNIX;
+ addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
+ addr->u.q_unix.data->path = g_strdup(addrstr + 5);
+
+ if (websocket) {
+ error_setg(errp, "UNIX sockets not supported with websock");
+ goto cleanup;
+ }
+
+ if (to) {
+ error_setg(errp, "Port range not support with UNIX socket");
+ goto cleanup;
+ }
+ ret = 0;
+ } else {
+ const char *port;
+ size_t hostlen;
+ unsigned long long baseport = 0;
+ InetSocketAddress *inet;
+
+ port = strrchr(addrstr, ':');
+ if (!port) {
+ if (websocket) {
+ hostlen = 0;
+ port = addrstr;
+ } else {
+ error_setg(errp, "no vnc port specified");
+ goto cleanup;
+ }
+ } else {
+ hostlen = port - addrstr;
+ port++;
+ if (*port == '\0') {
+ error_setg(errp, "vnc port cannot be empty");
+ goto cleanup;
+ }
+ }
+
+ addr->type = SOCKET_ADDRESS_KIND_INET;
+ inet = addr->u.inet.data = g_new0(InetSocketAddress, 1);
+ if (addrstr[0] == '[' && addrstr[hostlen - 1] == ']') {
+ inet->host = g_strndup(addrstr + 1, hostlen - 2);
+ } else {
+ inet->host = g_strndup(addrstr, hostlen);
+ }
+ /* plain VNC port is just an offset, for websocket
+ * port is absolute */
+ if (websocket) {
+ if (g_str_equal(addrstr, "") ||
+ g_str_equal(addrstr, "on")) {
+ if (displaynum == -1) {
+ error_setg(errp, "explicit websocket port is required");
+ goto cleanup;
+ }
+ inet->port = g_strdup_printf(
+ "%d", displaynum + 5700);
+ if (to) {
+ inet->has_to = true;
+ inet->to = to + 5700;
+ }
+ } else {
+ inet->port = g_strdup(port);
+ }
+ } else {
+ if (parse_uint_full(port, &baseport, 10) < 0) {
+ error_setg(errp, "can't convert to a number: %s", port);
+ goto cleanup;
+ }
+ if (baseport > 65535 ||
+ baseport + 5900 > 65535) {
+ error_setg(errp, "port %s out of range", port);
+ goto cleanup;
+ }
+ inet->port = g_strdup_printf(
+ "%d", (int)baseport + 5900);
+
+ if (to) {
+ inet->has_to = true;
+ inet->to = to + 5900;
+ }
+ }
+
+ inet->ipv4 = ipv4;
+ inet->has_ipv4 = has_ipv4;
+ inet->ipv6 = ipv6;
+ inet->has_ipv6 = has_ipv6;
+
+ ret = baseport;
+ }
+
+ *retaddr = addr;
+
+ cleanup:
+ if (ret < 0) {
+ qapi_free_SocketAddress(addr);
+ }
+ return ret;
+}
+
+static int vnc_display_get_addresses(QemuOpts *opts,
+ SocketAddress ***retsaddr,
+ size_t *retnsaddr,
+ SocketAddress ***retwsaddr,
+ size_t *retnwsaddr,
+ Error **errp)
+{
+ SocketAddress *saddr = NULL;
+ SocketAddress *wsaddr = NULL;
+ QemuOptsIter addriter;
+ const char *addr;
+ int to = qemu_opt_get_number(opts, "to", 0);
+ bool has_ipv4 = qemu_opt_get(opts, "ipv4");
+ bool has_ipv6 = qemu_opt_get(opts, "ipv6");
+ bool ipv4 = qemu_opt_get_bool(opts, "ipv4", false);
+ bool ipv6 = qemu_opt_get_bool(opts, "ipv6", false);
+ size_t i;
+ 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;
+ }
+ 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;
+ }
+
+ qemu_opt_iter_init(&addriter, opts, "vnc");
+ while ((addr = qemu_opt_iter_next(&addriter)) != NULL) {
+ int rv;
+ rv = vnc_display_get_address(addr, false, 0, to,
+ has_ipv4, has_ipv6,
+ ipv4, ipv6,
+ &saddr, errp);
+ if (rv < 0) {
+ goto cleanup;
+ }
+ /* 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;
+ }
+
+ /* If we had multiple primary displays, we don't do defaults
+ * for websocket, and require explicit config instead. */
+ if (*retnsaddr > 1) {
+ displaynum = -1;
+ }
+
+ qemu_opt_iter_init(&addriter, opts, "websocket");
+ while ((addr = qemu_opt_iter_next(&addriter)) != NULL) {
+ if (vnc_display_get_address(addr, true, displaynum, to,
+ has_ipv4, has_ipv6,
+ ipv4, ipv6,
+ &wsaddr, errp) < 0) {
+ goto cleanup;
+ }
+
+ /* 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_KIND_INET &&
+ wsaddr->type == SOCKET_ADDRESS_KIND_INET &&
+ g_str_equal(wsaddr->u.inet.data->host, "") &&
+ !g_str_equal((*retsaddr)[0]->u.inet.data->host, "")) {
+ g_free(wsaddr->u.inet.data->host);
+ wsaddr->u.inet.data->host =
+ g_strdup((*retsaddr)[0]->u.inet.data->host);
+ }
+
+ *retwsaddr = g_renew(SocketAddress *, *retwsaddr, *retnwsaddr + 1);
+ (*retwsaddr)[(*retnwsaddr)++] = wsaddr;
+ }
+
+ ret = 0;
+ cleanup:
+ if (ret < 0) {
+ for (i = 0; i < *retnsaddr; i++) {
+ qapi_free_SocketAddress((*retsaddr)[i]);
+ }
+ g_free(*retsaddr);
+ for (i = 0; i < *retnwsaddr; i++) {
+ qapi_free_SocketAddress((*retwsaddr)[i]);
+ }
+ g_free(*retwsaddr);
+ *retsaddr = *retwsaddr = NULL;
+ *retnsaddr = *retnwsaddr = 0;
+ }
+ return ret;
+}
+
+static int vnc_display_connect(VncDisplay *vd,
+ SocketAddress **saddr,
+ size_t nsaddr,
+ SocketAddress **wsaddr,
+ size_t nwsaddr,
+ Error **errp)
+{
+ /* connect to viewer */
+ QIOChannelSocket *sioc = NULL;
+ if (nwsaddr != 0) {
+ error_setg(errp, "Cannot use websockets in reverse mode");
+ return -1;
+ }
+ if (nsaddr != 1) {
+ error_setg(errp, "Expected a single address in reverse mode");
+ return -1;
+ }
+ vd->is_unix = saddr[0]->type == SOCKET_ADDRESS_KIND_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) {
+ return -1;
+ }
+ vnc_connect(vd, sioc, false, false);
+ object_unref(OBJECT(sioc));
+ return 0;
+}
+
+
+static int vnc_display_listen_addr(VncDisplay *vd,
+ SocketAddress *addr,
+ const char *name,
+ QIOChannelSocket ***lsock,
+ guint **lsock_tag,
+ size_t *nlsock,
+ Error **errp)
+{
+ QIODNSResolver *resolver = qio_dns_resolver_get_instance();
+ SocketAddress **rawaddrs = NULL;
+ size_t nrawaddrs = 0;
+ Error *listenerr = NULL;
+ bool listening = false;
+ size_t i;
+
+ if (qio_dns_resolver_lookup_sync(resolver, addr, &nrawaddrs,
+ &rawaddrs, errp) < 0) {
+ return -1;
+ }
+
+ for (i = 0; i < nrawaddrs; i++) {
+ QIOChannelSocket *sioc = qio_channel_socket_new();
+
+ qio_channel_set_name(QIO_CHANNEL(sioc), name);
+ if (qio_channel_socket_listen_sync(
+ sioc, rawaddrs[i], listenerr == NULL ? &listenerr : NULL) < 0) {
+ object_unref(OBJECT(sioc));
+ continue;
+ }
+ listening = true;
+ (*nlsock)++;
+ *lsock = g_renew(QIOChannelSocket *, *lsock, *nlsock);
+ *lsock_tag = g_renew(guint, *lsock_tag, *nlsock);
+
+ (*lsock)[*nlsock - 1] = sioc;
+ (*lsock_tag)[*nlsock - 1] = 0;
+ }
+
+ for (i = 0; i < nrawaddrs; i++) {
+ qapi_free_SocketAddress(rawaddrs[i]);
+ }
+ g_free(rawaddrs);
+
+ if (listenerr) {
+ if (!listening) {
+ error_propagate(errp, listenerr);
+ return -1;
+ } else {
+ error_free(listenerr);
+ }
+ }
+
+ for (i = 0; i < *nlsock; i++) {
+ (*lsock_tag)[i] = qio_channel_add_watch(
+ QIO_CHANNEL((*lsock)[i]),
+ G_IO_IN, vnc_listen_io, vd, NULL);
+ }
+
+ return 0;
+}
+
+
+static int vnc_display_listen(VncDisplay *vd,
+ SocketAddress **saddr,
+ size_t nsaddr,
+ SocketAddress **wsaddr,
+ size_t nwsaddr,
+ Error **errp)
+{
+ size_t i;
+
+ for (i = 0; i < nsaddr; i++) {
+ if (vnc_display_listen_addr(vd, saddr[i],
+ "vnc-listen",
+ &vd->lsock,
+ &vd->lsock_tag,
+ &vd->nlsock,
+ errp) < 0) {
+ return -1;
+ }
+ }
+ for (i = 0; i < nwsaddr; i++) {
+ if (vnc_display_listen_addr(vd, wsaddr[i],
+ "vnc-ws-listen",
+ &vd->lwebsock,
+ &vd->lwebsock_tag,
+ &vd->nlwebsock,
+ errp) < 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
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;
+ SocketAddress **saddr = NULL, **wsaddr = NULL;
+ size_t nsaddr, nwsaddr;
const char *share, *device_id;
QemuConsole *con;
bool password = false;
bool reverse = false;
- const char *vnc;
- char *h;
const char *credid;
- int show_vnc_port = 0;
bool sasl = false;
#ifdef CONFIG_VNC_SASL
int saslErr;
int acl = 0;
int lock_key_sync = 1;
int key_delay_ms;
- bool ws_enabled = false;
+ size_t i;
if (!vd) {
error_setg(errp, "VNC display not active");
if (!opts) {
return;
}
- vnc = qemu_opt_get(opts, "vnc");
- if (!vnc || strcmp(vnc, "none") == 0) {
- return;
- }
-
- h = strrchr(vnc, ':');
- if (h) {
- size_t hlen = h - vnc;
-
- const char *websocket = qemu_opt_get(opts, "websocket");
- int to = qemu_opt_get_number(opts, "to", 0);
- bool has_ipv4 = qemu_opt_get(opts, "ipv4");
- bool has_ipv6 = qemu_opt_get(opts, "ipv6");
- bool ipv4 = qemu_opt_get_bool(opts, "ipv4", false);
- bool ipv6 = qemu_opt_get_bool(opts, "ipv6", false);
-
- saddr = g_new0(SocketAddress, 1);
- if (websocket) {
- if (!qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA1)) {
- error_setg(errp,
- "SHA1 hash support is required for websockets");
- goto fail;
- }
-
- wsaddr = g_new0(SocketAddress, 1);
- ws_enabled = true;
- }
- if (strncmp(vnc, "unix:", 5) == 0) {
- saddr->type = SOCKET_ADDRESS_KIND_UNIX;
- saddr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
- saddr->u.q_unix.data->path = g_strdup(vnc + 5);
-
- if (ws_enabled) {
- error_setg(errp, "UNIX sockets not supported with websock");
- goto fail;
- }
- } else {
- unsigned long long baseport;
- InetSocketAddress *inet;
- saddr->type = SOCKET_ADDRESS_KIND_INET;
- inet = saddr->u.inet.data = g_new0(InetSocketAddress, 1);
- if (vnc[0] == '[' && vnc[hlen - 1] == ']') {
- inet->host = g_strndup(vnc + 1, hlen - 2);
- } else {
- inet->host = g_strndup(vnc, hlen);
- }
- if (parse_uint_full(h + 1, &baseport, 10) < 0) {
- error_setg(errp, "can't convert to a number: %s", h + 1);
- goto fail;
- }
- if (baseport > 65535 ||
- baseport + 5900 > 65535) {
- error_setg(errp, "port %s out of range", h + 1);
- goto fail;
- }
- inet->port = g_strdup_printf(
- "%d", (int)baseport + 5900);
-
- if (to) {
- inet->has_to = true;
- inet->to = to + 5900;
- show_vnc_port = 1;
- }
- inet->ipv4 = ipv4;
- inet->has_ipv4 = has_ipv4;
- inet->ipv6 = ipv6;
- inet->has_ipv6 = has_ipv6;
-
- if (ws_enabled) {
- wsaddr->type = SOCKET_ADDRESS_KIND_INET;
- inet = wsaddr->u.inet.data = g_new0(InetSocketAddress, 1);
- inet->host = g_strdup(saddr->u.inet.data->host);
- inet->port = g_strdup(websocket);
-
- if (to) {
- inet->has_to = true;
- inet->to = to;
- }
- inet->ipv4 = ipv4;
- inet->has_ipv4 = has_ipv4;
- inet->ipv6 = ipv6;
- inet->has_ipv6 = has_ipv6;
- }
- }
- } else {
- error_setg(errp, "no vnc port specified");
+ if (vnc_display_get_addresses(opts, &saddr, &nsaddr,
+ &wsaddr, &nwsaddr, errp) < 0) {
goto fail;
}
+ if (saddr == NULL) {
+ return;
+ }
+
password = qemu_opt_get_bool(opts, "password", false);
if (password) {
if (fips_get_state()) {
goto fail;
}
if (!qcrypto_cipher_supports(
- QCRYPTO_CIPHER_ALG_DES_RFB)) {
+ QCRYPTO_CIPHER_ALG_DES_RFB, QCRYPTO_CIPHER_MODE_ECB)) {
error_setg(errp,
"Cipher backend does not support DES RFB algorithm");
goto fail;
}
#endif
vd->lock_key_sync = lock_key_sync;
+ if (lock_key_sync) {
+ vd->led = qemu_add_led_event_handler(kbd_leds, vd);
+ }
+ vd->ledstate = 0;
vd->key_delay_ms = key_delay_ms;
device_id = qemu_opt_get(opts, "display");
}
if (reverse) {
- /* connect to viewer */
- QIOChannelSocket *sioc = NULL;
- vd->lsock = NULL;
- vd->lwebsock = NULL;
- if (ws_enabled) {
- error_setg(errp, "Cannot use websockets in reverse mode");
+ if (vnc_display_connect(vd, saddr, nsaddr, wsaddr, nwsaddr, errp) < 0) {
goto fail;
}
- vd->is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX;
- sioc = qio_channel_socket_new();
- if (qio_channel_socket_connect_sync(sioc, saddr, errp) < 0) {
- goto fail;
- }
- vnc_connect(vd, sioc, false, false);
- object_unref(OBJECT(sioc));
} else {
- vd->lsock = qio_channel_socket_new();
- if (qio_channel_socket_listen_sync(vd->lsock, saddr, errp) < 0) {
+ if (vnc_display_listen(vd, saddr, nsaddr, wsaddr, nwsaddr, errp) < 0) {
goto fail;
}
- vd->is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX;
-
- if (ws_enabled) {
- vd->lwebsock = qio_channel_socket_new();
- if (qio_channel_socket_listen_sync(vd->lwebsock,
- wsaddr, errp) < 0) {
- object_unref(OBJECT(vd->lsock));
- vd->lsock = NULL;
- goto fail;
- }
- }
-
- vd->lsock_tag = qio_channel_add_watch(
- QIO_CHANNEL(vd->lsock),
- G_IO_IN, vnc_listen_io, vd, NULL);
- if (ws_enabled) {
- vd->lwebsock_tag = qio_channel_add_watch(
- QIO_CHANNEL(vd->lwebsock),
- G_IO_IN, vnc_listen_io, vd, NULL);
- }
}
- if (show_vnc_port) {
+ if (qemu_opt_get(opts, "to")) {
vnc_display_print_local_addr(vd);
}
- qapi_free_SocketAddress(saddr);
- qapi_free_SocketAddress(wsaddr);
+ cleanup:
+ for (i = 0; i < nsaddr; i++) {
+ qapi_free_SocketAddress(saddr[i]);
+ }
+ for (i = 0; i < nwsaddr; i++) {
+ qapi_free_SocketAddress(wsaddr[i]);
+ }
return;
fail:
- qapi_free_SocketAddress(saddr);
- qapi_free_SocketAddress(wsaddr);
- ws_enabled = false;
+ vnc_display_close(vd);
+ goto cleanup;
}
void vnc_display_add_client(const char *id, int csock, bool skipauth)
sioc = qio_channel_socket_new_fd(csock, NULL);
if (sioc) {
+ qio_channel_set_name(QIO_CHANNEL(sioc), "vnc-server");
vnc_connect(vd, sioc, skipauth, false);
object_unref(OBJECT(sioc));
}