Commit 04d2529d authored by Daniel P. Berrangé's avatar Daniel P. Berrangé
Browse files

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: default avatarGerd Hoffmann <kraxel@redhat.com>
Signed-off-by: default avatarDaniel P. Berrange <berrange@redhat.com>
parent 18f49881
Loading
Loading
Loading
Loading
+45 −12
Original line number Diff line number Diff line
@@ -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;
    }
+22 −5
Original line number Diff line number Diff line
@@ -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;
}


+12 −8
Original line number Diff line number Diff line
@@ -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);
+41 −10
Original line number Diff line number Diff line
@@ -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;
+6 −2
Original line number Diff line number Diff line
@@ -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);
Loading