diff mbox

[v1,RFC,29/34] ui: convert VNC server to use QEMUIOChannelSocket classes

Message ID 1429280557-8887-30-git-send-email-berrange@redhat.com
State New
Headers show

Commit Message

Daniel P. Berrangé April 17, 2015, 2:22 p.m. UTC
The minimal first step conversion to use QEMUIOChannelSocket
classes instead of directly using POSIX sockets API. This
will later be extended to also cover the TLS, SASL and
websockets code.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 ui/vnc-auth-sasl.c     |  39 ++--
 ui/vnc-auth-vencrypt.c |  45 ++--
 ui/vnc-jobs.c          |  12 +-
 ui/vnc-tls.c           |  24 +--
 ui/vnc-ws.c            |  53 +++--
 ui/vnc-ws.h            |   8 +-
 ui/vnc.c               | 545 +++++++++++++++++++++++++------------------------
 ui/vnc.h               |  33 ++-
 8 files changed, 422 insertions(+), 337 deletions(-)
diff mbox

Patch

diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c
index 9733dfc..7851037 100644
--- a/ui/vnc-auth-sasl.c
+++ b/ui/vnc-auth-sasl.c
@@ -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_handler2(vs->csock, NULL, 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);
     }
 
     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);
     qio_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);
     }
@@ -495,13 +499,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_local_addr(vs->sioc, "%s;%s", NULL);
+    if (!localAddr) {
         goto authabort;
+    }
 
-    if (!(remoteAddr = vnc_socket_remote_addr("%s;%s", vs->csock))) {
+    remoteAddr = vnc_socket_remote_addr(vs->sioc, "%s;%s", NULL);
+    if (!remoteAddr) {
         g_free(localAddr);
         goto authabort;
     }
@@ -533,7 +540,8 @@  void start_auth_sasl(VncState *vs)
         sasl_ssf_t ssf;
 
         cipher = gnutls_cipher_get(vs->tls.session);
-        if (!(ssf = (sasl_ssf_t)gnutls_cipher_get_key_size(cipher))) {
+        ssf = (sasl_ssf_t)gnutls_cipher_get_key_size(cipher);
+        if (!ssf) {
             VNC_DEBUG("%s", "cannot TLS get cipher size\n");
             sasl_dispose(&vs->sasl.conn);
             vs->sasl.conn = NULL;
@@ -549,9 +557,12 @@  void start_auth_sasl(VncState *vs)
             vs->sasl.conn = NULL;
             goto authabort;
         }
-    } else
+    } else {
 #endif /* CONFIG_VNC_TLS */
         vs->sasl.wantSSF = 1;
+#ifdef CONFIG_VNC_TLS
+    }
+#endif
 
     memset (&secprops, 0, sizeof secprops);
     /* Inform SASL that we've got an external SSF layer from TLS */
diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c
index a420ccb..55f24e4 100644
--- a/ui/vnc-auth-vencrypt.c
+++ b/ui/vnc-auth-vencrypt.c
@@ -63,23 +63,32 @@  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(struct VncState *vs) {
     int ret;
 
     if ((ret = gnutls_handshake(vs->tls.session)) < 0) {
-       if (!gnutls_error_is_fatal(ret)) {
-           VNC_DEBUG("Handshake interrupted (blocking)\n");
-           if (!gnutls_record_get_direction(vs->tls.session))
-               qemu_set_fd_handler(vs->csock, vnc_tls_handshake_io, NULL, vs);
-           else
-               qemu_set_fd_handler(vs->csock, NULL, vnc_tls_handshake_io, vs);
-           return 0;
-       }
-       VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret));
-       vnc_client_error(vs);
-       return -1;
+        if (!gnutls_error_is_fatal(ret)) {
+            GIOCondition condition;
+            VNC_DEBUG("Handshake interrupted (blocking)\n");
+            if (!gnutls_record_get_direction(vs->tls.session)) {
+                condition = G_IO_IN;
+            } else {
+                condition = G_IO_OUT;
+            }
+            if (vs->ioc_tag) {
+                g_source_remove(vs->ioc_tag);
+            }
+            vs->ioc_tag = qio_channel_add_watch(
+                vs->ioc, condition, vnc_tls_handshake_io, vs);
+            return 0;
+        }
+        VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret));
+        vnc_client_error(vs);
+        return -1;
     }
 
     if (vs->vd->tls.x509verify) {
@@ -93,18 +102,26 @@  static int vnc_start_vencrypt_handshake(struct VncState *vs) {
     }
 
     VNC_DEBUG("Handshake done, switching to TLS data mode\n");
-    qemu_set_fd_handler2(vs->csock, NULL, 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);
 
     start_auth_vencrypt_subauth(vs);
 
     return 0;
 }
 
-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)
+{
     struct VncState *vs = (struct VncState *)opaque;
 
     VNC_DEBUG("Handshake IO continue\n");
     vnc_start_vencrypt_handshake(vs);
+    return TRUE;
 }
 
 
diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c
index 333c35e..3262ac6 100644
--- a/ui/vnc-jobs.c
+++ b/ui/vnc-jobs.c
@@ -168,7 +168,7 @@  void vnc_jobs_consume_buffer(VncState *vs)
         vnc_write(vs, vs->jobs_buffer.buffer, vs->jobs_buffer.offset);
         qio_buffer_reset(&vs->jobs_buffer);
     }
-    flush = vs->csock != -1 && vs->abort != true;
+    flush = vs->ioc != NULL && vs->abort != true;
     vnc_unlock_output(vs);
 
     if (flush) {
@@ -193,7 +193,8 @@  static void vnc_async_encoding_start(VncState *orig, VncState *local)
     local->hextile = orig->hextile;
     local->zrle = orig->zrle;
     local->output =  queue->buffer;
-    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 */
 
     qio_buffer_reset(&local->output);
 }
@@ -230,7 +231,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;
     }
@@ -250,7 +251,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);
@@ -272,8 +273,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) {
         qio_buffer_reserve(&job->vs->jobs_buffer, vs.output.offset);
         qio_buffer_append(&job->vs->jobs_buffer, vs.output.buffer,
                           vs.output.offset);
diff --git a/ui/vnc-tls.c b/ui/vnc-tls.c
index eddd39b..17e1c8f 100644
--- a/ui/vnc-tls.c
+++ b/ui/vnc-tls.c
@@ -69,13 +69,13 @@  static ssize_t vnc_tls_push(gnutls_transport_ptr_t transport,
                             const void *data,
                             size_t len) {
     struct VncState *vs = (struct VncState *)transport;
-    int ret;
-
- retry:
-    ret = send(vs->csock, data, len, 0);
+    ssize_t ret = qio_channel_write(vs->ioc, data, len, NULL);
     if (ret < 0) {
-        if (errno == EINTR)
-            goto retry;
+        if (ret == QIO_CHANNEL_ERR_BLOCK) {
+            errno = EAGAIN;
+        } else {
+            errno = EIO;
+        }
         return -1;
     }
     return ret;
@@ -86,13 +86,13 @@  static ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport,
                             void *data,
                             size_t len) {
     struct VncState *vs = (struct VncState *)transport;
-    int ret;
-
- retry:
-    ret = qemu_recv(vs->csock, data, len, 0);
+    ssize_t ret = qio_channel_read(vs->ioc, data, len, NULL);
     if (ret < 0) {
-        if (errno == EINTR)
-            goto retry;
+        if (ret == QIO_CHANNEL_ERR_BLOCK) {
+            errno = EAGAIN;
+        } else {
+            errno = EIO;
+        }
         return -1;
     }
     return ret;
diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c
index d3ca97e..ad22694 100644
--- a/ui/vnc-ws.c
+++ b/ui/vnc-ws.c
@@ -25,20 +25,26 @@ 
 #ifdef CONFIG_VNC_TLS
 #include "qemu/sockets.h"
 
+static void vncws_handshake_read(VncState *vs);
+
 static int vncws_start_tls_handshake(struct VncState *vs)
 {
     int ret = gnutls_handshake(vs->tls.session);
 
     if (ret < 0) {
         if (!gnutls_error_is_fatal(ret)) {
+            GIOCondition condition;
             VNC_DEBUG("Handshake interrupted (blocking)\n");
             if (!gnutls_record_get_direction(vs->tls.session)) {
-                qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io,
-                                    NULL, vs);
+                condition = G_IO_IN;
             } else {
-                qemu_set_fd_handler(vs->csock, NULL, vncws_tls_handshake_io,
-                                    vs);
+                condition = G_IO_OUT;
+            }
+            if (vs->ioc_tag) {
+                g_source_remove(vs->ioc_tag);
             }
+            vs->ioc_tag = qio_channel_add_watch(
+                vs->ioc, condition, vncws_tls_handshake_io, vs);
             return 0;
         }
         VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret));
@@ -57,29 +63,35 @@  static int vncws_start_tls_handshake(struct VncState *vs)
     }
 
     VNC_DEBUG("Handshake done, switching to TLS data mode\n");
-    qemu_set_fd_handler2(vs->csock, NULL, 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);
 
     return 0;
 }
 
-void vncws_tls_handshake_io(void *opaque)
+gboolean vncws_tls_handshake_io(QIOChannel *ioc G_GNUC_UNUSED,
+                                GIOCondition condition G_GNUC_UNUSED,
+                                void *opaque)
 {
     struct VncState *vs = (struct VncState *)opaque;
 
     if (!vs->tls.session) {
         VNC_DEBUG("TLS Websocket setup\n");
         if (vnc_tls_client_setup(vs, vs->vd->tls.x509cert != NULL) < 0) {
-            return;
+            return TRUE;
         }
     }
     VNC_DEBUG("Handshake IO continue\n");
     vncws_start_tls_handshake(vs);
+    return TRUE;
 }
 #endif /* CONFIG_VNC_TLS */
 
-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
@@ -89,7 +101,7 @@  void vncws_handshake_read(void *opaque)
     ret = vnc_client_read_buf(vs, qio_buffer_end(&vs->ws_input), want);
 
     if (!ret) {
-        if (vs->csock == -1) {
+        if (vs->disconnecting) {
             vnc_disconnect_finish(vs);
         }
         return;
@@ -99,7 +111,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_handler2(vs->csock, NULL, 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);
         vncws_process_handshake(vs, vs->ws_input.buffer, vs->ws_input.offset);
         qio_buffer_advance(&vs->ws_input, handshake_end - vs->ws_input.buffer +
                 strlen(WS_HANDSHAKE_END));
@@ -110,6 +126,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;
@@ -177,7 +202,11 @@  long vnc_client_write_ws(VncState *vs)
     qio_buffer_advance(&vs->ws_output, ret);
 
     if (vs->ws_output.offset == 0) {
-        qemu_set_fd_handler2(vs->csock, NULL, 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);
     }
 
     return ret;
diff --git a/ui/vnc-ws.h b/ui/vnc-ws.h
index 5437102..2e79324 100644
--- a/ui/vnc-ws.h
+++ b/ui/vnc-ws.h
@@ -73,9 +73,13 @@  enum {
 };
 
 #ifdef CONFIG_VNC_TLS
-void vncws_tls_handshake_io(void *opaque);
+gboolean vncws_tls_handshake_io(QIOChannel *ioc,
+                                GIOCondition condition,
+                                void *opaque);
 #endif /* CONFIG_VNC_TLS */
-void vncws_handshake_read(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);
diff --git a/ui/vnc.c b/ui/vnc.c
index af1f792..6c08711 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -40,6 +40,8 @@ 
 #include "qapi-event.h"
 #include "crypto/hash.h"
 
+#include <glib/gi18n.h>
+
 #define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT
 #define VNC_REFRESH_INTERVAL_INC  50
 #define VNC_REFRESH_INTERVAL_MAX  GUI_REFRESH_INTERVAL_IDLE
@@ -65,8 +67,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) {
@@ -100,105 +102,74 @@  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;
+char *
+vnc_socket_local_addr(QIOChannelSocket *ioc,
+                      const char *format,
+                      Error **errp)
+{
+    char *host, *serv, *ret;
+    NetworkAddressFamily family;
 
-    salen = sizeof(sa);
-    if (getpeername(fd, (struct sockaddr*)&sa, &salen) < 0)
+    if (qio_channel_socket_get_local_addr_string(
+            ioc, &host, &serv, &family, errp) < 0) {
         return NULL;
+    }
 
-    return addr_to_string(format, &sa, salen);
+    ret = g_strdup_printf(format, host, serv);
+    g_free(host);
+    g_free(serv);
+    return ret;
 }
 
-static VncBasicInfo *vnc_basic_info_get(struct sockaddr_storage *sa,
-                                        socklen_t salen)
+char *
+vnc_socket_remote_addr(QIOChannelSocket *ioc,
+                       const char *format,
+                       Error **errp)
 {
-    VncBasicInfo *info;
-    char host[NI_MAXHOST];
-    char serv[NI_MAXSERV];
-    int err;
-
-    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));
+    char *host, *serv, *ret;
+    NetworkAddressFamily family;
+
+    if (qio_channel_socket_get_remote_addr_string(
+            ioc, &host, &serv, &family, errp) < 0) {
         return NULL;
     }
 
-    info = g_malloc0(sizeof(VncBasicInfo));
-    info->host = g_strdup(host);
-    info->service = g_strdup(serv);
-    info->family = inet_netfamily(sa->ss_family);
-    return info;
+    ret = g_strdup_printf(format, host, serv);
+    g_free(host);
+    g_free(serv);
+    return ret;
 }
 
-static VncBasicInfo *vnc_basic_info_get_from_server_addr(int fd)
+
+static VncBasicInfo *
+vnc_basic_info_get_from_server_addr(QIOChannelSocket *ioc,
+                                    Error **errp)
 {
-    struct sockaddr_storage sa;
-    socklen_t salen;
+    VncBasicInfo *info = g_new0(VncBasicInfo, 1);
 
-    salen = sizeof(sa);
-    if (getsockname(fd, (struct sockaddr*)&sa, &salen) < 0) {
+    if (qio_channel_socket_get_local_addr_string(
+            ioc, &info->host, &info->service,
+            &info->family, errp) < 0) {
+        g_free(info);
         return NULL;
     }
-
-    return vnc_basic_info_get(&sa, salen);
+    return info;
 }
 
-static VncBasicInfo *vnc_basic_info_get_from_remote_addr(int fd)
+static VncBasicInfo *
+vnc_basic_info_get_from_remote_addr(QIOChannelSocket *ioc,
+                                    Error **errp)
 {
-    struct sockaddr_storage sa;
-    socklen_t salen;
+    VncBasicInfo *info = g_new0(VncBasicInfo, 1);
 
-    salen = sizeof(sa);
-    if (getpeername(fd, (struct sockaddr*)&sa, &salen) < 0) {
+    if (qio_channel_socket_get_remote_addr_string(
+            ioc, &info->host, &info->service,
+            &info->family, errp) < 0) {
+        g_free(info);
         return NULL;
     }
-
-    return vnc_basic_info_get(&sa, salen);
+    return info;
 }
 
 static const char *vnc_auth_name(VncDisplay *vd) {
@@ -255,8 +226,11 @@  static const char *vnc_auth_name(VncDisplay *vd) {
 static VncServerInfo *vnc_server_info_get(VncDisplay *vd)
 {
     VncServerInfo *info;
-    VncBasicInfo *bi = vnc_basic_info_get_from_server_addr(vd->lsock);
+    Error *err = NULL;
+    VncBasicInfo *bi = vnc_basic_info_get_from_server_addr(
+        vd->lsock, &err);
     if (!bi) {
+        error_free(err);
         return NULL;
     }
 
@@ -291,11 +265,15 @@  static void vnc_client_cache_auth(VncState *client)
 
 static void vnc_client_cache_addr(VncState *client)
 {
-    VncBasicInfo *bi = vnc_basic_info_get_from_remote_addr(client->csock);
+    Error *err = NULL;
+    VncBasicInfo *bi = vnc_basic_info_get_from_remote_addr(
+        client->sioc, &err);
 
     if (bi) {
         client->info = g_malloc0(sizeof(*client->info));
         client->info->base = bi;
+    } else {
+        error_free(err);
     }
 }
 
@@ -332,28 +310,19 @@  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;
+    VncBasicInfo *binfo;
+    Error *err = NULL;
 
-    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) {
+    binfo = vnc_basic_info_get_from_remote_addr(
+        client->sioc, &err);
+    if (!binfo) {
+        error_free(err);
         return NULL;
     }
 
     info = g_malloc0(sizeof(*info));
-    info->base = g_malloc0(sizeof(*info->base));
-    info->base->host = g_strdup(host);
-    info->base->service = g_strdup(serv);
-    info->base->family = inet_netfamily(sa.ss_family);
+    info->base = binfo;
     info->base->websocket = client->websocket;
 
 #ifdef CONFIG_VNC_TLS
@@ -409,43 +378,25 @@  VncInfo *qmp_query_vnc(Error **errp)
     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_set(errp, QERR_UNDEFINED_ERROR);
-            goto out_error;
-        }
-
-        if (getnameinfo((struct sockaddr *)&sa, salen,
-                        host, sizeof(host),
-                        serv, sizeof(serv),
-                        NI_NUMERICHOST | NI_NUMERICSERV) < 0) {
-            error_set(errp, QERR_UNDEFINED_ERROR);
+        if (qio_channel_socket_get_local_addr_string(
+                vd->lsock, &info->host, &info->service,
+                &info->family, errp) < 0) {
             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));
@@ -458,28 +409,22 @@  out_error:
     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];
-
-    if (getsockname(socket, (struct sockaddr *)&sa, &salen) < 0 ||
-        getnameinfo((struct sockaddr *)&sa, salen,
-                    host, sizeof(host), serv, sizeof(serv),
-                    NI_NUMERICHOST | NI_NUMERICSERV) < 0) {
-        return prev;
-    }
+    Error *err = NULL;
 
     info = g_new0(VncBasicInfo, 1);
-    info->host = g_strdup(host);
-    info->service = g_strdup(serv);
-    info->family = inet_netfamily(sa.ss_family);
+    if (qio_channel_socket_get_local_addr_string(
+            ioc, &info->host, &info->service,
+            &info->family, &err) < 0) {
+        error_free(err);
+        g_free(info);
+        return prev;
+    }
     info->websocket = websocket;
 
     list = g_new0(VncBasicInfoList, 1);
@@ -575,13 +520,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);
@@ -654,7 +599,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) &&
@@ -1018,7 +963,7 @@  static int find_and_clear_dirty_height(struct 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;
@@ -1082,7 +1027,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);
@@ -1164,12 +1109,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_handler2(vs->csock, NULL, 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)
@@ -1220,29 +1168,28 @@  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);
 }
 
-int vnc_client_io_error(VncState *vs, int ret, int last_errno)
+int vnc_client_io_error(VncState *vs, int 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 %d, errno %d\n",
-                  ret, ret < 0 ? last_errno : 0);
         vnc_disconnect_start(vs);
-
+        if (errp) {
+            error_free(*errp);
+            *errp = NULL;
+        }
         return 0;
     }
     return ret;
@@ -1256,18 +1203,20 @@  void vnc_client_error(VncState *vs)
 }
 
 #ifdef CONFIG_VNC_TLS
-static long vnc_client_write_tls(gnutls_session_t *session,
-                                 const uint8_t *data,
-                                 size_t datalen)
+static ssize_t vnc_client_write_tls(gnutls_session_t *session,
+                                    const uint8_t *data,
+                                    size_t datalen,
+                                    Error **errp)
 {
-    long ret = gnutls_write(*session, data, datalen);
+    ssize_t ret = gnutls_write(*session, data, datalen);
     if (ret < 0) {
         if (ret == GNUTLS_E_AGAIN) {
-            errno = EAGAIN;
+            ret = -2;
         } else {
-            errno = EIO;
+            ret = -1;
+            error_setg_errno(errp, -EIO, "%s",
+                             _("Cannot write to TLS socket"));
         }
-        ret = -1;
     }
     return ret;
 }
@@ -1288,20 +1237,22 @@  static long vnc_client_write_tls(gnutls_session_t *session,
  * the requested 'datalen' if the socket would block. Returns
  * -1 on error, and disconnects the client socket.
  */
-long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen)
+ssize_t vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen)
 {
-    long ret;
+    ssize_t ret;
+    Error *err = NULL;
 #ifdef CONFIG_VNC_TLS
     if (vs->tls.session) {
-        ret = vnc_client_write_tls(&vs->tls.session, data, datalen);
+        ret = vnc_client_write_tls(&vs->tls.session, data, datalen, &err);
     } else {
 #endif /* CONFIG_VNC_TLS */
-        ret = send(vs->csock, (const void *)data, datalen, 0);
+        ret = qio_channel_write(
+            vs->ioc, (const char *)data, datalen, &err);
 #ifdef CONFIG_VNC_TLS
     }
 #endif /* CONFIG_VNC_TLS */
     VNC_DEBUG("Wrote wire %p %zd -> %ld\n", data, datalen, ret);
-    return vnc_client_io_error(vs, ret, socket_error());
+    return vnc_client_io_error(vs, ret, &err);
 }
 
 
@@ -1315,9 +1266,9 @@  long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen)
  * the buffered output data if the socket would block. Returns
  * -1 on error, and disconnects the client socket.
  */
-static long vnc_client_write_plain(VncState *vs)
+static ssize_t vnc_client_write_plain(VncState *vs)
 {
-    long ret;
+    ssize_t ret;
 
 #ifdef CONFIG_VNC_SASL
     VNC_DEBUG("Write Plain: Pending output %p size %zd offset %zd. Wait SSF %d\n",
@@ -1339,7 +1290,11 @@  static long vnc_client_write_plain(VncState *vs)
     qio_buffer_advance(&vs->output, ret);
 
     if (vs->output.offset == 0) {
-        qemu_set_fd_handler2(vs->csock, NULL, 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);
     }
 
     return ret;
@@ -1351,10 +1306,8 @@  static long 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 &&
@@ -1371,15 +1324,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_handler2(vs->csock, NULL, 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);
     }
     vnc_unlock_output(vs);
 }
@@ -1391,17 +1347,18 @@  void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting)
 }
 
 #ifdef CONFIG_VNC_TLS
-static long vnc_client_read_tls(gnutls_session_t *session, uint8_t *data,
-                                size_t datalen)
+static ssize_t vnc_client_read_tls(gnutls_session_t *session, uint8_t *data,
+                                   size_t datalen, Error **errp)
 {
-    long ret = gnutls_read(*session, data, datalen);
+    ssize_t ret = gnutls_read(*session, data, datalen);
     if (ret < 0) {
         if (ret == GNUTLS_E_AGAIN) {
-            errno = EAGAIN;
+            ret = QIO_CHANNEL_ERR_BLOCK;
         } else {
-            errno = EIO;
+            ret = -1;
+            error_setg_errno(errp, -EIO, "%s",
+                             _("Cannot read from TLS socket"));
         }
-        ret = -1;
     }
     return ret;
 }
@@ -1422,20 +1379,22 @@  static long vnc_client_read_tls(gnutls_session_t *session, uint8_t *data,
  * the requested 'datalen' if the socket would block. Returns
  * -1 on error, and disconnects the client socket.
  */
-long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen)
+ssize_t vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen)
 {
-    long ret;
+    ssize_t ret;
+    Error *err = NULL;
 #ifdef CONFIG_VNC_TLS
     if (vs->tls.session) {
-        ret = vnc_client_read_tls(&vs->tls.session, data, datalen);
+        ret = vnc_client_read_tls(&vs->tls.session, data, datalen, &err);
     } else {
 #endif /* CONFIG_VNC_TLS */
-        ret = qemu_recv(vs->csock, data, datalen, 0);
+        ret = qio_channel_read(
+            vs->ioc, (char *)data, datalen, &err);
 #ifdef CONFIG_VNC_TLS
     }
 #endif /* CONFIG_VNC_TLS */
     VNC_DEBUG("Read wire %p %zd -> %ld\n", data, datalen, ret);
-    return vnc_client_io_error(vs, ret, socket_error());
+    return vnc_client_io_error(vs, ret, &err);
 }
 
 
@@ -1447,7 +1406,7 @@  long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen)
  * Returns the number of bytes read. Returns -1 on error, and
  * disconnects the client socket.
  */
-static long vnc_client_read_plain(VncState *vs)
+static ssize_t vnc_client_read_plain(VncState *vs)
 {
     int ret;
     VNC_DEBUG("Read plain %p size %zd offset %zd\n",
@@ -1472,10 +1431,9 @@  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;
-    long ret;
+    ssize_t ret;
 
 #ifdef CONFIG_VNC_SASL
     if (vs->sasl.conn && vs->sasl.runSSF)
@@ -1495,8 +1453,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;
     }
 
@@ -1505,7 +1464,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;
         }
@@ -1518,12 +1477,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)
 {
     qio_buffer_reserve(&vs->output, len);
 
-    if (vs->csock != -1 && qio_buffer_empty(&vs->output)) {
-        qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs);
+    if (vs->ioc != NULL && qio_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);
     }
 
     qio_buffer_append(&vs->output, data, len);
@@ -1564,8 +1541,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);
@@ -2946,13 +2922,16 @@  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_malloc0(sizeof(VncState));
     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;
 
     if (skipauth) {
@@ -2975,23 +2954,24 @@  static void vnc_connect(VncDisplay *vd, int csock,
         vs->lossy_rect[i] = g_malloc0(VNC_STAT_COLS * sizeof (uint8_t));
     }
 
-    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);
     if (websocket) {
         vs->websocket = 1;
 #ifdef CONFIG_VNC_TLS
         if (vd->ws_tls) {
-            qemu_set_fd_handler2(vs->csock, NULL, vncws_tls_handshake_io,
-                                 NULL, vs);
+            vs->ioc_tag = qio_channel_add_watch(
+                vs->ioc, G_IO_IN, vncws_tls_handshake_io, vs);
         } else
 #endif /* CONFIG_VNC_TLS */
         {
-            qemu_set_fd_handler2(vs->csock, NULL, vncws_handshake_read,
-                                 NULL, vs);
+            vs->ioc_tag = qio_channel_add_watch(
+                vs->ioc, G_IO_IN, vncws_handshake_io, vs);
         }
     } else {
-        qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+        vs->ioc_tag = qio_channel_add_watch(
+            vs->ioc, G_IO_IN, vnc_client_io, vs);
     }
 
     vnc_client_cache_addr(vs);
@@ -3045,35 +3025,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_socket_set_nodelay(sioc, true);
+        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 = {
@@ -3099,9 +3072,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;
 
@@ -3129,16 +3099,20 @@  static void vnc_display_close(VncDisplay *vs)
         return;
     vs->enabled = false;
     vs->is_unix = false;
-    if (vs->lsock != -1) {
-        qemu_set_fd_handler2(vs->lsock, NULL, 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_handler2(vs->lwebsock, NULL, 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;
@@ -3183,7 +3157,7 @@  char *vnc_display_local_addr(const char *id)
     VncDisplay *vs = vnc_display_find(id);
 
     assert(vs);
-    return vnc_socket_local_addr("%s:%s", vs->lsock);
+    return vnc_socket_local_addr(vs->lsock, "%s:%s", NULL);
 }
 
 static QemuOptsList qemu_vnc_opts = {
@@ -3408,6 +3382,7 @@  void vnc_display_open(const char *id, Error **errp)
     int acl = 0;
 #endif
     int lock_key_sync = 1;
+    Error *err = NULL;
 
     if (!vs) {
         error_setg(errp, "VNC display not active");
@@ -3613,8 +3588,9 @@  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 (strncmp(vnc, "unix:", 5) == 0) {
             csock = unix_connect(vnc+5, errp);
         } else {
@@ -3623,34 +3599,58 @@  void vnc_display_open(const char *id, Error **errp)
         if (csock < 0) {
             goto fail;
         }
-        vnc_connect(vs, csock, false, false);
+        sioc = qio_channel_socket_new_fd(csock, &err);
+        if (!sioc) {
+            close(csock);
+            goto fail;
+        }
+        vnc_connect(vs, sioc, false, false);
+        object_unref(OBJECT(sioc));
     } else {
         /* listen for connects */
+        char *dpy;
+        int lsock;
+        dpy = g_malloc(256);
         if (strncmp(vnc, "unix:", 5) == 0) {
-            vs->lsock = unix_listen(vnc+5, NULL, 0, errp);
+            lsock = unix_listen(vnc+5, NULL, 0, errp);
             vs->is_unix = true;
         } else {
-            vs->lsock = inet_listen_opts(sopts, 5900, errp);
-            if (vs->lsock < 0) {
+            lsock = inet_listen_opts(sopts, 5900, errp);
+            if (lsock < 0) {
+                g_free(dpy);
                 goto fail;
             }
             if (vs->ws_enabled) {
-                vs->lwebsock = inet_listen_opts(wsopts, 0, errp);
-                if (vs->lwebsock < 0) {
-                    if (vs->lsock != -1) {
-                        close(vs->lsock);
-                        vs->lsock = -1;
-                    }
+                int lwebsock;
+                lwebsock = inet_listen_opts(wsopts, 0, errp);
+                if (lwebsock < 0) {
+                    close(lsock);
+                    lsock = -1;
                     goto fail;
                 }
+                vs->lwebsock = qio_channel_socket_new_fd(lwebsock, &err);
+                if (!vs->lwebsock) {
+                    close(lsock);
+                    close(lwebsock);
+                    lwebsock = lsock = -1;
+                }
             }
         }
         vs->enabled = true;
-        qemu_set_fd_handler2(vs->lsock, NULL,
-                vnc_listen_regular_read, NULL, vs);
+        vs->lsock = qio_channel_socket_new_fd(lsock, &err);
+        if (!vs->lsock) {
+            object_unref(OBJECT(vs->lwebsock));
+            vs->lwebsock = NULL;
+            close(lsock);
+            goto fail;
+        }
+        vs->lsock_tag = qio_channel_add_watch(
+            QIO_CHANNEL(vs->lsock),
+            G_IO_IN, vnc_listen_io, vs);
         if (vs->ws_enabled) {
-            qemu_set_fd_handler2(vs->lwebsock, NULL,
-                    vnc_listen_websocket_read, NULL, vs);
+            vs->lwebsock_tag = qio_channel_add_watch(
+                QIO_CHANNEL(vs->lwebsock),
+                G_IO_IN, vnc_listen_io, vs);
         }
     }
     qemu_opts_del(sopts);
@@ -3662,16 +3662,27 @@  fail:
     qemu_opts_del(wsopts);
     vs->enabled = false;
     vs->ws_enabled = false;
+    if (err) {
+        fprintf(stderr, "Unable to initialize VNC: %s\n",
+                error_get_pretty(err));
+        error_free(err);
+    }
 }
 
 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)
diff --git a/ui/vnc.h b/ui/vnc.h
index f44a949..593e6b6 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -34,6 +34,7 @@ 
 #include "audio/audio.h"
 #include "qemu/bitmap.h"
 #include "io/buffer.h"
+#include "io/channel-socket.h"
 #include <zlib.h>
 #include <stdbool.h>
 
@@ -147,8 +148,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;
@@ -251,7 +254,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
@@ -504,11 +510,12 @@  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);
 
-long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen);
-long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen);
+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);
 
 /* Protocol I/O functions */
 void vnc_write(VncState *vs, const void *data, size_t len);
@@ -527,7 +534,7 @@  uint32_t read_u32(uint8_t *data, size_t offset);
 
 /* Protocol stage functions */
 void vnc_client_error(VncState *vs);
-int vnc_client_io_error(VncState *vs, int ret, int last_errno);
+int vnc_client_io_error(VncState *vs, int ret, Error **errp);
 
 void start_client_init(VncState *vs);
 void start_auth_vnc(VncState *vs);
@@ -535,8 +542,14 @@  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);
+char *
+vnc_socket_local_addr(QIOChannelSocket *ioc,
+                      const char *format,
+                      Error **errp);
+char *
+vnc_socket_remote_addr(QIOChannelSocket *ioc,
+                       const char *format,
+                       Error **errp);
 
 static inline uint32_t vnc_has_feature(VncState *vs, int feature) {
     return (vs->features & (1 << feature));