From e095f1bd0bb9ff6dc8f1ceeff2277537bc3de153 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Mon, 1 Jul 2013 14:38:20 -0500 Subject: main/xen: splice in websockets support on qemu-xen device-model --- main/xen/APKBUILD | 8 +- main/xen/qemu-xen-websocket.patch | 910 +++++++++++++++++++++++++++++++++++ main/xen/use-xen-qemu-upstream.patch | 10 + 3 files changed, 926 insertions(+), 2 deletions(-) create mode 100644 main/xen/qemu-xen-websocket.patch create mode 100644 main/xen/use-xen-qemu-upstream.patch (limited to 'main/xen') diff --git a/main/xen/APKBUILD b/main/xen/APKBUILD index a2b82db3e..e78791259 100644 --- a/main/xen/APKBUILD +++ b/main/xen/APKBUILD @@ -3,7 +3,7 @@ # Maintainer: William Pitcock pkgname=xen pkgver=4.2.2 -pkgrel=6 +pkgrel=7 pkgdesc="Xen hypervisor" url="http://www.xen.org/" arch="x86 x86_64" @@ -11,7 +11,7 @@ license="GPL" depends="syslinux bash screen iproute2 logrotate perl" depends_dev="openssl-dev python-dev e2fsprogs-dev gettext zlib-dev ncurses-dev libiconv-dev dev86 texinfo perl iasl pciutils-dev glib-dev yajl-dev - spice-dev" + spice-dev gnutls-dev" makedepends="$depends_dev" install="" subpackages="$pkgname-doc $pkgname-dev $pkgname-libs $pkgname-hypervisor $pkgname-xend" @@ -35,6 +35,7 @@ source="http://bits.xensource.com/oss-xen/release/$pkgver/$pkgname-$pkgver.tar.g xsa58-4.2.patch fix-pod2man-choking.patch + qemu-xen-websocket.patch xenstored.initd xenstored.confd @@ -166,6 +167,7 @@ e70b9128ffc2175cea314a533a7d8457 xsa56.patch 7475158130474ee062a4eb878259af61 xsa57.patch 7de2cd11c10d6a554f3c81e0688c38b7 xsa58-4.2.patch c1d1a415415b0192e5dae9032962bf61 fix-pod2man-choking.patch +b72ee3a59d4e4e4f9816c03e0128ccc1 qemu-xen-websocket.patch 95d8af17bf844d41a015ff32aae51ba1 xenstored.initd b017ccdd5e1c27bbf1513e3569d4ff07 xenstored.confd ed262f15fb880badb53575539468646c xenconsoled.initd @@ -195,6 +197,7 @@ a691c5f5332a42c0d38ddb4dc037eb902f01ba31033b64c47d02909a8de0257d xsa56.patch b6a5106848541972519cc529859d9ff3083c79367276c7031560fa4ce6f9f770 xsa57.patch 194d6610fc38b767d643e5d58a1268f45921fb35e309b47aca6a388b861311c2 xsa58-4.2.patch b4e7d43364a06b2cb04527db3e9567524bc489fef475709fd8493ebf1e62406d fix-pod2man-choking.patch +1b658baa846a472bceb33457e6f2414711eeaa79819f9492ba978c3daf00dc8f qemu-xen-websocket.patch 81d335946c81311c86e2f2112b773a568a5a530c0db9802b2fe559e71bb8b381 xenstored.initd ea9171e71ab3d33061979bcf3bb737156192aa4b0be4d1234438ced75b6fdef3 xenstored.confd 93bea2eb90ea1b4628854c8141dd351bbd1fbc5959b12795447ea933ad025f01 xenconsoled.initd @@ -224,6 +227,7 @@ b4f43095163146a29ae258575bb03bd45f5a315d3cca7434a0b88c18eb1b6e1cf17ef13b4ac428a0 5ccc1654d9f0270485495f9fc913e41663ddbda602ffe049e0a9c3247c6246690b7ec4165482f96921c5253a2a5205ca384048339996e611c07ab60a6a75cf6a xsa57.patch 60813c01f6bb909da8748919df4d0ffa923baf4b7b55287e0bec3389fb83020158225182e112941c9e126b4df57e7b8724f2a69d0c1fa9ce3b37c0bdf1a49da4 xsa58-4.2.patch ffb1113fcec0853b690c177655c7d1136388efdebf0d7f625b80481b98eadd3e9ef461442ced53e11acf0e347800a2b0a41e18b05065b5d04bffdd8a4e127cec fix-pod2man-choking.patch +a48337c7f687e6d2dff400e99e2f544361c5dcaf6a4c745892603c73087f7f9be48eccadf77baee3e106c01b749b335646ccb10b29272b2f5ca681d2514228f6 qemu-xen-websocket.patch 792b062e8a16a2efd3cb4662d379d1500527f2a7ca9228d7831c2bd34f3b9141df949153ea05463a7758c3e3dd9a4182492ad5505fa38e298ecf8c99db77b4ee xenstored.initd 100cf4112f401f45c1e4e885a5074698c484b40521262f6268fad286498e95f4c51e746f0e94eb43a590bb8e813a397bb53801ccacebec9541020799d8d70514 xenstored.confd 12f981b2459c65d66e67ec0b32d0d19b95a029bc54c2a79138cfe488d3524a22e51860f755abfe25ddcdaf1b27f2ded59b6e350b9d5f8791193d00e2d3673137 xenconsoled.initd diff --git a/main/xen/qemu-xen-websocket.patch b/main/xen/qemu-xen-websocket.patch new file mode 100644 index 000000000..9d1b2e6da --- /dev/null +++ b/main/xen/qemu-xen-websocket.patch @@ -0,0 +1,910 @@ +--- xen-4.2.2.orig/tools/Makefile ++++ xen-4.2.2/tools/Makefile +@@ -202,6 +202,7 @@ + --docdir=$(PREFIX)/share/doc \ + --sysconfdir=/etc/qemu \ + --disable-kvm \ ++ --enable-vnc-ws \ + --python=$(PYTHON) \ + $(IOEMU_CONFIGURE_CROSS); \ + $(MAKE) all +--- xen-4.2.2.orig/tools/qemu-xen/Makefile.objs ++++ xen-4.2.2/tools/qemu-xen/Makefile.objs +@@ -149,6 +149,7 @@ + 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 + ifdef CONFIG_VNC_THREAD + vnc-obj-y += vnc-jobs-async.o + else +--- xen-4.2.2.orig/tools/qemu-xen/configure ++++ xen-4.2.2/tools/qemu-xen/configure +@@ -124,6 +124,7 @@ + vnc_sasl="" + vnc_jpeg="" + vnc_png="" ++vnc_ws="" + vnc_thread="no" + xen="" + xen_ctrl_version="" +@@ -638,6 +639,10 @@ + ;; + --enable-vnc-thread) vnc_thread="yes" + ;; ++ --disable-vnc-ws) vnc_ws="no" ++ ;; ++ --enable-vnc-ws) vnc_ws="yes" ++ ;; + --disable-slirp) slirp="no" + ;; + --disable-uuid) uuid="no" +@@ -1008,6 +1013,8 @@ + echo " --enable-vnc-png enable PNG compression for VNC server" + echo " --disable-vnc-thread disable threaded VNC server" + echo " --enable-vnc-thread enable threaded 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" +@@ -1554,8 +1561,8 @@ + fi + + ########################################## +-# VNC TLS detection +-if test "$vnc" = "yes" -a "$vnc_tls" != "no" ; then ++# VNC TLS/WS detection ++if test "$vnc" = "yes" -a \( "$vnc_tls" != "no" -o "$vnc_ws" != "no" \) ; then + cat > $TMPC < + int main(void) { gnutls_session_t s; gnutls_init(&s, GNUTLS_SERVER); return 0; } +@@ -1563,13 +1570,22 @@ + vnc_tls_cflags=`$pkg_config --cflags gnutls 2> /dev/null` + vnc_tls_libs=`$pkg_config --libs gnutls 2> /dev/null` + if compile_prog "$vnc_tls_cflags" "$vnc_tls_libs" ; then +- vnc_tls=yes ++ if test "$vnc_tls" != "no" ; then ++ vnc_tls=yes ++ fi ++ if test "$vnc_ws" != "no" ; then ++ vnc_ws=yes ++ fi + libs_softmmu="$vnc_tls_libs $libs_softmmu" + else + if test "$vnc_tls" = "yes" ; then + feature_not_found "vnc-tls" + fi ++ if test "$vnc_ws" = "yes" ; then ++ feature_not_found "vnc-ws" ++ fi + vnc_tls=no ++ vnc_ws=no + fi + fi + +@@ -2864,6 +2880,7 @@ + echo "VNC SASL support $vnc_sasl" + echo "VNC JPEG support $vnc_jpeg" + echo "VNC PNG support $vnc_png" ++ echo "VNC WS support $vnc_ws" + echo "VNC thread $vnc_thread" + fi + if test -n "$sparc_cpu"; then +@@ -3053,6 +3070,10 @@ + fi + if test "$vnc_thread" = "yes" ; then + echo "CONFIG_VNC_THREAD=y" >> $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 +--- xen-4.2.2.orig/tools/qemu-xen/qemu-options.hx ++++ xen-4.2.2/tools/qemu-xen/qemu-options.hx +@@ -976,6 +976,14 @@ + connections (@var{host}:@var{d},@code{reverse}), the @var{d} argument + is a TCP port number, not a display number. + ++@item websocket ++ ++Opens an additional TCP listening port dedicated to VNC Websocket connections. ++By defintion the Websocket port is 5700+@var{display}. If @var{host} is ++specified connections will only be allowed from this host. ++As an alternative the Websocket port could be specified by using ++@code{websocket}=@var{port}. ++ + @item password + + Require that password based authentication is used for client connections. +--- /dev/null ++++ xen-4.2.2/tools/qemu-xen/ui/vnc-ws.c +@@ -0,0 +1,284 @@ ++/* ++ * 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, see . ++ */ ++ ++#include "vnc.h" ++ ++void vncws_handshake_read(void *opaque) ++{ ++ VncState *vs = opaque; ++ uint8_t *handshake_end; ++ long ret; ++ buffer_reserve(&vs->ws_input, 4096); ++ ret = vnc_client_read_buf(vs, buffer_end(&vs->ws_input), 4096); ++ ++ if (!ret) { ++ if (vs->csock == -1) { ++ vnc_disconnect_finish(vs); ++ } ++ return; ++ } ++ vs->ws_input.offset += ret; ++ ++ handshake_end = (uint8_t *)g_strstr_len((char *)vs->ws_input.buffer, ++ vs->ws_input.offset, WS_HANDSHAKE_END); ++ if (handshake_end) { ++ qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); ++ vncws_process_handshake(vs, vs->ws_input.buffer, vs->ws_input.offset); ++ buffer_advance(&vs->ws_input, handshake_end - vs->ws_input.buffer + ++ strlen(WS_HANDSHAKE_END)); ++ } ++} ++ ++ ++long vnc_client_read_ws(VncState *vs) ++{ ++ int ret, err; ++ uint8_t *payload; ++ size_t payload_size, frame_size; ++ VNC_DEBUG("Read websocket %p size %zd offset %zd\n", vs->ws_input.buffer, ++ vs->ws_input.capacity, vs->ws_input.offset); ++ buffer_reserve(&vs->ws_input, 4096); ++ ret = vnc_client_read_buf(vs, buffer_end(&vs->ws_input), 4096); ++ if (!ret) { ++ return 0; ++ } ++ vs->ws_input.offset += ret; ++ ++ /* make sure that nothing is left in the ws_input buffer */ ++ do { ++ err = vncws_decode_frame(&vs->ws_input, &payload, ++ &payload_size, &frame_size); ++ if (err <= 0) { ++ return err; ++ } ++ ++ buffer_reserve(&vs->input, payload_size); ++ buffer_append(&vs->input, payload, payload_size); ++ ++ buffer_advance(&vs->ws_input, frame_size); ++ } while (vs->ws_input.offset > 0); ++ ++ return ret; ++} ++ ++long vnc_client_write_ws(VncState *vs) ++{ ++ long ret; ++ VNC_DEBUG("Write WS: Pending output %p size %zd offset %zd\n", ++ vs->output.buffer, vs->output.capacity, vs->output.offset); ++ vncws_encode_frame(&vs->ws_output, vs->output.buffer, vs->output.offset); ++ buffer_reset(&vs->output); ++ ret = vnc_client_write_buf(vs, vs->ws_output.buffer, vs->ws_output.offset); ++ if (!ret) { ++ return 0; ++ } ++ ++ buffer_advance(&vs->ws_output, ret); ++ ++ if (vs->ws_output.offset == 0) { ++ qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); ++ } ++ ++ return ret; ++} ++ ++static char *vncws_extract_handshake_entry(const char *handshake, ++ size_t handshake_len, const char *name) ++{ ++ char *begin, *end, *ret = NULL; ++ char *line = g_strdup_printf("%s%s: ", WS_HANDSHAKE_DELIM, name); ++ begin = g_strstr_len(handshake, handshake_len, line); ++ if (begin != NULL) { ++ begin += strlen(line); ++ end = g_strstr_len(begin, handshake_len - (begin - handshake), ++ WS_HANDSHAKE_DELIM); ++ if (end != NULL) { ++ ret = g_strndup(begin, end - begin); ++ } ++ } ++ g_free(line); ++ return ret; ++} ++ ++static void vncws_send_handshake_response(VncState *vs, const char* key) ++{ ++ char combined_key[WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1]; ++ char hash[SHA1_DIGEST_LEN]; ++ size_t hash_size = SHA1_DIGEST_LEN; ++ char *accept = NULL, *response = NULL; ++ gnutls_datum_t in; ++ ++ g_strlcpy(combined_key, key, WS_CLIENT_KEY_LEN + 1); ++ g_strlcat(combined_key, WS_GUID, WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1); ++ ++ /* 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((guchar *)hash, SHA1_DIGEST_LEN); ++ } ++ if (accept == NULL) { ++ VNC_DEBUG("Hashing Websocket combined key failed\n"); ++ vnc_client_error(vs); ++ return; ++ } ++ ++ response = g_strdup_printf(WS_HANDSHAKE, accept); ++ vnc_write(vs, response, strlen(response)); ++ vnc_flush(vs); ++ ++ g_free(accept); ++ g_free(response); ++ ++ vs->encode_ws = 1; ++ vnc_init_state(vs); ++} ++ ++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 ++ && g_strrstr(protocols, "binary") ++ && !strcmp(version, WS_SUPPORTED_VERSION) ++ && strlen(key) == WS_CLIENT_KEY_LEN) { ++ vncws_send_handshake_response(vs, key); ++ } else { ++ VNC_DEBUG("Defective Websockets header or unsupported protocol\n"); ++ vnc_client_error(vs); ++ } ++ ++ g_free(protocols); ++ g_free(version); ++ g_free(key); ++} ++ ++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; ++ union { ++ char buf[WS_HEAD_MAX_LEN]; ++ WsHeader ws; ++ } header; ++ ++ if (!payload_size) { ++ return; ++ } ++ ++ header.ws.b0 = 0x80 | (opcode & 0x0f); ++ if (payload_size <= 125) { ++ header.ws.b1 = (uint8_t)payload_size; ++ header_size = 2; ++ } else if (payload_size < 65536) { ++ header.ws.b1 = 0x7e; ++ header.ws.u.s16.l16 = cpu_to_be16((uint16_t)payload_size); ++ header_size = 4; ++ } else { ++ header.ws.b1 = 0x7f; ++ header.ws.u.s64.l64 = cpu_to_be64(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; ++ size_t header_size = 0; ++ uint32_t *payload32; ++ WsHeader *header = (WsHeader *)input->buffer; ++ WsMask mask; ++ int i; ++ ++ if (input->offset < WS_HEAD_MIN_LEN + 4) { ++ /* 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; ++ } ++ ++ if (*payload_size < 126) { ++ header_size = 6; ++ mask = header->u.m; ++ } else if (*payload_size == 126 && input->offset >= 8) { ++ *payload_size = be16_to_cpu(header->u.s16.l16); ++ header_size = 8; ++ mask = header->u.s16.m16; ++ } else if (*payload_size == 127 && input->offset >= 14) { ++ *payload_size = be64_to_cpu(header->u.s64.l64); ++ header_size = 14; ++ mask = header->u.s64.m64; ++ } else { ++ /* header not complete */ ++ return 0; ++ } ++ ++ *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; ++} +--- /dev/null ++++ xen-4.2.2/tools/qemu-xen/ui/vnc-ws.h +@@ -0,0 +1,86 @@ ++/* ++ * 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, see . ++ */ ++ ++#ifndef __QEMU_UI_VNC_WS_H ++#define __QEMU_UI_VNC_WS_H ++ ++#include ++ ++#define B64LEN(__x) (((__x + 2) / 3) * 12 / 3) ++#define SHA1_DIGEST_LEN 20 ++ ++#define WS_ACCEPT_LEN (B64LEN(SHA1_DIGEST_LEN) + 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 "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 sizeof(uint16_t) ++#define WS_HEAD_MAX_LEN (WS_HEAD_MIN_LEN + sizeof(uint64_t) + sizeof(uint32_t)) ++ ++typedef union WsMask { ++ char c[4]; ++ uint32_t u; ++} WsMask; ++ ++typedef struct QEMU_PACKED WsHeader { ++ unsigned char b0; ++ unsigned char b1; ++ union { ++ struct QEMU_PACKED { ++ uint16_t l16; ++ WsMask m16; ++ } s16; ++ struct QEMU_PACKED { ++ uint64_t l64; ++ WsMask m64; ++ } s64; ++ WsMask m; ++ } u; ++} WsHeader; ++ ++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 vncws_handshake_read(void *opaque); ++long vnc_client_write_ws(VncState *vs); ++long vnc_client_read_ws(VncState *vs); ++void vncws_process_handshake(VncState *vs, uint8_t *line, size_t size); ++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_UI_VNC_WS_H */ +--- xen-4.2.2.orig/tools/qemu-xen/ui/vnc.c ++++ xen-4.2.2/tools/qemu-xen/ui/vnc.c +@@ -391,7 +391,6 @@ + static int vnc_update_client(VncState *vs, int has_dirty); + static int vnc_update_client_sync(VncState *vs, int has_dirty); + static void vnc_disconnect_start(VncState *vs); +-static void vnc_disconnect_finish(VncState *vs); + static void vnc_init_timer(VncDisplay *vd); + static void vnc_remove_timer(VncDisplay *vd); + +@@ -479,6 +478,13 @@ + 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; +@@ -1002,7 +1008,7 @@ + vs->csock = -1; + } + +-static void vnc_disconnect_finish(VncState *vs) ++void vnc_disconnect_finish(VncState *vs) + { + int i; + +@@ -1013,6 +1019,10 @@ + + buffer_free(&vs->input); + buffer_free(&vs->output); ++#ifdef CONFIG_VNC_WS ++ buffer_free(&vs->ws_input); ++ buffer_free(&vs->ws_output); ++#endif /* CONFIG_VNC_WS */ + + qobject_decref(vs->info); + +@@ -1150,8 +1160,7 @@ + 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); +@@ -1177,7 +1186,16 @@ + vnc_client_write_sasl(vs); + } else + #endif /* CONFIG_VNC_SASL */ +- vnc_client_write_plain(vs); ++ { ++#ifdef CONFIG_VNC_WS ++ if (vs->encode_ws) { ++ vnc_client_write_ws(vs); ++ } else ++#endif /* CONFIG_VNC_WS */ ++ { ++ vnc_client_write_plain(vs); ++ } ++ } + } + + void vnc_client_write(void *opaque) +@@ -1185,7 +1203,11 @@ + VncState *vs = opaque; + + vnc_lock_output(vs); +- if (vs->output.offset) { ++ if (vs->output.offset ++#ifdef CONFIG_VNC_WS ++ || vs->ws_output.offset ++#endif ++ ) { + vnc_client_write_locked(opaque); + } else if (vs->csock != -1) { + qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); +@@ -1273,7 +1295,21 @@ + ret = vnc_client_read_sasl(vs); + else + #endif /* CONFIG_VNC_SASL */ ++#ifdef CONFIG_VNC_WS ++ if (vs->encode_ws) { ++ ret = vnc_client_read_ws(vs); ++ if (ret == -1) { ++ vnc_disconnect_start(vs); ++ return; ++ } else if (ret == -2) { ++ vnc_client_error(vs); ++ return; ++ } ++ } else ++#endif /* CONFIG_VNC_WS */ ++ { + ret = vnc_client_read_plain(vs); ++ } + if (!ret) { + if (vs->csock == -1) + vnc_disconnect_finish(vs); +@@ -1291,8 +1327,7 @@ + } + + if (!ret) { +- memmove(vs->input.buffer, vs->input.buffer + len, (vs->input.offset - len)); +- vs->input.offset -= len; ++ buffer_advance(&vs->input, len); + } else { + vs->read_handler_expect = ret; + } +@@ -1345,7 +1380,11 @@ + void vnc_flush(VncState *vs) + { + vnc_lock_output(vs); +- if (vs->csock != -1 && vs->output.offset) { ++ if (vs->csock != -1 && (vs->output.offset ++#ifdef CONFIG_VNC_WS ++ || vs->ws_output.offset ++#endif ++ )) { + vnc_client_write_locked(vs); + } + vnc_unlock_output(vs); +@@ -2525,7 +2564,7 @@ + } + } + +-static void vnc_connect(VncDisplay *vd, int csock, int skipauth) ++static void vnc_connect(VncDisplay *vd, int csock, int skipauth, bool websocket) + { + VncState *vs = g_malloc0(sizeof(VncState)); + int i; +@@ -2552,12 +2591,33 @@ + VNC_DEBUG("New client on socket %d\n", csock); + dcl->idle = 0; + socket_set_nonblock(vs->csock); +- qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); ++#ifdef CONFIG_VNC_WS ++ if (websocket) { ++ vs->websocket = 1; ++ qemu_set_fd_handler2(vs->csock, NULL, vncws_handshake_read, NULL, vs); ++ } else ++#endif /* CONFIG_VNC_WS */ ++ { ++ qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); ++ } + + vnc_client_cache_addr(vs); + vnc_qmp_event(vs, QEVENT_VNC_CONNECTED); + + vs->vd = vd; ++ ++#ifdef CONFIG_VNC_WS ++ if (!vs->websocket) ++#endif ++ { ++ vnc_init_state(vs); ++ } ++} ++ ++void vnc_init_state(VncState *vs) ++{ ++ VncDisplay *vd = vs->vd; ++ + vs->ds = vd->ds; + vs->last_x = -1; + vs->last_y = -1; +@@ -2590,21 +2650,41 @@ + /* vs might be free()ed here */ + } + +-static void vnc_listen_read(void *opaque) ++static void vnc_listen_read(void *opaque, bool websocket) + { + VncDisplay *vs = opaque; + struct sockaddr_in addr; + socklen_t addrlen = sizeof(addr); ++ int csock; + + /* Catch-up */ + vga_hw_update(); ++#ifdef CONFIG_VNC_WS ++ if (websocket) { ++ csock = qemu_accept(vs->lwebsock, (struct sockaddr *)&addr, &addrlen); ++ } else ++#endif /* CONFIG_VNC_WS */ ++ { ++ csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen); ++ } + +- int csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen); + if (csock != -1) { +- vnc_connect(vs, csock, 0); ++ vnc_connect(vs, csock, 0, websocket); + } + } + ++static void vnc_listen_regular_read(void *opaque) ++{ ++ vnc_listen_read(opaque, 0); ++} ++ ++#ifdef CONFIG_VNC_WS ++static void vnc_listen_websocket_read(void *opaque) ++{ ++ vnc_listen_read(opaque, 1); ++} ++#endif /* CONFIG_VNC_WS */ ++ + void vnc_display_init(DisplayState *ds) + { + VncDisplay *vs = g_malloc0(sizeof(*vs)); +@@ -2616,6 +2696,9 @@ + vnc_display = vs; + + vs->lsock = -1; ++#ifdef CONFIG_VNC_WS ++ vs->lwebsock = -1; ++#endif + + vs->ds = ds; + QTAILQ_INIT(&vs->clients); +@@ -2659,6 +2742,15 @@ + close(vs->lsock); + vs->lsock = -1; + } ++#ifdef CONFIG_VNC_WS ++ g_free(vs->ws_display); ++ vs->ws_display = NULL; ++ if (vs->lwebsock != -1) { ++ qemu_set_fd_handler2(vs->lwebsock, NULL, NULL, NULL, NULL); ++ close(vs->lwebsock); ++ vs->lwebsock = -1; ++ } ++#endif /* CONFIG_VNC_WS */ + vs->auth = VNC_AUTH_INVALID; + #ifdef CONFIG_VNC_TLS + vs->subauth = VNC_AUTH_INVALID; +@@ -2769,6 +2861,36 @@ + } else if (strncmp(options, "sasl", 4) == 0) { + sasl = 1; /* Require SASL auth */ + #endif ++#ifdef CONFIG_VNC_WS ++ } else if (strncmp(options, "websocket", 9) == 0) { ++ char *start, *end; ++ vs->websocket = 1; ++ ++ /* Check for 'websocket=' */ ++ start = strchr(options, '='); ++ end = strchr(options, ','); ++ if (start && (!end || (start < end))) { ++ int len = end ? end-(start+1) : strlen(start+1); ++ if (len < 6) { ++ /* extract the host specification from display */ ++ char *host = NULL, *port = NULL, *host_end = NULL; ++ port = g_strndup(start + 1, len); ++ ++ /* ipv6 hosts have colons */ ++ end = strchr(display, ','); ++ host_end = g_strrstr_len(display, end - display, ":"); ++ ++ if (host_end) { ++ host = g_strndup(display, host_end - display + 1); ++ } else { ++ host = g_strndup(":", 1); ++ } ++ vs->ws_display = g_strconcat(host, port, NULL); ++ g_free(host); ++ g_free(port); ++ } ++ } ++#endif /* CONFIG_VNC_WS */ + #ifdef CONFIG_VNC_TLS + } else if (strncmp(options, "tls", 3) == 0) { + tls = 1; /* Require TLS */ +@@ -2931,7 +3053,10 @@ + } else { + int csock = vs->lsock; + vs->lsock = -1; +- vnc_connect(vs, csock, 0); ++#ifdef CONFIG_VNC_WS ++ vs->lwebsock = -1; ++#endif ++ vnc_connect(vs, csock, 0, 0); + } + return 0; + +@@ -2944,6 +3069,30 @@ + vs->lsock = unix_listen(display+5, dpy+5, 256-5); + } else { + vs->lsock = inet_listen(display, dpy, 256, SOCK_STREAM, 5900); ++ if (-1 == vs->lsock) { ++ g_free(dpy); ++ goto fail; ++ } ++#ifdef CONFIG_VNC_WS ++ if (vs->websocket) { ++ if (vs->ws_display) { ++ vs->lwebsock = inet_listen(vs->ws_display, NULL, 256, ++ SOCK_STREAM, 0); ++ } else { ++ vs->lwebsock = inet_listen(vs->display, NULL, 256, ++ SOCK_STREAM, 5700); ++ } ++ ++ if (vs->lwebsock < 0) { ++ if (vs->lsock) { ++ close(vs->lsock); ++ vs->lsock = -1; ++ } ++ g_free(dpy); ++ goto fail; ++ } ++ } ++#endif /* CONFIG_VNC_WS */ + } + if (-1 == vs->lsock) { + g_free(dpy); +@@ -2954,11 +3103,20 @@ + } + } + return qemu_set_fd_handler2(vs->lsock, NULL, vnc_listen_read, NULL, vs); ++ ++fail: ++ g_free(vs->display); ++ vs->display = NULL; ++#ifdef CONFIG_VNC_WS ++ g_free(vs->ws_display); ++ vs->ws_display = NULL; ++#endif /* CONFIG_VNC_WS */ ++ return -1; + } + + void vnc_display_add_client(DisplayState *ds, int csock, int skipauth) + { + VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; + +- return vnc_connect(vs, csock, skipauth); ++ return vnc_connect(vs, csock, skipauth, 0); + } +--- xen-4.2.2.orig/tools/qemu-xen/ui/vnc.h ++++ xen-4.2.2/tools/qemu-xen/ui/vnc.h +@@ -101,6 +101,9 @@ + #ifdef CONFIG_VNC_SASL + #include "vnc-auth-sasl.h" + #endif ++#ifdef CONFIG_VNC_WS ++#include "vnc-ws.h" ++#endif + + struct VncRectStat + { +@@ -128,6 +131,11 @@ + QEMUTimer *timer; + int timer_interval; + int lsock; ++#ifdef CONFIG_VNC_WS ++ int lwebsock; ++ bool websocket; ++ char *ws_display; ++#endif + DisplayState *ds; + kbd_layout_t *kbd_layout; + int lock_key_sync; +@@ -265,11 +273,19 @@ + #ifdef CONFIG_VNC_SASL + VncStateSASL sasl; + #endif ++#ifdef CONFIG_VNC_WS ++ bool encode_ws; ++ bool websocket; ++#endif + + QObject *info; + + Buffer output; + Buffer input; ++#ifdef CONFIG_VNC_WS ++ Buffer ws_input; ++ Buffer ws_output; ++#endif + /* current output mode information */ + VncWritePixels *write_pixels; + DisplaySurface clientds; +@@ -489,6 +505,8 @@ + void vnc_write_u8(VncState *vs, uint8_t value); + void vnc_flush(VncState *vs); + void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting); ++void vnc_disconnect_finish(VncState *vs); ++void vnc_init_state(VncState *vs); + + + /* Buffer I/O functions */ +@@ -507,10 +525,11 @@ + /* Buffer management */ + void buffer_reserve(Buffer *buffer, size_t len); + int buffer_empty(Buffer *buffer); +-uint8_t *buffer_end(Buffer *buffer); + 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); ++uint8_t *buffer_end(Buffer *buffer); + + + /* Misc helpers */ diff --git a/main/xen/use-xen-qemu-upstream.patch b/main/xen/use-xen-qemu-upstream.patch new file mode 100644 index 000000000..6525a3421 --- /dev/null +++ b/main/xen/use-xen-qemu-upstream.patch @@ -0,0 +1,10 @@ +--- xen-4.2.2.orig/tools/Makefile ++++ xen-4.2.2/tools/Makefile +@@ -202,6 +202,7 @@ + --docdir=$(PREFIX)/share/doc \ + --sysconfdir=/etc/qemu \ + --disable-kvm \ ++ --enable-vnc-ws \ + --python=$(PYTHON) \ + $(IOEMU_CONFIGURE_CROSS); \ + $(MAKE) all -- cgit v1.2.3