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