aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuilhem Moulin <guilhem@fripost.org>2015-09-17 22:05:09 +0200
committerGuilhem Moulin <guilhem@fripost.org>2015-09-21 01:01:25 +0200
commit612b9e2102e1907709dde325f91d5fdf70ed2534 (patch)
tree007d69ddd0998924ce81493b2a49a9076d8f020b
parent683a3973a32ee3618824d08ed7ee6cfc7ee9ab02 (diff)
Use TCP keepalive to detect dead peers.
-rw-r--r--Changelog3
-rwxr-xr-xinterimap1
-rw-r--r--lib/Net/IMAP/InterIMAP.pm20
3 files changed, 21 insertions, 3 deletions
diff --git a/Changelog b/Changelog
index 79a7ea4..8cd8be2 100644
--- a/Changelog
+++ b/Changelog
@@ -22,9 +22,6 @@ interimap (0.2) upstream;
IPv6. (Both are core Perl module.)
* Add a configuration option 'proxy' to proxy TCP connections to the
IMAP server.
- * Don't set SO_KEEPALIVE on the socket. This is most likely useless
- in our case since the TCP keepalive time is usually much higher than
- the IMAP timeout.
* Set X.509 certificate purpose to 'SSL Server' for SSL_verify=YES.
* Display the certificate chain, SSL protocol and cipher in debug
mode.
diff --git a/interimap b/interimap
index 45a6643..54ae0aa 100755
--- a/interimap
+++ b/interimap
@@ -248,6 +248,7 @@ foreach my $name (qw/local remote/) {
$config{name} = $name;
$config{'logger-fd'} = $LOGGER_FD if defined $LOGGER_FD;
$config{'compress'} //= ($name eq 'local' ? 0 : 1);
+ $config{keepalive} = 1 if $CONFIG{watch} and $config{type} ne 'tunnel';
$IMAP->{$name} = { client => Net::IMAP::InterIMAP::->new(%config) };
my $client = $IMAP->{$name}->{client};
diff --git a/lib/Net/IMAP/InterIMAP.pm b/lib/Net/IMAP/InterIMAP.pm
index bf33294..d6c46a8 100644
--- a/lib/Net/IMAP/InterIMAP.pm
+++ b/lib/Net/IMAP/InterIMAP.pm
@@ -228,6 +228,9 @@ our $IMAP_text;
#
# - 'logger-fd': An optional filehandle to use for debug output.
#
+# - 'keepalive': Whether to enable sending of keep-alive messages.
+# (type=imap or type=imaps).
+#
sub new($%) {
my $class = shift;
my $self = { @_ };
@@ -289,6 +292,23 @@ sub new($%) {
}
my $socket = defined $self->{proxy} ? $self->_proxify(@$self{qw/proxy host port/})
: $self->_tcp_connect(@$self{qw/host port/});
+ my ($cnt, $intvl) = (3, 5);
+ if (defined $self->{keepalive}) {
+ # detect dead peers and drop the connection after 60 secs + $cnt*$intvl
+ setsockopt($socket, Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1)
+ or $self->fail("Can't setsockopt SO_KEEPALIVE: $!");
+ setsockopt($socket, Socket::IPPROTO_TCP, Socket::TCP_KEEPIDLE, 60)
+ or $self->fail("Can't setsockopt TCP_KEEPIDLE: $!");
+ setsockopt($socket, Socket::IPPROTO_TCP, Socket::TCP_KEEPCNT, $cnt)
+ or $self->fail("Can't setsockopt TCP_KEEPCNT: $!");
+ setsockopt($socket, Socket::IPPROTO_TCP, Socket::TCP_KEEPINTVL, $intvl)
+ or $self->fail("Can't setsockopt TCP_KEEPINTVL: $!");
+ }
+ # Abort after 15secs if write(2) isn't acknowledged
+ # XXX Socket::TCP_USER_TIMEOUT isn't defined.
+ # `grep TCP_USER_TIMEOUT /usr/include/linux/tcp.h` gives 18
+ setsockopt($socket, Socket::IPPROTO_TCP, 18, 1000 * $cnt * $intvl)
+ or $self->fail("Can't setsockopt TCP_USER_TIMEOUT: $!");
$self->_start_ssl($socket) if $self->{type} eq 'imaps';
$self->{$_} = $socket for qw/STDOUT STDIN/;