* [Qemu-devel] [PATCH] vnc: added initial websockets support
@ 2012-11-16 15:13 Tim Hardeck
2012-11-16 15:13 ` Tim Hardeck
2012-11-18 9:31 ` Stefan Hajnoczi
0 siblings, 2 replies; 8+ messages in thread
From: Tim Hardeck @ 2012-11-16 15:13 UTC (permalink / raw)
To: qemu-devel; +Cc: Tim Hardeck, github
This patch adds basic Websockets support to the QEMU VNC component. Websockets allow every modern web browser to connect to QEMU VNC without any additional plugins.
Because of the GnuTLS requirement the Websockets implementation is optional (--enable-vnc-ws).
Websockets connections are recognized by waiting 500ms for a Websocket handshake. If no data is received a regular vnc connection is assumed.
If this is not acceptable please suggest if Websockets should be activated by a new VNC option or by an additional console parameter on a different port.
SHA1 is required for the handshake which is generated with GnuTLS. Since using GnuTLS does automatically activate VNC-TLS, which has warnings about deprecated parts, I have changed the configure script to disable VNC-TLS if not explicitly enabled.
The Websockets support was tested with noVNC http://kanaka.github.com/noVNC/ .
I have used parts of the LibVNC websockets implementation that's why I have added the GPL header to the new files. I hope that it is fine that way.
Tim Hardeck (1):
vnc: added initial websockets support
configure | 34 +++++++-
ui/Makefile.objs | 1 +
ui/vnc-ws.c | 236 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
ui/vnc-ws.h | 104 ++++++++++++++++++++++++
ui/vnc.c | 93 ++++++++++++++++++---
ui/vnc.h | 15 ++++
6 files changed, 471 insertions(+), 12 deletions(-)
create mode 100644 ui/vnc-ws.c
create mode 100644 ui/vnc-ws.h
--
1.7.10.4
^ permalink raw reply [flat|nested] 8+ messages in thread
* [Qemu-devel] [PATCH] vnc: added initial websockets support
2012-11-16 15:13 [Qemu-devel] [PATCH] vnc: added initial websockets support Tim Hardeck
@ 2012-11-16 15:13 ` Tim Hardeck
2012-11-18 9:31 ` Stefan Hajnoczi
1 sibling, 0 replies; 8+ messages in thread
From: Tim Hardeck @ 2012-11-16 15:13 UTC (permalink / raw)
To: qemu-devel; +Cc: Tim Hardeck, github
This patch adds basic Websockets version 13 - RFC 6455 - support to QEMU
VNC. Binary encoding support on the client side is required.
Websockets support in QEMU is enabled by the configure option
--enable-vnc-ws.
Websockets connections are recognized by waiting 500ms for a Websocket
handshake. If no data is received a regular vnc connection is assumed.
SHA1 is required for the handshake which is generated by GnuTLS.
Since using GnuTLS does automatically activate VNC-TLS, which has
warnings about deprecated parts, I have changed the configure script
to disable VNC-TLS if not explicitly enabled.
Parts of the implementation base on Anthony Liguori's QEMU Websockets
patch from 2010 and on Joel Martin's LibVNC Websockets implementation.
Signed-off-by: Tim Hardeck <thardeck@suse.de>
---
configure | 34 +++++++-
ui/Makefile.objs | 1 +
ui/vnc-ws.c | 236 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
ui/vnc-ws.h | 104 ++++++++++++++++++++++++
ui/vnc.c | 93 ++++++++++++++++++---
ui/vnc.h | 15 ++++
6 files changed, 471 insertions(+), 12 deletions(-)
create mode 100644 ui/vnc-ws.c
create mode 100644 ui/vnc-ws.h
diff --git a/configure b/configure
index 7290f50..d252da5 100755
--- a/configure
+++ b/configure
@@ -154,10 +154,11 @@ vnc="yes"
sparse="no"
uuid=""
vde=""
-vnc_tls=""
+vnc_tls="no"
vnc_sasl=""
vnc_jpeg=""
vnc_png=""
+vnc_ws=""
xen=""
xen_ctrl_version=""
xen_pci_passthrough=""
@@ -703,6 +704,10 @@ for opt do
;;
--enable-vnc-png) vnc_png="yes"
;;
+ --disable-vnc-ws) vnc_ws="no"
+ ;;
+ --enable-vnc-ws) vnc_ws="yes"
+ ;;
--disable-slirp) slirp="no"
;;
--disable-uuid) uuid="no"
@@ -1048,6 +1053,8 @@ echo " --disable-vnc-jpeg disable JPEG lossy compression for VNC server"
echo " --enable-vnc-jpeg enable JPEG lossy compression for VNC server"
echo " --disable-vnc-png disable PNG compression for VNC server (default)"
echo " --enable-vnc-png enable PNG compression for VNC server"
+echo " --disable-vnc-ws disable Websockets support for VNC server"
+echo " --enable-vnc-ws enable Websockets support for VNC server"
echo " --disable-curses disable curses output"
echo " --enable-curses enable curses output"
echo " --disable-curl disable curl connectivity"
@@ -1772,6 +1779,26 @@ EOF
fi
##########################################
+# VNC WS detection
+if test "$vnc" = "yes" -a "$vnc_ws" != "no" ; then
+ cat > $TMPC <<EOF
+#include <gnutls/gnutls.h>
+int main(void) { gnutls_session_t s; gnutls_init(&s, GNUTLS_SERVER); return 0; }
+EOF
+ vnc_ws_cflags=`$pkg_config --cflags gnutls 2> /dev/null`
+ vnc_ws_libs=`$pkg_config --libs gnutls 2> /dev/null`
+ if compile_prog "$vnc_ws_cflags" "$vnc_ws_libs" ; then
+ vnc_ws=yes
+ libs_softmmu="$vnc_ws_libs $libs_softmmu"
+ else
+ if test "$vnc_ws" = "yes" ; then
+ feature_not_found "vnc-ws"
+ fi
+ vnc_ws=no
+ fi
+fi
+
+##########################################
# fnmatch() probe, used for ACL routines
fnmatch="no"
cat > $TMPC << EOF
@@ -3191,6 +3218,7 @@ if test "$vnc" = "yes" ; then
echo "VNC SASL support $vnc_sasl"
echo "VNC JPEG support $vnc_jpeg"
echo "VNC PNG support $vnc_png"
+ echo "VNC WS support $vnc_ws"
fi
if test -n "$sparc_cpu"; then
echo "Target Sparc Arch $sparc_cpu"
@@ -3367,6 +3395,10 @@ if test "$vnc_png" = "yes" ; then
echo "CONFIG_VNC_PNG=y" >> $config_host_mak
echo "VNC_PNG_CFLAGS=$vnc_png_cflags" >> $config_host_mak
fi
+if test "$vnc_ws" = "yes" ; then
+ echo "CONFIG_VNC_WS=y" >> $config_host_mak
+ echo "VNC_WS_CFLAGS=$vnc_ws_cflags" >> $config_host_mak
+fi
if test "$fnmatch" = "yes" ; then
echo "CONFIG_FNMATCH=y" >> $config_host_mak
fi
diff --git a/ui/Makefile.objs b/ui/Makefile.objs
index adc07be..58e191b 100644
--- a/ui/Makefile.objs
+++ b/ui/Makefile.objs
@@ -4,6 +4,7 @@ vnc-obj-y += vnc-enc-tight.o vnc-palette.o
vnc-obj-y += vnc-enc-zrle.o
vnc-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o
vnc-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o
+vnc-obj-$(CONFIG_VNC_WS) += vnc-ws.o
vnc-obj-y += vnc-jobs.o
common-obj-y += keymaps.o
diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c
new file mode 100644
index 0000000..c40266a
--- /dev/null
+++ b/ui/vnc-ws.c
@@ -0,0 +1,236 @@
+/*
+ * QEMU VNC display driver: Websockets support
+ *
+ * Copyright (C) 2010 Joel Martin
+ * Copyright (C) 2012 Tim Hardeck
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include "vnc.h"
+
+void vnc_recognize_connection_type(void *opaque)
+{
+ VncState *vs = opaque;
+ qemu_del_timer(vs->ws_recognition_timer);
+
+ /*
+ * Regular VNC clients wait for a server response while
+ * Websocket VNC clients send a handshake directly
+ */
+ if (vs->input.offset > 0) {
+ VNC_DEBUG("Websockets Handshake found, using Websockets\n");
+ uint8_t *loc = memmem(vs->input.buffer, vs->input.offset,
+ WS_HANDSHAKE_END, strlen(WS_HANDSHAKE_END));
+ if (loc == NULL) {
+ VNC_DEBUG("Received faulty Websocket header, disconnecting\n");
+ vnc_client_error(vs);
+ return;
+ }
+
+ vncws_process_handshake(vs, vs->input.buffer, vs->input.offset);
+
+ buffer_reset(&vs->input);
+ }
+
+ vnc_init_state(vs);
+}
+
+char *vncws_extract_handshake_entry(const char *header, size_t header_len,
+ const char *name)
+{
+ int name_len = strlen(name);
+ char *begin, *end;
+ begin = memmem(header, header_len, name, name_len);
+ if (begin != NULL) {
+ begin += name_len;
+ end = memmem(begin, header_len - (begin - header), WS_HANDSHAKE_DELIM,
+ strlen(WS_HANDSHAKE_DELIM));
+ if (end != NULL) {
+ return g_strndup(begin, end - begin);
+ }
+ }
+ return NULL;
+}
+
+void vncws_process_handshake(VncState *vs, uint8_t *line, size_t size)
+{
+ char *protocols = vncws_extract_handshake_entry((const char *)line, size,
+ "Sec-WebSocket-Protocol: ");
+ char *version = vncws_extract_handshake_entry((const char *)line, size,
+ "Sec-WebSocket-Version: ");
+ char *key = vncws_extract_handshake_entry((const char *)line, size,
+ "Sec-WebSocket-Key: ");
+
+ if (protocols && version && key
+ && memmem(protocols, strlen(protocols), "binary", 6) != NULL
+ && strcmp(version, WS_SUPPORTED_VERSION) == 0
+ && strlen(key) == WS_CLIENT_KEY_LEN) {
+ vncws_send_handshake_response(vs, key);
+ } else {
+ VNC_DEBUG("Defective Websockets header or unsupported protocol, disconnecting\n");
+ vnc_client_error(vs);
+ }
+
+ g_free(protocols);
+ g_free(version);
+ g_free(key);
+}
+
+void vncws_send_handshake_response(VncState *vs, const char* key)
+{
+ char combined_key[WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1];
+ char response[WS_HANDSHAKE_MAX_LEN];
+ char hash[SHA1_DIGEST_SIZE + 1];
+ char *accept = NULL;
+ size_t hash_size = SHA1_DIGEST_SIZE, response_size = 0;
+ gnutls_datum_t in;
+
+ /* create combined key */
+ pstrcpy(combined_key, WS_CLIENT_KEY_LEN + 1, key);
+ pstrcat(combined_key, WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1, WS_GUID);
+
+ /* hash and encode it*/
+ in.data = (void *)combined_key;
+ in.size = WS_CLIENT_KEY_LEN + WS_GUID_LEN;
+ if (gnutls_fingerprint(GNUTLS_DIG_SHA1, &in, hash, &hash_size)
+ == GNUTLS_E_SUCCESS) {
+ accept = g_base64_encode((const guchar *)hash, SHA1_DIGEST_SIZE);
+ }
+ if(accept == NULL) {
+ VNC_DEBUG("Hashing Websocket combined key failed\n");
+ vnc_client_error(vs);
+ return;
+ }
+
+ /* create handshake response */
+ response_size = snprintf(response, WS_HANDSHAKE_MAX_LEN, WS_HANDSHAKE, accept);
+ g_free(accept);
+
+ vnc_write(vs, response, response_size);
+ vnc_flush(vs);
+
+ vs->encode_ws = 1;
+}
+
+void vncws_encode_frame(Buffer *output, const void *payload,
+ const size_t payload_size)
+{
+ size_t header_size = 0;
+ unsigned char opcode = WS_OPCODE_BINARY_FRAME;
+ char header_buf[WS_HEAD_MAX_LEN];
+ ws_header_t *header = (ws_header_t *)header_buf;
+
+ if (!payload_size) {
+ return;
+ }
+
+ header->b0 = 0x80 | (opcode & 0x0f);
+ if (payload_size <= 125) {
+ header->b1 = (uint8_t)payload_size;
+ header_size = 2;
+ } else if (payload_size < 65536) {
+ header->b1 = 0x7e;
+ header->u.s16.l16 = WS_HTON16((uint16_t)payload_size);
+ header_size = 4;
+ } else {
+ header->b1 = 0x7f;
+ header->u.s64.l64 = WS_HTON64(payload_size);
+ header_size = 10;
+ }
+
+ buffer_reserve(output, header_size + payload_size);
+ buffer_append(output, header_buf, header_size);
+ buffer_append(output, payload, payload_size);
+}
+
+int vncws_decode_frame(Buffer *input, uint8_t **payload,
+ size_t *payload_size, size_t *frame_size)
+{
+ unsigned char opcode = 0, fin = 0, has_mask = 0;
+ uint32_t *payload32;
+ size_t header_size = 0;
+ ws_header_t *header = (ws_header_t *)input->buffer;
+ ws_mask_t mask;
+ int mask_size = 0;
+ int i;
+
+ if (input->offset < WS_HEAD_MIN_LEN) {
+ /* header not complete */
+ return 0;
+ }
+
+ fin = (header->b0 & 0x80) >> 7;
+ opcode = header->b0 & 0x0f;
+ has_mask = (header->b1 & 0x80) >> 7;
+ *payload_size = header->b1 & 0x7f;
+
+ if (opcode == WS_OPCODE_CLOSE) {
+ /* disconnect */
+ return -1;
+ }
+
+ /* Websocket frame sanity check:
+ * * Websocket fragmentation is not supported.
+ * * All websockets frames sent by a client have to be masked.
+ * * Only binary encoding is supported.
+ */
+ if (!fin || !has_mask || opcode != WS_OPCODE_BINARY_FRAME) {
+ VNC_DEBUG("Received faulty/unsupported Websocket frame\n");
+ return -2;
+ }
+
+ mask_size = 4 * has_mask;
+
+ if (*payload_size < 126) {
+ header_size = 2;
+ mask = header->u.m;
+ } else if (*payload_size == 126 && input->offset >= 4 + mask_size) {
+ *payload_size = WS_NTOH16(header->u.s16.l16);
+ header_size = 4;
+ mask = header->u.s16.m16;
+ } else if (*payload_size == 127 && input->offset >= 10 + mask_size) {
+ *payload_size = WS_NTOH64(header->u.s64.l64);
+ header_size = 10;
+ mask = header->u.s64.m64;
+ } else {
+ /* header not complete */
+ return 0;
+ }
+
+ header_size += mask_size;
+ *frame_size = header_size + *payload_size;
+
+ if (input->offset < *frame_size) {
+ /* frame not complete */
+ return 0;
+ }
+
+ *payload = input->buffer + header_size;
+
+ /* unmask frame */
+ /* process 1 frame (32 bit op) */
+ payload32 = (uint32_t *)(*payload);
+ for (i = 0; i < *payload_size / 4; i++) {
+ payload32[i] ^= mask.u;
+ }
+ /* process the remaining bytes (if any) */
+ for (i *= 4; i < *payload_size; i++) {
+ (*payload)[i] ^= mask.c[i % 4];
+ }
+
+ return 1;
+}
diff --git a/ui/vnc-ws.h b/ui/vnc-ws.h
new file mode 100644
index 0000000..88f9f8b
--- /dev/null
+++ b/ui/vnc-ws.h
@@ -0,0 +1,104 @@
+/*
+ * QEMU VNC display driver: Websockets support
+ *
+ * Copyright (C) 2010 Joel Martin
+ * Copyright (C) 2012 Tim Hardeck
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef __QEMU_VNC_WS_H
+#define __QEMU_VNC_WS_H
+
+#ifndef CONFIG_VNC_TLS
+#include <gnutls/gnutls.h>
+#endif
+
+#define B64LEN(__x) (((__x + 2) / 3) * 12 / 3)
+#define SHA1_DIGEST_SIZE 20
+
+#define WS_HANDSHAKE_TIMEOUT 500
+
+#define WS_ACCEPT_LEN (B64LEN(SHA1_DIGEST_SIZE) + 1)
+#define WS_CLIENT_KEY_LEN 24
+#define WS_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+#define WS_GUID_LEN strlen(WS_GUID)
+
+#define WS_HANDSHAKE_MAX_LEN 192
+#define WS_HANDSHAKE "HTTP/1.1 101 Switching Protocols\r\n\
+Upgrade: websocket\r\n\
+Connection: Upgrade\r\n\
+Sec-WebSocket-Accept: %s\r\n\
+Sec-WebSocket-Protocol: binary\r\n\
+\r\n"
+#define WS_HANDSHAKE_DELIM "\r\n"
+#define WS_HANDSHAKE_END "\r\n\r\n"
+#define WS_SUPPORTED_VERSION "13"
+
+#define WS_HEAD_MIN_LEN 2
+#define WS_HEAD_MAX_LEN 14 /* 2 + sizeof(uint64_t) + sizeof(uint32_t) */
+
+#define WS_HTON64(n) htobe64(n)
+#define WS_HTON16(n) htobe16(n)
+#define WS_NTOH16(n) htobe16(n)
+#define WS_NTOH64(n) htobe64(n)
+
+typedef union ws_mask_s {
+ char c[4];
+ uint32_t u;
+} ws_mask_t;
+
+/* XXX: The union and the structs do not need to be named.
+ * We are working around a bug present in GCC < 4.6 which prevented
+ * it from recognizing anonymous structs and unions.
+ * See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=4784
+ */
+typedef struct __attribute__ ((__packed__)) ws_header_s {
+ unsigned char b0;
+ unsigned char b1;
+ union {
+ struct __attribute__ ((__packed__)) {
+ uint16_t l16;
+ ws_mask_t m16;
+ } s16;
+ struct __attribute__ ((__packed__)) {
+ uint64_t l64;
+ ws_mask_t m64;
+ } s64;
+ ws_mask_t m;
+ } u;
+} ws_header_t;
+
+enum {
+ WS_OPCODE_CONTINUATION = 0x0,
+ WS_OPCODE_TEXT_FRAME = 0x1,
+ WS_OPCODE_BINARY_FRAME = 0x2,
+ WS_OPCODE_CLOSE = 0x8,
+ WS_OPCODE_PING = 0x9,
+ WS_OPCODE_PONG = 0xA
+};
+
+void vnc_recognize_connection_type(void *opaque);
+char *vncws_extract_handshake_entry(const char *header, size_t header_len,
+ const char *name);
+void vncws_process_handshake(VncState *vs, uint8_t *line, size_t size);
+void vncws_send_handshake_response(VncState *vs, const char* key);
+void vncws_encode_frame(Buffer *output, const void *payload,
+ const size_t payload_size);
+int vncws_decode_frame(Buffer *input, uint8_t **payload,
+ size_t *payload_size, size_t *frame_size);
+
+#endif /* __QEMU_VNC_WS_H */
diff --git a/ui/vnc.c b/ui/vnc.c
index 61f120e..6d57888 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -510,6 +510,13 @@ void buffer_append(Buffer *buffer, const void *data, size_t len)
buffer->offset += len;
}
+void buffer_advance(Buffer *buf, size_t len)
+{
+ memmove(buf->buffer, buf->buffer + len,
+ (buf->offset - len));
+ buf->offset -= len;
+}
+
static void vnc_desktop_resize(VncState *vs)
{
DisplayState *ds = vs->ds;
@@ -1027,6 +1034,9 @@ static void vnc_disconnect_finish(VncState *vs)
buffer_free(&vs->input);
buffer_free(&vs->output);
+#ifdef CONFIG_VNC_WS
+ buffer_free(&vs->ws_input);
+#endif
qobject_decref(vs->info);
@@ -1166,8 +1176,7 @@ static long vnc_client_write_plain(VncState *vs)
if (!ret)
return 0;
- memmove(vs->output.buffer, vs->output.buffer + ret, (vs->output.offset - ret));
- vs->output.offset -= ret;
+ buffer_advance(&vs->output, ret);
if (vs->output.offset == 0) {
qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
@@ -1289,6 +1298,7 @@ void vnc_client_read(void *opaque)
{
VncState *vs = opaque;
long ret;
+ Buffer *buf;
#ifdef CONFIG_VNC_SASL
if (vs->sasl.conn && vs->sasl.runSSF)
@@ -1302,19 +1312,49 @@ void vnc_client_read(void *opaque)
return;
}
- while (vs->read_handler && vs->input.offset >= vs->read_handler_expect) {
+#ifdef CONFIG_VNC_WS
+ if (vs->encode_ws) {
+ uint8_t *payload;
+ size_t payload_size, frame_size;
+
+ /* make sure that nothing is left in the input buffer */
+ do {
+ ret = vncws_decode_frame(&vs->input, &payload,
+ &payload_size, &frame_size);
+
+ if (ret == 0) {
+ /* not enough data to process, wait for more */
+ return;
+ } else if (ret == -1) {
+ vnc_disconnect_start(vs);
+ return;
+ } else if (ret == -2) {
+ vnc_client_error(vs);
+ return;
+ }
+
+ buffer_reserve(&vs->ws_input, payload_size);
+ buffer_append(&vs->ws_input, payload, payload_size);
+
+ buffer_advance(&vs->input, frame_size);
+ } while (vs->input.offset > 0);
+ buf = &vs->ws_input;
+ } else
+#endif /* CONFIG_VNC_WS */
+ buf = &vs->input;
+
+ while (vs->read_handler && buf->offset >= vs->read_handler_expect) {
size_t len = vs->read_handler_expect;
int ret;
- ret = vs->read_handler(vs, vs->input.buffer, len);
+ ret = vs->read_handler(vs, buf->buffer, len);
if (vs->csock == -1) {
vnc_disconnect_finish(vs);
return;
}
if (!ret) {
- memmove(vs->input.buffer, vs->input.buffer + len, (vs->input.offset - len));
- vs->input.offset -= len;
+ buffer_advance(buf, len);
} else {
vs->read_handler_expect = ret;
}
@@ -1323,13 +1363,26 @@ void vnc_client_read(void *opaque)
void vnc_write(VncState *vs, const void *data, size_t len)
{
- buffer_reserve(&vs->output, len);
+#ifdef CONFIG_VNC_WS
+ if (vs->encode_ws) {
+ if (vs->csock != -1 && buffer_empty(&vs->output)) {
+ qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read,
+ vnc_client_write, vs);
+ }
+ vncws_encode_frame(&vs->output, data, len);
+ } else {
+#endif /* CONFIG_VNC_WS */
+ buffer_reserve(&vs->output, len);
- if (vs->csock != -1 && buffer_empty(&vs->output)) {
- qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs);
- }
+ if (vs->csock != -1 && buffer_empty(&vs->output)) {
+ qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read,
+ vnc_client_write, vs);
+ }
- buffer_append(&vs->output, data, len);
+ buffer_append(&vs->output, data, len);
+#ifdef CONFIG_VNC_WS
+ }
+#endif
}
void vnc_write_s32(VncState *vs, int32_t value)
@@ -2691,6 +2744,24 @@ static void vnc_connect(VncDisplay *vd, int csock, int skipauth)
vnc_set_share_mode(vs, VNC_SHARE_MODE_CONNECTING);
vs->vd = vd;
+
+#ifdef CONFIG_VNC_WS
+ /* Recognize connection type
+ *
+ * Regular VNC clients wait for a server response while
+ * Websocket VNC clients send a handshake directly
+ */
+ vs->ws_recognition_timer = qemu_new_timer_ms(rt_clock,
+ vnc_recognize_connection_type, vs);
+ qemu_mod_timer(vs->ws_recognition_timer, qemu_get_clock_ms(rt_clock)
+ + WS_HANDSHAKE_TIMEOUT);
+}
+
+void vnc_init_state(VncState *vs)
+{
+ VncDisplay *vd = vs->vd;
+#endif /* CONFIG_VNC_WS */
+
vs->ds = vd->ds;
vs->last_x = -1;
vs->last_y = -1;
diff --git a/ui/vnc.h b/ui/vnc.h
index 6141e88..86acb6b 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -99,6 +99,9 @@ typedef struct VncDisplay VncDisplay;
#ifdef CONFIG_VNC_SASL
#include "vnc-auth-sasl.h"
#endif
+#ifdef CONFIG_VNC_WS
+#include "vnc-ws.h"
+#endif
struct VncRectStat
{
@@ -269,11 +272,18 @@ struct VncState
#ifdef CONFIG_VNC_SASL
VncStateSASL sasl;
#endif
+#ifdef CONFIG_VNC_WS
+ QEMUTimer *ws_recognition_timer;
+ int encode_ws;
+#endif
QObject *info;
Buffer output;
Buffer input;
+#ifdef CONFIG_VNC_WS
+ Buffer ws_input;
+#endif
/* current output mode information */
VncWritePixels *write_pixels;
PixelFormat client_pf;
@@ -505,11 +515,16 @@ int vnc_client_io_error(VncState *vs, int ret, int last_errno);
void start_client_init(VncState *vs);
void start_auth_vnc(VncState *vs);
+#ifdef CONFIG_VNC_WS
+void vnc_init_state(VncState *vs);
+#endif
+
/* Buffer management */
void buffer_reserve(Buffer *buffer, size_t len);
void buffer_reset(Buffer *buffer);
void buffer_free(Buffer *buffer);
void buffer_append(Buffer *buffer, const void *data, size_t len);
+void buffer_advance(Buffer *buf, size_t len);
/* Misc helpers */
--
1.7.10.4
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [Qemu-devel] [PATCH] vnc: added initial websockets support
2012-11-16 15:13 [Qemu-devel] [PATCH] vnc: added initial websockets support Tim Hardeck
2012-11-16 15:13 ` Tim Hardeck
@ 2012-11-18 9:31 ` Stefan Hajnoczi
2012-11-18 23:29 ` Tim Hardeck
1 sibling, 1 reply; 8+ messages in thread
From: Stefan Hajnoczi @ 2012-11-18 9:31 UTC (permalink / raw)
To: Tim Hardeck; +Cc: github, qemu-devel
On Fri, Nov 16, 2012 at 4:13 PM, Tim Hardeck <thardeck@suse.de> wrote:
> Websockets connections are recognized by waiting 500ms for a Websocket handshake. If no data is received a regular vnc connection is assumed.
> If this is not acceptable please suggest if Websockets should be activated by a new VNC option or by an additional console parameter on a different port.
Perhaps it's better to speak Websockets on a different port. That way
no timer is required and we never fail to detect the correct protocol
to speak.
Stefan
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Qemu-devel] [PATCH] vnc: added initial websockets support
2012-11-18 9:31 ` Stefan Hajnoczi
@ 2012-11-18 23:29 ` Tim Hardeck
2012-11-19 8:56 ` Gerd Hoffmann
2012-11-19 9:07 ` Stefan Hajnoczi
0 siblings, 2 replies; 8+ messages in thread
From: Tim Hardeck @ 2012-11-18 23:29 UTC (permalink / raw)
To: Stefan Hajnoczi; +Cc: github, qemu-devel
[-- Attachment #1: Type: text/plain, Size: 1352 bytes --]
Hi Stefan,
thanks for your input but how should it be implemented?
I personally would like activating Websockets as a VNC option like: "
-vnc :0 -vnc :1,websockets"
I have already tested this locally and it does work but only for one
protocol since QEMU does only interpret the last vnc option.
So is allowing more than one VNC command line entry in combination with
having more than one VNC thread for the same display worth working on or
do you have something different in mind?
Regards
Tim
On Sun, 2012-11-18 at 10:31 +0100, Stefan Hajnoczi wrote:
> On Fri, Nov 16, 2012 at 4:13 PM, Tim Hardeck <thardeck@suse.de> wrote:
> > Websockets connections are recognized by waiting 500ms for a Websocket handshake. If no data is received a regular vnc connection is assumed.
> > If this is not acceptable please suggest if Websockets should be activated by a new VNC option or by an additional console parameter on a different port.
>
> Perhaps it's better to speak Websockets on a different port. That way
> no timer is required and we never fail to detect the correct protocol
> to speak.
>
> Stefan
--
SUSE LINUX Products GmbH, GF: Jeff Hawn, Jennifer Guild, Felix
Imendörffer, HRB 16746 (AG Nürnberg)
Maxfeldstr. 5, 90409 Nürnberg, Germany
T: +49 (0) 911 74053-0 F: +49 (0) 911 74053-483
http://www.suse.de/
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 490 bytes --]
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Qemu-devel] [PATCH] vnc: added initial websockets support
2012-11-18 23:29 ` Tim Hardeck
@ 2012-11-19 8:56 ` Gerd Hoffmann
2012-11-19 10:09 ` Tim Hardeck
2012-11-19 9:07 ` Stefan Hajnoczi
1 sibling, 1 reply; 8+ messages in thread
From: Gerd Hoffmann @ 2012-11-19 8:56 UTC (permalink / raw)
To: Tim Hardeck; +Cc: Stefan Hajnoczi, github, qemu-devel
On 11/19/12 00:29, Tim Hardeck wrote:
> Hi Stefan,
>
> thanks for your input but how should it be implemented?
>
> I personally would like activating Websockets as a VNC option like:
> " -vnc :0 -vnc :1,websockets" I have already tested this locally
> and it does work but only for one protocol since QEMU does only
> interpret the last vnc option. So is allowing more than one VNC
> command line entry in combination with having more than one VNC
> thread for the same display worth working on or do you have
> something different in mind?
-vnc :0,websock=$portnr ?
cheers,
Gerd
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Qemu-devel] [PATCH] vnc: added initial websockets support
2012-11-18 23:29 ` Tim Hardeck
2012-11-19 8:56 ` Gerd Hoffmann
@ 2012-11-19 9:07 ` Stefan Hajnoczi
2012-11-19 10:00 ` Tim Hardeck
1 sibling, 1 reply; 8+ messages in thread
From: Stefan Hajnoczi @ 2012-11-19 9:07 UTC (permalink / raw)
To: Tim Hardeck; +Cc: github, qemu-devel
On Mon, Nov 19, 2012 at 12:29:44AM +0100, Tim Hardeck wrote:
> Hi Stefan,
>
> thanks for your input but how should it be implemented?
>
> I personally would like activating Websockets as a VNC option like: "
> -vnc :0 -vnc :1,websockets"
> I have already tested this locally and it does work but only for one
> protocol since QEMU does only interpret the last vnc option.
> So is allowing more than one VNC command line entry in combination with
> having more than one VNC thread for the same display worth working on or
> do you have something different in mind?
I'm not familiar enough with the VNC code to suggest how to best
implement this.
One thing to think about is that Websockets is a transport. Perhaps the
VNC code itself shouldn't speak it, instead generic QEMU socket code
should implement Websockets so that non-VNC components can also make use
of it in the future.
Stefan
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Qemu-devel] [PATCH] vnc: added initial websockets support
2012-11-19 9:07 ` Stefan Hajnoczi
@ 2012-11-19 10:00 ` Tim Hardeck
0 siblings, 0 replies; 8+ messages in thread
From: Tim Hardeck @ 2012-11-19 10:00 UTC (permalink / raw)
To: Stefan Hajnoczi; +Cc: github, qemu-devel
[-- Attachment #1: Type: text/plain, Size: 1535 bytes --]
On 11/19/2012 10:07 AM, Stefan Hajnoczi wrote:
> On Mon, Nov 19, 2012 at 12:29:44AM +0100, Tim Hardeck wrote:
>> Hi Stefan,
>>
>> thanks for your input but how should it be implemented?
>>
>> I personally would like activating Websockets as a VNC option like: "
>> -vnc :0 -vnc :1,websockets"
>> I have already tested this locally and it does work but only for one
>> protocol since QEMU does only interpret the last vnc option.
>> So is allowing more than one VNC command line entry in combination with
>> having more than one VNC thread for the same display worth working on or
>> do you have something different in mind?
>
> I'm not familiar enough with the VNC code to suggest how to best
> implement this.
>
> One thing to think about is that Websockets is a transport. Perhaps the
> VNC code itself shouldn't speak it, instead generic QEMU socket code
> should implement Websockets so that non-VNC components can also make use
> of it in the future.
I have talked about this with a colleague and it seems like quite some
effort for no use case besides VNC.
It might be interesting in the long run though.
Is the rest of my implementation OK besides the protocol recognition by
timeout - which should only be an issue for high latency websockets
connection?
Regards
Tim
--
SUSE LINUX Products GmbH, GF: Jeff Hawn, Jennifer Guild, Felix
Imendörffer, HRB 16746 (AG Nürnberg)
Maxfeldstr. 5, 90409 Nürnberg, Germany
T: +49 (0) 911 74053-0 F: +49 (0) 911 74053-483
http://www.suse.de/
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 490 bytes --]
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Qemu-devel] [PATCH] vnc: added initial websockets support
2012-11-19 8:56 ` Gerd Hoffmann
@ 2012-11-19 10:09 ` Tim Hardeck
0 siblings, 0 replies; 8+ messages in thread
From: Tim Hardeck @ 2012-11-19 10:09 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: Stefan Hajnoczi, github, qemu-devel
[-- Attachment #1: Type: text/plain, Size: 1015 bytes --]
On 11/19/2012 09:56 AM, Gerd Hoffmann wrote:
> On 11/19/12 00:29, Tim Hardeck wrote:
>> Hi Stefan,
>>
>> thanks for your input but how should it be implemented?
>>
>> I personally would like activating Websockets as a VNC option like:
>> " -vnc :0 -vnc :1,websockets" I have already tested this locally
>> and it does work but only for one protocol since QEMU does only
>> interpret the last vnc option. So is allowing more than one VNC
>> command line entry in combination with having more than one VNC
>> thread for the same display worth working on or do you have
>> something different in mind?
>
> -vnc :0,websock=$portnr ?
This sounds resonable although maybe the websockets port could just
always be display + 1 so there is no need to parse a port.
Regards
Tim
--
SUSE LINUX Products GmbH, GF: Jeff Hawn, Jennifer Guild, Felix
Imendörffer, HRB 16746 (AG Nürnberg)
Maxfeldstr. 5, 90409 Nürnberg, Germany
T: +49 (0) 911 74053-0 F: +49 (0) 911 74053-483
http://www.suse.de/
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 490 bytes --]
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2012-11-19 10:09 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-11-16 15:13 [Qemu-devel] [PATCH] vnc: added initial websockets support Tim Hardeck
2012-11-16 15:13 ` Tim Hardeck
2012-11-18 9:31 ` Stefan Hajnoczi
2012-11-18 23:29 ` Tim Hardeck
2012-11-19 8:56 ` Gerd Hoffmann
2012-11-19 10:09 ` Tim Hardeck
2012-11-19 9:07 ` Stefan Hajnoczi
2012-11-19 10:00 ` Tim Hardeck
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).