Hi,
Started a new thread for this improved patch. This should fix the
SSL_accept, SSL_shutdown and SSL_read issues. It temporarily sets the
socket to non-blocking and timesout after the configured time.
This is a replacement for the previous patch, apply to a clean
spamdyke-4.0.10 code base.
Thanks,
-trog
--- spamdyke-4.0.10/spamdyke/tls.c 2008-10-08 02:08:08.000000000 +0100
+++ /tmp/tls.c 2010-03-18 15:58:02.232759517 +0000
@@ -26,6 +26,7 @@
#include <unistd.h>
#include <syslog.h>
#include <string.h>
+#include <fcntl.h>
#include "config.h"
@@ -276,6 +277,35 @@
{
return(tls_init_inner(current_settings, ¤t_settings->tls_context, ¤t_settings->tls_session));
}
+
+
+/* Return value:
+ * TIMEOUT: 0
+ * number of file descriptors ready
+ * ERROR: -1
+ */
+int tls_select_fd(int fd, int timeout, int to_read, int to_write)
+ {
+ fd_set read_set, write_set;
+ struct timeval listen_timeout;
+
+ if (timeout > 0)
+ listen_timeout.tv_sec = timeout;
+ else
+ listen_timeout.tv_sec = 2;
+
+ listen_timeout.tv_usec = 0;
+
+ FD_ZERO(&read_set);
+ FD_ZERO(&write_set);
+
+ if (to_read)
+ FD_SET(fd, &read_set);
+ if (to_write)
+ FD_SET(fd, &write_set);
+
+ return select(fd+1, &read_set, &write_set, NULL, &listen_timeout);
+ }
/*
* Return value:
@@ -286,25 +316,72 @@
{
int return_value;
int tls_return;
-
+ int flags;
+ int retval;
+ int timeout=0;
+ int start, now;
+
return_value = 0;
+ if (current_settings->current_options->timeout_command > 0)
+ timeout = MAXVAL(2, current_settings->current_options->timeout_command);
+
if ((current_settings->tls_session != NULL) &&
SSL_set_rfd(current_settings->tls_session, read_fd) &&
SSL_set_wfd(current_settings->tls_session, write_fd))
- if ((tls_return = SSL_accept(current_settings->tls_session)) == 1)
- {
- SSL_set_mode(current_settings->tls_session, SSL_MODE_ENABLE_PARTIAL_WRITE);
- SSL_set_mode(current_settings->tls_session, SSL_MODE_AUTO_RETRY);
- current_settings->tls_state = TLS_STATE_ACTIVE_SPAMDYKE;
-
- return_value = 1;
- }
- else
+ {
+ /* Temporarily set the socket to non-blocking */
+ do {
+ flags = fcntl(read_fd, F_GETFL, 0);
+ } while ((flags < 0) && (errno == EINTR));
+ do {
+ retval = fcntl(read_fd, F_SETFL, flags | O_NONBLOCK);
+ } while ((retval < 0) && (errno == EINTR));
+ if (retval < 0) {
+ SPAMDYKE_LOG_ERROR(current_settings, LOG_ERROR_TLS_INIT);
+ }
+ start = time(NULL);
+ for (;;) {
+ now = time(NULL);
+ timeout -= (now - start);
+ if (timeout <= 0)
+ break;
+ start = now;
+ tls_return = SSL_accept(current_settings->tls_session);
+ retval = SSL_get_error(current_settings->tls_session, tls_return);
+ if (retval == SSL_ERROR_NONE)
+ {
+ /* We're done */
+ SSL_set_mode(current_settings->tls_session, SSL_MODE_ENABLE_PARTIAL_WRITE);
+ SSL_set_mode(current_settings->tls_session, SSL_MODE_AUTO_RETRY);
+ current_settings->tls_state = TLS_STATE_ACTIVE_SPAMDYKE;
+ return_value = 1;
+ break;
+ }
+ if ((retval == SSL_ERROR_WANT_READ) || (retval == SSL_ERROR_WANT_WRITE))
+ {
+ /* Note: can get WANT_WRITE during re-negotiation */
+ if (tls_select_fd(read_fd, timeout, retval==SSL_ERROR_WANT_READ, retval==SSL_ERROR_WANT_WRITE) >= 1);
+ continue;
+ SPAMDYKE_LOG_ERROR(current_settings, LOG_ERROR_TLS_INIT);
+ break;
+ }
+ if (retval == SSL_ERROR_SYSCALL)
+ {
+ if ((errno == EINTR) || (errno == EAGAIN))
+ continue;
+ break;
+ }
SPAMDYKE_LOG_VERBOSE(current_settings, LOG_ERROR_TLS_ACCEPT ": %s", tls_error(current_settings, tls_return));
- else
- SPAMDYKE_LOG_ERROR(current_settings, LOG_ERROR_TLS_INIT);
+ break;
+ }
+ /* Set the socket back to blocking */
+ do {
+ retval = fcntl(read_fd, F_SETFL, flags);
+ } while ((retval < 0) && (errno == EINTR));
+ }
+
return(return_value);
}
@@ -316,29 +393,34 @@
int tls_end_inner(struct filter_settings *current_settings, int read_fd, SSL_CTX **target_tls_context, SSL **target_tls_session)
{
int return_value;
- fd_set read_fds;
- struct timeval tmp_timeout;
+ int i;
+ int flags;
+ int retval;
return_value = 0;
if (current_settings->tls_state == TLS_STATE_ACTIVE_SPAMDYKE)
{
- if (((SSL_get_shutdown(*target_tls_session) & SSL_RECEIVED_SHUTDOWN) == 0) &&
- !SSL_shutdown(*target_tls_session))
- {
- /* The socket is going to be closed, so proper SSL shutdown isn't a big
- deal. We'll give it a chance to happen though. */
- FD_ZERO(&read_fds);
- FD_SET(read_fd, &read_fds);
+ /* Temporarily set the socket to non-blocking */
+ do {
+ flags = fcntl(read_fd, F_GETFL, 0);
+ } while ((flags < 0) && (errno == EINTR));
+ do {
+ retval = fcntl(read_fd, F_SETFL, flags | O_NONBLOCK);
+ } while ((retval < 0) && (errno == EINTR));
+
+ SSL_set_shutdown(*target_tls_session, SSL_RECEIVED_SHUTDOWN);
+ /* Give SSL a chance to shutdown gracefully */
+ for (i = 0; i < 4 ; i++) {
+ if (SSL_shutdown(*target_tls_session))
+ break;
+ }
- tmp_timeout.tv_sec = TIMEOUT_TLS_SHUTDOWN_SECS;
- tmp_timeout.tv_usec = 0;
+ /* Set the socket back to blocking */
+ do {
+ retval = fcntl(read_fd, F_SETFL, flags);
+ } while ((retval < 0) && (errno == EINTR));
- select(read_fd + 1, &read_fds, NULL, NULL, &tmp_timeout);
-
- if ((SSL_get_shutdown(*target_tls_session) & SSL_RECEIVED_SHUTDOWN) == 0)
- SSL_shutdown(*target_tls_session);
- }
current_settings->tls_state = TLS_STATE_INACTIVE;
return_value = 1;
@@ -410,12 +492,65 @@
*/
ssize_t tls_read(struct filter_settings *current_settings, int target_fd, void *target_buf, size_t num_bytes)
{
- ssize_t return_value;
+ ssize_t return_value=0;
+ int flags;
+ int retval;
+ int timeout=0;
+ int start, now;
if (current_settings->tls_state == TLS_STATE_ACTIVE_SPAMDYKE)
{
- if ((return_value = SSL_read(current_settings->tls_session, target_buf, num_bytes)) < 0)
+ /* Temporarily set the socket to non-blocking */
+ do {
+ flags = fcntl(target_fd, F_GETFL, 0);
+ } while ((flags < 0) && (errno == EINTR));
+ do {
+ retval = fcntl(target_fd, F_SETFL, flags | O_NONBLOCK);
+ } while ((retval < 0) && (errno == EINTR));
+ if (retval < 0) {
+ SPAMDYKE_LOG_ERROR(current_settings, LOG_ERROR_TLS_READ);
+ /* TODO: return?? */
+ }
+
+ if (current_settings->current_options->timeout_command > 0)
+ timeout = MAXVAL(2, current_settings->current_options->timeout_command);
+
+ start = time(NULL);
+ for (;;)
+ {
+ now = time(NULL);
+ timeout -= (now - start);
+ if (timeout <= 0)
+ break;
+ start = now;
+
+ if ((return_value = SSL_read(current_settings->tls_session, target_buf, num_bytes)) > 0)
+ break;
+
+ retval = SSL_get_error(current_settings->tls_session, return_value);
+ if ((retval == SSL_ERROR_WANT_READ) || (retval == SSL_ERROR_WANT_WRITE))
+ {
+ /* Note: can get WANT_WRITE during re-negotiation */
+ if (tls_select_fd(target_fd, timeout, retval==SSL_ERROR_WANT_READ, retval==SSL_ERROR_WANT_WRITE) >= 1);
+ continue;
+ SPAMDYKE_LOG_VERBOSE(current_settings, LOG_ERROR_TLS_READ ": %s", tls_error(current_settings, return_value));
+ break;
+ }
+ if (retval == SSL_ERROR_SYSCALL)
+ {
+ if ((errno == EINTR) || (errno == EAGAIN))
+ continue;
+ break;
+ }
SPAMDYKE_LOG_VERBOSE(current_settings, LOG_ERROR_TLS_READ ": %s", tls_error(current_settings, return_value));
+ return_value = -1;
+ break;
+ }
+ /* Set the socket back to blocking */
+ do {
+ retval = fcntl(target_fd, F_SETFL, flags);
+ } while ((retval < 0) && (errno == EINTR));
+
}
else
return_value = read(target_fd, target_buf, num_bytes);_______________________________________________
spamdyke-users mailing list
[email protected]
http://www.spamdyke.org/mailman/listinfo/spamdyke-users