#include "crypto/tlscredsanon.h"
#include "crypto/tlscredsx509.h"
#include "qom/object_interfaces.h"
+#include "qemu/cutils.h"
#define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT
#define VNC_REFRESH_INTERVAL_INC 50
{
switch (addr->type) {
case SOCKET_ADDRESS_KIND_INET:
- info->host = g_strdup(addr->u.inet->host);
- info->service = g_strdup(addr->u.inet->port);
- if (addr->u.inet->ipv6) {
+ info->host = g_strdup(addr->u.inet.data->host);
+ info->service = g_strdup(addr->u.inet.data->port);
+ if (addr->u.inet.data->ipv6) {
info->family = NETWORK_ADDRESS_FAMILY_IPV6;
} else {
info->family = NETWORK_ADDRESS_FAMILY_IPV4;
case SOCKET_ADDRESS_KIND_UNIX:
info->host = g_strdup("");
- info->service = g_strdup(addr->u.q_unix->path);
+ info->service = g_strdup(addr->u.q_unix.data->path);
info->family = NETWORK_ADDRESS_FAMILY_UNIX;
break;
{
SocketAddress *addr = NULL;
+ if (!ioc) {
+ error_setg(errp, "No listener socket available");
+ return;
+ }
+
addr = qio_channel_socket_get_local_address(ioc, errp);
if (!addr) {
return;
VncServerInfo *info;
Error *err = NULL;
- info = g_malloc(sizeof(*info));
+ info = g_malloc0(sizeof(*info));
vnc_init_basic_info_from_server_addr(vd->lsock,
qapi_VncServerInfo_base(info), &err);
info->has_auth = true;
VncDisplay *vd = vnc_display_find(NULL);
SocketAddress *addr = NULL;
- if (vd == NULL || !vd->enabled) {
+ if (vd == NULL || !vd->lsock) {
info->enabled = false;
} else {
info->enabled = true;
switch (addr->type) {
case SOCKET_ADDRESS_KIND_INET:
- info->host = g_strdup(addr->u.inet->host);
- info->service = g_strdup(addr->u.inet->port);
- if (addr->u.inet->ipv6) {
+ info->host = g_strdup(addr->u.inet.data->host);
+ info->service = g_strdup(addr->u.inet.data->port);
+ if (addr->u.inet.data->ipv6) {
info->family = NETWORK_ADDRESS_FAMILY_IPV6;
} else {
info->family = NETWORK_ADDRESS_FAMILY_IPV4;
case SOCKET_ADDRESS_KIND_UNIX:
info->host = g_strdup("");
- info->service = g_strdup(addr->u.q_unix->path);
+ info->service = g_strdup(addr->u.q_unix.data->path);
info->family = NETWORK_ADDRESS_FAMILY_UNIX;
break;
static void vnc_update_server_surface(VncDisplay *vd)
{
+ int width, height;
+
qemu_pixman_image_unref(vd->server);
vd->server = NULL;
return;
}
+ width = vnc_width(vd);
+ height = vnc_height(vd);
vd->server = pixman_image_create_bits(VNC_SERVER_FB_FORMAT,
- vnc_width(vd),
- vnc_height(vd),
+ width, height,
NULL, 0);
+
+ memset(vd->guest.dirty, 0x00, sizeof(vd->guest.dirty));
+ vnc_set_area_dirty(vd->guest.dirty, vd, 0, 0,
+ width, height);
}
static void vnc_dpy_switch(DisplayChangeListener *dcl,
{
VncDisplay *vd = container_of(dcl, VncDisplay, dcl);
VncState *vs;
- int width, height;
vnc_abort_display_jobs(vd);
vd->ds = surface;
qemu_pixman_image_unref(vd->guest.fb);
vd->guest.fb = pixman_image_ref(surface->image);
vd->guest.format = surface->format;
- width = vnc_width(vd);
- height = vnc_height(vd);
- memset(vd->guest.dirty, 0x00, sizeof(vd->guest.dirty));
- vnc_set_area_dirty(vd->guest.dirty, vd, 0, 0,
- width, height);
QTAILQ_FOREACH(vs, &vd->clients, next) {
vnc_colordepth(vs);
}
memset(vs->dirty, 0x00, sizeof(vs->dirty));
vnc_set_area_dirty(vs->dirty, vd, 0, 0,
- width, height);
+ vnc_width(vd),
+ vnc_height(vd));
}
}
}
}
+ 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);
static int vnc_update_client(VncState *vs, int has_dirty, bool sync)
{
+ if (vs->disconnecting) {
+ vnc_disconnect_finish(vs);
+ return 0;
+ }
+
vs->has_dirty += has_dirty;
- if (vs->need_update && vs->ioc != NULL) {
+ if (vs->need_update && !vs->disconnecting) {
VncDisplay *vd = vs->vd;
VncJob *job;
int y;
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)
* First function called whenever there is more data to be read from
* the client socket. Will delegate actual work according to whether
* SASL SSF layers are enabled (thus requiring decryption calls)
+ * Returns 0 on success, -1 if client disconnected
*/
-static void vnc_client_read(VncState *vs)
+static int vnc_client_read(VncState *vs)
{
ssize_t ret;
if (!ret) {
if (vs->disconnecting) {
vnc_disconnect_finish(vs);
+ return -1;
}
- return;
+ return 0;
}
while (vs->read_handler && vs->input.offset >= vs->read_handler_expect) {
ret = vs->read_handler(vs, vs->input.buffer, len);
if (vs->disconnecting) {
vnc_disconnect_finish(vs);
- return;
+ return -1;
}
if (!ret) {
vs->read_handler_expect = ret;
}
}
+ return 0;
}
gboolean vnc_client_io(QIOChannel *ioc G_GNUC_UNUSED,
{
VncState *vs = opaque;
if (condition & G_IO_IN) {
- vnc_client_read(vs);
+ if (vnc_client_read(vs) < 0) {
+ return TRUE;
+ }
}
if (condition & G_IO_OUT) {
vnc_client_write(vs);
[INPUT_BUTTON_LEFT] = 0x01,
[INPUT_BUTTON_MIDDLE] = 0x02,
[INPUT_BUTTON_RIGHT] = 0x04,
- [INPUT_BUTTON_WHEELUP] = 0x08,
- [INPUT_BUTTON_WHEELDOWN] = 0x10,
+ [INPUT_BUTTON_WHEEL_UP] = 0x08,
+ [INPUT_BUTTON_WHEEL_DOWN] = 0x10,
};
QemuConsole *con = vs->vd->dcl.con;
int width = pixman_image_get_width(vs->vd->server);
for(i = 0; i < 256; i++) {
if (vs->modifiers_state[i]) {
qemu_input_event_send_key_number(vs->vd->dcl.con, i, false);
+ qemu_input_event_send_key_delay(vs->vd->key_delay_ms);
vs->modifiers_state[i] = 0;
}
}
{
int keycode = keysym2scancode(vs->vd->kbd_layout, keysym) & SCANCODE_KEYMASK;
qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, true);
- qemu_input_event_send_key_delay(0);
+ qemu_input_event_send_key_delay(vs->vd->key_delay_ms);
qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, false);
- qemu_input_event_send_key_delay(0);
+ qemu_input_event_send_key_delay(vs->vd->key_delay_ms);
}
static int current_led_state(VncState *vs)
if (qemu_console_is_graphic(NULL)) {
qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, down);
+ qemu_input_event_send_key_delay(vs->vd->key_delay_ms);
} else {
bool numlock = vs->modifiers_state[0x45];
bool control = (vs->modifiers_state[0x1d] ||
continue;
}
qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, false);
+ qemu_input_event_send_key_delay(vs->vd->key_delay_ms);
}
}
break;
case VNC_ENCODING_RICH_CURSOR:
vs->features |= VNC_FEATURE_RICH_CURSOR_MASK;
+ if (vs->vd->cursor) {
+ vnc_cursor_define(vs);
+ }
break;
case VNC_ENCODING_EXT_KEY_EVENT:
send_ext_key_event_ack(vs);
}
}
-static void set_pixel_format(VncState *vs,
- int bits_per_pixel, int depth,
+static void send_color_map(VncState *vs)
+{
+ int i;
+
+ vnc_write_u8(vs, VNC_MSG_SERVER_SET_COLOUR_MAP_ENTRIES);
+ vnc_write_u8(vs, 0); /* padding */
+ vnc_write_u16(vs, 0); /* first color */
+ vnc_write_u16(vs, 256); /* # of colors */
+
+ for (i = 0; i < 256; i++) {
+ PixelFormat *pf = &vs->client_pf;
+
+ vnc_write_u16(vs, (((i >> pf->rshift) & pf->rmax) << (16 - pf->rbits)));
+ vnc_write_u16(vs, (((i >> pf->gshift) & pf->gmax) << (16 - pf->gbits)));
+ vnc_write_u16(vs, (((i >> pf->bshift) & pf->bmax) << (16 - pf->bbits)));
+ }
+}
+
+static void set_pixel_format(VncState *vs, int bits_per_pixel,
int big_endian_flag, int true_color_flag,
int red_max, int green_max, int blue_max,
int red_shift, int green_shift, int blue_shift)
{
if (!true_color_flag) {
- vnc_client_error(vs);
- return;
+ /* Expose a reasonable default 256 color map */
+ bits_per_pixel = 8;
+ red_max = 7;
+ green_max = 7;
+ blue_max = 3;
+ red_shift = 0;
+ green_shift = 3;
+ blue_shift = 6;
}
switch (bits_per_pixel) {
vs->client_pf.depth = bits_per_pixel == 32 ? 24 : bits_per_pixel;
vs->client_be = big_endian_flag;
+ if (!true_color_flag) {
+ send_color_map(vs);
+ }
+
set_pixel_conversion(vs);
graphic_hw_invalidate(vs->vd->dcl.con);
if (len == 1)
return 20;
- set_pixel_format(vs, read_u8(data, 4), read_u8(data, 5),
+ set_pixel_format(vs, read_u8(data, 4),
read_u8(data, 6), read_u8(data, 7),
read_u16(data, 8), read_u16(data, 10),
read_u16(data, 12), read_u8(data, 14),
bool skipauth, bool websocket)
{
VncState *vs = g_new0(VncState, 1);
+ bool first_client = QTAILQ_EMPTY(&vd->clients);
int i;
vs->sioc = sioc;
qio_channel_set_blocking(vs->ioc, false, NULL);
if (websocket) {
vs->websocket = 1;
- if (vd->ws_tls) {
+ if (vd->tlscreds) {
vs->ioc_tag = qio_channel_add_watch(
vs->ioc, G_IO_IN, vncws_tls_handshake_io, vs, NULL);
} else {
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);
-
- /* vs might be free()ed here */
}
static gboolean vnc_listen_io(QIOChannel *ioc,
GIOCondition condition,
void *opaque)
{
- VncDisplay *vs = opaque;
+ VncDisplay *vd = opaque;
QIOChannelSocket *sioc = NULL;
Error *err = NULL;
- /* Catch-up */
- graphic_hw_update(vs->dcl.con);
sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc), &err);
if (sioc != NULL) {
+ qio_channel_set_name(QIO_CHANNEL(sioc),
+ ioc != QIO_CHANNEL(vd->lsock) ?
+ "vnc-ws-server" : "vnc-server");
qio_channel_set_delay(QIO_CHANNEL(sioc), false);
- vnc_connect(vs, sioc, false,
- ioc != QIO_CHANNEL(vs->lsock));
+ vnc_connect(vd, sioc, false,
+ ioc != QIO_CHANNEL(vd->lsock));
object_unref(OBJECT(sioc));
} else {
/* client probably closed connection before we got there */
void vnc_display_init(const char *id)
{
- VncDisplay *vs;
+ VncDisplay *vd;
if (vnc_display_find(id) != NULL) {
return;
}
- vs = g_malloc0(sizeof(*vs));
+ vd = g_malloc0(sizeof(*vd));
- vs->id = strdup(id);
- QTAILQ_INSERT_TAIL(&vnc_displays, vs, next);
+ vd->id = strdup(id);
+ QTAILQ_INSERT_TAIL(&vnc_displays, vd, next);
- QTAILQ_INIT(&vs->clients);
- vs->expires = TIME_MAX;
+ QTAILQ_INIT(&vd->clients);
+ vd->expires = TIME_MAX;
if (keyboard_layout) {
trace_vnc_key_map_init(keyboard_layout);
- vs->kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout);
+ vd->kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout);
} else {
- vs->kbd_layout = init_keyboard_layout(name2keysym, "en-us");
+ vd->kbd_layout = init_keyboard_layout(name2keysym, "en-us");
}
- if (!vs->kbd_layout)
+ if (!vd->kbd_layout) {
exit(1);
+ }
+
+ vd->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE;
+ vd->connections_limit = 32;
- qemu_mutex_init(&vs->mutex);
+ qemu_mutex_init(&vd->mutex);
vnc_start_worker_thread();
- vs->dcl.ops = &dcl_ops;
- register_displaychangelistener(&vs->dcl);
+ vd->dcl.ops = &dcl_ops;
+ register_displaychangelistener(&vd->dcl);
}
-static void vnc_display_close(VncDisplay *vs)
+static void vnc_display_close(VncDisplay *vd)
{
- if (!vs)
+ if (!vd) {
return;
- vs->enabled = false;
- vs->is_unix = false;
- if (vs->lsock != NULL) {
- if (vs->lsock_tag) {
- g_source_remove(vs->lsock_tag);
+ }
+ vd->is_unix = false;
+ if (vd->lsock != NULL) {
+ if (vd->lsock_tag) {
+ g_source_remove(vd->lsock_tag);
}
- object_unref(OBJECT(vs->lsock));
- vs->lsock = NULL;
+ object_unref(OBJECT(vd->lsock));
+ vd->lsock = NULL;
}
- vs->ws_enabled = false;
- if (vs->lwebsock != NULL) {
- if (vs->lwebsock_tag) {
- g_source_remove(vs->lwebsock_tag);
+ if (vd->lwebsock != NULL) {
+ if (vd->lwebsock_tag) {
+ g_source_remove(vd->lwebsock_tag);
}
- object_unref(OBJECT(vs->lwebsock));
- vs->lwebsock = NULL;
+ object_unref(OBJECT(vd->lwebsock));
+ vd->lwebsock = NULL;
}
- vs->auth = VNC_AUTH_INVALID;
- vs->subauth = VNC_AUTH_INVALID;
- if (vs->tlscreds) {
- object_unparent(OBJECT(vs->tlscreds));
- vs->tlscreds = NULL;
+ vd->auth = VNC_AUTH_INVALID;
+ vd->subauth = VNC_AUTH_INVALID;
+ if (vd->tlscreds) {
+ object_unparent(OBJECT(vd->tlscreds));
+ vd->tlscreds = NULL;
}
- g_free(vs->tlsaclname);
- vs->tlsaclname = NULL;
+ g_free(vd->tlsaclname);
+ vd->tlsaclname = NULL;
}
int vnc_display_password(const char *id, const char *password)
{
- VncDisplay *vs = vnc_display_find(id);
+ VncDisplay *vd = vnc_display_find(id);
- if (!vs) {
+ if (!vd) {
return -EINVAL;
}
- if (vs->auth == VNC_AUTH_NONE) {
+ if (vd->auth == VNC_AUTH_NONE) {
error_printf_unless_qmp("If you want use passwords please enable "
- "password auth using '-vnc ${dpy},password'.");
+ "password auth using '-vnc ${dpy},password'.\n");
return -EINVAL;
}
- g_free(vs->password);
- vs->password = g_strdup(password);
+ g_free(vd->password);
+ vd->password = g_strdup(password);
return 0;
}
int vnc_display_pw_expire(const char *id, time_t expires)
{
- VncDisplay *vs = vnc_display_find(id);
+ VncDisplay *vd = vnc_display_find(id);
- if (!vs) {
+ if (!vd) {
return -EINVAL;
}
- vs->expires = expires;
+ vd->expires = expires;
return 0;
}
-char *vnc_display_local_addr(const char *id)
+static void vnc_display_print_local_addr(VncDisplay *vd)
{
- VncDisplay *vs = vnc_display_find(id);
SocketAddress *addr;
- char *ret;
Error *err = NULL;
- assert(vs);
-
- addr = qio_channel_socket_get_local_address(vs->lsock, &err);
+ addr = qio_channel_socket_get_local_address(vd->lsock, &err);
if (!addr) {
- return NULL;
+ return;
}
if (addr->type != SOCKET_ADDRESS_KIND_INET) {
qapi_free_SocketAddress(addr);
- return NULL;
+ return;
}
- ret = g_strdup_printf("%s;%s", addr->u.inet->host, addr->u.inet->port);
+ error_printf_unless_qmp("VNC server running on %s:%s\n",
+ addr->u.inet.data->host,
+ addr->u.inet.data->port);
qapi_free_SocketAddress(addr);
-
- return ret;
}
static QemuOptsList qemu_vnc_opts = {
},{
.name = "lock-key-sync",
.type = QEMU_OPT_BOOL,
+ },{
+ .name = "key-delay-ms",
+ .type = QEMU_OPT_NUMBER,
},{
.name = "sasl",
.type = QEMU_OPT_BOOL,
static int
-vnc_display_setup_auth(VncDisplay *vs,
+vnc_display_setup_auth(int *auth,
+ int *subauth,
+ QCryptoTLSCreds *tlscreds,
bool password,
bool sasl,
bool websocket,
* VNC auth mechs for plain VNC vs websockets VNC, the end
* result has the same security characteristics.
*/
- if (password) {
- if (vs->tlscreds) {
- vs->auth = VNC_AUTH_VENCRYPT;
- if (websocket) {
- vs->ws_tls = true;
- }
- if (object_dynamic_cast(OBJECT(vs->tlscreds),
- TYPE_QCRYPTO_TLS_CREDS_X509)) {
- VNC_DEBUG("Initializing VNC server with x509 password auth\n");
- vs->subauth = VNC_AUTH_VENCRYPT_X509VNC;
- } else if (object_dynamic_cast(OBJECT(vs->tlscreds),
- TYPE_QCRYPTO_TLS_CREDS_ANON)) {
- VNC_DEBUG("Initializing VNC server with TLS password auth\n");
- vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC;
- } else {
- error_setg(errp,
- "Unsupported TLS cred type %s",
- object_get_typename(OBJECT(vs->tlscreds)));
- return -1;
- }
- } else {
+ if (websocket || !tlscreds) {
+ if (password) {
VNC_DEBUG("Initializing VNC server with password auth\n");
- vs->auth = VNC_AUTH_VNC;
- vs->subauth = VNC_AUTH_INVALID;
- }
- if (websocket) {
- vs->ws_auth = VNC_AUTH_VNC;
+ *auth = VNC_AUTH_VNC;
+ } else if (sasl) {
+ VNC_DEBUG("Initializing VNC server with SASL auth\n");
+ *auth = VNC_AUTH_SASL;
} else {
- vs->ws_auth = VNC_AUTH_INVALID;
+ VNC_DEBUG("Initializing VNC server with no auth\n");
+ *auth = VNC_AUTH_NONE;
}
- } else if (sasl) {
- if (vs->tlscreds) {
- vs->auth = VNC_AUTH_VENCRYPT;
- if (websocket) {
- vs->ws_tls = true;
+ *subauth = VNC_AUTH_INVALID;
+ } else {
+ bool is_x509 = object_dynamic_cast(OBJECT(tlscreds),
+ TYPE_QCRYPTO_TLS_CREDS_X509) != NULL;
+ bool is_anon = object_dynamic_cast(OBJECT(tlscreds),
+ TYPE_QCRYPTO_TLS_CREDS_ANON) != NULL;
+
+ if (!is_x509 && !is_anon) {
+ error_setg(errp,
+ "Unsupported TLS cred type %s",
+ object_get_typename(OBJECT(tlscreds)));
+ return -1;
+ }
+ *auth = VNC_AUTH_VENCRYPT;
+ if (password) {
+ if (is_x509) {
+ VNC_DEBUG("Initializing VNC server with x509 password auth\n");
+ *subauth = VNC_AUTH_VENCRYPT_X509VNC;
+ } else {
+ VNC_DEBUG("Initializing VNC server with TLS password auth\n");
+ *subauth = VNC_AUTH_VENCRYPT_TLSVNC;
}
- if (object_dynamic_cast(OBJECT(vs->tlscreds),
- TYPE_QCRYPTO_TLS_CREDS_X509)) {
+
+ } else if (sasl) {
+ if (is_x509) {
VNC_DEBUG("Initializing VNC server with x509 SASL auth\n");
- vs->subauth = VNC_AUTH_VENCRYPT_X509SASL;
- } else if (object_dynamic_cast(OBJECT(vs->tlscreds),
- TYPE_QCRYPTO_TLS_CREDS_ANON)) {
- VNC_DEBUG("Initializing VNC server with TLS SASL auth\n");
- vs->subauth = VNC_AUTH_VENCRYPT_TLSSASL;
+ *subauth = VNC_AUTH_VENCRYPT_X509SASL;
} else {
- error_setg(errp,
- "Unsupported TLS cred type %s",
- object_get_typename(OBJECT(vs->tlscreds)));
- return -1;
+ VNC_DEBUG("Initializing VNC server with TLS SASL auth\n");
+ *subauth = VNC_AUTH_VENCRYPT_TLSSASL;
}
} else {
- VNC_DEBUG("Initializing VNC server with SASL auth\n");
- vs->auth = VNC_AUTH_SASL;
- vs->subauth = VNC_AUTH_INVALID;
- }
- if (websocket) {
- vs->ws_auth = VNC_AUTH_SASL;
- } else {
- vs->ws_auth = VNC_AUTH_INVALID;
- }
- } else {
- if (vs->tlscreds) {
- vs->auth = VNC_AUTH_VENCRYPT;
- if (websocket) {
- vs->ws_tls = true;
- }
- if (object_dynamic_cast(OBJECT(vs->tlscreds),
- TYPE_QCRYPTO_TLS_CREDS_X509)) {
+ if (is_x509) {
VNC_DEBUG("Initializing VNC server with x509 no auth\n");
- vs->subauth = VNC_AUTH_VENCRYPT_X509NONE;
- } else if (object_dynamic_cast(OBJECT(vs->tlscreds),
- TYPE_QCRYPTO_TLS_CREDS_ANON)) {
- VNC_DEBUG("Initializing VNC server with TLS no auth\n");
- vs->subauth = VNC_AUTH_VENCRYPT_TLSNONE;
+ *subauth = VNC_AUTH_VENCRYPT_X509NONE;
} else {
- error_setg(errp,
- "Unsupported TLS cred type %s",
- object_get_typename(OBJECT(vs->tlscreds)));
- return -1;
+ VNC_DEBUG("Initializing VNC server with TLS no auth\n");
+ *subauth = VNC_AUTH_VENCRYPT_TLSNONE;
}
- } else {
- VNC_DEBUG("Initializing VNC server with no auth\n");
- vs->auth = VNC_AUTH_NONE;
- vs->subauth = VNC_AUTH_INVALID;
- }
- if (websocket) {
- vs->ws_auth = VNC_AUTH_NONE;
- } else {
- vs->ws_auth = VNC_AUTH_INVALID;
}
}
return 0;
void vnc_display_open(const char *id, Error **errp)
{
- VncDisplay *vs = vnc_display_find(id);
+ VncDisplay *vd = vnc_display_find(id);
QemuOpts *opts = qemu_opts_find(&qemu_vnc_opts, id);
SocketAddress *saddr = NULL, *wsaddr = NULL;
const char *share, *device_id;
const char *vnc;
char *h;
const char *credid;
+ int show_vnc_port = 0;
bool sasl = false;
#ifdef CONFIG_VNC_SASL
int saslErr;
#endif
int acl = 0;
int lock_key_sync = 1;
+ int key_delay_ms;
+ bool ws_enabled = false;
- if (!vs) {
+ if (!vd) {
error_setg(errp, "VNC display not active");
return;
}
- vnc_display_close(vs);
+ vnc_display_close(vd);
if (!opts) {
return;
}
wsaddr = g_new0(SocketAddress, 1);
- vs->ws_enabled = true;
+ ws_enabled = true;
}
if (strncmp(vnc, "unix:", 5) == 0) {
saddr->type = SOCKET_ADDRESS_KIND_UNIX;
- saddr->u.q_unix = g_new0(UnixSocketAddress, 1);
- saddr->u.q_unix->path = g_strdup(vnc + 5);
+ saddr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
+ saddr->u.q_unix.data->path = g_strdup(vnc + 5);
- if (vs->ws_enabled) {
+ 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;
- saddr->u.inet = g_new0(InetSocketAddress, 1);
+ inet = saddr->u.inet.data = g_new0(InetSocketAddress, 1);
if (vnc[0] == '[' && vnc[hlen - 1] == ']') {
- saddr->u.inet->host = g_strndup(vnc + 1, hlen - 2);
+ inet->host = g_strndup(vnc + 1, hlen - 2);
} else {
- saddr->u.inet->host = g_strndup(vnc, hlen);
+ 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);
error_setg(errp, "port %s out of range", h + 1);
goto fail;
}
- saddr->u.inet->port = g_strdup_printf(
+ inet->port = g_strdup_printf(
"%d", (int)baseport + 5900);
if (to) {
- saddr->u.inet->has_to = true;
- saddr->u.inet->to = to + 5900;
+ inet->has_to = true;
+ inet->to = to + 5900;
+ show_vnc_port = 1;
}
- saddr->u.inet->ipv4 = ipv4;
- saddr->u.inet->has_ipv4 = has_ipv4;
- saddr->u.inet->ipv6 = ipv6;
- saddr->u.inet->has_ipv6 = has_ipv6;
+ inet->ipv4 = ipv4;
+ inet->has_ipv4 = has_ipv4;
+ inet->ipv6 = ipv6;
+ inet->has_ipv6 = has_ipv6;
- if (vs->ws_enabled) {
+ if (ws_enabled) {
wsaddr->type = SOCKET_ADDRESS_KIND_INET;
- wsaddr->u.inet = g_new0(InetSocketAddress, 1);
- wsaddr->u.inet->host = g_strdup(saddr->u.inet->host);
- wsaddr->u.inet->port = g_strdup(websocket);
+ 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) {
- wsaddr->u.inet->has_to = true;
- wsaddr->u.inet->to = to;
+ inet->has_to = true;
+ inet->to = to;
}
- wsaddr->u.inet->ipv4 = ipv4;
- wsaddr->u.inet->has_ipv4 = has_ipv4;
- wsaddr->u.inet->ipv6 = ipv6;
- wsaddr->u.inet->has_ipv6 = has_ipv6;
+ inet->ipv4 = ipv4;
+ inet->has_ipv4 = has_ipv4;
+ inet->ipv6 = ipv6;
+ inet->has_ipv6 = has_ipv6;
}
}
} else {
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;
reverse = qemu_opt_get_bool(opts, "reverse", false);
lock_key_sync = qemu_opt_get_bool(opts, "lock-key-sync", true);
+ key_delay_ms = qemu_opt_get_number(opts, "key-delay-ms", 1);
sasl = qemu_opt_get_bool(opts, "sasl", false);
#ifndef CONFIG_VNC_SASL
if (sasl) {
credid);
goto fail;
}
- vs->tlscreds = (QCryptoTLSCreds *)
+ vd->tlscreds = (QCryptoTLSCreds *)
object_dynamic_cast(creds,
TYPE_QCRYPTO_TLS_CREDS);
- if (!vs->tlscreds) {
+ if (!vd->tlscreds) {
error_setg(errp, "Object with id '%s' is not TLS credentials",
credid);
goto fail;
}
- object_ref(OBJECT(vs->tlscreds));
+ object_ref(OBJECT(vd->tlscreds));
- if (vs->tlscreds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+ if (vd->tlscreds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
error_setg(errp,
"Expecting TLS credentials with a server endpoint");
goto fail;
x509verify = true;
}
}
- vs->tlscreds = vnc_display_create_creds(x509,
+ vd->tlscreds = vnc_display_create_creds(x509,
x509verify,
path,
- vs->id,
+ vd->id,
errp);
- if (!vs->tlscreds) {
+ if (!vd->tlscreds) {
goto fail;
}
}
share = qemu_opt_get(opts, "share");
if (share) {
if (strcmp(share, "ignore") == 0) {
- vs->share_policy = VNC_SHARE_POLICY_IGNORE;
+ vd->share_policy = VNC_SHARE_POLICY_IGNORE;
} else if (strcmp(share, "allow-exclusive") == 0) {
- vs->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE;
+ vd->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE;
} else if (strcmp(share, "force-shared") == 0) {
- vs->share_policy = VNC_SHARE_POLICY_FORCE_SHARED;
+ vd->share_policy = VNC_SHARE_POLICY_FORCE_SHARED;
} else {
error_setg(errp, "unknown vnc share= option");
goto fail;
}
} else {
- vs->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE;
+ vd->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE;
}
- vs->connections_limit = qemu_opt_get_number(opts, "connections", 32);
+ vd->connections_limit = qemu_opt_get_number(opts, "connections", 32);
#ifdef CONFIG_VNC_JPEG
- vs->lossy = qemu_opt_get_bool(opts, "lossy", false);
+ vd->lossy = qemu_opt_get_bool(opts, "lossy", false);
#endif
- vs->non_adaptive = qemu_opt_get_bool(opts, "non-adaptive", false);
+ vd->non_adaptive = qemu_opt_get_bool(opts, "non-adaptive", false);
/* adaptive updates are only used with tight encoding and
* if lossy updates are enabled so we can disable all the
* calculations otherwise */
- if (!vs->lossy) {
- vs->non_adaptive = true;
+ if (!vd->lossy) {
+ vd->non_adaptive = true;
}
if (acl) {
- if (strcmp(vs->id, "default") == 0) {
- vs->tlsaclname = g_strdup("vnc.x509dname");
+ if (strcmp(vd->id, "default") == 0) {
+ vd->tlsaclname = g_strdup("vnc.x509dname");
} else {
- vs->tlsaclname = g_strdup_printf("vnc.%s.x509dname", vs->id);
+ vd->tlsaclname = g_strdup_printf("vnc.%s.x509dname", vd->id);
}
- qemu_acl_init(vs->tlsaclname);
+ qemu_acl_init(vd->tlsaclname);
}
#ifdef CONFIG_VNC_SASL
if (acl && sasl) {
char *aclname;
- if (strcmp(vs->id, "default") == 0) {
+ if (strcmp(vd->id, "default") == 0) {
aclname = g_strdup("vnc.username");
} else {
- aclname = g_strdup_printf("vnc.%s.username", vs->id);
+ aclname = g_strdup_printf("vnc.%s.username", vd->id);
}
- vs->sasl.acl = qemu_acl_init(aclname);
+ vd->sasl.acl = qemu_acl_init(aclname);
g_free(aclname);
}
#endif
- if (vnc_display_setup_auth(vs, password, sasl, vs->ws_enabled, errp) < 0) {
+ if (vnc_display_setup_auth(&vd->auth, &vd->subauth,
+ vd->tlscreds, password,
+ sasl, false, errp) < 0) {
+ goto fail;
+ }
+
+ if (vnc_display_setup_auth(&vd->ws_auth, &vd->ws_subauth,
+ vd->tlscreds, password,
+ sasl, true, errp) < 0) {
goto fail;
}
goto fail;
}
#endif
- vs->lock_key_sync = lock_key_sync;
+ vd->lock_key_sync = lock_key_sync;
+ vd->key_delay_ms = key_delay_ms;
device_id = qemu_opt_get(opts, "display");
if (device_id) {
con = NULL;
}
- if (con != vs->dcl.con) {
- unregister_displaychangelistener(&vs->dcl);
- vs->dcl.con = con;
- register_displaychangelistener(&vs->dcl);
+ if (con != vd->dcl.con) {
+ unregister_displaychangelistener(&vd->dcl);
+ vd->dcl.con = con;
+ register_displaychangelistener(&vd->dcl);
}
if (reverse) {
/* connect to viewer */
QIOChannelSocket *sioc = NULL;
- vs->lsock = NULL;
- vs->lwebsock = NULL;
- if (vs->ws_enabled) {
+ vd->lsock = NULL;
+ vd->lwebsock = NULL;
+ if (ws_enabled) {
error_setg(errp, "Cannot use websockets in reverse mode");
goto fail;
}
- vs->is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX;
+ vd->is_unix = saddr->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, errp) < 0) {
goto fail;
}
- vnc_connect(vs, sioc, false, false);
+ vnc_connect(vd, sioc, false, false);
object_unref(OBJECT(sioc));
} else {
- vs->lsock = qio_channel_socket_new();
- if (qio_channel_socket_listen_sync(vs->lsock, saddr, errp) < 0) {
+ vd->lsock = qio_channel_socket_new();
+ qio_channel_set_name(QIO_CHANNEL(vd->lsock), "vnc-listen");
+ if (qio_channel_socket_listen_sync(vd->lsock, saddr, errp) < 0) {
goto fail;
}
- vs->is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX;
- vs->enabled = true;
+ vd->is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX;
- if (vs->ws_enabled) {
- vs->lwebsock = qio_channel_socket_new();
- if (qio_channel_socket_listen_sync(vs->lwebsock,
+ if (ws_enabled) {
+ vd->lwebsock = qio_channel_socket_new();
+ qio_channel_set_name(QIO_CHANNEL(vd->lwebsock), "vnc-ws-listen");
+ if (qio_channel_socket_listen_sync(vd->lwebsock,
wsaddr, errp) < 0) {
- object_unref(OBJECT(vs->lsock));
- vs->lsock = NULL;
+ object_unref(OBJECT(vd->lsock));
+ vd->lsock = NULL;
goto fail;
}
}
- vs->lsock_tag = qio_channel_add_watch(
- QIO_CHANNEL(vs->lsock),
- G_IO_IN, vnc_listen_io, vs, NULL);
- if (vs->ws_enabled) {
- vs->lwebsock_tag = qio_channel_add_watch(
- QIO_CHANNEL(vs->lwebsock),
- G_IO_IN, vnc_listen_io, vs, NULL);
+ 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) {
+ vnc_display_print_local_addr(vd);
+ }
+
qapi_free_SocketAddress(saddr);
qapi_free_SocketAddress(wsaddr);
return;
fail:
qapi_free_SocketAddress(saddr);
qapi_free_SocketAddress(wsaddr);
- vs->enabled = false;
- vs->ws_enabled = false;
+ ws_enabled = false;
}
void vnc_display_add_client(const char *id, int csock, bool skipauth)
{
- VncDisplay *vs = vnc_display_find(id);
+ VncDisplay *vd = vnc_display_find(id);
QIOChannelSocket *sioc;
- if (!vs) {
+ if (!vd) {
return;
}
sioc = qio_channel_socket_new_fd(csock, NULL);
if (sioc) {
- vnc_connect(vs, sioc, skipauth, false);
+ qio_channel_set_name(QIO_CHANNEL(sioc), "vnc-server");
+ vnc_connect(vd, sioc, skipauth, false);
object_unref(OBJECT(sioc));
}
}
{
qemu_add_opts(&qemu_vnc_opts);
}
-machine_init(vnc_register_config);
+opts_init(vnc_register_config);