mirror of
https://github.com/qemu/qemu.git
synced 2024-11-25 11:53:39 +08:00
2ddafce7f7
When we connect to vnc by websocket channel, and disconnect (maybe by some network exception) before handshake, qemu will left CLOSE_WAIT socket and never close it After04d2529da2
("ui: convert VNC server to use QIOChannelSocket") anddd154c4d9f
("io: fix handling of EOF / error conditions in websock GSource"), the vnc call qio_channel_add_watch only care about G_IO_IN, but mising G_IO_HUP and G_IO_ERR. When the websocket channel get EOF or error, it cannot callback, because the caller ignore the event, that leads to resource leak We need handle G_IO_HUP and G_IO_ERR event, then cleanup the channel Fixes:04d2529da2
("ui: convert VNC server to use QIOChannelSocket") Fixes:dd154c4d9f
("io: fix handling of EOF / error conditions in websock GSource") Cc: qemu-stable@nongnu.org Signed-off-by: Ding Hui <dinghui@sangfor.com.cn> Message-id: 20201029032241.11040-1-dinghui@sangfor.com.cn Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
151 lines
4.2 KiB
C
151 lines
4.2 KiB
C
/*
|
|
* 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;
|
|
}
|