[dovecot-cvs] dovecot/src/login-common ssl-proxy-openssl.c, 1.35,
1.36
cras at dovecot.org
cras at dovecot.org
Sun Apr 2 19:42:30 EEST 2006
Update of /var/lib/cvs/dovecot/src/login-common
In directory talvi:/tmp/cvs-serv29833
Modified Files:
ssl-proxy-openssl.c
Log Message:
Rewrite. Hopefully works better.
Index: ssl-proxy-openssl.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/login-common/ssl-proxy-openssl.c,v
retrieving revision 1.35
retrieving revision 1.36
diff -u -d -r1.35 -r1.36
--- ssl-proxy-openssl.c 29 Jan 2006 12:14:49 -0000 1.35
+++ ssl-proxy-openssl.c 2 Apr 2006 16:42:27 -0000 1.36
@@ -4,6 +4,7 @@
#include "array.h"
#include "ioloop.h"
#include "network.h"
+#include "ostream.h"
#include "read-full.h"
#include "hash.h"
#include "ssl-proxy.h"
@@ -25,11 +26,11 @@
/* Check every 30 minutes if parameters file has been updated */
#define SSL_PARAMFILE_CHECK_INTERVAL (60*30)
-enum ssl_io_action {
- SSL_ADD_INPUT,
- SSL_REMOVE_INPUT,
- SSL_ADD_OUTPUT,
- SSL_REMOVE_OUTPUT
+#define PLAIN_OUTPUT_OPTIMAL_SIZE 2048
+
+enum ssl_want {
+ WANT_INPUT,
+ WANT_OUTPUT
};
struct ssl_proxy {
@@ -39,10 +40,13 @@
struct ip_addr ip;
int fd_ssl, fd_plain;
- struct io *io_ssl_read, *io_ssl_write, *io_plain_read, *io_plain_write;
+ struct io *io_ssl, *io_plain_input;
- unsigned char plainout_buf[1024];
- unsigned int plainout_size;
+ enum ssl_want want;
+ void (*step)(struct ssl_proxy *);
+ unsigned int ssl_want_size;
+
+ struct ostream *plain_output;
unsigned char sslout_buf[1024];
unsigned int sslout_size;
@@ -66,9 +70,8 @@
static struct hash_table *ssl_proxies;
static struct ssl_parameters ssl_params;
-static void plain_read(void *context);
-static void plain_write(void *context);
-static void ssl_write(struct ssl_proxy *proxy);
+static void ssl_input(struct ssl_proxy *proxy);
+static void ssl_output(struct ssl_proxy *proxy);
static void ssl_step(void *context);
static void ssl_proxy_destroy(struct ssl_proxy *proxy);
static void ssl_proxy_unref(struct ssl_proxy *proxy);
@@ -178,118 +181,6 @@
}
}
-static void ssl_set_io(struct ssl_proxy *proxy, enum ssl_io_action action)
-{
- switch (action) {
- case SSL_ADD_INPUT:
- if (proxy->io_ssl_read != NULL)
- break;
- proxy->io_ssl_read = io_add(proxy->fd_ssl, IO_READ,
- ssl_step, proxy);
- break;
- case SSL_REMOVE_INPUT:
- if (proxy->io_ssl_read != NULL)
- io_remove(&proxy->io_ssl_read);
- break;
- case SSL_ADD_OUTPUT:
- if (proxy->io_ssl_write != NULL)
- break;
- proxy->io_ssl_write = io_add(proxy->fd_ssl, IO_WRITE,
- ssl_step, proxy);
- break;
- case SSL_REMOVE_OUTPUT:
- if (proxy->io_ssl_write != NULL)
- io_remove(&proxy->io_ssl_write);
- break;
- }
-}
-
-static void plain_block_input(struct ssl_proxy *proxy, bool block)
-{
- if (block) {
- if (proxy->io_plain_read != NULL)
- io_remove(&proxy->io_plain_read);
- } else {
- if (proxy->io_plain_read == NULL) {
- proxy->io_plain_read = io_add(proxy->fd_plain, IO_READ,
- plain_read, proxy);
- }
- }
-}
-
-static void plain_read(void *context)
-{
- struct ssl_proxy *proxy = context;
- ssize_t ret;
- bool corked = FALSE;
-
- if (proxy->sslout_size == sizeof(proxy->sslout_buf)) {
- /* buffer full, block input until it's written */
- plain_block_input(proxy, TRUE);
- return;
- }
-
- proxy->refcount++;
-
- while (proxy->sslout_size < sizeof(proxy->sslout_buf) &&
- !proxy->destroyed) {
- ret = net_receive(proxy->fd_plain,
- proxy->sslout_buf + proxy->sslout_size,
- sizeof(proxy->sslout_buf) -
- proxy->sslout_size);
- if (ret <= 0) {
- if (ret < 0)
- ssl_proxy_destroy(proxy);
- break;
- } else {
- proxy->sslout_size += ret;
- if (!corked) {
- net_set_cork(proxy->fd_ssl, TRUE);
- corked = TRUE;
- }
- ssl_write(proxy);
- }
- }
-
- if (corked)
- net_set_cork(proxy->fd_ssl, FALSE);
-
- ssl_proxy_unref(proxy);
-}
-
-static void plain_write(void *context)
-{
- struct ssl_proxy *proxy = context;
- ssize_t ret;
-
- proxy->refcount++;
-
- ret = net_transmit(proxy->fd_plain, proxy->plainout_buf,
- proxy->plainout_size);
- if (ret < 0)
- ssl_proxy_destroy(proxy);
- else {
- proxy->plainout_size -= ret;
- memmove(proxy->plainout_buf, proxy->plainout_buf + ret,
- proxy->plainout_size);
-
- if (proxy->plainout_size > 0) {
- if (proxy->io_plain_write == NULL) {
- proxy->io_plain_write =
- io_add(proxy->fd_plain, IO_WRITE,
- plain_write, proxy);
- }
- } else {
- if (proxy->io_plain_write != NULL)
- io_remove(&proxy->io_plain_write);
- }
-
- ssl_set_io(proxy, SSL_ADD_INPUT);
- }
-
- ssl_proxy_unref(proxy);
-}
-
static const char *ssl_last_error(void)
{
unsigned long err;
@@ -306,7 +197,30 @@
return buf;
}
-static void ssl_handle_error(struct ssl_proxy *proxy, int ret, const char *func)
+static void ssl_set_io(struct ssl_proxy *proxy, enum ssl_want want)
+{
+ if (proxy->io_ssl != NULL) {
+ if (want == proxy->want)
+ return;
+ io_remove(&proxy->io_ssl);
+ }
+
+ proxy->want = want;
+ switch (want) {
+ case WANT_INPUT:
+ proxy->io_ssl =
+ io_add(proxy->fd_ssl, IO_READ, ssl_step, proxy);
+ break;
+ case WANT_OUTPUT:
+ proxy->io_ssl =
+ io_add(proxy->fd_ssl, IO_WRITE, ssl_step, proxy);
+ break;
+ }
+}
+
+static void
+ssl_handle_error(struct ssl_proxy *proxy, int ret, const char *func_name,
+ void (*func)(struct ssl_proxy *), unsigned int want_size)
{
const char *errstr;
int err;
@@ -315,10 +229,14 @@
switch (err) {
case SSL_ERROR_WANT_READ:
- ssl_set_io(proxy, SSL_ADD_INPUT);
+ proxy->step = func;
+ proxy->ssl_want_size = want_size;
+ ssl_set_io(proxy, WANT_INPUT);
break;
case SSL_ERROR_WANT_WRITE:
- ssl_set_io(proxy, SSL_ADD_OUTPUT);
+ proxy->step = func;
+ proxy->ssl_want_size = want_size;
+ ssl_set_io(proxy, WANT_OUTPUT);
break;
case SSL_ERROR_SYSCALL:
/* eat up the error queue */
@@ -333,7 +251,7 @@
}
i_warning("%s syscall failed: %s [%s]",
- func, errstr, net_ip2addr(&proxy->ip));
+ func_name, errstr, net_ip2addr(&proxy->ip));
}
ssl_proxy_destroy(proxy);
break;
@@ -343,69 +261,147 @@
break;
case SSL_ERROR_SSL:
if (verbose_ssl) {
- i_warning("%s failed: %s [%s]", func, ssl_last_error(),
- net_ip2addr(&proxy->ip));
+ i_warning("%s failed: %s [%s]", func_name,
+ ssl_last_error(), net_ip2addr(&proxy->ip));
}
ssl_proxy_destroy(proxy);
break;
default:
i_warning("%s failed: unknown failure %d (%s) [%s]",
- func, err, ssl_last_error(), net_ip2addr(&proxy->ip));
+ func_name, err, ssl_last_error(),
+ net_ip2addr(&proxy->ip));
ssl_proxy_destroy(proxy);
break;
}
}
+static void plain_input(void *context)
+{
+ struct ssl_proxy *proxy = context;
+ ssize_t ret;
+
+ if (proxy->sslout_size == sizeof(proxy->sslout_buf)) {
+ /* buffer full, block input until it's written */
+ io_remove(&proxy->io_plain_input);
+ return;
+ }
+
+ ret = net_receive(proxy->fd_plain,
+ proxy->sslout_buf + proxy->sslout_size,
+ sizeof(proxy->sslout_buf) - proxy->sslout_size);
+ if (ret <= 0) {
+ if (ret < 0)
+ ssl_proxy_destroy(proxy);
+ } else {
+ proxy->sslout_size += ret;
+ if (SSL_want(proxy->ssl) == SSL_NOTHING) {
+ i_assert(proxy->ssl_want_size == 0);
+ ssl_output(proxy);
+ }
+ }
+}
+
+static int plain_output(void *context)
+{
+ struct ssl_proxy *proxy = context;
+ int ret;
+
+ if (proxy->ssl_want_size != 0)
+ return 0;
+
+ if ((ret = o_stream_flush(proxy->plain_output)) < 0) {
+ ssl_proxy_destroy(proxy);
+ return 1;
+ }
+
+ if (o_stream_get_buffer_used_size(proxy->plain_output) <
+ PLAIN_OUTPUT_OPTIMAL_SIZE &&
+ proxy->want == WANT_INPUT && proxy->io_ssl == NULL)
+ ssl_set_io(proxy, WANT_INPUT);
+
+ return ret;
+}
+
static void ssl_handshake(struct ssl_proxy *proxy)
{
int ret;
+ net_set_cork(proxy->fd_ssl, TRUE);
ret = SSL_accept(proxy->ssl);
+ net_set_cork(proxy->fd_ssl, FALSE);
if (ret != 1)
- ssl_handle_error(proxy, ret, "SSL_accept()");
+ ssl_handle_error(proxy, ret, "SSL_accept()", ssl_handshake, 0);
else {
proxy->handshaked = TRUE;
+ proxy->step = ssl_input;
+ ssl_set_io(proxy, WANT_INPUT);
- ssl_set_io(proxy, SSL_ADD_INPUT);
- plain_block_input(proxy, FALSE);
+ proxy->io_plain_input = io_add(proxy->fd_plain, IO_READ,
+ plain_input, proxy);
}
}
-static void ssl_read(struct ssl_proxy *proxy)
+static void ssl_input(struct ssl_proxy *proxy)
{
- int ret;
+ unsigned char buf[PLAIN_OUTPUT_OPTIMAL_SIZE];
+ size_t size, used;
+ ssize_t ret, ret2;
- while (proxy->plainout_size < sizeof(proxy->plainout_buf) &&
- !proxy->destroyed) {
- ret = SSL_read(proxy->ssl,
- proxy->plainout_buf + proxy->plainout_size,
- sizeof(proxy->plainout_buf) -
- proxy->plainout_size);
- if (ret <= 0) {
- ssl_handle_error(proxy, ret, "SSL_read()");
- break;
- } else {
- proxy->plainout_size += ret;
- plain_write(proxy);
- }
+ used = o_stream_get_buffer_used_size(proxy->plain_output);
+ if (used >= PLAIN_OUTPUT_OPTIMAL_SIZE) {
+ io_remove(&proxy->io_ssl);
+ return;
}
+
+ size = sizeof(buf) - used;
+ if (proxy->ssl_want_size != 0) {
+ i_assert(proxy->ssl_want_size <= size);
+ size = proxy->ssl_want_size;
+ proxy->ssl_want_size = 0;
+ }
+
+ ret = SSL_read(proxy->ssl, buf, size);
+ if (ret <= 0) {
+ ssl_handle_error(proxy, ret, "SSL_read()", ssl_input, size);
+ return;
+ }
+ o_stream_cork(proxy->plain_output);
+ ret2 = o_stream_send(proxy->plain_output, buf, ret);
+ i_assert(ret2 < 0 || ret2 == ret);
+ o_stream_uncork(proxy->plain_output);
+
+ if (proxy->sslout_size > 0)
+ ssl_output(proxy);
}
-static void ssl_write(struct ssl_proxy *proxy)
+static void ssl_output(struct ssl_proxy *proxy)
{
+ unsigned int size;
int ret;
- ret = SSL_write(proxy->ssl, proxy->sslout_buf, proxy->sslout_size);
- if (ret <= 0)
- ssl_handle_error(proxy, ret, "SSL_write()");
+ if (proxy->ssl_want_size == 0)
+ size = proxy->sslout_size;
else {
- proxy->sslout_size -= ret;
- memmove(proxy->sslout_buf, proxy->sslout_buf + ret,
- proxy->sslout_size);
+ i_assert(proxy->ssl_want_size <= proxy->sslout_size);
+ size = proxy->ssl_want_size;
+ proxy->ssl_want_size = 0;
+ }
- ssl_set_io(proxy, proxy->sslout_size > 0 ?
- SSL_ADD_OUTPUT : SSL_REMOVE_OUTPUT);
- plain_block_input(proxy, FALSE);
+ net_set_cork(proxy->fd_ssl, TRUE);
+ ret = SSL_write(proxy->ssl, proxy->sslout_buf, size);
+ net_set_cork(proxy->fd_ssl, FALSE);
+ if (ret <= 0) {
+ ssl_handle_error(proxy, ret, "SSL_write()", ssl_output, size);
+ return;
+ }
+
+ proxy->sslout_size -= ret;
+ memmove(proxy->sslout_buf, proxy->sslout_buf + ret, proxy->sslout_size);
+
+ ssl_set_io(proxy, proxy->sslout_size > 0 ? WANT_OUTPUT : WANT_INPUT);
+ if (proxy->io_plain_input == NULL) {
+ proxy->io_plain_input = io_add(proxy->fd_plain, IO_READ,
+ plain_input, proxy);
}
}
@@ -413,27 +409,7 @@
{
struct ssl_proxy *proxy = context;
- proxy->refcount++;
-
- if (!proxy->handshaked)
- ssl_handshake(proxy);
-
- if (proxy->handshaked) {
- if (proxy->plainout_size == sizeof(proxy->plainout_buf))
- ssl_set_io(proxy, SSL_REMOVE_INPUT);
- else
- ssl_read(proxy);
-
- if (proxy->sslout_size == 0)
- ssl_set_io(proxy, SSL_REMOVE_OUTPUT);
- else {
- net_set_cork(proxy->fd_ssl, TRUE);
- ssl_write(proxy);
- net_set_cork(proxy->fd_ssl, FALSE);
- }
- }
-
- ssl_proxy_unref(proxy);
+ proxy->step(proxy);
}
int ssl_proxy_new(int fd, struct ip_addr *ip, struct ssl_proxy **proxy_r)
@@ -479,12 +455,18 @@
proxy->fd_ssl = fd;
proxy->fd_plain = sfd[0];
proxy->ip = *ip;
+ proxy->plain_output =
+ o_stream_create_file(proxy->fd_plain, default_pool,
+ (size_t)-1, FALSE);
+ o_stream_set_flush_callback(proxy->plain_output, plain_output, proxy);
+
SSL_set_ex_data(ssl, extdata_index, proxy);
hash_insert(ssl_proxies, proxy, proxy);
+ proxy->step = ssl_handshake;
ssl_handshake(proxy);
- main_ref();
+ main_ref();
*proxy_r = proxy;
return sfd[1];
@@ -540,15 +522,12 @@
hash_remove(ssl_proxies, proxy);
- if (proxy->io_ssl_read != NULL)
- io_remove(&proxy->io_ssl_read);
- if (proxy->io_ssl_write != NULL)
- io_remove(&proxy->io_ssl_write);
- if (proxy->io_plain_read != NULL)
- io_remove(&proxy->io_plain_read);
- if (proxy->io_plain_write != NULL)
- io_remove(&proxy->io_plain_write);
+ if (proxy->io_ssl != NULL)
+ io_remove(&proxy->io_ssl);
+ if (proxy->io_plain_input != NULL)
+ io_remove(&proxy->io_plain_input);
+ o_stream_unref(&proxy->plain_output);
(void)net_disconnect(proxy->fd_ssl);
(void)net_disconnect(proxy->fd_plain);
More information about the dovecot-cvs
mailing list