Mercurial > dovecot > original-hg > dovecot-1.2
changeset 4127:60583fb75d9e HEAD
Rewrite. Hopefully works better.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 02 Apr 2006 19:42:27 +0300 |
parents | 63d6890803b3 |
children | 9198a457f022 |
files | src/login-common/ssl-proxy-openssl.c |
diffstat | 1 files changed, 170 insertions(+), 191 deletions(-) [+] |
line wrap: on
line diff
--- a/src/login-common/ssl-proxy-openssl.c Sun Apr 02 19:32:51 2006 +0300 +++ b/src/login-common/ssl-proxy-openssl.c Sun Apr 02 19:42:27 2006 +0300 @@ -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) { + unsigned char buf[PLAIN_OUTPUT_OPTIMAL_SIZE]; + size_t size, used; + ssize_t ret, ret2; + + 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_output(struct ssl_proxy *proxy) +{ + unsigned int size; int ret; - 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); - } + if (proxy->ssl_want_size == 0) + size = proxy->sslout_size; + else { + i_assert(proxy->ssl_want_size <= proxy->sslout_size); + size = proxy->ssl_want_size; + proxy->ssl_want_size = 0; } -} -static void ssl_write(struct ssl_proxy *proxy) -{ - int ret; + 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; + } - ret = SSL_write(proxy->ssl, proxy->sslout_buf, proxy->sslout_size); - if (ret <= 0) - ssl_handle_error(proxy, ret, "SSL_write()"); - else { - proxy->sslout_size -= ret; - memmove(proxy->sslout_buf, proxy->sslout_buf + ret, - proxy->sslout_size); + proxy->sslout_size -= ret; + memmove(proxy->sslout_buf, proxy->sslout_buf + ret, proxy->sslout_size); - ssl_set_io(proxy, proxy->sslout_size > 0 ? - SSL_ADD_OUTPUT : SSL_REMOVE_OUTPUT); - plain_block_input(proxy, FALSE); + 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);