From 612b9e2102e1907709dde325f91d5fdf70ed2534 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Thu, 17 Sep 2015 22:05:09 +0200 Subject: Use TCP keepalive to detect dead peers. --- Changelog | 3 --- interimap | 1 + lib/Net/IMAP/InterIMAP.pm | 20 ++++++++++++++++++++ 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/; -- cgit v1.2.3