diff options
author | Christophe Fergeau <cfergeau@redhat.com> | 2013-10-07 13:50:20 +0200 |
---|---|---|
committer | Christophe Fergeau <cfergeau@redhat.com> | 2014-01-20 12:15:41 +0100 |
commit | 8b347a641c885fdc31a41a1b1a55da982b580265 (patch) | |
tree | 659abc85944ebd4c41fb12eaf34223d807991abc /server/reds_stream.c | |
parent | 73c56e5a2d952bf566c3fbff5e9aefff70ddd59a (diff) | |
download | spice-8b347a641c885fdc31a41a1b1a55da982b580265.tar.gz spice-8b347a641c885fdc31a41a1b1a55da982b580265.tar.xz spice-8b347a641c885fdc31a41a1b1a55da982b580265.zip |
Add reds_stream.[ch]
Gather common RedsStream code there rather than having it
in reds.c
Diffstat (limited to 'server/reds_stream.c')
-rw-r--r-- | server/reds_stream.c | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/server/reds_stream.c b/server/reds_stream.c new file mode 100644 index 00000000..7adc745f --- /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 |