dovecot-2.2: lib-ssl-iostream: Make the input buffering behave t...
dovecot at dovecot.org
dovecot at dovecot.org
Sat Oct 13 00:24:58 EEST 2012
details: http://hg.dovecot.org/dovecot-2.2/rev/083bd881b9da
changeset: 15214:083bd881b9da
user: Timo Sirainen <tss at iki.fi>
date: Sat Oct 13 00:24:47 2012 +0300
description:
lib-ssl-iostream: Make the input buffering behave the same as in file-istream
Previously i_stream_read(ssl_input) could have still left some data buffered
into the underlying file-istream, which meant that I/O loop didn't detect
any new input from the fd and the connection got stuck.
diffstat:
src/lib-ssl-iostream/iostream-openssl.c | 42 ++++++++++++++++++++---
src/lib-ssl-iostream/iostream-openssl.h | 1 +
src/lib-ssl-iostream/istream-openssl.c | 58 +++++++++++++++++++++++++++++---
3 files changed, 88 insertions(+), 13 deletions(-)
diffs (178 lines):
diff -r 78749ae0c9c2 -r 083bd881b9da src/lib-ssl-iostream/iostream-openssl.c
--- a/src/lib-ssl-iostream/iostream-openssl.c Fri Oct 12 23:18:58 2012 +0300
+++ b/src/lib-ssl-iostream/iostream-openssl.c Sat Oct 13 00:24:47 2012 +0300
@@ -1,7 +1,7 @@
/* Copyright (c) 2009-2012 Dovecot authors, see the included COPYING file */
#include "lib.h"
-#include "istream.h"
+#include "istream-private.h"
#include "ostream-private.h"
#include "iostream-openssl.h"
@@ -326,18 +326,39 @@
return bytes_sent;
}
+static ssize_t
+ssl_iostream_read_more(struct ssl_iostream *ssl_io,
+ const unsigned char **data_r, size_t *size_r)
+{
+ *data_r = i_stream_get_data(ssl_io->plain_input, size_r);
+ if (*size_r > 0)
+ return 0;
+
+ if (!ssl_io->input_handler) {
+ /* read plain_input only when we came here from input handler.
+ this makes sure that we don't get stuck with some input
+ unexpectedly buffered. */
+ return 0;
+ }
+
+ if (i_stream_read_data(ssl_io->plain_input, data_r, size_r, 0) < 0)
+ return -1;
+ return 0;
+}
+
static bool ssl_iostream_bio_input(struct ssl_iostream *ssl_io)
{
const unsigned char *data;
size_t bytes, size;
+ int ret;
bool bytes_read = FALSE;
- int ret;
while ((bytes = BIO_ctrl_get_write_guarantee(ssl_io->bio_ext)) > 0) {
/* bytes contains how many bytes we can write to bio_ext */
- if (i_stream_read_data(ssl_io->plain_input,
- &data, &size, 0) == -1 &&
- size == 0 && !bytes_read) {
+ ssl_io->plain_input->real_stream->try_alloc_limit = bytes;
+ ret = ssl_iostream_read_more(ssl_io, &data, &size);
+ ssl_io->plain_input->real_stream->try_alloc_limit = 0;
+ if (ret == -1 && size == 0 && !bytes_read) {
ssl_io->plain_stream_errno =
ssl_io->plain_input->stream_errno;
ssl_io->closed = TRUE;
@@ -358,7 +379,16 @@
}
if (bytes == 0 && !bytes_read && ssl_io->want_read) {
/* shouldn't happen */
- i_panic("SSL BIO buffer size too small");
+ i_error("SSL BIO buffer size too small");
+ ssl_io->plain_stream_errno = EINVAL;
+ ssl_io->closed = TRUE;
+ return FALSE;
+ }
+ if (i_stream_get_data_size(ssl_io->plain_input) > 0) {
+ i_error("SSL: Too much data in buffered plain input buffer");
+ ssl_io->plain_stream_errno = EINVAL;
+ ssl_io->closed = TRUE;
+ return FALSE;
}
if (bytes_read) {
if (ssl_io->ostream_flush_waiting_input) {
diff -r 78749ae0c9c2 -r 083bd881b9da src/lib-ssl-iostream/iostream-openssl.h
--- a/src/lib-ssl-iostream/iostream-openssl.h Fri Oct 12 23:18:58 2012 +0300
+++ b/src/lib-ssl-iostream/iostream-openssl.h Sat Oct 13 00:24:47 2012 +0300
@@ -47,6 +47,7 @@
unsigned int cert_received:1;
unsigned int cert_broken:1;
unsigned int want_read:1;
+ unsigned int input_handler:1;
unsigned int ostream_flush_waiting_input:1;
unsigned int closed:1;
};
diff -r 78749ae0c9c2 -r 083bd881b9da src/lib-ssl-iostream/istream-openssl.c
--- a/src/lib-ssl-iostream/istream-openssl.c Fri Oct 12 23:18:58 2012 +0300
+++ b/src/lib-ssl-iostream/istream-openssl.c Sat Oct 13 00:24:47 2012 +0300
@@ -25,17 +25,27 @@
ssl_iostream_unref(&sstream->ssl_io);
}
-static ssize_t i_stream_ssl_read(struct istream_private *stream)
+static ssize_t i_stream_ssl_read_real(struct istream_private *stream)
{
struct ssl_istream *sstream = (struct ssl_istream *)stream;
+ struct ssl_iostream *ssl_io = sstream->ssl_io;
+ unsigned char buffer[IO_BLOCK_SIZE];
+ size_t orig_max_buffer_size = stream->max_buffer_size;
size_t size;
- ssize_t ret;
+ ssize_t ret, total_ret;
if (sstream->seen_eof) {
stream->istream.eof = TRUE;
return -1;
}
- ret = ssl_iostream_more(sstream->ssl_io);
+
+ if (stream->pos >= stream->max_buffer_size) {
+ i_stream_compress(stream);
+ if (stream->pos >= stream->max_buffer_size)
+ return -2;
+ }
+
+ ret = ssl_iostream_more(ssl_io);
if (ret <= 0) {
if (ret < 0) {
/* handshake failed */
@@ -46,13 +56,16 @@
}
if (!i_stream_try_alloc(stream, 1, &size))
- return -2;
+ i_unreached();
+ if (stream->pos + size > stream->max_buffer_size) {
+ i_assert(stream->max_buffer_size > stream->pos);
+ size = stream->max_buffer_size - stream->pos;
+ }
- while ((ret = SSL_read(sstream->ssl_io->ssl,
+ while ((ret = SSL_read(ssl_io->ssl,
stream->w_buffer + stream->pos, size)) <= 0) {
/* failed to read anything */
- ret = ssl_iostream_handle_error(sstream->ssl_io, ret,
- "SSL_read");
+ ret = ssl_iostream_handle_error(ssl_io, ret, "SSL_read");
if (ret <= 0) {
if (ret < 0) {
stream->istream.stream_errno = errno;
@@ -64,6 +77,37 @@
/* we did some BIO I/O, try reading again */
}
stream->pos += ret;
+ total_ret = ret;
+
+ /* now make sure that we read everything already buffered in OpenSSL
+ into the stream (without reading anything more). this makes I/O loop
+ behave similary for ssl-istream as file-istream. */
+ sstream->ssl_io->input_handler = FALSE;
+ stream->max_buffer_size = (size_t)-1;
+ while ((ret = SSL_read(ssl_io->ssl, buffer, sizeof(buffer))) > 0) {
+ if (!i_stream_try_alloc(stream, ret, &size))
+ i_unreached();
+ i_assert(size >= (size_t)ret);
+ memcpy(stream->w_buffer + stream->pos, buffer, ret);
+ stream->pos += ret;
+ total_ret += ret;
+ }
+ stream->max_buffer_size = orig_max_buffer_size;
+ return total_ret;
+}
+
+static ssize_t i_stream_ssl_read(struct istream_private *stream)
+{
+ struct ssl_istream *sstream = (struct ssl_istream *)stream;
+ ssize_t ret;
+
+ sstream->ssl_io->input_handler = TRUE;
+ if ((ret = i_stream_ssl_read_real(stream)) < 0)
+ ret = -1;
+ else {
+ i_assert(i_stream_get_data_size(sstream->ssl_io->plain_input) == 0);
+ }
+ sstream->ssl_io->input_handler = FALSE;
return ret;
}
More information about the dovecot-cvs
mailing list