Skip to content

Commit

Permalink
ui: convert VNC server to use QIOChannelSocket
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
berrange committed Dec 18, 2015
1 parent 18f4988 commit 04d2529
Show file tree
Hide file tree
Showing 7 changed files with 433 additions and 326 deletions.
57 changes: 45 additions & 12 deletions ui/vnc-auth-sasl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}
Expand Down
27 changes: 22 additions & 5 deletions ui/vnc-auth-vencrypt.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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;
}

Expand All @@ -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;
}


Expand Down
20 changes: 12 additions & 8 deletions ui/vnc-jobs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down
51 changes: 41 additions & 10 deletions ui/vnc-ws.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -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;
Expand All @@ -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,
Expand All @@ -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
Expand All @@ -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;
Expand All @@ -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));
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
8 changes: 6 additions & 2 deletions ui/vnc-ws.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading

0 comments on commit 04d2529

Please sign in to comment.