/* -*- 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 .
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include "main_dispatcher.h"
#include "red_common.h"
#include "reds_stream.h"
#include "common/log.h"
#include
#include
#include
#include
#include
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