#include "trace.h"
#include "sysemu/sysemu.h"
#include "qemu/error-report.h"
+#include "qemu/option.h"
#include "qemu/sockets.h"
#include "qemu/timer.h"
#include "qemu/acl.h"
#include "qemu/config-file.h"
-#include "qapi/qmp/qerror.h"
-#include "qapi/qmp/types.h"
-#include "qmp-commands.h"
+#include "qapi/error.h"
+#include "qapi/qapi-commands-ui.h"
#include "ui/input.h"
#include "qapi-event.h"
#include "crypto/hash.h"
VncServerInfo *info;
Error *err = NULL;
- if (!vd->nlsock) {
+ if (!vd->listener || !vd->listener->nsioc) {
return NULL;
}
info = g_malloc0(sizeof(*info));
- vnc_init_basic_info_from_server_addr(vd->lsock[0],
+ 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));
VncDisplay *vd = vnc_display_find(NULL);
SocketAddress *addr = NULL;
- if (vd == NULL || !vd->nlsock) {
+ if (vd == NULL || !vd->listener || !vd->listener->nsioc) {
info->enabled = false;
} else {
info->enabled = true;
info->has_clients = true;
info->clients = qmp_query_client_list(vd);
- if (vd->lsock == NULL) {
- return info;
- }
-
- addr = qio_channel_socket_get_local_address(vd->lsock[0], errp);
+ addr = qio_channel_socket_get_local_address(vd->listener->sioc[0],
+ errp);
if (!addr) {
goto out_error;
}
info->has_display = true;
info->display = g_strdup(dev->id);
}
- for (i = 0; i < vd->nlsock; i++) {
+ for (i = 0; vd->listener != NULL && i < vd->listener->nsioc; i++) {
info->server = qmp_query_server_entry(
- vd->lsock[i], false, vd->auth, vd->subauth, info->server);
+ vd->listener->sioc[i], false, vd->auth, vd->subauth,
+ info->server);
}
- for (i = 0; i < vd->nlwebsock; i++) {
+ for (i = 0; vd->wslistener != NULL && i < vd->wslistener->nsioc; i++) {
info->server = qmp_query_server_entry(
- vd->lwebsock[i], true, vd->ws_auth,
+ vd->wslistener->sioc[i], true, vd->ws_auth,
vd->ws_subauth, info->server);
}
vs->client_width * vs->client_height * vs->client_pf.bytes_per_pixel;
if (vs->audio_cap) {
- int freq = vs->as.freq;
- /* We don't limit freq when reading settings from client, so
- * it could be upto MAX_INT in size. 48khz is a sensible
- * upper bound for trustworthy clients */
int bps;
- if (freq > 48000) {
- freq = 48000;
- }
switch (vs->as.fmt) {
default:
case AUD_FMT_U8:
bps = 4;
break;
}
- offset += freq * bps * vs->as.nchannels;
+ offset += vs->as.freq * bps * vs->as.nchannels;
}
/* Put a floor of 1MB on offset, so that if we have a large pending
VncState *vs = opaque;
if (condition & G_IO_IN) {
if (vnc_client_read(vs) < 0) {
- return TRUE;
+ goto end;
}
}
if (condition & G_IO_OUT) {
vnc_client_write(vs);
}
+end:
+ if (vs->disconnecting) {
+ if (vs->ioc_tag != 0) {
+ g_source_remove(vs->ioc_tag);
+ }
+ vs->ioc_tag = 0;
+ }
return TRUE;
}
* handshake, or from the job thread's VncState clone
*/
if (vs->throttle_output_offset != 0 &&
- vs->output.offset > (vs->throttle_output_offset *
- VNC_THROTTLE_OUTPUT_LIMIT_SCALE)) {
+ (vs->output.offset / VNC_THROTTLE_OUTPUT_LIMIT_SCALE) >
+ vs->throttle_output_offset) {
trace_vnc_client_output_limit(vs, vs->ioc, vs->output.offset,
vs->throttle_output_offset);
vnc_disconnect_start(vs);
if (vs->ioc != NULL && vs->output.offset) {
vnc_client_write_locked(vs);
}
+ if (vs->disconnecting) {
+ if (vs->ioc_tag != 0) {
+ g_source_remove(vs->ioc_tag);
+ }
+ vs->ioc_tag = 0;
+ }
vnc_unlock_output(vs);
}
static void press_key(VncState *vs, int keysym)
{
- int keycode = keysym2scancode(vs->vd->kbd_layout, keysym) & SCANCODE_KEYMASK;
+ int keycode = keysym2scancode(vs->vd->kbd_layout, keysym,
+ false, false, false) & SCANCODE_KEYMASK;
qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, true);
qemu_input_event_send_key_delay(vs->vd->key_delay_ms);
qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, false);
static void key_event(VncState *vs, int down, uint32_t sym)
{
+ bool shift = vs->modifiers_state[0x2a] || vs->modifiers_state[0x36];
+ bool altgr = vs->modifiers_state[0xb8];
+ bool ctrl = vs->modifiers_state[0x1d] || vs->modifiers_state[0x9d];
int keycode;
int lsym = sym;
lsym = lsym - 'A' + 'a';
}
- keycode = keysym2scancode(vs->vd->kbd_layout, lsym & 0xFFFF) & SCANCODE_KEYMASK;
+ keycode = keysym2scancode(vs->vd->kbd_layout, lsym & 0xFFFF,
+ shift, altgr, ctrl) & SCANCODE_KEYMASK;
trace_vnc_key_event_map(down, sym, keycode, code2name(keycode));
do_key_event(vs, down, keycode, sym);
}
{
int i;
uint16_t limit;
+ uint32_t freq;
VncDisplay *vd = vs->vd;
if (data[0] > 3) {
vnc_client_error(vs);
break;
}
- vs->as.freq = read_u32(data, 6);
+ freq = read_u32(data, 6);
+ /* No official limit for protocol, but 48khz is a sensible
+ * upper bound for trustworthy clients, and this limit
+ * protects calculations involving 'vs->as.freq' later.
+ */
+ if (freq > 48000) {
+ VNC_DEBUG("Invalid audio frequency %u > 48000", freq);
+ vnc_client_error(vs);
+ break;
+ }
+ vs->as.freq = freq;
break;
default:
VNC_DEBUG("Invalid audio message %d\n", read_u8(data, 4));
qemu_add_mouse_mode_change_notifier(&vs->mouse_mode_notifier);
}
-static gboolean vnc_listen_io(QIOChannel *ioc,
- GIOCondition condition,
- void *opaque)
+static void vnc_listen_io(QIONetListener *listener,
+ QIOChannelSocket *cioc,
+ void *opaque)
{
VncDisplay *vd = opaque;
- QIOChannelSocket *sioc = NULL;
- Error *err = NULL;
- bool isWebsock = false;
- size_t i;
+ bool isWebsock = listener == vd->wslistener;
- 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, isWebsock);
- object_unref(OBJECT(sioc));
- } else {
- /* client probably closed connection before we got there */
- error_free(err);
- }
-
- return TRUE;
+ qio_channel_set_name(QIO_CHANNEL(cioc),
+ isWebsock ? "vnc-ws-server" : "vnc-server");
+ qio_channel_set_delay(QIO_CHANNEL(cioc), false);
+ vnc_connect(vd, cioc, false, isWebsock);
}
static const DisplayChangeListenerOps dcl_ops = {
static void vnc_display_close(VncDisplay *vd)
{
- size_t i;
if (!vd) {
return;
}
vd->is_unix = false;
- for (i = 0; i < vd->nlsock; i++) {
- if (vd->lsock_tag[i]) {
- g_source_remove(vd->lsock_tag[i]);
- }
- object_unref(OBJECT(vd->lsock[i]));
+
+ if (vd->listener) {
+ qio_net_listener_disconnect(vd->listener);
+ object_unref(OBJECT(vd->listener));
}
- g_free(vd->lsock);
- g_free(vd->lsock_tag);
- vd->lsock = NULL;
- vd->lsock_tag = NULL;
- vd->nlsock = 0;
+ vd->listener = NULL;
- for (i = 0; i < vd->nlwebsock; i++) {
- if (vd->lwebsock_tag[i]) {
- g_source_remove(vd->lwebsock_tag[i]);
- }
- object_unref(OBJECT(vd->lwebsock[i]));
+ if (vd->wslistener) {
+ qio_net_listener_disconnect(vd->wslistener);
+ object_unref(OBJECT(vd->wslistener));
}
- g_free(vd->lwebsock);
- g_free(vd->lwebsock_tag);
- vd->lwebsock = NULL;
- vd->lwebsock_tag = NULL;
- vd->nlwebsock = 0;
+ vd->wslistener = NULL;
vd->auth = VNC_AUTH_INVALID;
vd->subauth = VNC_AUTH_INVALID;
SocketAddress *addr;
Error *err = NULL;
- if (!vd->nlsock) {
+ if (!vd->listener || !vd->listener->nsioc) {
return;
}
- addr = qio_channel_socket_get_local_address(vd->lsock[0], &err);
+ addr = qio_channel_socket_get_local_address(vd->listener->sioc[0], &err);
if (!addr) {
return;
}
}
-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,
{
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;
+ if (nsaddr) {
+ vd->listener = qio_net_listener_new();
+ qio_net_listener_set_name(vd->listener, "vnc-listen");
+ for (i = 0; i < nsaddr; i++) {
+ if (qio_net_listener_open_sync(vd->listener,
+ saddr[i],
+ errp) < 0) {
+ return -1;
+ }
}
+
+ qio_net_listener_set_client_func(vd->listener,
+ vnc_listen_io, vd, NULL);
}
- 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;
+
+ if (nwsaddr) {
+ vd->wslistener = qio_net_listener_new();
+ qio_net_listener_set_name(vd->wslistener, "vnc-ws-listen");
+ for (i = 0; i < nwsaddr; i++) {
+ if (qio_net_listener_open_sync(vd->wslistener,
+ wsaddr[i],
+ errp) < 0) {
+ return -1;
+ }
}
+
+ qio_net_listener_set_client_func(vd->wslistener,
+ vnc_listen_io, vd, NULL);
}
return 0;