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, &current_settings->tls_context, &current_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

Reply via email to