[Spice-commits] 17 commits - server/Makefile.am server/inputs_channel.c server/red_channel.c server/red_channel.h server/red_common.h server/red_worker.c server/reds.c server/reds.h server/reds_stream.c server/reds_stream.h

Christophe Fergau teuf at kemper.freedesktop.org
Mon Jan 20 03:17:58 PST 2014


 server/Makefile.am      |    2 
 server/inputs_channel.c |    3 
 server/red_channel.c    |    1 
 server/red_channel.h    |    2 
 server/red_common.h     |    2 
 server/red_worker.c     |    1 
 server/reds.c           |  945 +++----------------------------------------
 server/reds.h           |   66 ---
 server/reds_stream.c    | 1053 ++++++++++++++++++++++++++++++++++++++++++++++++
 server/reds_stream.h    |  103 ++++
 10 files changed, 1249 insertions(+), 929 deletions(-)

New commits:
commit 2ea58dd7a7ec2952e85814ead55d1694584aa471
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Tue Oct 22 16:41:42 2013 +0200

    Make RedsStream::info private

diff --git a/server/reds_stream.c b/server/reds_stream.c
index 46f519e..873b953 100644
--- a/server/reds_stream.c
+++ b/server/reds_stream.c
@@ -75,6 +75,12 @@ struct RedsStreamPrivate {
 
     AsyncRead async_read;
 
+    /* life time of info:
+     * allocated when creating RedsStream.
+     * deallocated when main_dispatcher handles the SPICE_CHANNEL_EVENT_DISCONNECTED
+     * event, either from same thread or by call back from main thread. */
+    SpiceChannelEventInfo* info;
+
     ssize_t (*read)(RedsStream *s, void *buf, size_t nbyte);
     ssize_t (*write)(RedsStream *s, const void *buf, size_t nbyte);
     ssize_t (*writev)(RedsStream *s, const struct iovec *iov, int iovcnt);
@@ -261,34 +267,34 @@ void reds_stream_free(RedsStream *s)
 
 void reds_stream_push_channel_event(RedsStream *s, int event)
 {
-    main_dispatcher_channel_event(event, s->info);
+    main_dispatcher_channel_event(event, s->priv->info);
 }
 
 static void reds_stream_set_socket(RedsStream *stream, int socket)
 {
     stream->socket = socket;
     /* deprecated fields. Filling them for backward compatibility */
-    stream->info->llen = sizeof(stream->info->laddr);
-    stream->info->plen = sizeof(stream->info->paddr);
-    getsockname(stream->socket, (struct sockaddr*)(&stream->info->laddr), &stream->info->llen);
-    getpeername(stream->socket, (struct sockaddr*)(&stream->info->paddr), &stream->info->plen);
-
-    stream->info->flags |= SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT;
-    stream->info->llen_ext = sizeof(stream->info->laddr_ext);
-    stream->info->plen_ext = sizeof(stream->info->paddr_ext);
-    getsockname(stream->socket, (struct sockaddr*)(&stream->info->laddr_ext),
-                &stream->info->llen_ext);
-    getpeername(stream->socket, (struct sockaddr*)(&stream->info->paddr_ext),
-                &stream->info->plen_ext);
+    stream->priv->info->llen = sizeof(stream->priv->info->laddr);
+    stream->priv->info->plen = sizeof(stream->priv->info->paddr);
+    getsockname(stream->socket, (struct sockaddr*)(&stream->priv->info->laddr), &stream->priv->info->llen);
+    getpeername(stream->socket, (struct sockaddr*)(&stream->priv->info->paddr), &stream->priv->info->plen);
+
+    stream->priv->info->flags |= SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT;
+    stream->priv->info->llen_ext = sizeof(stream->priv->info->laddr_ext);
+    stream->priv->info->plen_ext = sizeof(stream->priv->info->paddr_ext);
+    getsockname(stream->socket, (struct sockaddr*)(&stream->priv->info->laddr_ext),
+                &stream->priv->info->llen_ext);
+    getpeername(stream->socket, (struct sockaddr*)(&stream->priv->info->paddr_ext),
+                &stream->priv->info->plen_ext);
 }
 
 
 void reds_stream_set_channel(RedsStream *stream, int connection_id,
                              int channel_type, int channel_id)
 {
-    stream->info->connection_id = connection_id;
-    stream->info->type = channel_type;
-    stream->info->id   = channel_id;
+    stream->priv->info->connection_id = connection_id;
+    stream->priv->info->type = channel_type;
+    stream->priv->info->id   = channel_id;
 }
 
 RedsStream *reds_stream_new(int socket)
@@ -297,7 +303,7 @@ RedsStream *reds_stream_new(int socket)
 
     stream = spice_malloc0(sizeof(RedsStream) + sizeof(RedsStreamPrivate));
     stream->priv = (RedsStreamPrivate *)(((char *)stream) + sizeof(RedsStream));
-    stream->info = spice_new0(SpiceChannelEventInfo, 1);
+    stream->priv->info = spice_new0(SpiceChannelEventInfo, 1);
     reds_stream_set_socket(stream, socket);
 
     stream->priv->read = stream_read_cb;
@@ -319,7 +325,7 @@ void reds_stream_set_info_flag(RedsStream *stream, unsigned int flag)
     g_return_if_fail((flag == SPICE_CHANNEL_EVENT_FLAG_TLS)
                      || (flag == SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT));
 
-    stream->info->flags |= flag;
+    stream->priv->info->flags |= flag;
 }
 
 void reds_stream_disable_writev(RedsStream *stream)
@@ -571,14 +577,14 @@ static char *addr_to_string(const char *format,
 
 static char *reds_stream_get_local_address(RedsStream *stream)
 {
-    return addr_to_string("%s;%s", &stream->info->laddr_ext,
-                          stream->info->llen_ext);
+    return addr_to_string("%s;%s", &stream->priv->info->laddr_ext,
+                          stream->priv->info->llen_ext);
 }
 
 static char *reds_stream_get_remote_address(RedsStream *stream)
 {
-    return addr_to_string("%s;%s", &stream->info->paddr_ext,
-                          stream->info->plen_ext);
+    return addr_to_string("%s;%s", &stream->priv->info->paddr_ext,
+                          stream->priv->info->plen_ext);
 }
 
 static int auth_sasl_check_ssf(RedsSASL *sasl, int *runSSF)
diff --git a/server/reds_stream.h b/server/reds_stream.h
index 64657c2..ae3403a 100644
--- a/server/reds_stream.h
+++ b/server/reds_stream.h
@@ -53,12 +53,6 @@ struct RedsStream {
        receive may return data afterward. check the flag before calling receive*/
     int shutdown;
 
-    /* life time of info:
-     * allocated when creating RedsStream.
-     * deallocated when main_dispatcher handles the SPICE_CHANNEL_EVENT_DISCONNECTED
-     * event, either from same thread or by call back from main thread. */
-    SpiceChannelEventInfo* info;
-
     RedsStreamPrivate *priv;
 };
 
commit 28fa3b1b3fbc554dbbaf8d2be0fe789846b7aa42
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Mon Jan 6 22:38:44 2014 +0100

    Introduce reds_stream_set_channel()

diff --git a/server/reds.c b/server/reds.c
index 5bcad7a..1f02553 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -1417,9 +1417,9 @@ static void reds_info_new_channel(RedLinkInfo *link, int connection_id)
     if (reds_stream_is_ssl(link->stream)) {
         reds_stream_set_info_flag(link->stream, SPICE_CHANNEL_EVENT_FLAG_TLS);
     }
-    link->stream->info->connection_id = connection_id;
-    link->stream->info->type = link->link_mess->channel_type;
-    link->stream->info->id   = link->link_mess->channel_id;
+    reds_stream_set_channel(link->stream, connection_id,
+                            link->link_mess->channel_type,
+                            link->link_mess->channel_id);
     reds_stream_push_channel_event(link->stream, SPICE_CHANNEL_EVENT_INITIALIZED);
 }
 
diff --git a/server/reds_stream.c b/server/reds_stream.c
index d2c05d9..46f519e 100644
--- a/server/reds_stream.c
+++ b/server/reds_stream.c
@@ -282,6 +282,15 @@ static void reds_stream_set_socket(RedsStream *stream, int socket)
                 &stream->info->plen_ext);
 }
 
+
+void reds_stream_set_channel(RedsStream *stream, int connection_id,
+                             int channel_type, int channel_id)
+{
+    stream->info->connection_id = connection_id;
+    stream->info->type = channel_type;
+    stream->info->id   = channel_id;
+}
+
 RedsStream *reds_stream_new(int socket)
 {
     RedsStream *stream;
diff --git a/server/reds_stream.h b/server/reds_stream.h
index add9b2e..64657c2 100644
--- a/server/reds_stream.h
+++ b/server/reds_stream.h
@@ -81,6 +81,8 @@ void reds_stream_free(RedsStream *s);
 
 void reds_stream_push_channel_event(RedsStream *s, int event);
 void reds_stream_remove_watch(RedsStream* s);
+void reds_stream_set_channel(RedsStream *stream, int connection_id,
+                             int channel_type, int channel_id);
 RedsStream *reds_stream_new(int socket);
 bool reds_stream_is_ssl(RedsStream *stream);
 RedsStreamSslStatus reds_stream_ssl_accept(RedsStream *stream);
commit 82511418a08a55fdea3348c788b3a71e179ecd0f
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Mon Jan 6 22:38:26 2014 +0100

    Introduce reds_stream_set_info_flag()

diff --git a/server/reds.c b/server/reds.c
index 7fc48f1..5bcad7a 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -1415,7 +1415,7 @@ static void reds_info_new_channel(RedLinkInfo *link, int connection_id)
                reds_stream_is_ssl(link->stream) ? "Secure" : "Non Secure");
     /* add info + send event */
     if (reds_stream_is_ssl(link->stream)) {
-        link->stream->info->flags |= SPICE_CHANNEL_EVENT_FLAG_TLS;
+        reds_stream_set_info_flag(link->stream, SPICE_CHANNEL_EVENT_FLAG_TLS);
     }
     link->stream->info->connection_id = connection_id;
     link->stream->info->type = link->link_mess->channel_type;
diff --git a/server/reds_stream.c b/server/reds_stream.c
index 837d2cb..d2c05d9 100644
--- a/server/reds_stream.c
+++ b/server/reds_stream.c
@@ -29,6 +29,8 @@
 #include <unistd.h>
 #include <sys/socket.h>
 
+#include <glib.h>
+
 #include <openssl/err.h>
 
 extern SpiceCoreInterface *core;
@@ -303,6 +305,14 @@ bool reds_stream_is_ssl(RedsStream *stream)
     return (stream->priv->ssl != NULL);
 }
 
+void reds_stream_set_info_flag(RedsStream *stream, unsigned int flag)
+{
+    g_return_if_fail((flag == SPICE_CHANNEL_EVENT_FLAG_TLS)
+                     || (flag == SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT));
+
+    stream->info->flags |= flag;
+}
+
 void reds_stream_disable_writev(RedsStream *stream)
 {
     stream->priv->writev = NULL;
diff --git a/server/reds_stream.h b/server/reds_stream.h
index e2a6d61..add9b2e 100644
--- a/server/reds_stream.h
+++ b/server/reds_stream.h
@@ -85,6 +85,7 @@ RedsStream *reds_stream_new(int socket);
 bool reds_stream_is_ssl(RedsStream *stream);
 RedsStreamSslStatus reds_stream_ssl_accept(RedsStream *stream);
 int reds_stream_enable_ssl(RedsStream *stream, SSL_CTX *ctx);
+void reds_stream_set_info_flag(RedsStream *stream, unsigned int flag);
 
 typedef enum {
     REDS_SASL_ERROR_OK,
commit 25aca54f125a33c5c7f294a124a5aff1ca920b4c
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Mon Jan 6 22:55:18 2014 +0100

    Make RedsStream::async_read private

diff --git a/server/reds_stream.c b/server/reds_stream.c
index d0d8213..837d2cb 100644
--- a/server/reds_stream.c
+++ b/server/reds_stream.c
@@ -71,6 +71,8 @@ struct RedsStreamPrivate {
     RedsSASL sasl;
 #endif
 
+    AsyncRead async_read;
+
     ssize_t (*read)(RedsStream *s, void *buf, size_t nbyte);
     ssize_t (*write)(RedsStream *s, const void *buf, size_t nbyte);
     ssize_t (*writev)(RedsStream *s, const struct iovec *iov, int iovcnt);
@@ -291,7 +293,7 @@ RedsStream *reds_stream_new(int socket)
     stream->priv->write = stream_write_cb;
     stream->priv->writev = stream_writev_cb;
 
-    stream->async_read.stream = stream;
+    stream->priv->async_read.stream = stream;
 
     return stream;
 }
@@ -611,7 +613,7 @@ RedsSaslError reds_sasl_handle_auth_step(RedsStream *stream, AsyncReadDone read_
     char *clientdata = NULL;
     RedsSASL *sasl = &stream->priv->sasl;
     uint32_t datalen = sasl->len;
-    AsyncRead *obj = &stream->async_read;
+    AsyncRead *obj = &stream->priv->async_read;
 
     /* NB, distinction of NULL vs "" is *critical* in SASL */
     if (datalen) {
@@ -659,7 +661,7 @@ RedsSaslError reds_sasl_handle_auth_step(RedsStream *stream, AsyncReadDone read_
         obj->now = (uint8_t *)&sasl->len;
         obj->end = obj->now + sizeof(uint32_t);
         obj->done = read_cb;
-        async_read_handler(0, 0, &stream->async_read);
+        async_read_handler(0, 0, &stream->priv->async_read);
         return REDS_SASL_ERROR_CONTINUE;
     } else {
         int ssf;
@@ -691,7 +693,7 @@ authreject:
 
 RedsSaslError reds_sasl_handle_auth_steplen(RedsStream *stream, AsyncReadDone read_cb, void *opaque)
 {
-    AsyncRead *obj = &stream->async_read;
+    AsyncRead *obj = &stream->priv->async_read;
     RedsSASL *sasl = &stream->priv->sasl;
 
     spice_info("Got steplen %d", sasl->len);
@@ -734,7 +736,7 @@ RedsSaslError reds_sasl_handle_auth_steplen(RedsStream *stream, AsyncReadDone re
 
 RedsSaslError reds_sasl_handle_auth_start(RedsStream *stream, AsyncReadDone read_cb, void *opaque)
 {
-    AsyncRead *obj = &stream->async_read;
+    AsyncRead *obj = &stream->priv->async_read;
     const char *serverout;
     unsigned int serveroutlen;
     int err;
@@ -789,7 +791,7 @@ RedsSaslError reds_sasl_handle_auth_start(RedsStream *stream, AsyncReadDone read
         obj->now = (uint8_t *)&sasl->len;
         obj->end = obj->now + sizeof(uint32_t);
         obj->done = read_cb;
-        async_read_handler(0, 0, &stream->async_read);
+        async_read_handler(0, 0, &stream->priv->async_read);
         return REDS_SASL_ERROR_CONTINUE;
     } else {
         int ssf;
@@ -820,7 +822,7 @@ authreject:
 
 RedsSaslError reds_sasl_handle_auth_startlen(RedsStream *stream, AsyncReadDone read_cb, void *opaque)
 {
-    AsyncRead *obj = &stream->async_read;
+    AsyncRead *obj = &stream->priv->async_read;
     RedsSASL *sasl = &stream->priv->sasl;
 
     spice_info("Got client start len %d", sasl->len);
@@ -844,7 +846,7 @@ RedsSaslError reds_sasl_handle_auth_startlen(RedsStream *stream, AsyncReadDone r
 
 bool reds_sasl_handle_auth_mechname(RedsStream *stream, AsyncReadDone read_cb, void *opaque)
 {
-    AsyncRead *obj = &stream->async_read;
+    AsyncRead *obj = &stream->priv->async_read;
     RedsSASL *sasl = &stream->priv->sasl;
 
     sasl->mechname[sasl->len] = '\0';
@@ -879,14 +881,14 @@ bool reds_sasl_handle_auth_mechname(RedsStream *stream, AsyncReadDone read_cb, v
     obj->now = (uint8_t *)&sasl->len;
     obj->end = obj->now + sizeof(uint32_t);
     obj->done = read_cb;
-    async_read_handler(0, 0, &stream->async_read);
+    async_read_handler(0, 0, &stream->priv->async_read);
 
     return TRUE;
 }
 
 bool reds_sasl_handle_auth_mechlen(RedsStream *stream, AsyncReadDone read_cb, void *opaque)
 {
-    AsyncRead *obj = &stream->async_read;
+    AsyncRead *obj = &stream->priv->async_read;
     RedsSASL *sasl = &stream->priv->sasl;
 
     if (sasl->len < 1 || sasl->len > 100) {
@@ -900,7 +902,7 @@ bool reds_sasl_handle_auth_mechlen(RedsStream *stream, AsyncReadDone read_cb, vo
     obj->now = (uint8_t *)sasl->mechname;
     obj->end = obj->now + sasl->len;
     obj->done = read_cb;
-    async_read_handler(0, 0, &stream->async_read);
+    async_read_handler(0, 0, &stream->priv->async_read);
 
     return TRUE;
 }
@@ -912,7 +914,7 @@ bool reds_sasl_start_auth(RedsStream *stream, AsyncReadDone read_cb, void *opaqu
     int err;
     char *localAddr, *remoteAddr;
     int mechlistlen;
-    AsyncRead *obj = &stream->async_read;
+    AsyncRead *obj = &stream->priv->async_read;
     RedsSASL *sasl = &stream->priv->sasl;
 
     if (!(localAddr = reds_stream_get_local_address(stream))) {
diff --git a/server/reds_stream.h b/server/reds_stream.h
index c4d7208..e2a6d61 100644
--- a/server/reds_stream.h
+++ b/server/reds_stream.h
@@ -53,8 +53,6 @@ struct RedsStream {
        receive may return data afterward. check the flag before calling receive*/
     int shutdown;
 
-    AsyncRead async_read;
-
     /* life time of info:
      * allocated when creating RedsStream.
      * deallocated when main_dispatcher handles the SPICE_CHANNEL_EVENT_DISCONNECTED
commit dc04c076efd277d6df328c0dd9d96e6f25933295
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Mon Jan 6 22:54:32 2014 +0100

    Make RedsStream::sasl private

diff --git a/server/reds_stream.c b/server/reds_stream.c
index 7f6172f..d0d8213 100644
--- a/server/reds_stream.c
+++ b/server/reds_stream.c
@@ -33,9 +33,44 @@
 
 extern SpiceCoreInterface *core;
 
+#if HAVE_SASL
+#include <sasl/sasl.h>
+
+typedef struct RedsSASL {
+    sasl_conn_t *conn;
+
+    /* If we want to negotiate an SSF layer with client */
+    int wantSSF :1;
+    /* If we are now running the SSF layer */
+    int runSSF :1;
+
+    /*
+     * Buffering encoded data to allow more clear data
+     * to be stuffed onto the output buffer
+     */
+    const uint8_t *encoded;
+    unsigned int encodedLength;
+    unsigned int encodedOffset;
+
+    SpiceBuffer inbuffer;
+
+    char *username;
+    char *mechlist;
+    char *mechname;
+
+    /* temporary data during authentication */
+    unsigned int len;
+    char *data;
+} RedsSASL;
+#endif
+
 struct RedsStreamPrivate {
     SSL *ssl;
 
+#if HAVE_SASL
+    RedsSASL sasl;
+#endif
+
     ssize_t (*read)(RedsStream *s, void *buf, size_t nbyte);
     ssize_t (*write)(RedsStream *s, const void *buf, size_t nbyte);
     ssize_t (*writev)(RedsStream *s, const struct iovec *iov, int iovcnt);
@@ -124,7 +159,7 @@ ssize_t reds_stream_read(RedsStream *s, void *buf, size_t nbyte)
     ssize_t ret;
 
 #if HAVE_SASL
-    if (s->sasl.conn && s->sasl.runSSF) {
+    if (s->priv->sasl.conn && s->priv->sasl.runSSF) {
         ret = reds_stream_sasl_read(s, buf, nbyte);
     } else
 #endif
@@ -158,7 +193,7 @@ ssize_t reds_stream_write(RedsStream *s, const void *buf, size_t nbyte)
     ssize_t ret;
 
 #if HAVE_SASL
-    if (s->sasl.conn && s->sasl.runSSF) {
+    if (s->priv->sasl.conn && s->priv->sasl.runSSF) {
         ret = reds_stream_sasl_write(s, buf, nbyte);
     } else
 #endif
@@ -196,16 +231,16 @@ void reds_stream_free(RedsStream *s)
     reds_stream_push_channel_event(s, SPICE_CHANNEL_EVENT_DISCONNECTED);
 
 #if HAVE_SASL
-    if (s->sasl.conn) {
-        s->sasl.runSSF = s->sasl.wantSSF = 0;
-        s->sasl.len = 0;
-        s->sasl.encodedLength = s->sasl.encodedOffset = 0;
-        s->sasl.encoded = NULL;
-        free(s->sasl.mechlist);
-        free(s->sasl.mechname);
-        s->sasl.mechlist = NULL;
-        sasl_dispose(&s->sasl.conn);
-        s->sasl.conn = NULL;
+    if (s->priv->sasl.conn) {
+        s->priv->sasl.runSSF = s->priv->sasl.wantSSF = 0;
+        s->priv->sasl.len = 0;
+        s->priv->sasl.encodedLength = s->priv->sasl.encodedOffset = 0;
+        s->priv->sasl.encoded = NULL;
+        free(s->priv->sasl.mechlist);
+        free(s->priv->sasl.mechname);
+        s->priv->sasl.mechlist = NULL;
+        sasl_dispose(&s->priv->sasl.conn);
+        s->priv->sasl.conn = NULL;
     }
 #endif
 
@@ -402,39 +437,39 @@ static ssize_t reds_stream_sasl_write(RedsStream *s, const void *buf, size_t nby
 {
     ssize_t ret;
 
-    if (!s->sasl.encoded) {
+    if (!s->priv->sasl.encoded) {
         int err;
-        err = sasl_encode(s->sasl.conn, (char *)buf, nbyte,
-                          (const char **)&s->sasl.encoded,
-                          &s->sasl.encodedLength);
+        err = sasl_encode(s->priv->sasl.conn, (char *)buf, nbyte,
+                          (const char **)&s->priv->sasl.encoded,
+                          &s->priv->sasl.encodedLength);
         if (err != SASL_OK) {
             spice_warning("sasl_encode error: %d", err);
             return -1;
         }
 
-        if (s->sasl.encodedLength == 0) {
+        if (s->priv->sasl.encodedLength == 0) {
             return 0;
         }
 
-        if (!s->sasl.encoded) {
+        if (!s->priv->sasl.encoded) {
             spice_warning("sasl_encode didn't return a buffer!");
             return 0;
         }
 
-        s->sasl.encodedOffset = 0;
+        s->priv->sasl.encodedOffset = 0;
     }
 
-    ret = s->priv->write(s, s->sasl.encoded + s->sasl.encodedOffset,
-                         s->sasl.encodedLength - s->sasl.encodedOffset);
+    ret = s->priv->write(s, s->priv->sasl.encoded + s->priv->sasl.encodedOffset,
+                         s->priv->sasl.encodedLength - s->priv->sasl.encodedOffset);
 
     if (ret <= 0) {
         return ret;
     }
 
-    s->sasl.encodedOffset += ret;
-    if (s->sasl.encodedOffset == s->sasl.encodedLength) {
-        s->sasl.encoded = NULL;
-        s->sasl.encodedOffset = s->sasl.encodedLength = 0;
+    s->priv->sasl.encodedOffset += ret;
+    if (s->priv->sasl.encodedOffset == s->priv->sasl.encodedLength) {
+        s->priv->sasl.encoded = NULL;
+        s->priv->sasl.encodedOffset = s->priv->sasl.encodedLength = 0;
         return nbyte;
     }
 
@@ -451,9 +486,9 @@ static ssize_t reds_stream_sasl_read(RedsStream *s, uint8_t *buf, size_t nbyte)
     int err;
     int n;
 
-    n = spice_buffer_copy(&s->sasl.inbuffer, buf, nbyte);
+    n = spice_buffer_copy(&s->priv->sasl.inbuffer, buf, nbyte);
     if (n > 0) {
-        spice_buffer_remove(&s->sasl.inbuffer, n);
+        spice_buffer_remove(&s->priv->sasl.inbuffer, n);
         if (n == nbyte)
             return n;
         nbyte -= n;
@@ -465,7 +500,7 @@ static ssize_t reds_stream_sasl_read(RedsStream *s, uint8_t *buf, size_t nbyte)
         return n;
     }
 
-    err = sasl_decode(s->sasl.conn,
+    err = sasl_decode(s->priv->sasl.conn,
                       (char *)encoded, n,
                       &decoded, &decodedlen);
     if (err != SASL_OK) {
@@ -480,7 +515,7 @@ static ssize_t reds_stream_sasl_read(RedsStream *s, uint8_t *buf, size_t nbyte)
 
     n = MIN(nbyte, decodedlen);
     memcpy(buf, decoded, n);
-    spice_buffer_append(&s->sasl.inbuffer, decoded + n, decodedlen - n);
+    spice_buffer_append(&s->priv->sasl.inbuffer, decoded + n, decodedlen - n);
     return n;
 }
 
@@ -574,7 +609,7 @@ RedsSaslError reds_sasl_handle_auth_step(RedsStream *stream, AsyncReadDone read_
     unsigned int serveroutlen;
     int err;
     char *clientdata = NULL;
-    RedsSASL *sasl = &stream->sasl;
+    RedsSASL *sasl = &stream->priv->sasl;
     uint32_t datalen = sasl->len;
     AsyncRead *obj = &stream->async_read;
 
@@ -657,7 +692,7 @@ authreject:
 RedsSaslError reds_sasl_handle_auth_steplen(RedsStream *stream, AsyncReadDone read_cb, void *opaque)
 {
     AsyncRead *obj = &stream->async_read;
-    RedsSASL *sasl = &stream->sasl;
+    RedsSASL *sasl = &stream->priv->sasl;
 
     spice_info("Got steplen %d", sasl->len);
     if (sasl->len > SASL_DATA_MAX_LEN) {
@@ -704,7 +739,7 @@ RedsSaslError reds_sasl_handle_auth_start(RedsStream *stream, AsyncReadDone read
     unsigned int serveroutlen;
     int err;
     char *clientdata = NULL;
-    RedsSASL *sasl = &stream->sasl;
+    RedsSASL *sasl = &stream->priv->sasl;
     uint32_t datalen = sasl->len;
 
     /* NB, distinction of NULL vs "" is *critical* in SASL */
@@ -786,7 +821,7 @@ authreject:
 RedsSaslError reds_sasl_handle_auth_startlen(RedsStream *stream, AsyncReadDone read_cb, void *opaque)
 {
     AsyncRead *obj = &stream->async_read;
-    RedsSASL *sasl = &stream->sasl;
+    RedsSASL *sasl = &stream->priv->sasl;
 
     spice_info("Got client start len %d", sasl->len);
     if (sasl->len > SASL_DATA_MAX_LEN) {
@@ -810,7 +845,7 @@ RedsSaslError reds_sasl_handle_auth_startlen(RedsStream *stream, AsyncReadDone r
 bool reds_sasl_handle_auth_mechname(RedsStream *stream, AsyncReadDone read_cb, void *opaque)
 {
     AsyncRead *obj = &stream->async_read;
-    RedsSASL *sasl = &stream->sasl;
+    RedsSASL *sasl = &stream->priv->sasl;
 
     sasl->mechname[sasl->len] = '\0';
     spice_info("Got client mechname '%s' check against '%s'",
@@ -852,7 +887,7 @@ bool reds_sasl_handle_auth_mechname(RedsStream *stream, AsyncReadDone read_cb, v
 bool reds_sasl_handle_auth_mechlen(RedsStream *stream, AsyncReadDone read_cb, void *opaque)
 {
     AsyncRead *obj = &stream->async_read;
-    RedsSASL *sasl = &stream->sasl;
+    RedsSASL *sasl = &stream->priv->sasl;
 
     if (sasl->len < 1 || sasl->len > 100) {
         spice_warning("Got bad client mechname len %d", sasl->len);
@@ -878,7 +913,7 @@ bool reds_sasl_start_auth(RedsStream *stream, AsyncReadDone read_cb, void *opaqu
     char *localAddr, *remoteAddr;
     int mechlistlen;
     AsyncRead *obj = &stream->async_read;
-    RedsSASL *sasl = &stream->sasl;
+    RedsSASL *sasl = &stream->priv->sasl;
 
     if (!(localAddr = reds_stream_get_local_address(stream))) {
         goto error;
diff --git a/server/reds_stream.h b/server/reds_stream.h
index 9762898..c4d7208 100644
--- a/server/reds_stream.h
+++ b/server/reds_stream.h
@@ -25,40 +25,9 @@
 
 #include <openssl/ssl.h>
 
-#if HAVE_SASL
-#include <sasl/sasl.h>
-
 typedef void (*AsyncReadDone)(void *opaque);
 typedef void (*AsyncReadError)(void *opaque, int err);
 
-typedef struct RedsSASL {
-    sasl_conn_t *conn;
-
-    /* If we want to negotiate an SSF layer with client */
-    int wantSSF :1;
-    /* If we are now running the SSF layer */
-    int runSSF :1;
-
-    /*
-     * Buffering encoded data to allow more clear data
-     * to be stuffed onto the output buffer
-     */
-    const uint8_t *encoded;
-    unsigned int encodedLength;
-    unsigned int encodedOffset;
-
-    SpiceBuffer inbuffer;
-
-    char *username;
-    char *mechlist;
-    char *mechname;
-
-    /* temporary data during authentication */
-    unsigned int len;
-    char *data;
-} RedsSASL;
-#endif
-
 typedef struct RedsStream RedsStream;
 typedef struct AsyncRead {
     RedsStream *stream;
@@ -86,10 +55,6 @@ struct RedsStream {
 
     AsyncRead async_read;
 
-#if HAVE_SASL
-    RedsSASL sasl;
-#endif
-
     /* life time of info:
      * allocated when creating RedsStream.
      * deallocated when main_dispatcher handles the SPICE_CHANNEL_EVENT_DISCONNECTED
commit 284f9d0d7a7b26e517ad606d39051a877702edd1
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Tue Oct 22 16:00:34 2013 +0200

    Make RedsStream read/write functions private

diff --git a/server/reds_stream.c b/server/reds_stream.c
index f558897..7f6172f 100644
--- a/server/reds_stream.c
+++ b/server/reds_stream.c
@@ -35,6 +35,11 @@ extern SpiceCoreInterface *core;
 
 struct RedsStreamPrivate {
     SSL *ssl;
+
+    ssize_t (*read)(RedsStream *s, void *buf, size_t nbyte);
+    ssize_t (*write)(RedsStream *s, const void *buf, size_t nbyte);
+    ssize_t (*writev)(RedsStream *s, const struct iovec *iov, int iovcnt);
+
 };
 
 static ssize_t stream_write_cb(RedsStream *s, const void *buf, size_t size)
@@ -123,7 +128,7 @@ ssize_t reds_stream_read(RedsStream *s, void *buf, size_t nbyte)
         ret = reds_stream_sasl_read(s, buf, nbyte);
     } else
 #endif
-        ret = s->read(s, buf, nbyte);
+        ret = s->priv->read(s, buf, nbyte);
 
     return ret;
 }
@@ -157,7 +162,7 @@ ssize_t reds_stream_write(RedsStream *s, const void *buf, size_t nbyte)
         ret = reds_stream_sasl_write(s, buf, nbyte);
     } else
 #endif
-        ret = s->write(s, buf, nbyte);
+        ret = s->priv->write(s, buf, nbyte);
 
     return ret;
 }
@@ -168,8 +173,8 @@ ssize_t reds_stream_writev(RedsStream *s, const struct iovec *iov, int iovcnt)
     int n;
     ssize_t ret = 0;
 
-    if (s->writev != NULL) {
-        return s->writev(s, iov, iovcnt);
+    if (s->priv->writev != NULL) {
+        return s->priv->writev(s, iov, iovcnt);
     }
 
     for (i = 0; i < iovcnt; ++i) {
@@ -247,9 +252,9 @@ RedsStream *reds_stream_new(int socket)
     stream->info = spice_new0(SpiceChannelEventInfo, 1);
     reds_stream_set_socket(stream, socket);
 
-    stream->read = stream_read_cb;
-    stream->write = stream_write_cb;
-    stream->writev = stream_writev_cb;
+    stream->priv->read = stream_read_cb;
+    stream->priv->write = stream_write_cb;
+    stream->priv->writev = stream_writev_cb;
 
     stream->async_read.stream = stream;
 
@@ -263,7 +268,7 @@ bool reds_stream_is_ssl(RedsStream *stream)
 
 void reds_stream_disable_writev(RedsStream *stream)
 {
-    stream->writev = NULL;
+    stream->priv->writev = NULL;
 }
 
 RedsStreamSslStatus reds_stream_ssl_accept(RedsStream *stream)
@@ -313,9 +318,9 @@ int reds_stream_enable_ssl(RedsStream *stream, SSL_CTX *ctx)
 
     SSL_set_bio(stream->priv->ssl, sbio, sbio);
 
-    stream->write = stream_ssl_write_cb;
-    stream->read = stream_ssl_read_cb;
-    stream->writev = NULL;
+    stream->priv->write = stream_ssl_write_cb;
+    stream->priv->read = stream_ssl_read_cb;
+    stream->priv->writev = NULL;
 
     return reds_stream_ssl_accept(stream);
 }
@@ -419,8 +424,8 @@ static ssize_t reds_stream_sasl_write(RedsStream *s, const void *buf, size_t nby
         s->sasl.encodedOffset = 0;
     }
 
-    ret = s->write(s, s->sasl.encoded + s->sasl.encodedOffset,
-                   s->sasl.encodedLength - s->sasl.encodedOffset);
+    ret = s->priv->write(s, s->sasl.encoded + s->sasl.encodedOffset,
+                         s->sasl.encodedLength - s->sasl.encodedOffset);
 
     if (ret <= 0) {
         return ret;
@@ -455,7 +460,7 @@ static ssize_t reds_stream_sasl_read(RedsStream *s, uint8_t *buf, size_t nbyte)
         buf += n;
     }
 
-    n = s->read(s, encoded, sizeof(encoded));
+    n = s->priv->read(s, encoded, sizeof(encoded));
     if (n <= 0) {
         return n;
     }
diff --git a/server/reds_stream.h b/server/reds_stream.h
index 84eac14..9762898 100644
--- a/server/reds_stream.h
+++ b/server/reds_stream.h
@@ -96,11 +96,6 @@ struct RedsStream {
      * event, either from same thread or by call back from main thread. */
     SpiceChannelEventInfo* info;
 
-    /* private */
-    ssize_t (*read)(RedsStream *s, void *buf, size_t nbyte);
-    ssize_t (*write)(RedsStream *s, const void *buf, size_t nbyte);
-    ssize_t (*writev)(RedsStream *s, const struct iovec *iov, int iovcnt);
-
     RedsStreamPrivate *priv;
 };
 
commit 520ebdc815f4ea1982efc257813c6fb32dca1ed7
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Tue Oct 22 15:57:59 2013 +0200

    Make RedsStream::ssl private

diff --git a/server/reds_stream.c b/server/reds_stream.c
index 95c159f..f558897 100644
--- a/server/reds_stream.c
+++ b/server/reds_stream.c
@@ -34,6 +34,7 @@
 extern SpiceCoreInterface *core;
 
 struct RedsStreamPrivate {
+    SSL *ssl;
 };
 
 static ssize_t stream_write_cb(RedsStream *s, const void *buf, size_t size)
@@ -80,10 +81,10 @@ static ssize_t stream_ssl_write_cb(RedsStream *s, const void *buf, size_t size)
     int return_code;
     SPICE_GNUC_UNUSED int ssl_error;
 
-    return_code = SSL_write(s->ssl, buf, size);
+    return_code = SSL_write(s->priv->ssl, buf, size);
 
     if (return_code < 0) {
-        ssl_error = SSL_get_error(s->ssl, return_code);
+        ssl_error = SSL_get_error(s->priv->ssl, return_code);
     }
 
     return return_code;
@@ -94,10 +95,10 @@ static ssize_t stream_ssl_read_cb(RedsStream *s, void *buf, size_t size)
     int return_code;
     SPICE_GNUC_UNUSED int ssl_error;
 
-    return_code = SSL_read(s->ssl, buf, size);
+    return_code = SSL_read(s->priv->ssl, buf, size);
 
     if (return_code < 0) {
-        ssl_error = SSL_get_error(s->ssl, return_code);
+        ssl_error = SSL_get_error(s->priv->ssl, return_code);
     }
 
     return return_code;
@@ -203,8 +204,8 @@ void reds_stream_free(RedsStream *s)
     }
 #endif
 
-    if (s->ssl) {
-        SSL_free(s->ssl);
+    if (s->priv->ssl) {
+        SSL_free(s->priv->ssl);
     }
 
     reds_stream_remove_watch(s);
@@ -257,7 +258,7 @@ RedsStream *reds_stream_new(int socket)
 
 bool reds_stream_is_ssl(RedsStream *stream)
 {
-    return (stream->ssl != NULL);
+    return (stream->priv->ssl != NULL);
 }
 
 void reds_stream_disable_writev(RedsStream *stream)
@@ -270,12 +271,12 @@ RedsStreamSslStatus reds_stream_ssl_accept(RedsStream *stream)
     int ssl_error;
     int return_code;
 
-    return_code = SSL_accept(stream->ssl);
+    return_code = SSL_accept(stream->priv->ssl);
     if (return_code == 1) {
         return REDS_STREAM_SSL_STATUS_OK;
     }
 
-    ssl_error = SSL_get_error(stream->ssl, return_code);
+    ssl_error = SSL_get_error(stream->priv->ssl, return_code);
     if (return_code == -1 && (ssl_error == SSL_ERROR_WANT_READ ||
                               ssl_error == SSL_ERROR_WANT_WRITE)) {
         if (ssl_error == SSL_ERROR_WANT_READ) {
@@ -287,8 +288,8 @@ RedsStreamSslStatus reds_stream_ssl_accept(RedsStream *stream)
 
     ERR_print_errors_fp(stderr);
     spice_warning("SSL_accept failed, error=%d", ssl_error);
-    SSL_free(stream->ssl);
-    stream->ssl = NULL;
+    SSL_free(stream->priv->ssl);
+    stream->priv->ssl = NULL;
 
     return REDS_STREAM_SSL_STATUS_ERROR;
 }
@@ -303,14 +304,14 @@ int reds_stream_enable_ssl(RedsStream *stream, SSL_CTX *ctx)
         return REDS_STREAM_SSL_STATUS_ERROR;
     }
 
-    stream->ssl = SSL_new(ctx);
-    if (!stream->ssl) {
+    stream->priv->ssl = SSL_new(ctx);
+    if (!stream->priv->ssl) {
         spice_warning("could not allocate ssl context");
         BIO_free(sbio);
         return REDS_STREAM_SSL_STATUS_ERROR;
     }
 
-    SSL_set_bio(stream->ssl, sbio, sbio);
+    SSL_set_bio(stream->priv->ssl, sbio, sbio);
 
     stream->write = stream_ssl_write_cb;
     stream->read = stream_ssl_read_cb;
@@ -903,10 +904,10 @@ bool reds_sasl_start_auth(RedsStream *stream, AsyncReadDone read_cb, void *opaqu
     }
 
     /* Inform SASL that we've got an external SSF layer from TLS */
-    if (stream->ssl) {
+    if (stream->priv->ssl) {
         sasl_ssf_t ssf;
 
-        ssf = SSL_get_cipher_bits(stream->ssl, NULL);
+        ssf = SSL_get_cipher_bits(stream->priv->ssl, NULL);
         err = sasl_setprop(sasl->conn, SASL_SSF_EXTERNAL, &ssf);
         if (err != SASL_OK) {
             spice_warning("cannot set SASL external SSF %d (%s)",
@@ -919,7 +920,7 @@ bool reds_sasl_start_auth(RedsStream *stream, AsyncReadDone read_cb, void *opaqu
 
     memset(&secprops, 0, sizeof secprops);
     /* Inform SASL that we've got an external SSF layer from TLS */
-    if (stream->ssl) {
+    if (stream->priv->ssl) {
         /* If we've got TLS (or UNIX domain sock), we don't care about SSF */
         secprops.min_ssf = 0;
         secprops.max_ssf = 0;
diff --git a/server/reds_stream.h b/server/reds_stream.h
index fca2a71..84eac14 100644
--- a/server/reds_stream.h
+++ b/server/reds_stream.h
@@ -83,7 +83,6 @@ struct RedsStream {
     /* set it to TRUE if you shutdown the socket. shutdown read doesn't work as accepted -
        receive may return data afterward. check the flag before calling receive*/
     int shutdown;
-    SSL *ssl;
 
     AsyncRead async_read;
 
commit 30fecf87f86f4f8343182cd0f3348f9c8a59e92b
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Mon Jan 6 22:37:45 2014 +0100

    Introduce reds_stream_is_ssl()

diff --git a/server/inputs_channel.c b/server/inputs_channel.c
index 8d4feab..395b81f 100644
--- a/server/inputs_channel.c
+++ b/server/inputs_channel.c
@@ -551,7 +551,7 @@ static void inputs_connect(RedChannel *channel, RedClient *client,
     spice_assert(g_inputs_channel);
     spice_assert(channel == &g_inputs_channel->base);
 
-    if (!stream->ssl && !red_client_during_migrate_at_target(client)) {
+    if (!reds_stream_is_ssl(stream) && !red_client_during_migrate_at_target(client)) {
         main_channel_client_push_notify(red_client_get_main(client),
                                         "keyboard channel is insecure");
     }
diff --git a/server/reds.c b/server/reds.c
index 252cf5b..7fc48f1 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -1412,9 +1412,9 @@ static void reds_info_new_channel(RedLinkInfo *link, int connection_id)
     spice_info("channel %d:%d, connected successfully, over %s link",
                link->link_mess->channel_type,
                link->link_mess->channel_id,
-               link->stream->ssl == NULL ? "Non Secure" : "Secure");
+               reds_stream_is_ssl(link->stream) ? "Secure" : "Non Secure");
     /* add info + send event */
-    if (link->stream->ssl) {
+    if (reds_stream_is_ssl(link->stream)) {
         link->stream->info->flags |= SPICE_CHANNEL_EVENT_FLAG_TLS;
     }
     link->stream->info->connection_id = connection_id;
@@ -2033,8 +2033,8 @@ static int reds_security_check(RedLinkInfo *link)
 {
     ChannelSecurityOptions *security_option = find_channel_security(link->link_mess->channel_type);
     uint32_t security = security_option ? security_option->options : default_channel_security;
-    return (link->stream->ssl && (security & SPICE_CHANNEL_SECURITY_SSL)) ||
-        (!link->stream->ssl && (security & SPICE_CHANNEL_SECURITY_NONE));
+    return (reds_stream_is_ssl(link->stream) && (security & SPICE_CHANNEL_SECURITY_SSL)) ||
+        (!reds_stream_is_ssl(link->stream) && (security & SPICE_CHANNEL_SECURITY_NONE));
 }
 
 static void reds_handle_read_link_done(void *opaque)
@@ -2058,7 +2058,7 @@ static void reds_handle_read_link_done(void *opaque)
                                     SPICE_COMMON_CAP_PROTOCOL_AUTH_SELECTION);
 
     if (!reds_security_check(link)) {
-        if (link->stream->ssl) {
+        if (reds_stream_is_ssl(link->stream)) {
             spice_warning("spice channels %d should not be encrypted", link_mess->channel_type);
             reds_send_link_error(link, SPICE_LINK_ERR_NEED_UNSECURED);
         } else {
diff --git a/server/reds_stream.c b/server/reds_stream.c
index e94995c..95c159f 100644
--- a/server/reds_stream.c
+++ b/server/reds_stream.c
@@ -255,6 +255,11 @@ RedsStream *reds_stream_new(int socket)
     return stream;
 }
 
+bool reds_stream_is_ssl(RedsStream *stream)
+{
+    return (stream->ssl != NULL);
+}
+
 void reds_stream_disable_writev(RedsStream *stream)
 {
     stream->writev = NULL;
diff --git a/server/reds_stream.h b/server/reds_stream.h
index 4927336..fca2a71 100644
--- a/server/reds_stream.h
+++ b/server/reds_stream.h
@@ -125,6 +125,7 @@ void reds_stream_free(RedsStream *s);
 void reds_stream_push_channel_event(RedsStream *s, int event);
 void reds_stream_remove_watch(RedsStream* s);
 RedsStream *reds_stream_new(int socket);
+bool reds_stream_is_ssl(RedsStream *stream);
 RedsStreamSslStatus reds_stream_ssl_accept(RedsStream *stream);
 int reds_stream_enable_ssl(RedsStream *stream, SSL_CTX *ctx);
 
commit 1f7123298fbebce4134e12b33d35fe62621f631b
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Tue Oct 22 15:55:03 2013 +0200

    Add RedsStream::priv
    
    The private data is allocated at the same time as RedsStream and
    goes immediatly after the main RedsStream data.
    This private member will allow to hide internal RedsStream
    implementation details from the rest of spice-server.

diff --git a/server/reds_stream.c b/server/reds_stream.c
index 2703ec7..e94995c 100644
--- a/server/reds_stream.c
+++ b/server/reds_stream.c
@@ -33,6 +33,9 @@
 
 extern SpiceCoreInterface *core;
 
+struct RedsStreamPrivate {
+};
+
 static ssize_t stream_write_cb(RedsStream *s, const void *buf, size_t size)
 {
     return write(s->socket, buf, size);
@@ -238,7 +241,8 @@ RedsStream *reds_stream_new(int socket)
 {
     RedsStream *stream;
 
-    stream = spice_new0(RedsStream, 1);
+    stream = spice_malloc0(sizeof(RedsStream) + sizeof(RedsStreamPrivate));
+    stream->priv = (RedsStreamPrivate *)(((char *)stream) + sizeof(RedsStream));
     stream->info = spice_new0(SpiceChannelEventInfo, 1);
     reds_stream_set_socket(stream, socket);
 
diff --git a/server/reds_stream.h b/server/reds_stream.h
index cae244b..4927336 100644
--- a/server/reds_stream.h
+++ b/server/reds_stream.h
@@ -74,6 +74,8 @@ void async_read_set_error_handler(AsyncRead *async,
                                   AsyncReadError error_handler,
                                   void *opaque);
 
+typedef struct RedsStreamPrivate RedsStreamPrivate;
+
 struct RedsStream {
     int socket;
     SpiceWatch *watch;
@@ -99,6 +101,8 @@ struct RedsStream {
     ssize_t (*read)(RedsStream *s, void *buf, size_t nbyte);
     ssize_t (*write)(RedsStream *s, const void *buf, size_t nbyte);
     ssize_t (*writev)(RedsStream *s, const struct iovec *iov, int iovcnt);
+
+    RedsStreamPrivate *priv;
 };
 
 typedef enum {
commit 7ff743c43141739286aabcf08f3aa37e8e333a49
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Tue Oct 8 11:11:37 2013 +0200

    Move SASL authentication to reds_stream.h
    
    SASL authentication mostly use members from RedsStream to do its work, so
    it makes sense to have its code in reds_stream.c. This should allow to make
    RedsStream::sasl private in the future.

diff --git a/server/reds.c b/server/reds.c
index 15291c5..252cf5b 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -1882,61 +1882,6 @@ static void reds_get_spice_ticket(RedLinkInfo *link)
 }
 
 #if HAVE_SASL
-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) {
-        spice_warning("Cannot resolve address %d: %s",
-                      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 = spice_malloc(addrlen + 1);
-    snprintf(addr, addrlen, format, host, serv);
-    addr[addrlen] = '\0';
-
-    return addr;
-}
-
-static int auth_sasl_check_ssf(RedsSASL *sasl, int *runSSF)
-{
-    const void *val;
-    int err, ssf;
-
-    *runSSF = 0;
-    if (!sasl->wantSSF) {
-        return 1;
-    }
-
-    err = sasl_getprop(sasl->conn, SASL_SSF, &val);
-    if (err != SASL_OK) {
-        return 0;
-    }
-
-    ssf = *(const int *)val;
-    spice_info("negotiated an SSF of %d", ssf);
-    if (ssf < 56) {
-        return 0; /* 56 is good for Kerberos */
-    }
-
-    *runSSF = 1;
-
-    /* We have a SSF that's good enough */
-    return 1;
-}
-
 /*
  * Step Msg
  *
@@ -1957,115 +1902,25 @@ static void reds_handle_auth_sasl_steplen(void *opaque);
 
 static void reds_handle_auth_sasl_step(void *opaque)
 {
-    const char *serverout;
-    unsigned int serveroutlen;
-    int err;
-    char *clientdata = NULL;
     RedLinkInfo *link = (RedLinkInfo *)opaque;
-    RedsSASL *sasl = &link->stream->sasl;
-    uint32_t datalen = sasl->len;
-    AsyncRead *obj = &link->async_read;
-
-    /* NB, distinction of NULL vs "" is *critical* in SASL */
-    if (datalen) {
-        clientdata = sasl->data;
-        clientdata[datalen - 1] = '\0'; /* Wire includes '\0', but make sure */
-        datalen--; /* Don't count NULL byte when passing to _start() */
-    }
-
-    spice_info("Step using SASL Data %p (%d bytes)",
-               clientdata, datalen);
-    err = sasl_server_step(sasl->conn,
-                           clientdata,
-                           datalen,
-                           &serverout,
-                           &serveroutlen);
-    if (err != SASL_OK &&
-        err != SASL_CONTINUE) {
-        spice_warning("sasl step failed %d (%s)",
-                      err, sasl_errdetail(sasl->conn));
-        goto authabort;
-    }
-
-    if (serveroutlen > SASL_DATA_MAX_LEN) {
-        spice_warning("sasl step reply data too long %d",
-                      serveroutlen);
-        goto authabort;
-    }
-
-    spice_info("SASL return data %d bytes, %p", serveroutlen, serverout);
-
-    if (serveroutlen) {
-        serveroutlen += 1;
-        reds_stream_write_all(link->stream, &serveroutlen, sizeof(uint32_t));
-        reds_stream_write_all(link->stream, serverout, serveroutlen);
-    } else {
-        reds_stream_write_all(link->stream, &serveroutlen, sizeof(uint32_t));
-    }
-
-    /* Whether auth is complete */
-    reds_stream_write_u8(link->stream, err == SASL_CONTINUE ? 0 : 1);
-
-    if (err == SASL_CONTINUE) {
-        spice_info("%s", "Authentication must continue (step)");
-        /* Wait for step length */
-        obj->now = (uint8_t *)&sasl->len;
-        obj->end = obj->now + sizeof(uint32_t);
-        obj->done = reds_handle_auth_sasl_steplen;
-        async_read_handler(0, 0, &link->async_read);
-    } else {
-        int ssf;
-
-        if (auth_sasl_check_ssf(sasl, &ssf) == 0) {
-            spice_warning("Authentication rejected for weak SSF");
-            goto authreject;
-        }
-
-        spice_info("Authentication successful");
-        reds_stream_write_u32(link->stream, SPICE_LINK_ERR_OK); /* Accept auth */
-
-        /*
-         * Delay writing in SSF encoded until now
-         */
-        sasl->runSSF = ssf;
-        link->stream->writev = NULL; /* make sure writev isn't called directly anymore */
+    RedsSaslError status;
 
+    status = reds_sasl_handle_auth_start(link->stream, reds_handle_auth_sasl_steplen, link);
+    if (status == REDS_SASL_ERROR_OK) {
         reds_handle_link(link);
+    } else if (status != REDS_SASL_ERROR_CONTINUE) {
+        reds_link_free(link);
     }
-
-    return;
-
-authreject:
-    reds_stream_write_u32(link->stream, 1); /* Reject auth */
-    reds_stream_write_u32(link->stream, sizeof("Authentication failed"));
-    reds_stream_write_all(link->stream, "Authentication failed", sizeof("Authentication failed"));
-
-authabort:
-    reds_link_free(link);
-    return;
 }
 
 static void reds_handle_auth_sasl_steplen(void *opaque)
 {
     RedLinkInfo *link = (RedLinkInfo *)opaque;
-    AsyncRead *obj = &link->async_read;
-    RedsSASL *sasl = &link->stream->sasl;
+    RedsSaslError status;
 
-    spice_info("Got steplen %d", sasl->len);
-    if (sasl->len > SASL_DATA_MAX_LEN) {
-        spice_warning("Too much SASL data %d", sasl->len);
+    status = reds_sasl_handle_auth_steplen(link->stream, reds_handle_auth_sasl_step, link);
+    if (status != REDS_SASL_ERROR_OK) {
         reds_link_free(link);
-        return;
-    }
-
-    if (sasl->len == 0) {
-        return reds_handle_auth_sasl_step(opaque);
-    } else {
-        sasl->data = spice_realloc(sasl->data, sasl->len);
-        obj->now = (uint8_t *)sasl->data;
-        obj->end = obj->now + sasl->len;
-        obj->done = reds_handle_auth_sasl_step;
-        async_read_handler(0, 0, &link->async_read);
     }
 }
 
@@ -2088,307 +1943,64 @@ static void reds_handle_auth_sasl_steplen(void *opaque)
 static void reds_handle_auth_sasl_start(void *opaque)
 {
     RedLinkInfo *link = (RedLinkInfo *)opaque;
-    AsyncRead *obj = &link->async_read;
-    const char *serverout;
-    unsigned int serveroutlen;
-    int err;
-    char *clientdata = NULL;
-    RedsSASL *sasl = &link->stream->sasl;
-    uint32_t datalen = sasl->len;
-
-    /* NB, distinction of NULL vs "" is *critical* in SASL */
-    if (datalen) {
-        clientdata = sasl->data;
-        clientdata[datalen - 1] = '\0'; /* Should be on wire, but make sure */
-        datalen--; /* Don't count NULL byte when passing to _start() */
-    }
-
-    spice_info("Start SASL auth with mechanism %s. Data %p (%d bytes)",
-               sasl->mechlist, clientdata, datalen);
-    err = sasl_server_start(sasl->conn,
-                            sasl->mechlist,
-                            clientdata,
-                            datalen,
-                            &serverout,
-                            &serveroutlen);
-    if (err != SASL_OK &&
-        err != SASL_CONTINUE) {
-        spice_warning("sasl start failed %d (%s)",
-                    err, sasl_errdetail(sasl->conn));
-        goto authabort;
-    }
-
-    if (serveroutlen > SASL_DATA_MAX_LEN) {
-        spice_warning("sasl start reply data too long %d",
-                    serveroutlen);
-        goto authabort;
-    }
-
-    spice_info("SASL return data %d bytes, %p", serveroutlen, serverout);
-
-    if (serveroutlen) {
-        serveroutlen += 1;
-        reds_stream_write_all(link->stream, &serveroutlen, sizeof(uint32_t));
-        reds_stream_write_all(link->stream, serverout, serveroutlen);
-    } else {
-        reds_stream_write_all(link->stream, &serveroutlen, sizeof(uint32_t));
-    }
-
-    /* Whether auth is complete */
-    reds_stream_write_u8(link->stream, err == SASL_CONTINUE ? 0 : 1);
-
-    if (err == SASL_CONTINUE) {
-        spice_info("%s", "Authentication must continue (start)");
-        /* Wait for step length */
-        obj->now = (uint8_t *)&sasl->len;
-        obj->end = obj->now + sizeof(uint32_t);
-        obj->done = reds_handle_auth_sasl_steplen;
-        async_read_handler(0, 0, &link->async_read);
-    } else {
-        int ssf;
-
-        if (auth_sasl_check_ssf(sasl, &ssf) == 0) {
-            spice_warning("Authentication rejected for weak SSF");
-            goto authreject;
-        }
-
-        spice_info("Authentication successful");
-        reds_stream_write_u32(link->stream, SPICE_LINK_ERR_OK); /* Accept auth */
-
-        /*
-         * Delay writing in SSF encoded until now
-         */
-        sasl->runSSF = ssf;
-        link->stream->writev = NULL; /* make sure writev isn't called directly anymore */
+    RedsSaslError status;
 
+    status = reds_sasl_handle_auth_start(link->stream, reds_handle_auth_sasl_steplen, link);
+    if (status == REDS_SASL_ERROR_OK) {
         reds_handle_link(link);
+    } else if (status != REDS_SASL_ERROR_CONTINUE) {
+        reds_link_free(link);
     }
-
-    return;
-
-authreject:
-    reds_stream_write_u32(link->stream, 1); /* Reject auth */
-    reds_stream_write_u32(link->stream, sizeof("Authentication failed"));
-    reds_stream_write_all(link->stream, "Authentication failed", sizeof("Authentication failed"));
-
-authabort:
-    reds_link_free(link);
-    return;
 }
 
 static void reds_handle_auth_startlen(void *opaque)
 {
     RedLinkInfo *link = (RedLinkInfo *)opaque;
-    AsyncRead *obj = &link->async_read;
-    RedsSASL *sasl = &link->stream->sasl;
+    RedsSaslError status;
 
-    spice_info("Got client start len %d", sasl->len);
-    if (sasl->len > SASL_DATA_MAX_LEN) {
-        spice_warning("Too much SASL data %d", sasl->len);
-        reds_send_link_error(link, SPICE_LINK_ERR_INVALID_DATA);
-        reds_link_free(link);
-        return;
-    }
-
-    if (sasl->len == 0) {
-        reds_handle_auth_sasl_start(opaque);
-        return;
+    status = reds_sasl_handle_auth_startlen(link->stream, reds_handle_auth_sasl_start, link);
+    switch (status) {
+        case REDS_SASL_ERROR_OK:
+            break;
+        case REDS_SASL_ERROR_RETRY:
+            reds_handle_auth_sasl_start(opaque);
+            break;
+        case REDS_SASL_ERROR_GENERIC:
+        case REDS_SASL_ERROR_INVALID_DATA:
+            reds_send_link_error(link, SPICE_LINK_ERR_INVALID_DATA);
+            reds_link_free(link);
+            break;
+        default:
+            g_warn_if_reached();
+            reds_send_link_error(link, SPICE_LINK_ERR_INVALID_DATA);
+            reds_link_free(link);
+            break;
     }
-
-    sasl->data = spice_realloc(sasl->data, sasl->len);
-    obj->now = (uint8_t *)sasl->data;
-    obj->end = obj->now + sasl->len;
-    obj->done = reds_handle_auth_sasl_start;
-    async_read_handler(0, 0, &link->async_read);
 }
 
 static void reds_handle_auth_mechname(void *opaque)
 {
     RedLinkInfo *link = (RedLinkInfo *)opaque;
-    AsyncRead *obj = &link->async_read;
-    RedsSASL *sasl = &link->stream->sasl;
 
-    sasl->mechname[sasl->len] = '\0';
-    spice_info("Got client mechname '%s' check against '%s'",
-               sasl->mechname, sasl->mechlist);
-
-    if (strncmp(sasl->mechlist, sasl->mechname, sasl->len) == 0) {
-        if (sasl->mechlist[sasl->len] != '\0' &&
-            sasl->mechlist[sasl->len] != ',') {
-            spice_info("One %d", sasl->mechlist[sasl->len]);
-            reds_link_free(link);
-            return;
-        }
-    } else {
-        char *offset = strstr(sasl->mechlist, sasl->mechname);
-        spice_info("Two %p", offset);
-        if (!offset) {
+    if (!reds_sasl_handle_auth_mechname(link->stream, reds_handle_auth_startlen, link)) {
             reds_send_link_error(link, SPICE_LINK_ERR_INVALID_DATA);
-            return;
-        }
-        spice_info("Two '%s'", offset);
-        if (offset[-1] != ',' ||
-            (offset[sasl->len] != '\0'&&
-             offset[sasl->len] != ',')) {
-            reds_send_link_error(link, SPICE_LINK_ERR_INVALID_DATA);
-            return;
-        }
     }
-
-    free(sasl->mechlist);
-    sasl->mechlist = spice_strdup(sasl->mechname);
-
-    spice_info("Validated mechname '%s'", sasl->mechname);
-
-    obj->now = (uint8_t *)&sasl->len;
-    obj->end = obj->now + sizeof(uint32_t);
-    obj->done = reds_handle_auth_startlen;
-    async_read_handler(0, 0, &link->async_read);
-
-    return;
 }
 
 static void reds_handle_auth_mechlen(void *opaque)
 {
     RedLinkInfo *link = (RedLinkInfo *)opaque;
-    AsyncRead *obj = &link->async_read;
-    RedsSASL *sasl = &link->stream->sasl;
 
-    if (sasl->len < 1 || sasl->len > 100) {
-        spice_warning("Got bad client mechname len %d", sasl->len);
+    if (!reds_sasl_handle_auth_mechlen(link->stream, reds_handle_auth_mechname, link)) {
         reds_link_free(link);
-        return;
     }
-
-    sasl->mechname = spice_malloc(sasl->len + 1);
-
-    spice_info("Wait for client mechname");
-    obj->now = (uint8_t *)sasl->mechname;
-    obj->end = obj->now + sasl->len;
-    obj->done = reds_handle_auth_mechname;
-    async_read_handler(0, 0, &link->async_read);
 }
 
 static void reds_start_auth_sasl(RedLinkInfo *link)
 {
-    const char *mechlist = NULL;
-    sasl_security_properties_t secprops;
-    int err;
-    char *localAddr, *remoteAddr;
-    int mechlistlen;
-    AsyncRead *obj = &link->async_read;
-    RedsSASL *sasl = &link->stream->sasl;
-
-    /* Get local & remote client addresses in form  IPADDR;PORT */
-    if (!(localAddr = addr_to_string("%s;%s", &link->stream->info->laddr_ext,
-                                              link->stream->info->llen_ext))) {
-        goto error;
-    }
-
-    if (!(remoteAddr = addr_to_string("%s;%s", &link->stream->info->paddr_ext,
-                                               link->stream->info->plen_ext))) {
-        free(localAddr);
-        goto error;
-    }
-
-    err = sasl_server_new("spice",
-                          NULL, /* FQDN - just delegates to gethostname */
-                          NULL, /* User realm */
-                          localAddr,
-                          remoteAddr,
-                          NULL, /* Callbacks, not needed */
-                          SASL_SUCCESS_DATA,
-                          &sasl->conn);
-    free(localAddr);
-    free(remoteAddr);
-    localAddr = remoteAddr = NULL;
-
-    if (err != SASL_OK) {
-        spice_warning("sasl context setup failed %d (%s)",
-                    err, sasl_errstring(err, NULL, NULL));
-        sasl->conn = NULL;
-        goto error;
-    }
-
-    /* Inform SASL that we've got an external SSF layer from TLS */
-    if (link->stream->ssl) {
-        sasl_ssf_t ssf;
-
-        ssf = SSL_get_cipher_bits(link->stream->ssl, NULL);
-        err = sasl_setprop(sasl->conn, SASL_SSF_EXTERNAL, &ssf);
-        if (err != SASL_OK) {
-            spice_warning("cannot set SASL external SSF %d (%s)",
-                        err, sasl_errstring(err, NULL, NULL));
-            goto error_dispose;
-        }
-    } else {
-        sasl->wantSSF = 1;
-    }
-
-    memset(&secprops, 0, sizeof secprops);
-    /* Inform SASL that we've got an external SSF layer from TLS */
-    if (link->stream->ssl) {
-        /* If we've got TLS (or UNIX domain sock), we don't care about SSF */
-        secprops.min_ssf = 0;
-        secprops.max_ssf = 0;
-        secprops.maxbufsize = 8192;
-        secprops.security_flags = 0;
-    } else {
-        /* Plain TCP, better get an SSF layer */
-        secprops.min_ssf = 56; /* Good enough to require kerberos */
-        secprops.max_ssf = 100000; /* Arbitrary big number */
-        secprops.maxbufsize = 8192;
-        /* Forbid any anonymous or trivially crackable auth */
-        secprops.security_flags =
-            SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT;
-    }
-
-    err = sasl_setprop(sasl->conn, SASL_SEC_PROPS, &secprops);
-    if (err != SASL_OK) {
-        spice_warning("cannot set SASL security props %d (%s)",
-                      err, sasl_errstring(err, NULL, NULL));
-        goto error_dispose;
-    }
-
-    err = sasl_listmech(sasl->conn,
-                        NULL, /* Don't need to set user */
-                        "", /* Prefix */
-                        ",", /* Separator */
-                        "", /* Suffix */
-                        &mechlist,
-                        NULL,
-                        NULL);
-    if (err != SASL_OK || mechlist == NULL) {
-        spice_warning("cannot list SASL mechanisms %d (%s)",
-                      err, sasl_errdetail(sasl->conn));
-        goto error_dispose;
-    }
-
-    spice_info("Available mechanisms for client: '%s'", mechlist);
-
-    sasl->mechlist = spice_strdup(mechlist);
-
-    mechlistlen = strlen(mechlist);
-    if (!reds_stream_write_all(link->stream, &mechlistlen, sizeof(uint32_t))
-        || !reds_stream_write_all(link->stream, sasl->mechlist, mechlistlen)) {
-        spice_warning("SASL mechanisms write error");
-        goto error;
+    if (!reds_sasl_start_auth(link->stream, reds_handle_auth_mechlen, link)) {
+        reds_link_free(link);
     }
-
-    spice_info("Wait for client mechname length");
-    obj->now = (uint8_t *)&sasl->len;
-    obj->end = obj->now + sizeof(uint32_t);
-    obj->done = reds_handle_auth_mechlen;
-    async_read_handler(0, 0, &link->async_read);
-
-    return;
-
-error_dispose:
-    sasl_dispose(&sasl->conn);
-    sasl->conn = NULL;
-error:
-    reds_link_free(link);
-    return;
 }
 #endif
 
diff --git a/server/reds_stream.c b/server/reds_stream.c
index f4513e6..2703ec7 100644
--- a/server/reds_stream.c
+++ b/server/reds_stream.c
@@ -251,6 +251,11 @@ RedsStream *reds_stream_new(int socket)
     return stream;
 }
 
+void reds_stream_disable_writev(RedsStream *stream)
+{
+    stream->writev = NULL;
+}
+
 RedsStreamSslStatus reds_stream_ssl_accept(RedsStream *stream)
 {
     int ssl_error;
@@ -463,4 +468,509 @@ static ssize_t reds_stream_sasl_read(RedsStream *s, uint8_t *buf, size_t nbyte)
     spice_buffer_append(&s->sasl.inbuffer, decoded + n, decodedlen - n);
     return n;
 }
+
+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) {
+        spice_warning("Cannot resolve address %d: %s",
+                      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 = spice_malloc(addrlen + 1);
+    snprintf(addr, addrlen, format, host, serv);
+    addr[addrlen] = '\0';
+
+    return addr;
+}
+
+static char *reds_stream_get_local_address(RedsStream *stream)
+{
+    return addr_to_string("%s;%s", &stream->info->laddr_ext,
+                          stream->info->llen_ext);
+}
+
+static char *reds_stream_get_remote_address(RedsStream *stream)
+{
+    return addr_to_string("%s;%s", &stream->info->paddr_ext,
+                          stream->info->plen_ext);
+}
+
+static int auth_sasl_check_ssf(RedsSASL *sasl, int *runSSF)
+{
+    const void *val;
+    int err, ssf;
+
+    *runSSF = 0;
+    if (!sasl->wantSSF) {
+        return 1;
+    }
+
+    err = sasl_getprop(sasl->conn, SASL_SSF, &val);
+    if (err != SASL_OK) {
+        return 0;
+    }
+
+    ssf = *(const int *)val;
+    spice_info("negotiated an SSF of %d", ssf);
+    if (ssf < 56) {
+        return 0; /* 56 is good for Kerberos */
+    }
+
+    *runSSF = 1;
+
+    /* We have a SSF that's good enough */
+    return 1;
+}
+
+/*
+ * Step Msg
+ *
+ * Input from client:
+ *
+ * u32 clientin-length
+ * u8-array clientin-string
+ *
+ * Output to client:
+ *
+ * u32 serverout-length
+ * u8-array serverout-strin
+ * u8 continue
+ */
+#define SASL_DATA_MAX_LEN (1024 * 1024)
+
+RedsSaslError reds_sasl_handle_auth_step(RedsStream *stream, AsyncReadDone read_cb, void *opaque)
+{
+    const char *serverout;
+    unsigned int serveroutlen;
+    int err;
+    char *clientdata = NULL;
+    RedsSASL *sasl = &stream->sasl;
+    uint32_t datalen = sasl->len;
+    AsyncRead *obj = &stream->async_read;
+
+    /* NB, distinction of NULL vs "" is *critical* in SASL */
+    if (datalen) {
+        clientdata = sasl->data;
+        clientdata[datalen - 1] = '\0'; /* Wire includes '\0', but make sure */
+        datalen--; /* Don't count NULL byte when passing to _start() */
+    }
+
+    spice_info("Step using SASL Data %p (%d bytes)",
+               clientdata, datalen);
+    err = sasl_server_step(sasl->conn,
+                           clientdata,
+                           datalen,
+                           &serverout,
+                           &serveroutlen);
+    if (err != SASL_OK &&
+        err != SASL_CONTINUE) {
+        spice_warning("sasl step failed %d (%s)",
+                      err, sasl_errdetail(sasl->conn));
+        return REDS_SASL_ERROR_GENERIC;
+    }
+
+    if (serveroutlen > SASL_DATA_MAX_LEN) {
+        spice_warning("sasl step reply data too long %d",
+                      serveroutlen);
+        return REDS_SASL_ERROR_INVALID_DATA;
+    }
+
+    spice_info("SASL return data %d bytes, %p", serveroutlen, serverout);
+
+    if (serveroutlen) {
+        serveroutlen += 1;
+        reds_stream_write_all(stream, &serveroutlen, sizeof(uint32_t));
+        reds_stream_write_all(stream, serverout, serveroutlen);
+    } else {
+        reds_stream_write_all(stream, &serveroutlen, sizeof(uint32_t));
+    }
+
+    /* Whether auth is complete */
+    reds_stream_write_u8(stream, err == SASL_CONTINUE ? 0 : 1);
+
+    if (err == SASL_CONTINUE) {
+        spice_info("%s", "Authentication must continue (step)");
+        /* Wait for step length */
+        obj->now = (uint8_t *)&sasl->len;
+        obj->end = obj->now + sizeof(uint32_t);
+        obj->done = read_cb;
+        async_read_handler(0, 0, &stream->async_read);
+        return REDS_SASL_ERROR_CONTINUE;
+    } else {
+        int ssf;
+
+        if (auth_sasl_check_ssf(sasl, &ssf) == 0) {
+            spice_warning("Authentication rejected for weak SSF");
+            goto authreject;
+        }
+
+        spice_info("Authentication successful");
+        reds_stream_write_u32(stream, SPICE_LINK_ERR_OK); /* Accept auth */
+
+        /*
+         * Delay writing in SSF encoded until now
+         */
+        sasl->runSSF = ssf;
+        reds_stream_disable_writev(stream); /* make sure writev isn't called directly anymore */
+
+        return REDS_SASL_ERROR_OK;
+    }
+
+authreject:
+    reds_stream_write_u32(stream, 1); /* Reject auth */
+    reds_stream_write_u32(stream, sizeof("Authentication failed"));
+    reds_stream_write_all(stream, "Authentication failed", sizeof("Authentication failed"));
+
+    return REDS_SASL_ERROR_AUTH_FAILED;
+}
+
+RedsSaslError reds_sasl_handle_auth_steplen(RedsStream *stream, AsyncReadDone read_cb, void *opaque)
+{
+    AsyncRead *obj = &stream->async_read;
+    RedsSASL *sasl = &stream->sasl;
+
+    spice_info("Got steplen %d", sasl->len);
+    if (sasl->len > SASL_DATA_MAX_LEN) {
+        spice_warning("Too much SASL data %d", sasl->len);
+        return REDS_SASL_ERROR_INVALID_DATA;
+    }
+
+    if (sasl->len == 0) {
+        read_cb(opaque);
+        /* FIXME: can't report potential errors correctly here,
+         * but read_cb() will have done the needed RedLinkInfo cleanups
+         * if an error occurs, so the caller should not need to do more
+         * treatment */
+        return REDS_SASL_ERROR_OK;
+    } else {
+        sasl->data = spice_realloc(sasl->data, sasl->len);
+        obj->now = (uint8_t *)sasl->data;
+        obj->end = obj->now + sasl->len;
+        obj->done = read_cb;
+        async_read_handler(0, 0, obj);
+        return REDS_SASL_ERROR_OK;
+    }
+}
+
+/*
+ * Start Msg
+ *
+ * Input from client:
+ *
+ * u32 clientin-length
+ * u8-array clientin-string
+ *
+ * Output to client:
+ *
+ * u32 serverout-length
+ * u8-array serverout-strin
+ * u8 continue
+ */
+
+RedsSaslError reds_sasl_handle_auth_start(RedsStream *stream, AsyncReadDone read_cb, void *opaque)
+{
+    AsyncRead *obj = &stream->async_read;
+    const char *serverout;
+    unsigned int serveroutlen;
+    int err;
+    char *clientdata = NULL;
+    RedsSASL *sasl = &stream->sasl;
+    uint32_t datalen = sasl->len;
+
+    /* NB, distinction of NULL vs "" is *critical* in SASL */
+    if (datalen) {
+        clientdata = sasl->data;
+        clientdata[datalen - 1] = '\0'; /* Should be on wire, but make sure */
+        datalen--; /* Don't count NULL byte when passing to _start() */
+    }
+
+    spice_info("Start SASL auth with mechanism %s. Data %p (%d bytes)",
+               sasl->mechlist, clientdata, datalen);
+    err = sasl_server_start(sasl->conn,
+                            sasl->mechlist,
+                            clientdata,
+                            datalen,
+                            &serverout,
+                            &serveroutlen);
+    if (err != SASL_OK &&
+        err != SASL_CONTINUE) {
+        spice_warning("sasl start failed %d (%s)",
+                    err, sasl_errdetail(sasl->conn));
+        return REDS_SASL_ERROR_INVALID_DATA;
+    }
+
+    if (serveroutlen > SASL_DATA_MAX_LEN) {
+        spice_warning("sasl start reply data too long %d",
+                    serveroutlen);
+        return REDS_SASL_ERROR_INVALID_DATA;
+    }
+
+    spice_info("SASL return data %d bytes, %p", serveroutlen, serverout);
+
+    if (serveroutlen) {
+        serveroutlen += 1;
+        reds_stream_write_all(stream, &serveroutlen, sizeof(uint32_t));
+        reds_stream_write_all(stream, serverout, serveroutlen);
+    } else {
+        reds_stream_write_all(stream, &serveroutlen, sizeof(uint32_t));
+    }
+
+    /* Whether auth is complete */
+    reds_stream_write_u8(stream, err == SASL_CONTINUE ? 0 : 1);
+
+    if (err == SASL_CONTINUE) {
+        spice_info("%s", "Authentication must continue (start)");
+        /* Wait for step length */
+        obj->now = (uint8_t *)&sasl->len;
+        obj->end = obj->now + sizeof(uint32_t);
+        obj->done = read_cb;
+        async_read_handler(0, 0, &stream->async_read);
+        return REDS_SASL_ERROR_CONTINUE;
+    } else {
+        int ssf;
+
+        if (auth_sasl_check_ssf(sasl, &ssf) == 0) {
+            spice_warning("Authentication rejected for weak SSF");
+            goto authreject;
+        }
+
+        spice_info("Authentication successful");
+        reds_stream_write_u32(stream, SPICE_LINK_ERR_OK); /* Accept auth */
+
+        /*
+         * Delay writing in SSF encoded until now
+         */
+        sasl->runSSF = ssf;
+        reds_stream_disable_writev(stream); /* make sure writev isn't called directly anymore */
+        return REDS_SASL_ERROR_OK;
+    }
+
+authreject:
+    reds_stream_write_u32(stream, 1); /* Reject auth */
+    reds_stream_write_u32(stream, sizeof("Authentication failed"));
+    reds_stream_write_all(stream, "Authentication failed", sizeof("Authentication failed"));
+
+    return REDS_SASL_ERROR_AUTH_FAILED;
+}
+
+RedsSaslError reds_sasl_handle_auth_startlen(RedsStream *stream, AsyncReadDone read_cb, void *opaque)
+{
+    AsyncRead *obj = &stream->async_read;
+    RedsSASL *sasl = &stream->sasl;
+
+    spice_info("Got client start len %d", sasl->len);
+    if (sasl->len > SASL_DATA_MAX_LEN) {
+        spice_warning("Too much SASL data %d", sasl->len);
+        return REDS_SASL_ERROR_INVALID_DATA;
+    }
+
+    if (sasl->len == 0) {
+        return REDS_SASL_ERROR_RETRY;
+    }
+
+    sasl->data = spice_realloc(sasl->data, sasl->len);
+    obj->now = (uint8_t *)sasl->data;
+    obj->end = obj->now + sasl->len;
+    obj->done = read_cb;
+    async_read_handler(0, 0, obj);
+
+    return REDS_SASL_ERROR_OK;
+}
+
+bool reds_sasl_handle_auth_mechname(RedsStream *stream, AsyncReadDone read_cb, void *opaque)
+{
+    AsyncRead *obj = &stream->async_read;
+    RedsSASL *sasl = &stream->sasl;
+
+    sasl->mechname[sasl->len] = '\0';
+    spice_info("Got client mechname '%s' check against '%s'",
+               sasl->mechname, sasl->mechlist);
+
+    if (strncmp(sasl->mechlist, sasl->mechname, sasl->len) == 0) {
+        if (sasl->mechlist[sasl->len] != '\0' &&
+            sasl->mechlist[sasl->len] != ',') {
+            spice_info("One %d", sasl->mechlist[sasl->len]);
+            return FALSE;
+        }
+    } else {
+        char *offset = strstr(sasl->mechlist, sasl->mechname);
+        spice_info("Two %p", offset);
+        if (!offset) {
+            return FALSE;
+        }
+        spice_info("Two '%s'", offset);
+        if (offset[-1] != ',' ||
+            (offset[sasl->len] != '\0'&&
+             offset[sasl->len] != ',')) {
+            return FALSE;
+        }
+    }
+
+    free(sasl->mechlist);
+    sasl->mechlist = spice_strdup(sasl->mechname);
+
+    spice_info("Validated mechname '%s'", sasl->mechname);
+
+    obj->now = (uint8_t *)&sasl->len;
+    obj->end = obj->now + sizeof(uint32_t);
+    obj->done = read_cb;
+    async_read_handler(0, 0, &stream->async_read);
+
+    return TRUE;
+}
+
+bool reds_sasl_handle_auth_mechlen(RedsStream *stream, AsyncReadDone read_cb, void *opaque)
+{
+    AsyncRead *obj = &stream->async_read;
+    RedsSASL *sasl = &stream->sasl;
+
+    if (sasl->len < 1 || sasl->len > 100) {
+        spice_warning("Got bad client mechname len %d", sasl->len);
+        return FALSE;
+    }
+
+    sasl->mechname = spice_malloc(sasl->len + 1);
+
+    spice_info("Wait for client mechname");
+    obj->now = (uint8_t *)sasl->mechname;
+    obj->end = obj->now + sasl->len;
+    obj->done = read_cb;
+    async_read_handler(0, 0, &stream->async_read);
+
+    return TRUE;
+}
+
+bool reds_sasl_start_auth(RedsStream *stream, AsyncReadDone read_cb, void *opaque)
+{
+    const char *mechlist = NULL;
+    sasl_security_properties_t secprops;
+    int err;
+    char *localAddr, *remoteAddr;
+    int mechlistlen;
+    AsyncRead *obj = &stream->async_read;
+    RedsSASL *sasl = &stream->sasl;
+
+    if (!(localAddr = reds_stream_get_local_address(stream))) {
+        goto error;
+    }
+
+    if (!(remoteAddr = reds_stream_get_remote_address(stream))) {
+        free(localAddr);
+        goto error;
+    }
+
+    err = sasl_server_new("spice",
+                          NULL, /* FQDN - just delegates to gethostname */
+                          NULL, /* User realm */
+                          localAddr,
+                          remoteAddr,
+                          NULL, /* Callbacks, not needed */
+                          SASL_SUCCESS_DATA,
+                          &sasl->conn);
+    free(localAddr);
+    free(remoteAddr);
+    localAddr = remoteAddr = NULL;
+
+    if (err != SASL_OK) {
+        spice_warning("sasl context setup failed %d (%s)",
+                    err, sasl_errstring(err, NULL, NULL));
+        sasl->conn = NULL;
+        goto error;
+    }
+
+    /* Inform SASL that we've got an external SSF layer from TLS */
+    if (stream->ssl) {
+        sasl_ssf_t ssf;
+
+        ssf = SSL_get_cipher_bits(stream->ssl, NULL);
+        err = sasl_setprop(sasl->conn, SASL_SSF_EXTERNAL, &ssf);
+        if (err != SASL_OK) {
+            spice_warning("cannot set SASL external SSF %d (%s)",
+                        err, sasl_errstring(err, NULL, NULL));
+            goto error_dispose;
+        }
+    } else {
+        sasl->wantSSF = 1;
+    }
+
+    memset(&secprops, 0, sizeof secprops);
+    /* Inform SASL that we've got an external SSF layer from TLS */
+    if (stream->ssl) {
+        /* If we've got TLS (or UNIX domain sock), we don't care about SSF */
+        secprops.min_ssf = 0;
+        secprops.max_ssf = 0;
+        secprops.maxbufsize = 8192;
+        secprops.security_flags = 0;
+    } else {
+        /* Plain TCP, better get an SSF layer */
+        secprops.min_ssf = 56; /* Good enough to require kerberos */
+        secprops.max_ssf = 100000; /* Arbitrary big number */
+        secprops.maxbufsize = 8192;
+        /* Forbid any anonymous or trivially crackable auth */
+        secprops.security_flags =
+            SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT;
+    }
+
+    err = sasl_setprop(sasl->conn, SASL_SEC_PROPS, &secprops);
+    if (err != SASL_OK) {
+        spice_warning("cannot set SASL security props %d (%s)",
+                      err, sasl_errstring(err, NULL, NULL));
+        goto error_dispose;
+    }
+
+    err = sasl_listmech(sasl->conn,
+                        NULL, /* Don't need to set user */
+                        "", /* Prefix */
+                        ",", /* Separator */
+                        "", /* Suffix */
+                        &mechlist,
+                        NULL,
+                        NULL);
+    if (err != SASL_OK || mechlist == NULL) {
+        spice_warning("cannot list SASL mechanisms %d (%s)",
+                      err, sasl_errdetail(sasl->conn));
+        goto error_dispose;
+    }
+
+    spice_info("Available mechanisms for client: '%s'", mechlist);
+
+    sasl->mechlist = spice_strdup(mechlist);
+
+    mechlistlen = strlen(mechlist);
+    if (!reds_stream_write_all(stream, &mechlistlen, sizeof(uint32_t))
+        || !reds_stream_write_all(stream, sasl->mechlist, mechlistlen)) {
+        spice_warning("SASL mechanisms write error");
+        goto error;
+    }
+
+    spice_info("Wait for client mechname length");
+    obj->now = (uint8_t *)&sasl->len;
+    obj->end = obj->now + sizeof(uint32_t);
+    obj->done = read_cb;
+    obj->opaque = opaque;
+    async_read_handler(0, 0, obj);
+
+    return TRUE;
+
+error_dispose:
+    sasl_dispose(&sasl->conn);
+    sasl->conn = NULL;
+error:
+    return FALSE;
+}
 #endif
diff --git a/server/reds_stream.h b/server/reds_stream.h
index fa41cbb..cae244b 100644
--- a/server/reds_stream.h
+++ b/server/reds_stream.h
@@ -115,6 +115,7 @@ ssize_t reds_stream_writev(RedsStream *s, const struct iovec *iov, int iovcnt);
 bool reds_stream_write_all(RedsStream *stream, const void *in_buf, size_t n);
 bool reds_stream_write_u8(RedsStream *s, uint8_t n);
 bool reds_stream_write_u32(RedsStream *s, uint32_t n);
+void reds_stream_disable_writev(RedsStream *stream);
 void reds_stream_free(RedsStream *s);
 
 void reds_stream_push_channel_event(RedsStream *s, int event);
@@ -123,4 +124,21 @@ RedsStream *reds_stream_new(int socket);
 RedsStreamSslStatus reds_stream_ssl_accept(RedsStream *stream);
 int reds_stream_enable_ssl(RedsStream *stream, SSL_CTX *ctx);
 
+typedef enum {
+    REDS_SASL_ERROR_OK,
+    REDS_SASL_ERROR_GENERIC,
+    REDS_SASL_ERROR_INVALID_DATA,
+    REDS_SASL_ERROR_RETRY,
+    REDS_SASL_ERROR_CONTINUE,
+    REDS_SASL_ERROR_AUTH_FAILED
+} RedsSaslError;
+
+RedsSaslError reds_sasl_handle_auth_step(RedsStream *stream, AsyncReadDone read_cb, void *opaque);
+RedsSaslError reds_sasl_handle_auth_steplen(RedsStream *stream, AsyncReadDone read_cb, void *opaque);
+RedsSaslError reds_sasl_handle_auth_start(RedsStream *stream, AsyncReadDone read_cb, void *opaque);
+RedsSaslError reds_sasl_handle_auth_startlen(RedsStream *stream, AsyncReadDone read_cb, void *opaque);
+bool reds_sasl_handle_auth_mechname(RedsStream *stream, AsyncReadDone read_cb, void *opaque);
+bool reds_sasl_handle_auth_mechlen(RedsStream *stream, AsyncReadDone read_cb, void *opaque);
+bool reds_sasl_start_auth(RedsStream *stream, AsyncReadDone read_cb, void *opaque);
+
 #endif
commit 9feed6940ffda3171883a366a48693e8df6c5338
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Mon Oct 7 18:40:36 2013 +0200

    Move async code to RedsStream
    
    The AsyncRead structure in reds.h wraps an async read + callback to
    be done on a stream. Moving it to reds_stream.h is needed in order
    to move SASL authentication there.

diff --git a/server/reds.c b/server/reds.c
index b70b98d..15291c5 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -121,15 +121,6 @@ static bool exit_on_disconnect = FALSE;
 
 static RedsState *reds = NULL;
 
-typedef struct AsyncRead {
-    RedsStream *stream;
-    void *opaque;
-    uint8_t *now;
-    uint8_t *end;
-    void (*done)(void *opaque);
-    void (*error)(void *opaque, int err);
-} AsyncRead;
-
 typedef struct RedLinkInfo {
     RedsStream *stream;
     AsyncRead async_read;
@@ -1880,57 +1871,6 @@ end:
     g_free(password);
 }
 
-static inline void async_read_clear_handlers(AsyncRead *obj)
-{
-    if (!obj->stream->watch) {
-        return;
-    }
-
-    reds_stream_remove_watch(obj->stream);
-}
-
-static void async_read_handler(int fd, int event, void *data)
-{
-    AsyncRead *obj = (AsyncRead *)data;
-
-    for (;;) {
-        int n = obj->end - obj->now;
-
-        spice_assert(n > 0);
-        n = reds_stream_read(obj->stream, obj->now, n);
-        if (n <= 0) {
-            if (n < 0) {
-                switch (errno) {
-                case EAGAIN:
-                    if (!obj->stream->watch) {
-                        obj->stream->watch = core->watch_add(obj->stream->socket,
-                                                           SPICE_WATCH_EVENT_READ,
-                                                           async_read_handler, obj);
-                    }
-                    return;
-                case EINTR:
-                    break;
-                default:
-                    async_read_clear_handlers(obj);
-                    obj->error(obj->opaque, errno);
-                    return;
-                }
-            } else {
-                async_read_clear_handlers(obj);
-                obj->error(obj->opaque, 0);
-                return;
-            }
-        } else {
-            obj->now += n;
-            if (obj->now == obj->end) {
-                async_read_clear_handlers(obj);
-                obj->done(obj->opaque);
-                return;
-            }
-        }
-    }
-}
-
 static void reds_get_spice_ticket(RedLinkInfo *link)
 {
     AsyncRead *obj = &link->async_read;
diff --git a/server/reds_stream.c b/server/reds_stream.c
index b3614e6..f4513e6 100644
--- a/server/reds_stream.c
+++ b/server/reds_stream.c
@@ -246,6 +246,8 @@ RedsStream *reds_stream_new(int socket)
     stream->write = stream_write_cb;
     stream->writev = stream_writev_cb;
 
+    stream->async_read.stream = stream;
+
     return stream;
 }
 
@@ -303,6 +305,68 @@ int reds_stream_enable_ssl(RedsStream *stream, SSL_CTX *ctx)
     return reds_stream_ssl_accept(stream);
 }
 
+void async_read_set_error_handler(AsyncRead *async,
+                                  AsyncReadError error_handler,
+                                 void *opaque)
+{
+    async->error = error_handler;
+}
+
+static inline void async_read_clear_handlers(AsyncRead *obj)
+{
+    if (!obj->stream->watch) {
+        return;
+    }
+
+    reds_stream_remove_watch(obj->stream);
+}
+
+void async_read_handler(int fd, int event, void *data)
+{
+    AsyncRead *obj = (AsyncRead *)data;
+
+    for (;;) {
+        int n = obj->end - obj->now;
+
+        spice_assert(n > 0);
+        n = reds_stream_read(obj->stream, obj->now, n);
+        if (n <= 0) {
+            if (n < 0) {
+                switch (errno) {
+                case EAGAIN:
+                    if (!obj->stream->watch) {
+                        obj->stream->watch = core->watch_add(obj->stream->socket,
+                                                           SPICE_WATCH_EVENT_READ,
+                                                           async_read_handler, obj);
+                    }
+                    return;
+                case EINTR:
+                    break;
+                default:
+                    async_read_clear_handlers(obj);
+		    if (obj->error) {
+                        obj->error(obj->opaque, errno);
+		    }
+                    return;
+                }
+            } else {
+                async_read_clear_handlers(obj);
+		if (obj->error) {
+		    obj->error(obj->opaque, 0);
+		}
+                return;
+            }
+        } else {
+            obj->now += n;
+            if (obj->now == obj->end) {
+                async_read_clear_handlers(obj);
+                obj->done(obj->opaque);
+                return;
+            }
+        }
+    }
+}
+
 #if HAVE_SASL
 bool reds_stream_write_u8(RedsStream *s, uint8_t n)
 {
diff --git a/server/reds_stream.h b/server/reds_stream.h
index ed1c61d..fa41cbb 100644
--- a/server/reds_stream.h
+++ b/server/reds_stream.h
@@ -28,6 +28,9 @@
 #if HAVE_SASL
 #include <sasl/sasl.h>
 
+typedef void (*AsyncReadDone)(void *opaque);
+typedef void (*AsyncReadError)(void *opaque, int err);
+
 typedef struct RedsSASL {
     sasl_conn_t *conn;
 
@@ -57,6 +60,19 @@ typedef struct RedsSASL {
 #endif
 
 typedef struct RedsStream RedsStream;
+typedef struct AsyncRead {
+    RedsStream *stream;
+    void *opaque;
+    uint8_t *now;
+    uint8_t *end;
+    AsyncReadDone done;
+    AsyncReadError error;
+} AsyncRead;
+
+void async_read_handler(int fd, int event, void *data);
+void async_read_set_error_handler(AsyncRead *async,
+                                  AsyncReadError error_handler,
+                                  void *opaque);
 
 struct RedsStream {
     int socket;
@@ -67,6 +83,8 @@ struct RedsStream {
     int shutdown;
     SSL *ssl;
 
+    AsyncRead async_read;
+
 #if HAVE_SASL
     RedsSASL sasl;
 #endif
commit cdaab7272c25a63f1709e5c8fc012a6484326015
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Tue Oct 8 09:20:43 2013 +0200

    Move stream read/write callbacks to reds_stream.c
    
    Now that stream creation and SSL enabling are done by helpers
    in reds_stream.c, we can move the initialization of the vfunc
    read/write pointers there too.

diff --git a/server/reds.c b/server/reds.c
index 841a2de..b70b98d 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -191,73 +191,6 @@ void reds_handle_channel_event(int event, SpiceChannelEventInfo *info)
     }
 }
 
-static ssize_t stream_write_cb(RedsStream *s, const void *buf, size_t size)
-{
-    return write(s->socket, buf, size);
-}
-
-static ssize_t stream_writev_cb(RedsStream *s, const struct iovec *iov, int iovcnt)
-{
-    ssize_t ret = 0;
-    do {
-        int tosend;
-        ssize_t n, expected = 0;
-        int i;
-#ifdef IOV_MAX
-        tosend = MIN(iovcnt, IOV_MAX);
-#else
-        tosend = iovcnt;
-#endif
-        for (i = 0; i < tosend; i++) {
-            expected += iov[i].iov_len;
-        }
-        n = writev(s->socket, iov, tosend);
-        if (n <= expected) {
-            if (n > 0)
-                ret += n;
-            return ret == 0 ? n : ret;
-        }
-        ret += n;
-        iov += tosend;
-        iovcnt -= tosend;
-    } while(iovcnt > 0);
-
-    return ret;
-}
-
-static ssize_t stream_read_cb(RedsStream *s, void *buf, size_t size)
-{
-    return read(s->socket, buf, size);
-}
-
-static ssize_t stream_ssl_write_cb(RedsStream *s, const void *buf, size_t size)
-{
-    int return_code;
-    SPICE_GNUC_UNUSED int ssl_error;
-
-    return_code = SSL_write(s->ssl, buf, size);
-
-    if (return_code < 0) {
-        ssl_error = SSL_get_error(s->ssl, return_code);
-    }
-
-    return return_code;
-}
-
-static ssize_t stream_ssl_read_cb(RedsStream *s, void *buf, size_t size)
-{
-    int return_code;
-    SPICE_GNUC_UNUSED int ssl_error;
-
-    return_code = SSL_read(s->ssl, buf, size);
-
-    if (return_code < 0) {
-        ssl_error = SSL_get_error(s->ssl, return_code);
-    }
-
-    return return_code;
-}
-
 static void reds_link_free(RedLinkInfo *link)
 {
     reds_stream_free(link->stream);
@@ -2738,10 +2671,6 @@ static RedLinkInfo *reds_init_client_ssl_connection(int socket)
     if (link == NULL)
         goto error;
 
-    link->stream->write = stream_ssl_write_cb;
-    link->stream->read = stream_ssl_read_cb;
-    link->stream->writev = NULL;
-
     ssl_status = reds_stream_enable_ssl(link->stream, reds->ctx);
     switch (ssl_status) {
         case REDS_STREAM_SSL_STATUS_OK:
@@ -2801,7 +2730,6 @@ static void reds_accept(int fd, int event, void *data)
 SPICE_GNUC_VISIBLE int spice_server_add_client(SpiceServer *s, int socket, int skip_auth)
 {
     RedLinkInfo *link;
-    RedsStream *stream;
 
     spice_assert(reds == s);
     if (!(link = reds_init_client_connection(socket))) {
@@ -2811,11 +2739,6 @@ SPICE_GNUC_VISIBLE int spice_server_add_client(SpiceServer *s, int socket, int s
 
     link->skip_auth = skip_auth;
 
-    stream = link->stream;
-    stream->read = stream_read_cb;
-    stream->write = stream_write_cb;
-    stream->writev = stream_writev_cb;
-
     reds_handle_new_link(link);
     return 0;
 }
diff --git a/server/reds_stream.c b/server/reds_stream.c
index e9337cf..b3614e6 100644
--- a/server/reds_stream.c
+++ b/server/reds_stream.c
@@ -33,6 +33,73 @@
 
 extern SpiceCoreInterface *core;
 
+static ssize_t stream_write_cb(RedsStream *s, const void *buf, size_t size)
+{
+    return write(s->socket, buf, size);
+}
+
+static ssize_t stream_writev_cb(RedsStream *s, const struct iovec *iov, int iovcnt)
+{
+    ssize_t ret = 0;
+    do {
+        int tosend;
+        ssize_t n, expected = 0;
+        int i;
+#ifdef IOV_MAX
+        tosend = MIN(iovcnt, IOV_MAX);
+#else
+        tosend = iovcnt;
+#endif
+        for (i = 0; i < tosend; i++) {
+            expected += iov[i].iov_len;
+        }
+        n = writev(s->socket, iov, tosend);
+        if (n <= expected) {
+            if (n > 0)
+                ret += n;
+            return ret == 0 ? n : ret;
+        }
+        ret += n;
+        iov += tosend;
+        iovcnt -= tosend;
+    } while(iovcnt > 0);
+
+    return ret;
+}
+
+static ssize_t stream_read_cb(RedsStream *s, void *buf, size_t size)
+{
+    return read(s->socket, buf, size);
+}
+
+static ssize_t stream_ssl_write_cb(RedsStream *s, const void *buf, size_t size)
+{
+    int return_code;
+    SPICE_GNUC_UNUSED int ssl_error;
+
+    return_code = SSL_write(s->ssl, buf, size);
+
+    if (return_code < 0) {
+        ssl_error = SSL_get_error(s->ssl, return_code);
+    }
+
+    return return_code;
+}
+
+static ssize_t stream_ssl_read_cb(RedsStream *s, void *buf, size_t size)
+{
+    int return_code;
+    SPICE_GNUC_UNUSED int ssl_error;
+
+    return_code = SSL_read(s->ssl, buf, size);
+
+    if (return_code < 0) {
+        ssl_error = SSL_get_error(s->ssl, return_code);
+    }
+
+    return return_code;
+}
+
 void reds_stream_remove_watch(RedsStream* s)
 {
     if (s->watch) {
@@ -175,6 +242,10 @@ RedsStream *reds_stream_new(int socket)
     stream->info = spice_new0(SpiceChannelEventInfo, 1);
     reds_stream_set_socket(stream, socket);
 
+    stream->read = stream_read_cb;
+    stream->write = stream_write_cb;
+    stream->writev = stream_writev_cb;
+
     return stream;
 }
 
@@ -225,6 +296,10 @@ int reds_stream_enable_ssl(RedsStream *stream, SSL_CTX *ctx)
 
     SSL_set_bio(stream->ssl, sbio, sbio);
 
+    stream->write = stream_ssl_write_cb;
+    stream->read = stream_ssl_read_cb;
+    stream->writev = NULL;
+
     return reds_stream_ssl_accept(stream);
 }
 
commit e0abf1adc23886ecf99ad110d2151b187b75919e
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Mon Oct 7 14:18:32 2013 +0200

    Introduce reds_stream_new() helper
    
    Initializing a new stream means initializing quite a few fields.
    This commit factors this initialization in a dedicated reds_stream_new
    helper. This also helps moving more code from reds.c to reds_stream.c

diff --git a/server/reds.c b/server/reds.c
index 2e56758..841a2de 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -2694,7 +2694,6 @@ static void reds_handle_ssl_accept(int fd, int event, void *data)
 static RedLinkInfo *reds_init_client_connection(int socket)
 {
     RedLinkInfo *link;
-    RedsStream *stream;
     int delay_val = 1;
     int flags;
 
@@ -2715,28 +2714,11 @@ static RedLinkInfo *reds_init_client_connection(int socket)
     }
 
     link = spice_new0(RedLinkInfo, 1);
-    stream = spice_new0(RedsStream, 1);
-    stream->info = spice_new0(SpiceChannelEventInfo, 1);
-    link->stream = stream;
+    link->stream = reds_stream_new(socket);
 
-    stream->socket = socket;
     /* gather info + send event */
 
-    /* deprecated fields. Filling them for backward compatibility */
-    stream->info->llen = sizeof(stream->info->laddr);
-    stream->info->plen = sizeof(stream->info->paddr);
-    getsockname(stream->socket, (struct sockaddr*)(&stream->info->laddr), &stream->info->llen);
-    getpeername(stream->socket, (struct sockaddr*)(&stream->info->paddr), &stream->info->plen);
-
-    stream->info->flags |= SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT;
-    stream->info->llen_ext = sizeof(stream->info->laddr_ext);
-    stream->info->plen_ext = sizeof(stream->info->paddr_ext);
-    getsockname(stream->socket, (struct sockaddr*)(&stream->info->laddr_ext),
-                &stream->info->llen_ext);
-    getpeername(stream->socket, (struct sockaddr*)(&stream->info->paddr_ext),
-                &stream->info->plen_ext);
-
-    reds_stream_push_channel_event(stream, SPICE_CHANNEL_EVENT_CONNECTED);
+    reds_stream_push_channel_event(link->stream, SPICE_CHANNEL_EVENT_CONNECTED);
 
     openssl_init(link);
 
diff --git a/server/reds_stream.c b/server/reds_stream.c
index 5ec0efa..e9337cf 100644
--- a/server/reds_stream.c
+++ b/server/reds_stream.c
@@ -149,6 +149,35 @@ void reds_stream_push_channel_event(RedsStream *s, int event)
     main_dispatcher_channel_event(event, s->info);
 }
 
+static void reds_stream_set_socket(RedsStream *stream, int socket)
+{
+    stream->socket = socket;
+    /* deprecated fields. Filling them for backward compatibility */
+    stream->info->llen = sizeof(stream->info->laddr);
+    stream->info->plen = sizeof(stream->info->paddr);
+    getsockname(stream->socket, (struct sockaddr*)(&stream->info->laddr), &stream->info->llen);
+    getpeername(stream->socket, (struct sockaddr*)(&stream->info->paddr), &stream->info->plen);
+
+    stream->info->flags |= SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT;
+    stream->info->llen_ext = sizeof(stream->info->laddr_ext);
+    stream->info->plen_ext = sizeof(stream->info->paddr_ext);
+    getsockname(stream->socket, (struct sockaddr*)(&stream->info->laddr_ext),
+                &stream->info->llen_ext);
+    getpeername(stream->socket, (struct sockaddr*)(&stream->info->paddr_ext),
+                &stream->info->plen_ext);
+}
+
+RedsStream *reds_stream_new(int socket)
+{
+    RedsStream *stream;
+
+    stream = spice_new0(RedsStream, 1);
+    stream->info = spice_new0(SpiceChannelEventInfo, 1);
+    reds_stream_set_socket(stream, socket);
+
+    return stream;
+}
+
 RedsStreamSslStatus reds_stream_ssl_accept(RedsStream *stream)
 {
     int ssl_error;
diff --git a/server/reds_stream.h b/server/reds_stream.h
index a0a3651..ed1c61d 100644
--- a/server/reds_stream.h
+++ b/server/reds_stream.h
@@ -101,6 +101,7 @@ void reds_stream_free(RedsStream *s);
 
 void reds_stream_push_channel_event(RedsStream *s, int event);
 void reds_stream_remove_watch(RedsStream* s);
+RedsStream *reds_stream_new(int socket);
 RedsStreamSslStatus reds_stream_ssl_accept(RedsStream *stream);
 int reds_stream_enable_ssl(RedsStream *stream, SSL_CTX *ctx);
 
commit d533f72fe6d20a20f26846a3b3c43b3571f4f2a8
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Mon Oct 7 18:46:31 2013 +0200

    reds: Move SSL-related code to RedsStream
    
    Code to initiate a SSL stream belongs there

diff --git a/server/reds.c b/server/reds.c
index 3f625fa..2e56758 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -37,12 +37,8 @@
 #include <ctype.h>
 #include <stdbool.h>
 
-#include <openssl/bio.h>
-#include <openssl/pem.h>
-#include <openssl/bn.h>
-#include <openssl/rsa.h>
-#include <openssl/ssl.h>
 #include <openssl/err.h>
+
 #if HAVE_SASL
 #include <sasl/sasl.h>
 #endif
@@ -2677,24 +2673,22 @@ static void reds_handle_new_link(RedLinkInfo *link)
 static void reds_handle_ssl_accept(int fd, int event, void *data)
 {
     RedLinkInfo *link = (RedLinkInfo *)data;
-    int return_code;
+    int return_code = reds_stream_ssl_accept(link->stream);
 
-    if ((return_code = SSL_accept(link->stream->ssl)) != 1) {
-        int ssl_error = SSL_get_error(link->stream->ssl, return_code);
-        if (ssl_error != SSL_ERROR_WANT_READ && ssl_error != SSL_ERROR_WANT_WRITE) {
-            spice_warning("SSL_accept failed, error=%d", ssl_error);
+    switch (return_code) {
+        case REDS_STREAM_SSL_STATUS_ERROR:
             reds_link_free(link);
-        } else {
-            if (ssl_error == SSL_ERROR_WANT_READ) {
-                core->watch_update_mask(link->stream->watch, SPICE_WATCH_EVENT_READ);
-            } else {
-                core->watch_update_mask(link->stream->watch, SPICE_WATCH_EVENT_WRITE);
-            }
-        }
-        return;
+            return;
+        case REDS_STREAM_SSL_STATUS_WAIT_FOR_READ:
+            core->watch_update_mask(link->stream->watch, SPICE_WATCH_EVENT_READ);
+            return;
+        case REDS_STREAM_SSL_STATUS_WAIT_FOR_WRITE:
+            core->watch_update_mask(link->stream->watch, SPICE_WATCH_EVENT_WRITE);
+            return;
+        case REDS_STREAM_SSL_STATUS_OK:
+            reds_stream_remove_watch(link->stream);
+            reds_handle_new_link(link);
     }
-    reds_stream_remove_watch(link->stream);
-    reds_handle_new_link(link);
 }
 
 static RedLinkInfo *reds_init_client_connection(int socket)
@@ -2756,52 +2750,33 @@ error:
 static RedLinkInfo *reds_init_client_ssl_connection(int socket)
 {
     RedLinkInfo *link;
-    int return_code;
-    int ssl_error;
-    BIO *sbio;
+    int ssl_status;
 
     link = reds_init_client_connection(socket);
     if (link == NULL)
         goto error;
 
-    // Handle SSL handshaking
-    if (!(sbio = BIO_new_socket(link->stream->socket, BIO_NOCLOSE))) {
-        spice_warning("could not allocate ssl bio socket");
-        goto error;
-    }
-
-    link->stream->ssl = SSL_new(reds->ctx);
-    if (!link->stream->ssl) {
-        spice_warning("could not allocate ssl context");
-        BIO_free(sbio);
-        goto error;
-    }
-
-    SSL_set_bio(link->stream->ssl, sbio, sbio);
-
     link->stream->write = stream_ssl_write_cb;
     link->stream->read = stream_ssl_read_cb;
     link->stream->writev = NULL;
 
-    return_code = SSL_accept(link->stream->ssl);
-    if (return_code == 1) {
-        reds_handle_new_link(link);
-        return link;
-    }
-
-    ssl_error = SSL_get_error(link->stream->ssl, return_code);
-    if (return_code == -1 && (ssl_error == SSL_ERROR_WANT_READ ||
-                              ssl_error == SSL_ERROR_WANT_WRITE)) {
-        int eventmask = ssl_error == SSL_ERROR_WANT_READ ?
-            SPICE_WATCH_EVENT_READ : SPICE_WATCH_EVENT_WRITE;
-        link->stream->watch = core->watch_add(link->stream->socket, eventmask,
+    ssl_status = reds_stream_enable_ssl(link->stream, reds->ctx);
+    switch (ssl_status) {
+        case REDS_STREAM_SSL_STATUS_OK:
+            reds_handle_new_link(link);
+            return link;
+        case REDS_STREAM_SSL_STATUS_ERROR:
+            goto error;
+        case REDS_STREAM_SSL_STATUS_WAIT_FOR_READ:
+            link->stream->watch = core->watch_add(link->stream->socket, SPICE_WATCH_EVENT_READ,
                                             reds_handle_ssl_accept, link);
-        return link;
+            break;
+        case REDS_STREAM_SSL_STATUS_WAIT_FOR_WRITE:
+            link->stream->watch = core->watch_add(link->stream->socket, SPICE_WATCH_EVENT_WRITE,
+                                                  reds_handle_ssl_accept, link);
+            break;
     }
-
-    ERR_print_errors_fp(stderr);
-    spice_warning("SSL_accept failed, error=%d", ssl_error);
-    SSL_free(link->stream->ssl);
+    return link;
 
 error:
     free(link->stream);
diff --git a/server/reds_stream.c b/server/reds_stream.c
index 093621f..5ec0efa 100644
--- a/server/reds_stream.c
+++ b/server/reds_stream.c
@@ -149,6 +149,56 @@ void reds_stream_push_channel_event(RedsStream *s, int event)
     main_dispatcher_channel_event(event, s->info);
 }
 
+RedsStreamSslStatus reds_stream_ssl_accept(RedsStream *stream)
+{
+    int ssl_error;
+    int return_code;
+
+    return_code = SSL_accept(stream->ssl);
+    if (return_code == 1) {
+        return REDS_STREAM_SSL_STATUS_OK;
+    }
+
+    ssl_error = SSL_get_error(stream->ssl, return_code);
+    if (return_code == -1 && (ssl_error == SSL_ERROR_WANT_READ ||
+                              ssl_error == SSL_ERROR_WANT_WRITE)) {
+        if (ssl_error == SSL_ERROR_WANT_READ) {
+            return REDS_STREAM_SSL_STATUS_WAIT_FOR_READ;
+        } else {
+            return REDS_STREAM_SSL_STATUS_WAIT_FOR_WRITE;
+        }
+    }
+
+    ERR_print_errors_fp(stderr);
+    spice_warning("SSL_accept failed, error=%d", ssl_error);
+    SSL_free(stream->ssl);
+    stream->ssl = NULL;
+
+    return REDS_STREAM_SSL_STATUS_ERROR;
+}
+
+int reds_stream_enable_ssl(RedsStream *stream, SSL_CTX *ctx)
+{
+    BIO *sbio;
+
+    // Handle SSL handshaking
+    if (!(sbio = BIO_new_socket(stream->socket, BIO_NOCLOSE))) {
+        spice_warning("could not allocate ssl bio socket");
+        return REDS_STREAM_SSL_STATUS_ERROR;
+    }
+
+    stream->ssl = SSL_new(ctx);
+    if (!stream->ssl) {
+        spice_warning("could not allocate ssl context");
+        BIO_free(sbio);
+        return REDS_STREAM_SSL_STATUS_ERROR;
+    }
+
+    SSL_set_bio(stream->ssl, sbio, sbio);
+
+    return reds_stream_ssl_accept(stream);
+}
+
 #if HAVE_SASL
 bool reds_stream_write_u8(RedsStream *s, uint8_t n)
 {
diff --git a/server/reds_stream.h b/server/reds_stream.h
index c725414..a0a3651 100644
--- a/server/reds_stream.h
+++ b/server/reds_stream.h
@@ -101,5 +101,7 @@ void reds_stream_free(RedsStream *s);
 
 void reds_stream_push_channel_event(RedsStream *s, int event);
 void reds_stream_remove_watch(RedsStream* s);
+RedsStreamSslStatus reds_stream_ssl_accept(RedsStream *stream);
+int reds_stream_enable_ssl(RedsStream *stream, SSL_CTX *ctx);
 
 #endif
commit e46743100f1668bc1af358d65442b282f89784c1
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Mon Oct 7 13:58:55 2013 +0200

    Move sync_write* to reds_stream.h
    
    They are renamed to reds_stream_write*

diff --git a/server/reds.c b/server/reds.c
index 6e43fea..3f625fa 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -1396,24 +1396,6 @@ int reds_handle_migrate_data(MainChannelClient *mcc, SpiceMigrateDataMain *mig_d
     return TRUE;
 }
 
-static int sync_write(RedsStream *stream, const void *in_buf, size_t n)
-{
-    const uint8_t *buf = (uint8_t *)in_buf;
-
-    while (n) {
-        int now = reds_stream_write(stream, buf, n);
-        if (now <= 0) {
-            if (now == -1 && (errno == EINTR || errno == EAGAIN)) {
-                continue;
-            }
-            return FALSE;
-        }
-        n -= now;
-        buf += now;
-    }
-    return TRUE;
-}
-
 static void reds_channel_init_auth_caps(RedLinkInfo *link, RedChannel *channel)
 {
     if (sasl_enabled && !link->skip_auth) {
@@ -1474,13 +1456,13 @@ static int reds_send_link_ack(RedLinkInfo *link)
     BIO_get_mem_ptr(bio, &bmBuf);
     memcpy(ack.pub_key, bmBuf->data, sizeof(ack.pub_key));
 
-    if (!sync_write(link->stream, &header, sizeof(header)))
+    if (!reds_stream_write_all(link->stream, &header, sizeof(header)))
         goto end;
-    if (!sync_write(link->stream, &ack, sizeof(ack)))
+    if (!reds_stream_write_all(link->stream, &ack, sizeof(ack)))
         goto end;
-    if (!sync_write(link->stream, channel_caps->common_caps, channel_caps->num_common_caps * sizeof(uint32_t)))
+    if (!reds_stream_write_all(link->stream, channel_caps->common_caps, channel_caps->num_common_caps * sizeof(uint32_t)))
         goto end;
-    if (!sync_write(link->stream, channel_caps->caps, channel_caps->num_caps * sizeof(uint32_t)))
+    if (!reds_stream_write_all(link->stream, channel_caps->caps, channel_caps->num_caps * sizeof(uint32_t)))
         goto end;
 
     ret = TRUE;
@@ -1490,7 +1472,7 @@ end:
     return ret;
 }
 
-static int reds_send_link_error(RedLinkInfo *link, uint32_t error)
+static bool reds_send_link_error(RedLinkInfo *link, uint32_t error)
 {
     SpiceLinkHeader header;
     SpiceLinkReply reply;
@@ -1501,7 +1483,7 @@ static int reds_send_link_error(RedLinkInfo *link, uint32_t error)
     header.minor_version = SPICE_VERSION_MINOR;
     memset(&reply, 0, sizeof(reply));
     reply.error = error;
-    return sync_write(link->stream, &header, sizeof(header)) && sync_write(link->stream, &reply,
+    return reds_stream_write_all(link->stream, &header, sizeof(header)) && reds_stream_write_all(link->stream, &reply,
                                                                          sizeof(reply));
 }
 
@@ -1523,7 +1505,7 @@ static void reds_info_new_channel(RedLinkInfo *link, int connection_id)
 
 static void reds_send_link_result(RedLinkInfo *link, uint32_t error)
 {
-    sync_write(link->stream, &error, sizeof(error));
+    reds_stream_write_all(link->stream, &error, sizeof(error));
 }
 
 int reds_expects_link_id(uint32_t connection_id)
@@ -1978,18 +1960,6 @@ static inline void async_read_clear_handlers(AsyncRead *obj)
     reds_stream_remove_watch(obj->stream);
 }
 
-#if HAVE_SASL
-static int sync_write_u8(RedsStream *s, uint8_t n)
-{
-    return sync_write(s, &n, sizeof(uint8_t));
-}
-
-static int sync_write_u32(RedsStream *s, uint32_t n)
-{
-    return sync_write(s, &n, sizeof(uint32_t));
-}
-#endif
-
 static void async_read_handler(int fd, int event, void *data)
 {
     AsyncRead *obj = (AsyncRead *)data;
@@ -2158,14 +2128,14 @@ static void reds_handle_auth_sasl_step(void *opaque)
 
     if (serveroutlen) {
         serveroutlen += 1;
-        sync_write(link->stream, &serveroutlen, sizeof(uint32_t));
-        sync_write(link->stream, serverout, serveroutlen);
+        reds_stream_write_all(link->stream, &serveroutlen, sizeof(uint32_t));
+        reds_stream_write_all(link->stream, serverout, serveroutlen);
     } else {
-        sync_write(link->stream, &serveroutlen, sizeof(uint32_t));
+        reds_stream_write_all(link->stream, &serveroutlen, sizeof(uint32_t));
     }
 
     /* Whether auth is complete */
-    sync_write_u8(link->stream, err == SASL_CONTINUE ? 0 : 1);
+    reds_stream_write_u8(link->stream, err == SASL_CONTINUE ? 0 : 1);
 
     if (err == SASL_CONTINUE) {
         spice_info("%s", "Authentication must continue (step)");
@@ -2183,7 +2153,7 @@ static void reds_handle_auth_sasl_step(void *opaque)
         }
 
         spice_info("Authentication successful");
-        sync_write_u32(link->stream, SPICE_LINK_ERR_OK); /* Accept auth */
+        reds_stream_write_u32(link->stream, SPICE_LINK_ERR_OK); /* Accept auth */
 
         /*
          * Delay writing in SSF encoded until now
@@ -2197,9 +2167,9 @@ static void reds_handle_auth_sasl_step(void *opaque)
     return;
 
 authreject:
-    sync_write_u32(link->stream, 1); /* Reject auth */
-    sync_write_u32(link->stream, sizeof("Authentication failed"));
-    sync_write(link->stream, "Authentication failed", sizeof("Authentication failed"));
+    reds_stream_write_u32(link->stream, 1); /* Reject auth */
+    reds_stream_write_u32(link->stream, sizeof("Authentication failed"));
+    reds_stream_write_all(link->stream, "Authentication failed", sizeof("Authentication failed"));
 
 authabort:
     reds_link_free(link);
@@ -2289,14 +2259,14 @@ static void reds_handle_auth_sasl_start(void *opaque)
 
     if (serveroutlen) {
         serveroutlen += 1;
-        sync_write(link->stream, &serveroutlen, sizeof(uint32_t));
-        sync_write(link->stream, serverout, serveroutlen);
+        reds_stream_write_all(link->stream, &serveroutlen, sizeof(uint32_t));
+        reds_stream_write_all(link->stream, serverout, serveroutlen);
     } else {
-        sync_write(link->stream, &serveroutlen, sizeof(uint32_t));
+        reds_stream_write_all(link->stream, &serveroutlen, sizeof(uint32_t));
     }
 
     /* Whether auth is complete */
-    sync_write_u8(link->stream, err == SASL_CONTINUE ? 0 : 1);
+    reds_stream_write_u8(link->stream, err == SASL_CONTINUE ? 0 : 1);
 
     if (err == SASL_CONTINUE) {
         spice_info("%s", "Authentication must continue (start)");
@@ -2314,7 +2284,7 @@ static void reds_handle_auth_sasl_start(void *opaque)
         }
 
         spice_info("Authentication successful");
-        sync_write_u32(link->stream, SPICE_LINK_ERR_OK); /* Accept auth */
+        reds_stream_write_u32(link->stream, SPICE_LINK_ERR_OK); /* Accept auth */
 
         /*
          * Delay writing in SSF encoded until now
@@ -2328,9 +2298,9 @@ static void reds_handle_auth_sasl_start(void *opaque)
     return;
 
 authreject:
-    sync_write_u32(link->stream, 1); /* Reject auth */
-    sync_write_u32(link->stream, sizeof("Authentication failed"));
-    sync_write(link->stream, "Authentication failed", sizeof("Authentication failed"));
+    reds_stream_write_u32(link->stream, 1); /* Reject auth */
+    reds_stream_write_u32(link->stream, sizeof("Authentication failed"));
+    reds_stream_write_all(link->stream, "Authentication failed", sizeof("Authentication failed"));
 
 authabort:
     reds_link_free(link);
@@ -2530,8 +2500,8 @@ static void reds_start_auth_sasl(RedLinkInfo *link)
     sasl->mechlist = spice_strdup(mechlist);
 
     mechlistlen = strlen(mechlist);
-    if (!sync_write(link->stream, &mechlistlen, sizeof(uint32_t))
-        || !sync_write(link->stream, sasl->mechlist, mechlistlen)) {
+    if (!reds_stream_write_all(link->stream, &mechlistlen, sizeof(uint32_t))
+        || !reds_stream_write_all(link->stream, sasl->mechlist, mechlistlen)) {
         spice_warning("SASL mechanisms write error");
         goto error;
     }
diff --git a/server/reds_stream.c b/server/reds_stream.c
index 7adc745..093621f 100644
--- a/server/reds_stream.c
+++ b/server/reds_stream.c
@@ -57,6 +57,24 @@ ssize_t reds_stream_read(RedsStream *s, void *buf, size_t nbyte)
     return ret;
 }
 
+bool reds_stream_write_all(RedsStream *stream, const void *in_buf, size_t n)
+{
+    const uint8_t *buf = (uint8_t *)in_buf;
+
+    while (n) {
+        int now = reds_stream_write(stream, buf, n);
+        if (now <= 0) {
+            if (now == -1 && (errno == EINTR || errno == EAGAIN)) {
+                continue;
+            }
+            return FALSE;
+        }
+        n -= now;
+        buf += now;
+    }
+    return TRUE;
+}
+
 static ssize_t reds_stream_sasl_write(RedsStream *s, const void *buf, size_t nbyte);
 
 ssize_t reds_stream_write(RedsStream *s, const void *buf, size_t nbyte)
@@ -132,6 +150,16 @@ void reds_stream_push_channel_event(RedsStream *s, int event)
 }
 
 #if HAVE_SASL
+bool reds_stream_write_u8(RedsStream *s, uint8_t n)
+{
+    return reds_stream_write_all(s, &n, sizeof(uint8_t));
+}
+
+bool reds_stream_write_u32(RedsStream *s, uint32_t n)
+{
+    return reds_stream_write_all(s, &n, sizeof(uint32_t));
+}
+
 static ssize_t reds_stream_sasl_write(RedsStream *s, const void *buf, size_t nbyte)
 {
     ssize_t ret;
diff --git a/server/reds_stream.h b/server/reds_stream.h
index ca18f75..c725414 100644
--- a/server/reds_stream.h
+++ b/server/reds_stream.h
@@ -21,6 +21,8 @@
 #include "spice.h"
 #include "common/mem.h"
 
+#include <stdbool.h>
+
 #include <openssl/ssl.h>
 
 #if HAVE_SASL
@@ -92,6 +94,9 @@ typedef enum {
 ssize_t reds_stream_read(RedsStream *s, void *buf, size_t nbyte);
 ssize_t reds_stream_write(RedsStream *s, const void *buf, size_t nbyte);
 ssize_t reds_stream_writev(RedsStream *s, const struct iovec *iov, int iovcnt);
+bool reds_stream_write_all(RedsStream *stream, const void *in_buf, size_t n);
+bool reds_stream_write_u8(RedsStream *s, uint8_t n);
+bool reds_stream_write_u32(RedsStream *s, uint32_t n);
 void reds_stream_free(RedsStream *s);
 
 void reds_stream_push_channel_event(RedsStream *s, int event);
commit 8b347a641c885fdc31a41a1b1a55da982b580265
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Mon Oct 7 13:50:20 2013 +0200

    Add reds_stream.[ch]
    
    Gather common RedsStream code there rather than having it
    in reds.c

diff --git a/server/Makefile.am b/server/Makefile.am
index 13c6223..34219c8 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -87,6 +87,8 @@ libspice_server_la_SOURCES =			\
 	reds.c					\
 	reds.h					\
 	reds-private.h				\
+	reds_stream.c				\
+	reds_stream.h				\
 	reds_sw_canvas.c			\
 	reds_sw_canvas.h			\
 	snd_worker.c				\
diff --git a/server/inputs_channel.c b/server/inputs_channel.c
index dd8f5ae..8d4feab 100644
--- a/server/inputs_channel.c
+++ b/server/inputs_channel.c
@@ -37,6 +37,7 @@
 #include "spice.h"
 #include "red_common.h"
 #include "reds.h"
+#include "reds_stream.h"
 #include "red_channel.h"
 #include "main_channel.h"
 #include "inputs_channel.h"
diff --git a/server/red_channel.c b/server/red_channel.c
index 24a8b64..b81deeb 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -40,6 +40,7 @@
 #include "stat.h"
 #include "red_channel.h"
 #include "reds.h"
+#include "reds_stream.h"
 #include "main_dispatcher.h"
 #include "red_time.h"
 
diff --git a/server/red_channel.h b/server/red_channel.h
index 9e54dce..f638d3c 100644
--- a/server/red_channel.h
+++ b/server/red_channel.h
@@ -31,6 +31,7 @@
 #include "spice.h"
 #include "red_common.h"
 #include "demarshallers.h"
+#include "reds_stream.h"
 
 #define MAX_SEND_BUFS 1000
 #define CLIENT_ACK_WINDOW 20
@@ -131,7 +132,6 @@ typedef struct BufDescriptor {
     uint8_t *data;
 } BufDescriptor;
 
-typedef struct RedsStream RedsStream;
 typedef struct RedChannel RedChannel;
 typedef struct RedChannelClient RedChannelClient;
 typedef struct RedClient RedClient;
diff --git a/server/red_worker.c b/server/red_worker.c
index afbdd91..619f7bc 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -64,6 +64,7 @@
 
 #include "spice.h"
 #include "red_worker.h"
+#include "reds_stream.h"
 #include "reds_sw_canvas.h"
 #include "glz_encoder_dictionary.h"
 #include "glz_encoder.h"
diff --git a/server/reds.c b/server/reds.c
index d79732c..6e43fea 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -73,6 +73,7 @@
 #ifdef USE_SMARTCARD
 #include "smartcard.h"
 #endif
+#include "reds_stream.h"
 
 #include "reds-private.h"
 
@@ -184,11 +185,6 @@ static ChannelSecurityOptions *find_channel_security(int id)
     return now;
 }
 
-static void reds_stream_push_channel_event(RedsStream *s, int event)
-{
-    main_dispatcher_channel_event(event, s->info);
-}
-
 void reds_handle_channel_event(int event, SpiceChannelEventInfo *info)
 {
     if (core->base.minor_version >= 3 && core->channel_event != NULL)
@@ -266,14 +262,6 @@ static ssize_t stream_ssl_read_cb(RedsStream *s, void *buf, size_t size)
     return return_code;
 }
 
-static void reds_stream_remove_watch(RedsStream* s)
-{
-    if (s->watch) {
-        core->watch_remove(s->watch);
-        s->watch = NULL;
-    }
-}
-
 static void reds_link_free(RedLinkInfo *link)
 {
     reds_stream_free(link->stream);
@@ -2000,92 +1988,6 @@ static int sync_write_u32(RedsStream *s, uint32_t n)
 {
     return sync_write(s, &n, sizeof(uint32_t));
 }
-
-static ssize_t reds_stream_sasl_write(RedsStream *s, const void *buf, size_t nbyte)
-{
-    ssize_t ret;
-
-    if (!s->sasl.encoded) {
-        int err;
-        err = sasl_encode(s->sasl.conn, (char *)buf, nbyte,
-                          (const char **)&s->sasl.encoded,
-                          &s->sasl.encodedLength);
-        if (err != SASL_OK) {
-            spice_warning("sasl_encode error: %d", err);
-            return -1;
-        }
-
-        if (s->sasl.encodedLength == 0) {
-            return 0;
-        }
-
-        if (!s->sasl.encoded) {
-            spice_warning("sasl_encode didn't return a buffer!");
-            return 0;
-        }
-
-        s->sasl.encodedOffset = 0;
-    }
-
-    ret = s->write(s, s->sasl.encoded + s->sasl.encodedOffset,
-                   s->sasl.encodedLength - s->sasl.encodedOffset);
-
-    if (ret <= 0) {
-        return ret;
-    }
-
-    s->sasl.encodedOffset += ret;
-    if (s->sasl.encodedOffset == s->sasl.encodedLength) {
-        s->sasl.encoded = NULL;
-        s->sasl.encodedOffset = s->sasl.encodedLength = 0;
-        return nbyte;
-    }
-
-    /* we didn't flush the encoded buffer */
-    errno = EAGAIN;
-    return -1;
-}
-
-static ssize_t reds_stream_sasl_read(RedsStream *s, uint8_t *buf, size_t nbyte)
-{
-    uint8_t encoded[4096];
-    const char *decoded;
-    unsigned int decodedlen;
-    int err;
-    int n;
-
-    n = spice_buffer_copy(&s->sasl.inbuffer, buf, nbyte);
-    if (n > 0) {
-        spice_buffer_remove(&s->sasl.inbuffer, n);
-        if (n == nbyte)
-            return n;
-        nbyte -= n;
-        buf += n;
-    }
-
-    n = s->read(s, encoded, sizeof(encoded));
-    if (n <= 0) {
-        return n;
-    }
-
-    err = sasl_decode(s->sasl.conn,
-                      (char *)encoded, n,
-                      &decoded, &decodedlen);
-    if (err != SASL_OK) {
-        spice_warning("sasl_decode error: %d", err);
-        return -1;
-    }
-
-    if (decodedlen == 0) {
-        errno = EAGAIN;
-        return -1;
-    }
-
-    n = MIN(nbyte, decodedlen);
-    memcpy(buf, decoded, n);
-    spice_buffer_append(&s->sasl.inbuffer, decoded + n, decodedlen - n);
-    return n;
-}
 #endif
 
 static void async_read_handler(int fd, int event, void *data)
@@ -4509,84 +4411,3 @@ SPICE_GNUC_VISIBLE void spice_server_set_seamless_migration(SpiceServer *s, int
     reds->seamless_migration_enabled = enable && !reds->allow_multiple_clients;
     spice_debug("seamless migration enabled=%d", enable);
 }
-
-ssize_t reds_stream_read(RedsStream *s, void *buf, size_t nbyte)
-{
-    ssize_t ret;
-
-#if HAVE_SASL
-    if (s->sasl.conn && s->sasl.runSSF) {
-        ret = reds_stream_sasl_read(s, buf, nbyte);
-    } else
-#endif
-        ret = s->read(s, buf, nbyte);
-
-    return ret;
-}
-
-ssize_t reds_stream_write(RedsStream *s, const void *buf, size_t nbyte)
-{
-    ssize_t ret;
-
-#if HAVE_SASL
-    if (s->sasl.conn && s->sasl.runSSF) {
-        ret = reds_stream_sasl_write(s, buf, nbyte);
-    } else
-#endif
-        ret = s->write(s, buf, nbyte);
-
-    return ret;
-}
-
-ssize_t reds_stream_writev(RedsStream *s, const struct iovec *iov, int iovcnt)
-{
-    int i;
-    int n;
-    ssize_t ret = 0;
-
-    if (s->writev != NULL) {
-        return s->writev(s, iov, iovcnt);
-    }
-
-    for (i = 0; i < iovcnt; ++i) {
-        n = reds_stream_write(s, iov[i].iov_base, iov[i].iov_len);
-        if (n <= 0)
-            return ret == 0 ? n : ret;
-        ret += n;
-    }
-
-    return ret;
-}
-
-void reds_stream_free(RedsStream *s)
-{
-    if (!s) {
-        return;
-    }
-
-    reds_stream_push_channel_event(s, SPICE_CHANNEL_EVENT_DISCONNECTED);
-
-#if HAVE_SASL
-    if (s->sasl.conn) {
-        s->sasl.runSSF = s->sasl.wantSSF = 0;
-        s->sasl.len = 0;
-        s->sasl.encodedLength = s->sasl.encodedOffset = 0;
-        s->sasl.encoded = NULL;
-        free(s->sasl.mechlist);
-        free(s->sasl.mechname);
-        s->sasl.mechlist = NULL;
-        sasl_dispose(&s->sasl.conn);
-        s->sasl.conn = NULL;
-    }
-#endif
-
-    if (s->ssl) {
-        SSL_free(s->ssl);
-    }
-
-    reds_stream_remove_watch(s);
-    spice_info("close socket fd %d", s->socket);
-    close(s->socket);
-
-    free(s);
-}
diff --git a/server/reds.h b/server/reds.h
index 3efea6a..eabe0af 100644
--- a/server/reds.h
+++ b/server/reds.h
@@ -19,14 +19,10 @@
 #define _H_REDS
 
 #include <stdint.h>
-#include <openssl/ssl.h>
 #include <sys/uio.h>
 #include <spice/vd_agent.h>
 #include <config.h>
 
-#if HAVE_SASL
-#include <sasl/sasl.h>
-#endif
 
 #include "common/marshaller.h"
 #include "common/messages.h"
@@ -34,60 +30,6 @@
 #include "red_channel.h"
 #include "migration_protocol.h"
 
-#if HAVE_SASL
-typedef struct RedsSASL {
-    sasl_conn_t *conn;
-
-    /* If we want to negotiate an SSF layer with client */
-    int wantSSF :1;
-    /* If we are now running the SSF layer */
-    int runSSF :1;
-
-    /*
-     * Buffering encoded data to allow more clear data
-     * to be stuffed onto the output buffer
-     */
-    const uint8_t *encoded;
-    unsigned int encodedLength;
-    unsigned int encodedOffset;
-
-    SpiceBuffer inbuffer;
-
-    char *username;
-    char *mechlist;
-    char *mechname;
-
-    /* temporary data during authentication */
-    unsigned int len;
-    char *data;
-} RedsSASL;
-#endif
-
-struct RedsStream {
-    int socket;
-    SpiceWatch *watch;
-
-    /* set it to TRUE if you shutdown the socket. shutdown read doesn't work as accepted -
-       receive may return data afterward. check the flag before calling receive*/
-    int shutdown;
-    SSL *ssl;
-
-#if HAVE_SASL
-    RedsSASL sasl;
-#endif
-
-    /* life time of info:
-     * allocated when creating RedsStream.
-     * deallocated when main_dispatcher handles the SPICE_CHANNEL_EVENT_DISCONNECTED
-     * event, either from same thread or by call back from main thread. */
-    SpiceChannelEventInfo* info;
-
-    /* private */
-    ssize_t (*read)(RedsStream *s, void *buf, size_t nbyte);
-    ssize_t (*write)(RedsStream *s, const void *buf, size_t nbyte);
-    ssize_t (*writev)(RedsStream *s, const struct iovec *iov, int iovcnt);
-};
-
 struct QXLState {
     QXLInterface          *qif;
     struct RedDispatcher  *dispatcher;
@@ -109,12 +51,6 @@ typedef struct RedsMigSpice {
     int sport;
 } RedsMigSpice;
 
-/* any thread */
-ssize_t reds_stream_read(RedsStream *s, void *buf, size_t nbyte);
-ssize_t reds_stream_write(RedsStream *s, const void *buf, size_t nbyte);
-ssize_t reds_stream_writev(RedsStream *s, const struct iovec *iov, int iovcnt);
-void reds_stream_free(RedsStream *s);
-
 /* main thread only */
 void reds_handle_channel_event(int event, SpiceChannelEventInfo *info);
 
diff --git a/server/reds_stream.c b/server/reds_stream.c
new file mode 100644
index 0000000..7adc745
--- /dev/null
+++ b/server/reds_stream.c
@@ -0,0 +1,220 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2009, 2013 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "main_dispatcher.h"
+#include "red_common.h"
+#include "reds_stream.h"
+#include "common/log.h"
+
+#include <errno.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <sys/socket.h>
+
+#include <openssl/err.h>
+
+extern SpiceCoreInterface *core;
+
+void reds_stream_remove_watch(RedsStream* s)
+{
+    if (s->watch) {
+        core->watch_remove(s->watch);
+        s->watch = NULL;
+    }
+}
+
+static ssize_t reds_stream_sasl_read(RedsStream *s, uint8_t *buf, size_t nbyte);
+
+ssize_t reds_stream_read(RedsStream *s, void *buf, size_t nbyte)
+{
+    ssize_t ret;
+
+#if HAVE_SASL
+    if (s->sasl.conn && s->sasl.runSSF) {
+        ret = reds_stream_sasl_read(s, buf, nbyte);
+    } else
+#endif
+        ret = s->read(s, buf, nbyte);
+
+    return ret;
+}
+
+static ssize_t reds_stream_sasl_write(RedsStream *s, const void *buf, size_t nbyte);
+
+ssize_t reds_stream_write(RedsStream *s, const void *buf, size_t nbyte)
+{
+    ssize_t ret;
+
+#if HAVE_SASL
+    if (s->sasl.conn && s->sasl.runSSF) {
+        ret = reds_stream_sasl_write(s, buf, nbyte);
+    } else
+#endif
+        ret = s->write(s, buf, nbyte);
+
+    return ret;
+}
+
+ssize_t reds_stream_writev(RedsStream *s, const struct iovec *iov, int iovcnt)
+{
+    int i;
+    int n;
+    ssize_t ret = 0;
+
+    if (s->writev != NULL) {
+        return s->writev(s, iov, iovcnt);
+    }
+
+    for (i = 0; i < iovcnt; ++i) {
+        n = reds_stream_write(s, iov[i].iov_base, iov[i].iov_len);
+        if (n <= 0)
+            return ret == 0 ? n : ret;
+        ret += n;
+    }
+
+    return ret;
+}
+
+void reds_stream_free(RedsStream *s)
+{
+    if (!s) {
+        return;
+    }
+
+    reds_stream_push_channel_event(s, SPICE_CHANNEL_EVENT_DISCONNECTED);
+
+#if HAVE_SASL
+    if (s->sasl.conn) {
+        s->sasl.runSSF = s->sasl.wantSSF = 0;
+        s->sasl.len = 0;
+        s->sasl.encodedLength = s->sasl.encodedOffset = 0;
+        s->sasl.encoded = NULL;
+        free(s->sasl.mechlist);
+        free(s->sasl.mechname);
+        s->sasl.mechlist = NULL;
+        sasl_dispose(&s->sasl.conn);
+        s->sasl.conn = NULL;
+    }
+#endif
+
+    if (s->ssl) {
+        SSL_free(s->ssl);
+    }
+
+    reds_stream_remove_watch(s);
+    spice_info("close socket fd %d", s->socket);
+    close(s->socket);
+
+    free(s);
+}
+
+void reds_stream_push_channel_event(RedsStream *s, int event)
+{
+    main_dispatcher_channel_event(event, s->info);
+}
+
+#if HAVE_SASL
+static ssize_t reds_stream_sasl_write(RedsStream *s, const void *buf, size_t nbyte)
+{
+    ssize_t ret;
+
+    if (!s->sasl.encoded) {
+        int err;
+        err = sasl_encode(s->sasl.conn, (char *)buf, nbyte,
+                          (const char **)&s->sasl.encoded,
+                          &s->sasl.encodedLength);
+        if (err != SASL_OK) {
+            spice_warning("sasl_encode error: %d", err);
+            return -1;
+        }
+
+        if (s->sasl.encodedLength == 0) {
+            return 0;
+        }
+
+        if (!s->sasl.encoded) {
+            spice_warning("sasl_encode didn't return a buffer!");
+            return 0;
+        }
+
+        s->sasl.encodedOffset = 0;
+    }
+
+    ret = s->write(s, s->sasl.encoded + s->sasl.encodedOffset,
+                   s->sasl.encodedLength - s->sasl.encodedOffset);
+
+    if (ret <= 0) {
+        return ret;
+    }
+
+    s->sasl.encodedOffset += ret;
+    if (s->sasl.encodedOffset == s->sasl.encodedLength) {
+        s->sasl.encoded = NULL;
+        s->sasl.encodedOffset = s->sasl.encodedLength = 0;
+        return nbyte;
+    }
+
+    /* we didn't flush the encoded buffer */
+    errno = EAGAIN;
+    return -1;
+}
+
+static ssize_t reds_stream_sasl_read(RedsStream *s, uint8_t *buf, size_t nbyte)
+{
+    uint8_t encoded[4096];
+    const char *decoded;
+    unsigned int decodedlen;
+    int err;
+    int n;
+
+    n = spice_buffer_copy(&s->sasl.inbuffer, buf, nbyte);
+    if (n > 0) {
+        spice_buffer_remove(&s->sasl.inbuffer, n);
+        if (n == nbyte)
+            return n;
+        nbyte -= n;
+        buf += n;
+    }
+
+    n = s->read(s, encoded, sizeof(encoded));
+    if (n <= 0) {
+        return n;
+    }
+
+    err = sasl_decode(s->sasl.conn,
+                      (char *)encoded, n,
+                      &decoded, &decodedlen);
+    if (err != SASL_OK) {
+        spice_warning("sasl_decode error: %d", err);
+        return -1;
+    }
+
+    if (decodedlen == 0) {
+        errno = EAGAIN;
+        return -1;
+    }
+
+    n = MIN(nbyte, decodedlen);
+    memcpy(buf, decoded, n);
+    spice_buffer_append(&s->sasl.inbuffer, decoded + n, decodedlen - n);
+    return n;
+}
+#endif
diff --git a/server/reds_stream.h b/server/reds_stream.h
new file mode 100644
index 0000000..ca18f75
--- /dev/null
+++ b/server/reds_stream.h
@@ -0,0 +1,100 @@
+/*
+   Copyright (C) 2009, 2013 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _H_REDS_STREAM
+#define _H_REDS_STREAM
+
+#include "spice.h"
+#include "common/mem.h"
+
+#include <openssl/ssl.h>
+
+#if HAVE_SASL
+#include <sasl/sasl.h>
+
+typedef struct RedsSASL {
+    sasl_conn_t *conn;
+
+    /* If we want to negotiate an SSF layer with client */
+    int wantSSF :1;
+    /* If we are now running the SSF layer */
+    int runSSF :1;
+
+    /*
+     * Buffering encoded data to allow more clear data
+     * to be stuffed onto the output buffer
+     */
+    const uint8_t *encoded;
+    unsigned int encodedLength;
+    unsigned int encodedOffset;
+
+    SpiceBuffer inbuffer;
+
+    char *username;
+    char *mechlist;
+    char *mechname;
+
+    /* temporary data during authentication */
+    unsigned int len;
+    char *data;
+} RedsSASL;
+#endif
+
+typedef struct RedsStream RedsStream;
+
+struct RedsStream {
+    int socket;
+    SpiceWatch *watch;
+
+    /* set it to TRUE if you shutdown the socket. shutdown read doesn't work as accepted -
+       receive may return data afterward. check the flag before calling receive*/
+    int shutdown;
+    SSL *ssl;
+
+#if HAVE_SASL
+    RedsSASL sasl;
+#endif
+
+    /* life time of info:
+     * allocated when creating RedsStream.
+     * deallocated when main_dispatcher handles the SPICE_CHANNEL_EVENT_DISCONNECTED
+     * event, either from same thread or by call back from main thread. */
+    SpiceChannelEventInfo* info;
+
+    /* private */
+    ssize_t (*read)(RedsStream *s, void *buf, size_t nbyte);
+    ssize_t (*write)(RedsStream *s, const void *buf, size_t nbyte);
+    ssize_t (*writev)(RedsStream *s, const struct iovec *iov, int iovcnt);
+};
+
+typedef enum {
+    REDS_STREAM_SSL_STATUS_OK,
+    REDS_STREAM_SSL_STATUS_ERROR,
+    REDS_STREAM_SSL_STATUS_WAIT_FOR_READ,
+    REDS_STREAM_SSL_STATUS_WAIT_FOR_WRITE
+} RedsStreamSslStatus;
+
+/* any thread */
+ssize_t reds_stream_read(RedsStream *s, void *buf, size_t nbyte);
+ssize_t reds_stream_write(RedsStream *s, const void *buf, size_t nbyte);
+ssize_t reds_stream_writev(RedsStream *s, const struct iovec *iov, int iovcnt);
+void reds_stream_free(RedsStream *s);
+
+void reds_stream_push_channel_event(RedsStream *s, int event);
+void reds_stream_remove_watch(RedsStream* s);
+
+#endif
commit 73c56e5a2d952bf566c3fbff5e9aefff70ddd59a
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Mon Oct 7 14:01:24 2013 +0200

    Move SPICE_GNUC_VISIBLE to red_common.h

diff --git a/server/red_common.h b/server/red_common.h
index 585b13c..b6b643a 100644
--- a/server/red_common.h
+++ b/server/red_common.h
@@ -29,6 +29,8 @@
 
 #include "spice.h"
 
+#define SPICE_GNUC_VISIBLE __attribute__ ((visibility ("default")))
+
 enum {
     STREAM_VIDEO_INVALID,
     STREAM_VIDEO_OFF,
diff --git a/server/reds.h b/server/reds.h
index 1c5ae84..3efea6a 100644
--- a/server/reds.h
+++ b/server/reds.h
@@ -34,8 +34,6 @@
 #include "red_channel.h"
 #include "migration_protocol.h"
 
-#define SPICE_GNUC_VISIBLE __attribute__ ((visibility ("default")))
-
 #if HAVE_SASL
 typedef struct RedsSASL {
     sasl_conn_t *conn;


More information about the Spice-commits mailing list