summaryrefslogtreecommitdiffstats
path: root/server/reds_stream.c
diff options
context:
space:
mode:
authorChristophe Fergeau <cfergeau@redhat.com>2013-10-07 13:50:20 +0200
committerChristophe Fergeau <cfergeau@redhat.com>2014-01-20 12:15:41 +0100
commit8b347a641c885fdc31a41a1b1a55da982b580265 (patch)
tree659abc85944ebd4c41fb12eaf34223d807991abc /server/reds_stream.c
parent73c56e5a2d952bf566c3fbff5e9aefff70ddd59a (diff)
downloadspice-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.c220
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