ui: convert VNC server to use QIOChannelSocket

The minimal first step conversion to use QIOChannelSocket
classes instead of directly using POSIX sockets API. This
will later be extended to also cover the TLS, SASL and
websockets code.

Reviewed-by: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
This commit is contained in:
Daniel P. Berrange 2015-02-27 16:20:57 +00:00
parent 18f49881cf
commit 04d2529da2
7 changed files with 435 additions and 328 deletions

View File

@ -62,7 +62,7 @@ long vnc_client_write_sasl(VncState *vs)
(const char **)&vs->sasl.encoded,
&vs->sasl.encodedLength);
if (err != SASL_OK)
return vnc_client_io_error(vs, -1, EIO);
return vnc_client_io_error(vs, -1, NULL);
vs->sasl.encodedOffset = 0;
}
@ -86,7 +86,11 @@ long vnc_client_write_sasl(VncState *vs)
* SASL encoded output
*/
if (vs->output.offset == 0) {
qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
if (vs->ioc_tag) {
g_source_remove(vs->ioc_tag);
}
vs->ioc_tag = qio_channel_add_watch(
vs->ioc, G_IO_IN, vnc_client_io, vs, NULL);
}
return ret;
@ -110,7 +114,7 @@ long vnc_client_read_sasl(VncState *vs)
&decoded, &decodedLen);
if (err != SASL_OK)
return vnc_client_io_error(vs, -1, -EIO);
return vnc_client_io_error(vs, -1, NULL);
VNC_DEBUG("Read SASL Encoded %p size %ld Decoded %p size %d\n",
encoded, ret, decoded, decodedLen);
buffer_reserve(&vs->input, decodedLen);
@ -255,17 +259,17 @@ static int protocol_client_auth_sasl_step(VncState *vs, uint8_t *data, size_t le
vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4);
} else {
if (!vnc_auth_sasl_check_ssf(vs)) {
VNC_DEBUG("Authentication rejected for weak SSF %d\n", vs->csock);
VNC_DEBUG("Authentication rejected for weak SSF %p\n", vs->ioc);
goto authreject;
}
/* Check username whitelist ACL */
if (vnc_auth_sasl_check_access(vs) < 0) {
VNC_DEBUG("Authentication rejected for ACL %d\n", vs->csock);
VNC_DEBUG("Authentication rejected for ACL %p\n", vs->ioc);
goto authreject;
}
VNC_DEBUG("Authentication successful %d\n", vs->csock);
VNC_DEBUG("Authentication successful %p\n", vs->ioc);
vnc_write_u32(vs, 0); /* Accept auth */
/*
* Delay writing in SSF encoded mode until pending output
@ -383,17 +387,17 @@ static int protocol_client_auth_sasl_start(VncState *vs, uint8_t *data, size_t l
vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4);
} else {
if (!vnc_auth_sasl_check_ssf(vs)) {
VNC_DEBUG("Authentication rejected for weak SSF %d\n", vs->csock);
VNC_DEBUG("Authentication rejected for weak SSF %p\n", vs->ioc);
goto authreject;
}
/* Check username whitelist ACL */
if (vnc_auth_sasl_check_access(vs) < 0) {
VNC_DEBUG("Authentication rejected for ACL %d\n", vs->csock);
VNC_DEBUG("Authentication rejected for ACL %p\n", vs->ioc);
goto authreject;
}
VNC_DEBUG("Authentication successful %d\n", vs->csock);
VNC_DEBUG("Authentication successful %p\n", vs->ioc);
vnc_write_u32(vs, 0); /* Accept auth */
start_client_init(vs);
}
@ -487,6 +491,32 @@ static int protocol_client_auth_sasl_mechname_len(VncState *vs, uint8_t *data, s
return 0;
}
static char *
vnc_socket_ip_addr_string(QIOChannelSocket *ioc,
bool local,
Error **errp)
{
SocketAddress *addr;
char *ret;
if (local) {
addr = qio_channel_socket_get_local_address(ioc, errp);
} else {
addr = qio_channel_socket_get_remote_address(ioc, errp);
}
if (!addr) {
return NULL;
}
if (addr->type != SOCKET_ADDRESS_KIND_INET) {
error_setg(errp, "Not an inet socket type");
return NULL;
}
ret = g_strdup_printf("%s;%s", addr->u.inet->host, addr->u.inet->port);
qapi_free_SocketAddress(addr);
return ret;
}
void start_auth_sasl(VncState *vs)
{
const char *mechlist = NULL;
@ -495,13 +525,16 @@ void start_auth_sasl(VncState *vs)
char *localAddr, *remoteAddr;
int mechlistlen;
VNC_DEBUG("Initialize SASL auth %d\n", vs->csock);
VNC_DEBUG("Initialize SASL auth %p\n", vs->ioc);
/* Get local & remote client addresses in form IPADDR;PORT */
if (!(localAddr = vnc_socket_local_addr("%s;%s", vs->csock)))
localAddr = vnc_socket_ip_addr_string(vs->sioc, true, NULL);
if (!localAddr) {
goto authabort;
}
if (!(remoteAddr = vnc_socket_remote_addr("%s;%s", vs->csock))) {
remoteAddr = vnc_socket_ip_addr_string(vs->sioc, false, NULL);
if (!remoteAddr) {
g_free(localAddr);
goto authabort;
}

View File

@ -63,7 +63,9 @@ static void start_auth_vencrypt_subauth(VncState *vs)
}
}
static void vnc_tls_handshake_io(void *opaque);
static gboolean vnc_tls_handshake_io(QIOChannel *ioc,
GIOCondition condition,
void *opaque);
static int vnc_start_vencrypt_handshake(VncState *vs)
{
@ -80,19 +82,31 @@ static int vnc_start_vencrypt_handshake(VncState *vs)
goto error;
}
VNC_DEBUG("Client verification passed, starting TLS I/O\n");
qemu_set_fd_handler(vs->csock, vnc_client_read, vnc_client_write, 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_OUT, vnc_client_io, vs, NULL);
start_auth_vencrypt_subauth(vs);
break;
case QCRYPTO_TLS_HANDSHAKE_RECVING:
VNC_DEBUG("Handshake interrupted (blocking read)\n");
qemu_set_fd_handler(vs->csock, vnc_tls_handshake_io, NULL, vs);
if (vs->ioc_tag) {
g_source_remove(vs->ioc_tag);
}
vs->ioc_tag = qio_channel_add_watch(
vs->ioc, G_IO_IN, vnc_tls_handshake_io, vs, NULL);
break;
case QCRYPTO_TLS_HANDSHAKE_SENDING:
VNC_DEBUG("Handshake interrupted (blocking write)\n");
qemu_set_fd_handler(vs->csock, NULL, vnc_tls_handshake_io, vs);
if (vs->ioc_tag) {
g_source_remove(vs->ioc_tag);
}
vs->ioc_tag = qio_channel_add_watch(
vs->ioc, G_IO_OUT, vnc_tls_handshake_io, vs, NULL);
break;
}
@ -105,12 +119,15 @@ static int vnc_start_vencrypt_handshake(VncState *vs)
return -1;
}
static void vnc_tls_handshake_io(void *opaque)
static gboolean vnc_tls_handshake_io(QIOChannel *ioc G_GNUC_UNUSED,
GIOCondition condition G_GNUC_UNUSED,
void *opaque)
{
VncState *vs = (VncState *)opaque;
VNC_DEBUG("Handshake IO continue\n");
vnc_start_vencrypt_handshake(vs);
return TRUE;
}

View File

@ -166,13 +166,16 @@ void vnc_jobs_consume_buffer(VncState *vs)
vnc_lock_output(vs);
if (vs->jobs_buffer.offset) {
if (vs->csock != -1 && buffer_empty(&vs->output)) {
qemu_set_fd_handler(vs->csock, vnc_client_read,
vnc_client_write, vs);
if (vs->ioc != NULL && buffer_empty(&vs->output)) {
if (vs->ioc_tag) {
g_source_remove(vs->ioc_tag);
}
vs->ioc_tag = qio_channel_add_watch(
vs->ioc, G_IO_IN | G_IO_OUT, vnc_client_io, vs, NULL);
}
buffer_move(&vs->output, &vs->jobs_buffer);
}
flush = vs->csock != -1 && vs->abort != true;
flush = vs->ioc != NULL && vs->abort != true;
vnc_unlock_output(vs);
if (flush) {
@ -186,7 +189,8 @@ void vnc_jobs_consume_buffer(VncState *vs)
static void vnc_async_encoding_start(VncState *orig, VncState *local)
{
buffer_init(&local->output, "vnc-worker-output");
local->csock = -1; /* Don't do any network work on this thread */
local->sioc = NULL; /* Don't do any network work on this thread */
local->ioc = NULL; /* Don't do any network work on this thread */
local->vnc_encoding = orig->vnc_encoding;
local->features = orig->features;
@ -231,7 +235,7 @@ static int vnc_worker_thread_loop(VncJobQueue *queue)
}
vnc_lock_output(job->vs);
if (job->vs->csock == -1 || job->vs->abort == true) {
if (job->vs->ioc == NULL || job->vs->abort == true) {
vnc_unlock_output(job->vs);
goto disconnected;
}
@ -259,7 +263,7 @@ static int vnc_worker_thread_loop(VncJobQueue *queue)
QLIST_FOREACH_SAFE(entry, &job->rectangles, next, tmp) {
int n;
if (job->vs->csock == -1) {
if (job->vs->ioc == NULL) {
vnc_unlock_display(job->vs->vd);
/* Copy persistent encoding data */
vnc_async_encoding_end(job->vs, &vs);
@ -281,7 +285,7 @@ static int vnc_worker_thread_loop(VncJobQueue *queue)
vs.output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
vnc_lock_output(job->vs);
if (job->vs->csock != -1) {
if (job->vs->ioc != NULL) {
buffer_move(&job->vs->jobs_buffer, &vs.output);
/* Copy persistent encoding data */
vnc_async_encoding_end(job->vs, &vs);

View File

@ -37,17 +37,29 @@ static int vncws_start_tls_handshake(VncState *vs)
goto error;
}
VNC_DEBUG("Client verification passed, starting TLS I/O\n");
qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs);
if (vs->ioc_tag) {
g_source_remove(vs->ioc_tag);
}
vs->ioc_tag = qio_channel_add_watch(
vs->ioc, G_IO_IN, vncws_handshake_io, vs, NULL);
break;
case QCRYPTO_TLS_HANDSHAKE_RECVING:
VNC_DEBUG("Handshake interrupted (blocking read)\n");
qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io, NULL, vs);
if (vs->ioc_tag) {
g_source_remove(vs->ioc_tag);
}
vs->ioc_tag = qio_channel_add_watch(
vs->ioc, G_IO_IN, vncws_tls_handshake_io, vs, NULL);
break;
case QCRYPTO_TLS_HANDSHAKE_SENDING:
VNC_DEBUG("Handshake interrupted (blocking write)\n");
qemu_set_fd_handler(vs->csock, NULL, vncws_tls_handshake_io, vs);
if (vs->ioc_tag) {
g_source_remove(vs->ioc_tag);
}
vs->ioc_tag = qio_channel_add_watch(
vs->ioc, G_IO_OUT, vncws_tls_handshake_io, vs, NULL);
break;
}
@ -60,7 +72,9 @@ static int vncws_start_tls_handshake(VncState *vs)
return -1;
}
void vncws_tls_handshake_io(void *opaque)
gboolean vncws_tls_handshake_io(QIOChannel *ioc G_GNUC_UNUSED,
GIOCondition condition G_GNUC_UNUSED,
void *opaque)
{
VncState *vs = (VncState *)opaque;
Error *err = NULL;
@ -75,7 +89,7 @@ void vncws_tls_handshake_io(void *opaque)
error_get_pretty(err));
error_free(err);
vnc_client_error(vs);
return;
return TRUE;
}
qcrypto_tls_session_set_callbacks(vs->tls,
@ -85,11 +99,11 @@ void vncws_tls_handshake_io(void *opaque)
VNC_DEBUG("Start TLS WS handshake process\n");
vncws_start_tls_handshake(vs);
return TRUE;
}
void vncws_handshake_read(void *opaque)
static void vncws_handshake_read(VncState *vs)
{
VncState *vs = opaque;
uint8_t *handshake_end;
long ret;
/* Typical HTTP headers from novnc are 512 bytes, so limiting
@ -99,7 +113,7 @@ void vncws_handshake_read(void *opaque)
ret = vnc_client_read_buf(vs, buffer_end(&vs->ws_input), want);
if (!ret) {
if (vs->csock == -1) {
if (vs->disconnecting) {
vnc_disconnect_finish(vs);
}
return;
@ -109,7 +123,11 @@ void vncws_handshake_read(void *opaque)
handshake_end = (uint8_t *)g_strstr_len((char *)vs->ws_input.buffer,
vs->ws_input.offset, WS_HANDSHAKE_END);
if (handshake_end) {
qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
if (vs->ioc_tag) {
g_source_remove(vs->ioc_tag);
}
vs->ioc_tag = qio_channel_add_watch(
vs->ioc, G_IO_IN, vnc_client_io, vs, NULL);
vncws_process_handshake(vs, vs->ws_input.buffer, vs->ws_input.offset);
buffer_advance(&vs->ws_input, handshake_end - vs->ws_input.buffer +
strlen(WS_HANDSHAKE_END));
@ -120,6 +138,15 @@ void vncws_handshake_read(void *opaque)
}
gboolean vncws_handshake_io(QIOChannel *ioc G_GNUC_UNUSED,
GIOCondition condition G_GNUC_UNUSED,
void *opaque)
{
VncState *vs = opaque;
vncws_handshake_read(vs);
return TRUE;
}
long vnc_client_read_ws(VncState *vs)
{
int ret, err;
@ -187,7 +214,11 @@ long vnc_client_write_ws(VncState *vs)
buffer_advance(&vs->ws_output, ret);
if (vs->ws_output.offset == 0) {
qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
if (vs->ioc_tag) {
g_source_remove(vs->ioc_tag);
}
vs->ioc_tag = qio_channel_add_watch(
vs->ioc, G_IO_IN, vnc_client_io, vs, NULL);
}
return ret;

View File

@ -72,8 +72,12 @@ enum {
WS_OPCODE_PONG = 0xA
};
void vncws_tls_handshake_io(void *opaque);
void vncws_handshake_read(void *opaque);
gboolean vncws_tls_handshake_io(QIOChannel *ioc,
GIOCondition condition,
void *opaque);
gboolean vncws_handshake_io(QIOChannel *ioc,
GIOCondition condition,
void *opaque);
long vnc_client_write_ws(VncState *vs);
long vnc_client_read_ws(VncState *vs);
void vncws_process_handshake(VncState *vs, uint8_t *line, size_t size);

578
ui/vnc.c
View File

@ -70,8 +70,8 @@ static void vnc_set_share_mode(VncState *vs, VncShareMode mode)
[VNC_SHARE_MODE_EXCLUSIVE] = "exclusive",
[VNC_SHARE_MODE_DISCONNECTED] = "disconnected",
};
fprintf(stderr, "%s/%d: %s -> %s\n", __func__,
vs->csock, mn[vs->share_mode], mn[mode]);
fprintf(stderr, "%s/%p: %s -> %s\n", __func__,
vs->ioc, mn[vs->share_mode], mn[mode]);
#endif
switch (vs->share_mode) {
@ -105,108 +105,65 @@ static void vnc_set_share_mode(VncState *vs, VncShareMode mode)
}
}
static char *addr_to_string(const char *format,
struct sockaddr_storage *sa,
socklen_t salen) {
char *addr;
char host[NI_MAXHOST];
char serv[NI_MAXSERV];
int err;
size_t addrlen;
if ((err = getnameinfo((struct sockaddr *)sa, salen,
host, sizeof(host),
serv, sizeof(serv),
NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
VNC_DEBUG("Cannot resolve address %d: %s\n",
err, gai_strerror(err));
return NULL;
}
/* Enough for the existing format + the 2 vars we're
* substituting in. */
addrlen = strlen(format) + strlen(host) + strlen(serv);
addr = g_malloc(addrlen + 1);
snprintf(addr, addrlen, format, host, serv);
addr[addrlen] = '\0';
return addr;
}
char *vnc_socket_local_addr(const char *format, int fd) {
struct sockaddr_storage sa;
socklen_t salen;
salen = sizeof(sa);
if (getsockname(fd, (struct sockaddr*)&sa, &salen) < 0)
return NULL;
return addr_to_string(format, &sa, salen);
}
char *vnc_socket_remote_addr(const char *format, int fd) {
struct sockaddr_storage sa;
socklen_t salen;
salen = sizeof(sa);
if (getpeername(fd, (struct sockaddr*)&sa, &salen) < 0)
return NULL;
return addr_to_string(format, &sa, salen);
}
static void vnc_init_basic_info(struct sockaddr_storage *sa,
socklen_t salen,
static void vnc_init_basic_info(SocketAddress *addr,
VncBasicInfo *info,
Error **errp)
{
char host[NI_MAXHOST];
char serv[NI_MAXSERV];
int err;
switch (addr->type) {
case SOCKET_ADDRESS_KIND_INET:
info->host = g_strdup(addr->u.inet->host);
info->service = g_strdup(addr->u.inet->port);
if (addr->u.inet->ipv6) {
info->family = NETWORK_ADDRESS_FAMILY_IPV6;
} else {
info->family = NETWORK_ADDRESS_FAMILY_IPV4;
}
break;
if ((err = getnameinfo((struct sockaddr *)sa, salen,
host, sizeof(host),
serv, sizeof(serv),
NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
error_setg(errp, "Cannot resolve address: %s",
gai_strerror(err));
return;
case SOCKET_ADDRESS_KIND_UNIX:
info->host = g_strdup("");
info->service = g_strdup(addr->u.q_unix->path);
info->family = NETWORK_ADDRESS_FAMILY_UNIX;
break;
default:
error_setg(errp, "Unsupported socket kind %d",
addr->type);
break;
}
info->host = g_strdup(host);
info->service = g_strdup(serv);
info->family = inet_netfamily(sa->ss_family);
return;
}
static void vnc_init_basic_info_from_server_addr(int fd, VncBasicInfo *info,
static void vnc_init_basic_info_from_server_addr(QIOChannelSocket *ioc,
VncBasicInfo *info,
Error **errp)
{
struct sockaddr_storage sa;
socklen_t salen;
SocketAddress *addr = NULL;
salen = sizeof(sa);
if (getsockname(fd, (struct sockaddr*)&sa, &salen) < 0) {
error_setg_errno(errp, errno, "getsockname failed");
addr = qio_channel_socket_get_local_address(ioc, errp);
if (!addr) {
return;
}
vnc_init_basic_info(&sa, salen, info, errp);
vnc_init_basic_info(addr, info, errp);
qapi_free_SocketAddress(addr);
}
static void vnc_init_basic_info_from_remote_addr(int fd, VncBasicInfo *info,
static void vnc_init_basic_info_from_remote_addr(QIOChannelSocket *ioc,
VncBasicInfo *info,
Error **errp)
{
struct sockaddr_storage sa;
socklen_t salen;
SocketAddress *addr = NULL;
salen = sizeof(sa);
if (getpeername(fd, (struct sockaddr*)&sa, &salen) < 0) {
error_setg_errno(errp, errno, "getpeername failed");
addr = qio_channel_socket_get_remote_address(ioc, errp);
if (!addr) {
return;
}
vnc_init_basic_info(&sa, salen, info, errp);
vnc_init_basic_info(addr, info, errp);
qapi_free_SocketAddress(addr);
}
static const char *vnc_auth_name(VncDisplay *vd) {
@ -300,7 +257,7 @@ static void vnc_client_cache_addr(VncState *client)
Error *err = NULL;
client->info = g_malloc0(sizeof(*client->info));
vnc_init_basic_info_from_remote_addr(client->csock,
vnc_init_basic_info_from_remote_addr(client->sioc,
qapi_VncClientInfo_base(client->info),
&err);
if (err) {
@ -343,27 +300,20 @@ static void vnc_qmp_event(VncState *vs, QAPIEvent event)
static VncClientInfo *qmp_query_vnc_client(const VncState *client)
{
struct sockaddr_storage sa;
socklen_t salen = sizeof(sa);
char host[NI_MAXHOST];
char serv[NI_MAXSERV];
VncClientInfo *info;
if (getpeername(client->csock, (struct sockaddr *)&sa, &salen) < 0) {
return NULL;
}
if (getnameinfo((struct sockaddr *)&sa, salen,
host, sizeof(host),
serv, sizeof(serv),
NI_NUMERICHOST | NI_NUMERICSERV) < 0) {
return NULL;
}
Error *err = NULL;
info = g_malloc0(sizeof(*info));
info->host = g_strdup(host);
info->service = g_strdup(serv);
info->family = inet_netfamily(sa.ss_family);
vnc_init_basic_info_from_remote_addr(client->sioc,
qapi_VncClientInfo_base(info),
&err);
if (err) {
error_free(err);
qapi_free_VncClientInfo(info);
return NULL;
}
info->websocket = client->websocket;
if (client->tls) {
@ -413,81 +363,89 @@ VncInfo *qmp_query_vnc(Error **errp)
{
VncInfo *info = g_malloc0(sizeof(*info));
VncDisplay *vd = vnc_display_find(NULL);
SocketAddress *addr = NULL;
if (vd == NULL || !vd->enabled) {
info->enabled = false;
} else {
struct sockaddr_storage sa;
socklen_t salen = sizeof(sa);
char host[NI_MAXHOST];
char serv[NI_MAXSERV];
info->enabled = true;
/* for compatibility with the original command */
info->has_clients = true;
info->clients = qmp_query_client_list(vd);
if (vd->lsock == -1) {
if (vd->lsock == NULL) {
return info;
}
if (getsockname(vd->lsock, (struct sockaddr *)&sa,
&salen) == -1) {
error_setg(errp, QERR_UNDEFINED_ERROR);
addr = qio_channel_socket_get_local_address(vd->lsock, errp);
if (!addr) {
goto out_error;
}
if (getnameinfo((struct sockaddr *)&sa, salen,
host, sizeof(host),
serv, sizeof(serv),
NI_NUMERICHOST | NI_NUMERICSERV) < 0) {
error_setg(errp, QERR_UNDEFINED_ERROR);
switch (addr->type) {
case SOCKET_ADDRESS_KIND_INET:
info->host = g_strdup(addr->u.inet->host);
info->service = g_strdup(addr->u.inet->port);
if (addr->u.inet->ipv6) {
info->family = NETWORK_ADDRESS_FAMILY_IPV6;
} else {
info->family = NETWORK_ADDRESS_FAMILY_IPV4;
}
break;
case SOCKET_ADDRESS_KIND_UNIX:
info->host = g_strdup("");
info->service = g_strdup(addr->u.q_unix->path);
info->family = NETWORK_ADDRESS_FAMILY_UNIX;
break;
default:
error_setg(errp, "Unsupported socket kind %d",
addr->type);
goto out_error;
}
info->has_host = true;
info->host = g_strdup(host);
info->has_service = true;
info->service = g_strdup(serv);
info->has_family = true;
info->family = inet_netfamily(sa.ss_family);
info->has_auth = true;
info->auth = g_strdup(vnc_auth_name(vd));
}
qapi_free_SocketAddress(addr);
return info;
out_error:
qapi_free_SocketAddress(addr);
qapi_free_VncInfo(info);
return NULL;
}
static VncBasicInfoList *qmp_query_server_entry(int socket,
static VncBasicInfoList *qmp_query_server_entry(QIOChannelSocket *ioc,
bool websocket,
VncBasicInfoList *prev)
{
VncBasicInfoList *list;
VncBasicInfo *info;
struct sockaddr_storage sa;
socklen_t salen = sizeof(sa);
char host[NI_MAXHOST];
char serv[NI_MAXSERV];
Error *err = NULL;
SocketAddress *addr;
if (getsockname(socket, (struct sockaddr *)&sa, &salen) < 0 ||
getnameinfo((struct sockaddr *)&sa, salen,
host, sizeof(host), serv, sizeof(serv),
NI_NUMERICHOST | NI_NUMERICSERV) < 0) {
addr = qio_channel_socket_get_local_address(ioc, &err);
if (!addr) {
error_free(err);
return prev;
}
info = g_new0(VncBasicInfo, 1);
info->host = g_strdup(host);
info->service = g_strdup(serv);
info->family = inet_netfamily(sa.ss_family);
vnc_init_basic_info(addr, info, &err);
qapi_free_SocketAddress(addr);
if (err) {
qapi_free_VncBasicInfo(info);
error_free(err);
return prev;
}
info->websocket = websocket;
list = g_new0(VncBasicInfoList, 1);
@ -581,13 +539,13 @@ VncInfo2List *qmp_query_vnc_servers(Error **errp)
info->has_display = true;
info->display = g_strdup(dev->id);
}
if (vd->lsock != -1) {
info->server = qmp_query_server_entry(vd->lsock, false,
info->server);
if (vd->lsock != NULL) {
info->server = qmp_query_server_entry(
vd->lsock, false, info->server);
}
if (vd->lwebsock != -1) {
info->server = qmp_query_server_entry(vd->lwebsock, true,
info->server);
if (vd->lwebsock != NULL) {
info->server = qmp_query_server_entry(
vd->lwebsock, true, info->server);
}
item = g_new0(VncInfo2List, 1);
@ -673,7 +631,7 @@ void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
static void vnc_desktop_resize(VncState *vs)
{
if (vs->csock == -1 || !vnc_has_feature(vs, VNC_FEATURE_RESIZE)) {
if (vs->ioc == NULL || !vnc_has_feature(vs, VNC_FEATURE_RESIZE)) {
return;
}
if (vs->client_width == pixman_image_get_width(vs->vd->server) &&
@ -1066,7 +1024,7 @@ static int find_and_clear_dirty_height(VncState *vs,
static int vnc_update_client(VncState *vs, int has_dirty, bool sync)
{
vs->has_dirty += has_dirty;
if (vs->need_update && vs->csock != -1) {
if (vs->need_update && vs->ioc != NULL) {
VncDisplay *vd = vs->vd;
VncJob *job;
int y;
@ -1130,7 +1088,7 @@ static int vnc_update_client(VncState *vs, int has_dirty, bool sync)
return n;
}
if (vs->csock == -1) {
if (vs->disconnecting) {
vnc_disconnect_finish(vs);
} else if (sync) {
vnc_jobs_join(vs);
@ -1212,12 +1170,15 @@ static void audio_del(VncState *vs)
static void vnc_disconnect_start(VncState *vs)
{
if (vs->csock == -1)
if (vs->disconnecting) {
return;
}
vnc_set_share_mode(vs, VNC_SHARE_MODE_DISCONNECTED);
qemu_set_fd_handler(vs->csock, NULL, NULL, NULL);
closesocket(vs->csock);
vs->csock = -1;
if (vs->ioc_tag) {
g_source_remove(vs->ioc_tag);
}
qio_channel_close(vs->ioc, NULL);
vs->disconnecting = TRUE;
}
void vnc_disconnect_finish(VncState *vs)
@ -1270,29 +1231,29 @@ void vnc_disconnect_finish(VncState *vs)
g_free(vs->lossy_rect[i]);
}
g_free(vs->lossy_rect);
object_unref(OBJECT(vs->ioc));
vs->ioc = NULL;
object_unref(OBJECT(vs->sioc));
vs->sioc = NULL;
g_free(vs);
}
ssize_t vnc_client_io_error(VncState *vs, ssize_t ret, int last_errno)
ssize_t vnc_client_io_error(VncState *vs, ssize_t ret, Error **errp)
{
if (ret == 0 || ret == -1) {
if (ret == -1) {
switch (last_errno) {
case EINTR:
case EAGAIN:
#ifdef _WIN32
case WSAEWOULDBLOCK:
#endif
return 0;
default:
break;
}
if (ret <= 0) {
if (ret == 0) {
VNC_DEBUG("Closing down client sock: EOF\n");
} else if (ret != QIO_CHANNEL_ERR_BLOCK) {
VNC_DEBUG("Closing down client sock: ret %d (%s)\n",
ret, errp ? error_get_pretty(*errp) : "Unknown");
}
VNC_DEBUG("Closing down client sock: ret %zd, errno %d\n",
ret, ret < 0 ? last_errno : 0);
vnc_disconnect_start(vs);
if (errp) {
error_free(*errp);
*errp = NULL;
}
return 0;
}
return ret;
@ -1309,13 +1270,12 @@ void vnc_client_error(VncState *vs)
ssize_t vnc_tls_pull(char *buf, size_t len, void *opaque)
{
VncState *vs = opaque;
ssize_t ret;
retry:
ret = qemu_recv(vs->csock, buf, len, 0);
ssize_t ret = qio_channel_read(vs->ioc, buf, len, NULL);
if (ret < 0) {
if (errno == EINTR) {
goto retry;
if (ret == QIO_CHANNEL_ERR_BLOCK) {
errno = EAGAIN;
} else {
errno = EIO;
}
return -1;
}
@ -1326,13 +1286,12 @@ ssize_t vnc_tls_pull(char *buf, size_t len, void *opaque)
ssize_t vnc_tls_push(const char *buf, size_t len, void *opaque)
{
VncState *vs = opaque;
ssize_t ret;
retry:
ret = send(vs->csock, buf, len, 0);
ssize_t ret = qio_channel_write(vs->ioc, buf, len, NULL);
if (ret < 0) {
if (errno == EINTR) {
goto retry;
if (ret == QIO_CHANNEL_ERR_BLOCK) {
errno = EAGAIN;
} else {
errno = EIO;
}
return -1;
}
@ -1357,21 +1316,25 @@ ssize_t vnc_tls_push(const char *buf, size_t len, void *opaque)
*/
ssize_t vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen)
{
Error *err = NULL;
ssize_t ret;
int err = 0;
if (vs->tls) {
ret = qcrypto_tls_session_write(vs->tls, (const char *)data, datalen);
if (ret < 0) {
err = errno;
if (errno == EAGAIN) {
ret = QIO_CHANNEL_ERR_BLOCK;
} else {
ret = -1;
error_setg_errno(&err, errno, "%s",
"Cannot write to TLS socket");
}
}
} else {
ret = send(vs->csock, (const void *)data, datalen, 0);
if (ret < 0) {
err = socket_error();
}
ret = qio_channel_write(
vs->ioc, (const char *)data, datalen, &err);
}
VNC_DEBUG("Wrote wire %p %zd -> %ld\n", data, datalen, ret);
return vnc_client_io_error(vs, ret, err);
return vnc_client_io_error(vs, ret, &err);
}
@ -1409,7 +1372,11 @@ static ssize_t vnc_client_write_plain(VncState *vs)
buffer_advance(&vs->output, ret);
if (vs->output.offset == 0) {
qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
if (vs->ioc_tag) {
g_source_remove(vs->ioc_tag);
}
vs->ioc_tag = qio_channel_add_watch(
vs->ioc, G_IO_IN, vnc_client_io, vs, NULL);
}
return ret;
@ -1421,10 +1388,8 @@ static ssize_t vnc_client_write_plain(VncState *vs)
* the client socket. Will delegate actual work according to whether
* SASL SSF layers are enabled (thus requiring encryption calls)
*/
static void vnc_client_write_locked(void *opaque)
static void vnc_client_write_locked(VncState *vs)
{
VncState *vs = opaque;
#ifdef CONFIG_VNC_SASL
if (vs->sasl.conn &&
vs->sasl.runSSF &&
@ -1441,15 +1406,18 @@ static void vnc_client_write_locked(void *opaque)
}
}
void vnc_client_write(void *opaque)
static void vnc_client_write(VncState *vs)
{
VncState *vs = opaque;
vnc_lock_output(vs);
if (vs->output.offset || vs->ws_output.offset) {
vnc_client_write_locked(opaque);
} else if (vs->csock != -1) {
qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
vnc_client_write_locked(vs);
} else if (vs->ioc != NULL) {
if (vs->ioc_tag) {
g_source_remove(vs->ioc_tag);
}
vs->ioc_tag = qio_channel_add_watch(
vs->ioc, G_IO_IN, vnc_client_io, vs, NULL);
}
vnc_unlock_output(vs);
}
@ -1479,20 +1447,24 @@ void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting)
ssize_t vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen)
{
ssize_t ret;
int err = -1;
Error *err = NULL;
if (vs->tls) {
ret = qcrypto_tls_session_read(vs->tls, (char *)data, datalen);
if (ret < 0) {
err = errno;
if (errno == EAGAIN) {
ret = QIO_CHANNEL_ERR_BLOCK;
} else {
ret = -1;
error_setg_errno(&err, errno, "%s",
"Cannot read from TLS socket");
}
}
} else {
ret = qemu_recv(vs->csock, data, datalen, 0);
if (ret < 0) {
err = socket_error();
}
ret = qio_channel_read(
vs->ioc, (char *)data, datalen, &err);
}
VNC_DEBUG("Read wire %p %zd -> %ld\n", data, datalen, ret);
return vnc_client_io_error(vs, ret, err);
return vnc_client_io_error(vs, ret, &err);
}
@ -1529,9 +1501,8 @@ static void vnc_jobs_bh(void *opaque)
* the client socket. Will delegate actual work according to whether
* SASL SSF layers are enabled (thus requiring decryption calls)
*/
void vnc_client_read(void *opaque)
static void vnc_client_read(VncState *vs)
{
VncState *vs = opaque;
ssize_t ret;
#ifdef CONFIG_VNC_SASL
@ -1552,8 +1523,9 @@ void vnc_client_read(void *opaque)
ret = vnc_client_read_plain(vs);
}
if (!ret) {
if (vs->csock == -1)
if (vs->disconnecting) {
vnc_disconnect_finish(vs);
}
return;
}
@ -1562,7 +1534,7 @@ void vnc_client_read(void *opaque)
int ret;
ret = vs->read_handler(vs, vs->input.buffer, len);
if (vs->csock == -1) {
if (vs->disconnecting) {
vnc_disconnect_finish(vs);
return;
}
@ -1575,12 +1547,30 @@ void vnc_client_read(void *opaque)
}
}
gboolean vnc_client_io(QIOChannel *ioc G_GNUC_UNUSED,
GIOCondition condition, void *opaque)
{
VncState *vs = opaque;
if (condition & G_IO_IN) {
vnc_client_read(vs);
}
if (condition & G_IO_OUT) {
vnc_client_write(vs);
}
return TRUE;
}
void vnc_write(VncState *vs, const void *data, size_t len)
{
buffer_reserve(&vs->output, len);
if (vs->csock != -1 && buffer_empty(&vs->output)) {
qemu_set_fd_handler(vs->csock, vnc_client_read, vnc_client_write, vs);
if (vs->ioc != NULL && buffer_empty(&vs->output)) {
if (vs->ioc_tag) {
g_source_remove(vs->ioc_tag);
}
vs->ioc_tag = qio_channel_add_watch(
vs->ioc, G_IO_IN | G_IO_OUT, vnc_client_io, vs, NULL);
}
buffer_append(&vs->output, data, len);
@ -1621,8 +1611,7 @@ void vnc_write_u8(VncState *vs, uint8_t value)
void vnc_flush(VncState *vs)
{
vnc_lock_output(vs);
if (vs->csock != -1 && (vs->output.offset ||
vs->ws_output.offset)) {
if (vs->ioc != NULL && (vs->output.offset || vs->ws_output.offset)) {
vnc_client_write_locked(vs);
}
vnc_unlock_output(vs);
@ -3006,34 +2995,37 @@ static void vnc_refresh(DisplayChangeListener *dcl)
}
}
static void vnc_connect(VncDisplay *vd, int csock,
static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc,
bool skipauth, bool websocket)
{
VncState *vs = g_new0(VncState, 1);
int i;
vs->csock = csock;
vs->sioc = sioc;
object_ref(OBJECT(vs->sioc));
vs->ioc = QIO_CHANNEL(sioc);
object_ref(OBJECT(vs->ioc));
vs->vd = vd;
buffer_init(&vs->input, "vnc-input/%d", csock);
buffer_init(&vs->output, "vnc-output/%d", csock);
buffer_init(&vs->ws_input, "vnc-ws_input/%d", csock);
buffer_init(&vs->ws_output, "vnc-ws_output/%d", csock);
buffer_init(&vs->jobs_buffer, "vnc-jobs_buffer/%d", csock);
buffer_init(&vs->input, "vnc-input/%p", sioc);
buffer_init(&vs->output, "vnc-output/%p", sioc);
buffer_init(&vs->ws_input, "vnc-ws_input/%p", sioc);
buffer_init(&vs->ws_output, "vnc-ws_output/%p", sioc);
buffer_init(&vs->jobs_buffer, "vnc-jobs_buffer/%p", sioc);
buffer_init(&vs->tight.tight, "vnc-tight/%d", csock);
buffer_init(&vs->tight.zlib, "vnc-tight-zlib/%d", csock);
buffer_init(&vs->tight.gradient, "vnc-tight-gradient/%d", csock);
buffer_init(&vs->tight.tight, "vnc-tight/%p", sioc);
buffer_init(&vs->tight.zlib, "vnc-tight-zlib/%p", sioc);
buffer_init(&vs->tight.gradient, "vnc-tight-gradient/%p", sioc);
#ifdef CONFIG_VNC_JPEG
buffer_init(&vs->tight.jpeg, "vnc-tight-jpeg/%d", csock);
buffer_init(&vs->tight.jpeg, "vnc-tight-jpeg/%p", sioc);
#endif
#ifdef CONFIG_VNC_PNG
buffer_init(&vs->tight.png, "vnc-tight-png/%d", csock);
buffer_init(&vs->tight.png, "vnc-tight-png/%p", sioc);
#endif
buffer_init(&vs->zlib.zlib, "vnc-zlib/%d", csock);
buffer_init(&vs->zrle.zrle, "vnc-zrle/%d", csock);
buffer_init(&vs->zrle.fb, "vnc-zrle-fb/%d", csock);
buffer_init(&vs->zrle.zlib, "vnc-zrle-zlib/%d", csock);
buffer_init(&vs->zlib.zlib, "vnc-zlib/%p", sioc);
buffer_init(&vs->zrle.zrle, "vnc-zrle/%p", sioc);
buffer_init(&vs->zrle.fb, "vnc-zrle-fb/%p", sioc);
buffer_init(&vs->zrle.zlib, "vnc-zrle-zlib/%p", sioc);
if (skipauth) {
vs->auth = VNC_AUTH_NONE;
@ -3047,27 +3039,29 @@ static void vnc_connect(VncDisplay *vd, int csock,
vs->subauth = vd->subauth;
}
}
VNC_DEBUG("Client sock=%d ws=%d auth=%d subauth=%d\n",
csock, websocket, vs->auth, vs->subauth);
VNC_DEBUG("Client sioc=%p ws=%d auth=%d subauth=%d\n",
sioc, websocket, vs->auth, vs->subauth);
vs->lossy_rect = g_malloc0(VNC_STAT_ROWS * sizeof (*vs->lossy_rect));
for (i = 0; i < VNC_STAT_ROWS; ++i) {
vs->lossy_rect[i] = g_new0(uint8_t, VNC_STAT_COLS);
}
VNC_DEBUG("New client on socket %d\n", csock);
VNC_DEBUG("New client on socket %p\n", vs->sioc);
update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
qemu_set_nonblock(vs->csock);
qio_channel_set_blocking(vs->ioc, false, NULL);
if (websocket) {
vs->websocket = 1;
if (vd->ws_tls) {
qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io, NULL, vs);
vs->ioc_tag = qio_channel_add_watch(
vs->ioc, G_IO_IN, vncws_tls_handshake_io, vs, NULL);
} else {
qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs);
vs->ioc_tag = qio_channel_add_watch(
vs->ioc, G_IO_IN, vncws_handshake_io, vs, NULL);
}
} else
{
qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
} else {
vs->ioc_tag = qio_channel_add_watch(
vs->ioc, G_IO_IN, vnc_client_io, vs, NULL);
}
vnc_client_cache_addr(vs);
@ -3125,35 +3119,28 @@ void vnc_init_state(VncState *vs)
/* vs might be free()ed here */
}
static void vnc_listen_read(void *opaque, bool websocket)
static gboolean vnc_listen_io(QIOChannel *ioc,
GIOCondition condition,
void *opaque)
{
VncDisplay *vs = opaque;
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
int csock;
QIOChannelSocket *sioc = NULL;
Error *err = NULL;
/* Catch-up */
graphic_hw_update(vs->dcl.con);
if (websocket) {
csock = qemu_accept(vs->lwebsock, (struct sockaddr *)&addr, &addrlen);
sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc), &err);
if (sioc != NULL) {
qio_channel_set_delay(QIO_CHANNEL(sioc), false);
vnc_connect(vs, sioc, false,
ioc != QIO_CHANNEL(vs->lsock));
object_unref(OBJECT(sioc));
} else {
csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen);
/* client probably closed connection before we got there */
error_free(err);
}
if (csock != -1) {
socket_set_nodelay(csock);
vnc_connect(vs, csock, false, websocket);
}
}
static void vnc_listen_regular_read(void *opaque)
{
vnc_listen_read(opaque, false);
}
static void vnc_listen_websocket_read(void *opaque)
{
vnc_listen_read(opaque, true);
return TRUE;
}
static const DisplayChangeListenerOps dcl_ops = {
@ -3179,9 +3166,6 @@ void vnc_display_init(const char *id)
vs->id = strdup(id);
QTAILQ_INSERT_TAIL(&vnc_displays, vs, next);
vs->lsock = -1;
vs->lwebsock = -1;
QTAILQ_INIT(&vs->clients);
vs->expires = TIME_MAX;
@ -3209,16 +3193,20 @@ static void vnc_display_close(VncDisplay *vs)
return;
vs->enabled = false;
vs->is_unix = false;
if (vs->lsock != -1) {
qemu_set_fd_handler(vs->lsock, NULL, NULL, NULL);
close(vs->lsock);
vs->lsock = -1;
if (vs->lsock != NULL) {
if (vs->lsock_tag) {
g_source_remove(vs->lsock_tag);
}
object_unref(OBJECT(vs->lsock));
vs->lsock = NULL;
}
vs->ws_enabled = false;
if (vs->lwebsock != -1) {
qemu_set_fd_handler(vs->lwebsock, NULL, NULL, NULL);
close(vs->lwebsock);
vs->lwebsock = -1;
if (vs->lwebsock != NULL) {
if (vs->lwebsock_tag) {
g_source_remove(vs->lwebsock_tag);
}
object_unref(OBJECT(vs->lwebsock));
vs->lwebsock = NULL;
}
vs->auth = VNC_AUTH_INVALID;
vs->subauth = VNC_AUTH_INVALID;
@ -3263,9 +3251,25 @@ int vnc_display_pw_expire(const char *id, time_t expires)
char *vnc_display_local_addr(const char *id)
{
VncDisplay *vs = vnc_display_find(id);
SocketAddress *addr;
char *ret;
Error *err = NULL;
assert(vs);
return vnc_socket_local_addr("%s:%s", vs->lsock);
addr = qio_channel_socket_get_local_address(vs->lsock, &err);
if (!addr) {
return NULL;
}
if (addr->type != SOCKET_ADDRESS_KIND_INET) {
qapi_free_SocketAddress(addr);
return NULL;
}
ret = g_strdup_printf("%s;%s", addr->u.inet->host, addr->u.inet->port);
qapi_free_SocketAddress(addr);
return ret;
}
static QemuOptsList qemu_vnc_opts = {
@ -3826,41 +3830,45 @@ void vnc_display_open(const char *id, Error **errp)
if (reverse) {
/* connect to viewer */
int csock;
vs->lsock = -1;
vs->lwebsock = -1;
QIOChannelSocket *sioc = NULL;
vs->lsock = NULL;
vs->lwebsock = NULL;
if (vs->ws_enabled) {
error_setg(errp, "Cannot use websockets in reverse mode");
goto fail;
}
csock = socket_connect(saddr, errp, NULL, NULL);
if (csock < 0) {
vs->is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX;
sioc = qio_channel_socket_new();
if (qio_channel_socket_connect_sync(sioc, saddr, errp) < 0) {
goto fail;
}
vs->is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX;
vnc_connect(vs, csock, false, false);
vnc_connect(vs, sioc, false, false);
object_unref(OBJECT(sioc));
} else {
/* listen for connects */
vs->lsock = socket_listen(saddr, errp);
if (vs->lsock < 0) {
vs->lsock = qio_channel_socket_new();
if (qio_channel_socket_listen_sync(vs->lsock, saddr, errp) < 0) {
goto fail;
}
vs->is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX;
vs->enabled = true;
if (vs->ws_enabled) {
vs->lwebsock = socket_listen(wsaddr, errp);
if (vs->lwebsock < 0) {
if (vs->lsock != -1) {
close(vs->lsock);
vs->lsock = -1;
}
vs->lwebsock = qio_channel_socket_new();
if (qio_channel_socket_listen_sync(vs->lwebsock,
wsaddr, errp) < 0) {
object_unref(OBJECT(vs->lsock));
vs->lsock = NULL;
goto fail;
}
}
vs->enabled = true;
qemu_set_fd_handler(vs->lsock, vnc_listen_regular_read, NULL, vs);
vs->lsock_tag = qio_channel_add_watch(
QIO_CHANNEL(vs->lsock),
G_IO_IN, vnc_listen_io, vs, NULL);
if (vs->ws_enabled) {
qemu_set_fd_handler(vs->lwebsock, vnc_listen_websocket_read,
NULL, vs);
vs->lwebsock_tag = qio_channel_add_watch(
QIO_CHANNEL(vs->lwebsock),
G_IO_IN, vnc_listen_io, vs, NULL);
}
}
@ -3878,11 +3886,17 @@ fail:
void vnc_display_add_client(const char *id, int csock, bool skipauth)
{
VncDisplay *vs = vnc_display_find(id);
QIOChannelSocket *sioc;
if (!vs) {
return;
}
vnc_connect(vs, csock, skipauth, false);
sioc = qio_channel_socket_new_fd(csock, NULL);
if (sioc) {
vnc_connect(vs, sioc, skipauth, false);
object_unref(OBJECT(sioc));
}
}
static void vnc_auto_assign_id(QemuOptsList *olist, QemuOpts *opts)

View File

@ -35,6 +35,7 @@
#include "qemu/bitmap.h"
#include "crypto/tlssession.h"
#include "qemu/buffer.h"
#include "io/channel-socket.h"
#include <zlib.h>
#include <stdbool.h>
@ -145,8 +146,10 @@ struct VncDisplay
int num_exclusive;
int connections_limit;
VncSharePolicy share_policy;
int lsock;
int lwebsock;
QIOChannelSocket *lsock;
guint lsock_tag;
QIOChannelSocket *lwebsock;
guint lwebsock_tag;
bool ws_enabled;
DisplaySurface *ds;
DisplayChangeListener dcl;
@ -248,7 +251,10 @@ struct VncJob
struct VncState
{
int csock;
QIOChannelSocket *sioc; /* The underlying socket */
QIOChannel *ioc; /* The channel currently used for I/O */
guint ioc_tag;
gboolean disconnecting;
DECLARE_BITMAP(dirty[VNC_MAX_HEIGHT], VNC_DIRTY_BITS);
uint8_t **lossy_rect; /* Not an Array to avoid costly memcpy in
@ -499,8 +505,9 @@ enum {
*****************************************************************************/
/* Event loop functions */
void vnc_client_read(void *opaque);
void vnc_client_write(void *opaque);
gboolean vnc_client_io(QIOChannel *ioc,
GIOCondition condition,
void *opaque);
ssize_t vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen);
ssize_t vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen);
@ -524,7 +531,7 @@ uint32_t read_u32(uint8_t *data, size_t offset);
/* Protocol stage functions */
void vnc_client_error(VncState *vs);
ssize_t vnc_client_io_error(VncState *vs, ssize_t ret, int last_errno);
ssize_t vnc_client_io_error(VncState *vs, ssize_t ret, Error **errp);
void start_client_init(VncState *vs);
void start_auth_vnc(VncState *vs);
@ -532,9 +539,6 @@ void start_auth_vnc(VncState *vs);
/* Misc helpers */
char *vnc_socket_local_addr(const char *format, int fd);
char *vnc_socket_remote_addr(const char *format, int fd);
static inline uint32_t vnc_has_feature(VncState *vs, int feature) {
return (vs->features & (1 << feature));
}