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