/* * 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 <http://www.gnu.org/licenses/>. */ #include "qemu/osdep.h" #include "qapi/error.h" #include "vnc.h" #include "io/channel-websock.h" #include "qemu/bswap.h" static void vncws_tls_handshake_done(Object *source, Error *err, gpointer user_data) { VncState *vs = user_data; if (err) { VNC_DEBUG("Handshake failed %s\n", error_get_pretty(err)); vnc_client_error(vs); } else { VNC_DEBUG("TLS handshake complete, starting websocket handshake\n"); vs->ioc_tag = qio_channel_add_watch( QIO_CHANNEL(vs->ioc), G_IO_IN, vncws_handshake_io, vs, NULL); } } gboolean vncws_tls_handshake_io(QIOChannel *ioc G_GNUC_UNUSED, GIOCondition condition G_GNUC_UNUSED, void *opaque) { VncState *vs = opaque; QIOChannelTLS *tls; Error *err = NULL; VNC_DEBUG("TLS Websocket connection required\n"); if (vs->ioc_tag) { g_source_remove(vs->ioc_tag); vs->ioc_tag = 0; } tls = qio_channel_tls_new_server( vs->ioc, vs->vd->tlscreds, vs->vd->tlsaclname, &err); if (!tls) { VNC_DEBUG("Failed to setup TLS %s\n", error_get_pretty(err)); error_free(err); vnc_client_error(vs); return TRUE; } VNC_DEBUG("Start TLS WS handshake process\n"); object_unref(OBJECT(vs->ioc)); vs->ioc = QIO_CHANNEL(tls); vs->tls = qio_channel_tls_get_session(tls); qio_channel_tls_handshake(tls, vncws_tls_handshake_done, vs, NULL); return TRUE; } static void vncws_handshake_done(Object *source, Error *err, gpointer user_data) { VncState *vs = user_data; if (err) { VNC_DEBUG("Websock handshake failed %s\n", error_get_pretty(err)); vnc_client_error(vs); } else { VNC_DEBUG("Websock handshake complete, starting VNC protocol\n"); vnc_init_state(vs); vs->ioc_tag = qio_channel_add_watch( vs->ioc, G_IO_IN, vnc_client_io, vs, NULL); } } gboolean vncws_handshake_io(QIOChannel *ioc G_GNUC_UNUSED, GIOCondition condition G_GNUC_UNUSED, void *opaque) { VncState *vs = opaque; QIOChannelWebsock *wioc; VNC_DEBUG("Websocket negotiate starting\n"); if (vs->ioc_tag) { g_source_remove(vs->ioc_tag); vs->ioc_tag = 0; } wioc = qio_channel_websock_new_server(vs->ioc); object_unref(OBJECT(vs->ioc)); vs->ioc = QIO_CHANNEL(wioc); qio_channel_websock_handshake(wioc, vncws_handshake_done, vs, NULL); return TRUE; }