|  | /* | 
|  | * 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(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; | 
|  | } |