ostream-openssl.c revision 71da447014454c84828d9dface77219875554d7d
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen/* Copyright (c) 2009 Dovecot authors, see the included COPYING file */
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen#include "lib.h"
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen#include "buffer.h"
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen#include "ostream-internal.h"
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen#include "iostream-openssl.h"
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainenstruct ssl_ostream {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen struct ostream_private ostream;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen struct ssl_iostream *ssl_io;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen buffer_t *buffer;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen};
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
52d19b57c2411bb1c60f8248ea5c8c1a1cfc1b4fTimo Sirainenstatic void o_stream_ssl_close(struct iostream_private *stream)
52d19b57c2411bb1c60f8248ea5c8c1a1cfc1b4fTimo Sirainen{
52d19b57c2411bb1c60f8248ea5c8c1a1cfc1b4fTimo Sirainen struct ssl_ostream *sstream = (struct ssl_ostream *)stream;
52d19b57c2411bb1c60f8248ea5c8c1a1cfc1b4fTimo Sirainen
52d19b57c2411bb1c60f8248ea5c8c1a1cfc1b4fTimo Sirainen o_stream_close(sstream->ssl_io->plain_output);
52d19b57c2411bb1c60f8248ea5c8c1a1cfc1b4fTimo Sirainen}
52d19b57c2411bb1c60f8248ea5c8c1a1cfc1b4fTimo Sirainen
52d19b57c2411bb1c60f8248ea5c8c1a1cfc1b4fTimo Sirainenstatic void o_stream_ssl_destroy(struct iostream_private *stream)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen struct ssl_ostream *sstream = (struct ssl_ostream *)stream;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen sstream->ssl_io->ssl_output = NULL;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_iostream_unref(&sstream->ssl_io);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen i_free(sstream->buffer);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainenstatic size_t
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Siraineno_stream_ssl_buffer(struct ssl_ostream *sstream, const struct const_iovec *iov,
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen unsigned int iov_count, size_t bytes_sent)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen size_t avail, skip_left, size;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen unsigned int i;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (sstream->buffer == NULL)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen sstream->buffer = buffer_create_dynamic(default_pool, 4096);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen skip_left = bytes_sent;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen for (i = 0; i < iov_count; i++) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (skip_left < iov[i].iov_len)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen break;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen skip_left -= iov[i].iov_len;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen avail = sstream->ostream.max_buffer_size > sstream->buffer->used ?
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen sstream->ostream.max_buffer_size - sstream->buffer->used : 0;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (i < iov_count && skip_left > 0) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen size = I_MIN(iov[i].iov_len - skip_left, avail);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen buffer_append(sstream->buffer,
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen CONST_PTR_OFFSET(iov[i].iov_base, skip_left),
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen size);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen bytes_sent += size;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen avail -= size;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (size != iov[i].iov_len)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen i = iov_count;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen for (; i < iov_count; i++) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen size = I_MIN(iov[i].iov_len, avail);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen buffer_append(sstream->buffer, iov[i].iov_base, size);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen bytes_sent += size;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen avail -= size;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (size != iov[i].iov_len)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen break;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen sstream->ostream.ostream.offset += bytes_sent;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return bytes_sent;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainenstatic int o_stream_ssl_flush_buffer(struct ssl_ostream *sstream)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen size_t pos = 0;
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen int ret = 1;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen while (pos < sstream->buffer->used) {
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen /* we're writing plaintext data to OpenSSL, which it encrypts
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen and writes to bio_int's buffer. ssl_iostream_bio_sync()
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen reads it from there and adds to plain_output stream. */
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ret = SSL_write(sstream->ssl_io->ssl,
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen CONST_PTR_OFFSET(sstream->buffer->data, pos),
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen sstream->buffer->used - pos);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (ret <= 0) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ret = ssl_iostream_handle_error(sstream->ssl_io, ret,
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen "SSL_write");
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen if (ret < 0) {
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen sstream->ostream.ostream.stream_errno = errno;
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen break;
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen }
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen if (ret == 0) {
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen /* bio_int's buffer is full */
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen break;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen } else {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen pos += ret;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen (void)ssl_iostream_bio_sync(sstream->ssl_io);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen buffer_delete(sstream->buffer, 0, pos);
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen return ret <= 0 ? ret : 1;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainenstatic int o_stream_ssl_flush(struct ostream_private *stream)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen struct ssl_ostream *sstream = (struct ssl_ostream *)stream;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen int ret;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen if ((ret = ssl_iostream_more(sstream->ssl_io)) < 0) {
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen /* handshake failed */
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen stream->ostream.stream_errno = errno;
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen } else if (ret > 0 && sstream->buffer != NULL &&
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen sstream->buffer->used > 0) {
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen /* we can try to send some of our buffered data */
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen ret = o_stream_ssl_flush_buffer(sstream);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen if (ret == 0 && sstream->ssl_io->want_read) {
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen /* we need to read more data until we can continue. */
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen sstream->ssl_io->ostream_flush_waiting_input = TRUE;
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen ret = 1;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen return ret;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainenstatic ssize_t
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Siraineno_stream_ssl_sendv_try(struct ssl_ostream *sstream,
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen const struct const_iovec *iov, unsigned int iov_count,
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen size_t *bytes_sent_r)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen unsigned int i;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen size_t pos;
da4baa95cbd8074f62835fa6d83d7998d40c2105Timo Sirainen ssize_t ret = 0;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen *bytes_sent_r = 0;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen for (i = 0, pos = 0; i < iov_count; ) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ret = SSL_write(sstream->ssl_io->ssl,
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen CONST_PTR_OFFSET(iov[i].iov_base, pos),
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen iov[i].iov_len - pos);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (ret <= 0) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ret = ssl_iostream_handle_error(sstream->ssl_io, ret,
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen "SSL_write");
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen if (ret < 0) {
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen sstream->ostream.ostream.stream_errno = errno;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen break;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen if (ret == 0)
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen break;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen } else {
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen *bytes_sent_r += ret;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if ((size_t)ret < iov[i].iov_len)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen pos += ret;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen else {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen i++;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen pos = 0;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen (void)ssl_iostream_bio_sync(sstream->ssl_io);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
da4baa95cbd8074f62835fa6d83d7998d40c2105Timo Sirainen return ret < 0 ? -1 : 0;
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen}
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainenstatic ssize_t
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Siraineno_stream_ssl_sendv(struct ostream_private *stream,
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen const struct const_iovec *iov, unsigned int iov_count)
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen{
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen struct ssl_ostream *sstream = (struct ssl_ostream *)stream;
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen size_t bytes_sent = 0;
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen int ret;
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen if (!sstream->ssl_io->handshaked)
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen ret = 0;
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen else {
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen ret = o_stream_ssl_sendv_try(sstream, iov, iov_count,
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen &bytes_sent);
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen }
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen bytes_sent = o_stream_ssl_buffer(sstream, iov, iov_count, bytes_sent);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return bytes_sent != 0 ? (ssize_t)bytes_sent : ret;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
71da447014454c84828d9dface77219875554d7dTimo Sirainenstatic void o_stream_ssl_switch_ioloop(struct ostream_private *stream)
71da447014454c84828d9dface77219875554d7dTimo Sirainen{
71da447014454c84828d9dface77219875554d7dTimo Sirainen struct ssl_ostream *sstream = (struct ssl_ostream *)stream;
71da447014454c84828d9dface77219875554d7dTimo Sirainen
71da447014454c84828d9dface77219875554d7dTimo Sirainen o_stream_switch_ioloop(sstream->ssl_io->plain_output);
71da447014454c84828d9dface77219875554d7dTimo Sirainen}
71da447014454c84828d9dface77219875554d7dTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainenstatic int plain_flush_callback(struct ssl_ostream *sstream)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen int ret, ret2;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen /* try to actually flush the pending data */
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if ((ret = o_stream_flush(sstream->ssl_io->plain_output)) < 0)
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen return -1;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen /* we may be able to copy more data, try it */
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen ret2 = o_stream_flush(&sstream->ostream.ostream);
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen if (ret2 < 0)
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen return -1;
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen return ret > 0 && ret2 > 0 ? 1 : 0;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainenstruct ostream *o_stream_create_ssl(struct ssl_iostream *ssl_io)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen struct ssl_ostream *sstream;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_io->refcount++;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen sstream = i_new(struct ssl_ostream, 1);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen sstream->ssl_io = ssl_io;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen sstream->ostream.max_buffer_size =
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ssl_io->plain_output->real_stream->max_buffer_size;
52d19b57c2411bb1c60f8248ea5c8c1a1cfc1b4fTimo Sirainen sstream->ostream.iostream.close = o_stream_ssl_close;
52d19b57c2411bb1c60f8248ea5c8c1a1cfc1b4fTimo Sirainen sstream->ostream.iostream.destroy = o_stream_ssl_destroy;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen sstream->ostream.sendv = o_stream_ssl_sendv;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen sstream->ostream.flush = o_stream_ssl_flush;
71da447014454c84828d9dface77219875554d7dTimo Sirainen sstream->ostream.switch_ioloop = o_stream_ssl_switch_ioloop;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen o_stream_set_flush_callback(ssl_io->plain_output,
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen plain_flush_callback, sstream);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return o_stream_create(&sstream->ostream);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}