/* * 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" #include "trace.h" static void vncws_tls_handshake_done(QIOTask *task, gpointer user_data) { VncState *vs = user_data; Error *err = NULL; if (qio_task_propagate_error(task, &err)) { VNC_DEBUG("Handshake failed %s\n", error_get_pretty(err)); vnc_client_error(vs); error_free(err); } else { VNC_DEBUG("TLS handshake complete, starting websocket handshake\n"); if (vs->ioc_tag) { g_source_remove(vs->ioc_tag); } vs->ioc_tag = qio_channel_add_watch( QIO_CHANNEL(vs->ioc), G_IO_IN | G_IO_HUP | G_IO_ERR, vncws_handshake_io, vs, NULL); } } gboolean vncws_tls_handshake_io(QIOChannel *ioc G_GNUC_UNUSED, GIOCondition condition, void *opaque) { VncState *vs = opaque; QIOChannelTLS *tls; Error *err = NULL; if (vs->ioc_tag) { g_source_remove(vs->ioc_tag); vs->ioc_tag = 0; } if (condition & (G_IO_HUP | G_IO_ERR)) { vnc_client_error(vs); return TRUE; } tls = qio_channel_tls_new_server( vs->ioc, vs->vd->tlscreds, vs->vd->tlsauthzid, &err); if (!tls) { VNC_DEBUG("Failed to setup TLS %s\n", error_get_pretty(err)); error_free(err); vnc_client_error(vs); return TRUE; } qio_channel_set_name(QIO_CHANNEL(tls), "vnc-ws-server-tls"); object_unref(OBJECT(vs->ioc)); vs->ioc = QIO_CHANNEL(tls); trace_vnc_client_io_wrap(vs, vs->ioc, "tls"); vs->tls = qio_channel_tls_get_session(tls); qio_channel_tls_handshake(tls, vncws_tls_handshake_done, vs, NULL, NULL); return TRUE; } static void vncws_handshake_done(QIOTask *task, gpointer user_data) { VncState *vs = user_data; Error *err = NULL; if (qio_task_propagate_error(task, &err)) { VNC_DEBUG("Websock handshake failed %s\n", error_get_pretty(err)); vnc_client_error(vs); error_free(err); } else { VNC_DEBUG("Websock handshake complete, starting VNC protocol\n"); vnc_start_protocol(vs); if (vs->ioc_tag) { g_source_remove(vs->ioc_tag); } vs->ioc_tag = qio_channel_add_watch( vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR, vnc_client_io, vs, NULL); } } gboolean vncws_handshake_io(QIOChannel *ioc G_GNUC_UNUSED, GIOCondition condition, void *opaque) { VncState *vs = opaque; QIOChannelWebsock *wioc; if (vs->ioc_tag) { g_source_remove(vs->ioc_tag); vs->ioc_tag = 0; } if (condition & (G_IO_HUP | G_IO_ERR)) { vnc_client_error(vs); return TRUE; } wioc = qio_channel_websock_new_server(vs->ioc); qio_channel_set_name(QIO_CHANNEL(wioc), "vnc-ws-server-websock"); object_unref(OBJECT(vs->ioc)); vs->ioc = QIO_CHANNEL(wioc); trace_vnc_client_io_wrap(vs, vs->ioc, "websock"); qio_channel_websock_handshake(wioc, vncws_handshake_done, vs, NULL); return TRUE; }