Kevin J. McCarthy wrote:
> Kevin J. McCarthy wrote:
> > I wasn't really happy having a loop counter option for GnuTLS.  It turns
> > out the GNUTLS_E_AGAIN is translated directly from EAGAIN/EWOULDBLOCK.
> > Since we're not using non-blocking io, this will only be triggered by
> > our timeout.
> 
> Sorry, I was wrong.  It will return GNUTLS_E_AGAIN for situations other
> than the timeout we set, so my patch is incorrect.  I'll have to think
> about this some more.

After looking some more, I've re-convinced myself the patch approach is
actually okay.

It looks like GnuTLS will return the GNUTLS_E_AGAIN for a partial
read only if recv returns a EAGAIN or EINTR.  Otherwise it will keep
looping until it gets the needed amount. (see _gnutls_stream_read() and
_gnutls_io_read_buffered())

The EAGAIN should only occur for a timeout on no data, and the EINTR
should only occur for a signal.  Since the timeout is ours and we aren't
setting signals, there is no more reason to loop or handle these errors
than there is with raw_socket_read or raw_socket_write.  Therefore I
believe patch should be okay after all.

I am attaching a somewhat more aggressive patch that removes the looping
from tls_socket_read, and returns tls_socket_write to its behavior of
aborting on any error.

Feedback and testing is definitely appreciated!

-- 
Kevin J. McCarthy
GPG Fingerprint: 8975 A9B3 3AA3 7910 385C  5308 ADEF 7684 8031 6BDA
http://www.8t8.us/configs/gpg-key-transition-statement.txt
# HG changeset patch
# User Kevin McCarthy <ke...@8t8.us>
# Date 1437016507 25200
#      Wed Jul 15 20:15:07 2015 -0700
# Node ID f9da32c0615726601a5ae5cd5e9211aa7afca12e
# Parent  2ca89bed64480780d0a435e89c13dba06c748094
Add $socket_timeout configuration option.

(closes #3491) (see #3640) (see #3533) (see #3074)

This allows socket reads and writes to time out rather than lock up
completely in some circumstances.  Note that Mutt won't currently try
to do anything intelligent with the failure - it will simply abort the
operation and/or close the mailbox.

$socket_timeout by default is set to a conservative 30 seconds, to
hopefully avoid causing other problems.

Change tls_socket_read() to abort on GNUTLS_E_AGAIN or
GNUTLS_E_INTERRUPTED, instead of looping.  These will only occur if the
recv returns EAGAIN or EINTR, and there is no more reason to retry here
than there is with raw_socket_read or raw_socket_write.

Thanks to Allen "egbert." for the original patch.

diff --git a/globals.h b/globals.h
--- a/globals.h
+++ b/globals.h
@@ -198,16 +198,17 @@
 WHERE short MenuContext;
 WHERE short PagerContext;
 WHERE short PagerIndexLines;
 WHERE short ReadInc;
 WHERE short ReflowWrap;
 WHERE short SaveHist;
 WHERE short SendmailWait;
 WHERE short SleepTime INITVAL (1);
+WHERE short SocketTimeout;
 WHERE short TimeInc;
 WHERE short Timeout;
 WHERE short Wrap;
 WHERE short WrapHeaders;
 WHERE short WriteInc;
 
 WHERE short ScoreThresholdDelete;
 WHERE short ScoreThresholdRead;
diff --git a/init.h b/init.h
--- a/init.h
+++ b/init.h
@@ -2883,16 +2883,22 @@
   ** smtp[s]://[user[:pass]@]host[:port]
   ** .te
   ** .pp
   ** where ``[...]'' denotes an optional part.
   ** Setting this variable overrides the value of the $$sendmail
   ** variable.
   */
 #endif /* USE_SMTP */
+  { "socket_timeout", DT_NUM, R_NONE, UL &SocketTimeout, 30 },
+  /*
+  ** .pp
+  ** Set socket send and receive timeouts to this many seconds.  When
+  ** set to zero, send and receive operations will not time out.
+  */
   { "sort",            DT_SORT, R_INDEX|R_RESORT, UL &Sort, SORT_DATE },
   /*
   ** .pp
   ** Specifies how to sort messages in the ``index'' menu.  Valid values
   ** are:
   ** .il
   ** .dd date or date-sent
   ** .dd date-received
diff --git a/mutt_socket.c b/mutt_socket.c
--- a/mutt_socket.c
+++ b/mutt_socket.c
@@ -339,16 +339,17 @@
   return 0;
 }
 
 /* socket_connect: set up to connect to a socket fd. */
 static int socket_connect (int fd, struct sockaddr* sa)
 {
   int sa_size;
   int save_errno;
+  struct timeval socket_timeout_tv = { 0, 0 };
 
   if (sa->sa_family == AF_INET)
     sa_size = sizeof (struct sockaddr_in);
 #ifdef HAVE_GETADDRINFO
   else if (sa->sa_family == AF_INET6)
     sa_size = sizeof (struct sockaddr_in6);
 #endif
   else
@@ -359,16 +360,35 @@
   
   if (ConnectTimeout > 0)
       alarm (ConnectTimeout);
 
   mutt_allow_interrupt (1);
 
   save_errno = 0;
 
+  if (SocketTimeout > 0)
+  {
+    socket_timeout_tv.tv_sec = SocketTimeout;
+    if (setsockopt (fd, SOL_SOCKET, SO_RCVTIMEO, &socket_timeout_tv,
+                    sizeof(struct timeval)))
+    {
+      mutt_perror ("setsockopt");
+      mutt_sleep(3);
+      return -1;
+    }
+    if (setsockopt (fd, SOL_SOCKET, SO_SNDTIMEO, &socket_timeout_tv,
+                    sizeof(struct timeval)))
+    {
+      mutt_perror ("setsockopt");
+      mutt_sleep(3);
+      return -1;
+    }
+  }
+
   if (connect (fd, sa, sa_size) < 0)
   {
       save_errno = errno;
       dprint (2, (debugfile, "Connection failed. errno: %d...\n", errno));
       SigInt = 0;      /* reset in case we caught SIGINTR while in connect() */
   }
 
   if (ConnectTimeout > 0)
diff --git a/mutt_ssl_gnutls.c b/mutt_ssl_gnutls.c
--- a/mutt_ssl_gnutls.c
+++ b/mutt_ssl_gnutls.c
@@ -134,26 +134,23 @@
 
   if (!data)
   {
     mutt_error (_("Error: no TLS socket open"));
     mutt_sleep (2);
     return -1;
   }
 
-  do {
-    ret = gnutls_record_recv (data->state, buf, len);
-    if (ret < 0 && gnutls_error_is_fatal(ret) == 1)
-    {
-      mutt_error ("tls_socket_read (%s)", gnutls_strerror (ret));
-      mutt_sleep (4);
-      return -1;
-    }
+  ret = gnutls_record_recv (data->state, buf, len);
+  if (ret < 0)
+  {
+    mutt_error ("tls_socket_read (%s)", gnutls_strerror (ret));
+    mutt_sleep (4);
+    return -1;
   }
-  while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
 
   return ret;
 }
 
 static int tls_socket_write (CONNECTION* conn, const char* buf, size_t len)
 {
   tlssockdata *data = conn->sockdata;
   int ret;
@@ -166,25 +163,22 @@
     return -1;
   }
 
   do
   {
     ret = gnutls_record_send (data->state, buf + sent, len - sent);
     if (ret < 0)
     {
-      if (gnutls_error_is_fatal(ret) == 1)
-      {
-       mutt_error ("tls_socket_write (%s)", gnutls_strerror (ret));
-       mutt_sleep (4);
-       return -1;
-      }
-      return ret;
+      mutt_error ("tls_socket_write (%s)", gnutls_strerror (ret));
+      mutt_sleep (4);
+      return -1;
     }
-    sent += ret;
+    else
+      sent += ret;
   } while (sent < len);
 
   return sent;
 }
 
 static int tls_socket_open (CONNECTION* conn)
 {
   if (raw_socket_open (conn) < 0)

Attachment: signature.asc
Description: PGP signature

Reply via email to