egbert. wrote: > Attached is my patch for the hanging socket problem. > > I hope this fixes 3369 and 3491, which indeed sound very similar to the > problem I reported. > > All comments welcome. > > I used hg export against the tip of the default branch to produce the diff; > please let me know if you would rather have it another way.
Sorry for the delay, I've had this on my todo list for a while but needed some time to research gnutls error handling first. I'm attaching a revised patch based on your original patch. I've made the following changes: * Add the SO_SNDTIMEO too. * Change the default timeout to 30 seconds. If we're going to change default behavior, I want to be *really* conservative about it. * Remove the ssl_socket_num_tries option. * Fix tls_socket_write() to follow the return policy of tls_socket_read(). (retry on GNUTLS_E_INTERRUPTED or GNUTLS_E_AGAIN when $socket_timeout is 0) 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. I left the check for GNUTLS_E_AGAIN in the loop condition since it's just following the instructions given by gnutls_record_recv(), but it seems correct to abort for GNUTLS_E_AGAIN if $socket_timeout is set. If this looks good and tests okay, I'll go ahead and commit it. -- 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 1436924440 25200 # Tue Jul 14 18:40:40 2015 -0700 # Node ID 38d952e174e838eaef4920d9d256efa43c2d34cc # 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_write() to continue trying for GNUTLS_E_INTERRUPTED or GNUTLS_E_AGAIN (when $socket_timeout is 0), to match tls_socket_read() behavior and the recommendation of the gnutls_record_send() documentation. 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,23 +134,28 @@ if (!data) { mutt_error (_("Error: no TLS socket open")); mutt_sleep (2); return -1; } - do { + do + { ret = gnutls_record_recv (data->state, buf, len); - if (ret < 0 && gnutls_error_is_fatal(ret) == 1) + if (ret < 0) { - mutt_error ("tls_socket_read (%s)", gnutls_strerror (ret)); - mutt_sleep (4); - return -1; + if (gnutls_error_is_fatal (ret) || + ((ret == GNUTLS_E_AGAIN) && (SocketTimeout > 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) @@ -166,25 +171,28 @@ return -1; } do { ret = gnutls_record_send (data->state, buf + sent, len - sent); if (ret < 0) { - if (gnutls_error_is_fatal(ret) == 1) + if (gnutls_error_is_fatal (ret) || + ((ret == GNUTLS_E_AGAIN) && (SocketTimeout > 0))) { - mutt_error ("tls_socket_write (%s)", gnutls_strerror (ret)); - mutt_sleep (4); - return -1; + mutt_error ("tls_socket_write (%s)", gnutls_strerror (ret)); + mutt_sleep (4); + return -1; } - return ret; + else if ((ret != GNUTLS_E_AGAIN) && (ret != GNUTLS_E_INTERRUPTED)) + return ret; } - sent += ret; + else + sent += ret; } while (sent < len); return sent; } static int tls_socket_open (CONNECTION* conn) { if (raw_socket_open (conn) < 0)
signature.asc
Description: PGP signature