add socket timeout handling
This commit is contained in:
parent
5c4015aee5
commit
1eb88d4fea
4
NEWS
4
NEWS
|
@ -1,3 +1,7 @@
|
||||||
|
[1.3.0]
|
||||||
|
|
||||||
|
Network timeout handling has been added.
|
||||||
|
|
||||||
[1.2.0]
|
[1.2.0]
|
||||||
|
|
||||||
The 'isync' compatibility wrapper is now deprecated.
|
The 'isync' compatibility wrapper is now deprecated.
|
||||||
|
|
3
TODO
3
TODO
|
@ -8,8 +8,7 @@ won't cause the same error message for every attached store.
|
||||||
|
|
||||||
make SSL (connect) timeouts produce a bit more than "Unidentified socket error".
|
make SSL (connect) timeouts produce a bit more than "Unidentified socket error".
|
||||||
|
|
||||||
network timeout handling in general would be a good idea.
|
uidvalidity lock timeout handling would be a good idea.
|
||||||
lock timeout handling, too.
|
|
||||||
|
|
||||||
add message expiration based on arrival date (message date would be too
|
add message expiration based on arrival date (message date would be too
|
||||||
unreliable). MaxAge; probably mutually exclusive to MaxMessages.
|
unreliable). MaxAge; probably mutually exclusive to MaxMessages.
|
||||||
|
|
|
@ -319,6 +319,7 @@ send_imap_cmd( imap_store_t *ctx, struct imap_cmd *cmd )
|
||||||
*ctx->in_progress_append = cmd;
|
*ctx->in_progress_append = cmd;
|
||||||
ctx->in_progress_append = &cmd->next;
|
ctx->in_progress_append = &cmd->next;
|
||||||
ctx->num_in_progress++;
|
ctx->num_in_progress++;
|
||||||
|
socket_expect_read( &ctx->conn, 1 );
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
bail:
|
bail:
|
||||||
|
@ -371,6 +372,7 @@ cancel_submitted_imap_cmds( imap_store_t *ctx )
|
||||||
{
|
{
|
||||||
struct imap_cmd *cmd;
|
struct imap_cmd *cmd;
|
||||||
|
|
||||||
|
socket_expect_read( &ctx->conn, 0 );
|
||||||
while ((cmd = ctx->in_progress)) {
|
while ((cmd = ctx->in_progress)) {
|
||||||
ctx->in_progress = cmd->next;
|
ctx->in_progress = cmd->next;
|
||||||
/* don't update num_in_progress and in_progress_append - store is dead */
|
/* don't update num_in_progress and in_progress_append - store is dead */
|
||||||
|
@ -1316,6 +1318,7 @@ imap_socket_read( void *aux )
|
||||||
error( "IMAP error: unexpected reply: %s %s\n", arg, cmd ? cmd : "" );
|
error( "IMAP error: unexpected reply: %s %s\n", arg, cmd ? cmd : "" );
|
||||||
break; /* this may mean anything, so prefer not to spam the log */
|
break; /* this may mean anything, so prefer not to spam the log */
|
||||||
} else if (*arg == '+') {
|
} else if (*arg == '+') {
|
||||||
|
socket_expect_read( &ctx->conn, 0 );
|
||||||
/* There can be any number of commands in flight, but only the last
|
/* There can be any number of commands in flight, but only the last
|
||||||
* one can require a continuation, as it enforces a round-trip. */
|
* one can require a continuation, as it enforces a round-trip. */
|
||||||
cmdp = (struct imap_cmd *)((char *)ctx->in_progress_append -
|
cmdp = (struct imap_cmd *)((char *)ctx->in_progress_append -
|
||||||
|
@ -1340,6 +1343,7 @@ imap_socket_read( void *aux )
|
||||||
error( "IMAP error: unexpected command continuation request\n" );
|
error( "IMAP error: unexpected command continuation request\n" );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
socket_expect_read( &ctx->conn, 1 );
|
||||||
} else {
|
} else {
|
||||||
tag = atoi( arg );
|
tag = atoi( arg );
|
||||||
for (pcmdp = &ctx->in_progress; (cmdp = *pcmdp); pcmdp = &cmdp->next)
|
for (pcmdp = &ctx->in_progress; (cmdp = *pcmdp); pcmdp = &cmdp->next)
|
||||||
|
@ -1350,7 +1354,8 @@ imap_socket_read( void *aux )
|
||||||
gottag:
|
gottag:
|
||||||
if (!(*pcmdp = cmdp->next))
|
if (!(*pcmdp = cmdp->next))
|
||||||
ctx->in_progress_append = pcmdp;
|
ctx->in_progress_append = pcmdp;
|
||||||
ctx->num_in_progress--;
|
if (!--ctx->num_in_progress)
|
||||||
|
socket_expect_read( &ctx->conn, 0 );
|
||||||
arg = next_arg( &cmd );
|
arg = next_arg( &cmd );
|
||||||
if (!arg) {
|
if (!arg) {
|
||||||
error( "IMAP error: malformed tagged response\n" );
|
error( "IMAP error: malformed tagged response\n" );
|
||||||
|
@ -1614,6 +1619,8 @@ imap_open_store_connected( int ok, void *aux )
|
||||||
else if (srvc->ssl_type == SSL_IMAPS)
|
else if (srvc->ssl_type == SSL_IMAPS)
|
||||||
socket_start_tls( &ctx->conn, imap_open_store_tlsstarted1 );
|
socket_start_tls( &ctx->conn, imap_open_store_tlsstarted1 );
|
||||||
#endif
|
#endif
|
||||||
|
else
|
||||||
|
socket_expect_read( &ctx->conn, 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_LIBSSL
|
#ifdef HAVE_LIBSSL
|
||||||
|
@ -1624,12 +1631,15 @@ imap_open_store_tlsstarted1( int ok, void *aux )
|
||||||
|
|
||||||
if (!ok)
|
if (!ok)
|
||||||
imap_open_store_ssl_bail( ctx );
|
imap_open_store_ssl_bail( ctx );
|
||||||
|
else
|
||||||
|
socket_expect_read( &ctx->conn, 1 );
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void
|
static void
|
||||||
imap_open_store_greeted( imap_store_t *ctx )
|
imap_open_store_greeted( imap_store_t *ctx )
|
||||||
{
|
{
|
||||||
|
socket_expect_read( &ctx->conn, 0 );
|
||||||
if (!ctx->caps)
|
if (!ctx->caps)
|
||||||
imap_exec( ctx, 0, imap_open_store_p2, "CAPABILITY" );
|
imap_exec( ctx, 0, imap_open_store_p2, "CAPABILITY" );
|
||||||
else
|
else
|
||||||
|
@ -2694,6 +2704,7 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
|
||||||
} else
|
} else
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
server->sconf.timeout = 20;
|
||||||
#ifdef HAVE_LIBSSL
|
#ifdef HAVE_LIBSSL
|
||||||
server->ssl_type = -1;
|
server->ssl_type = -1;
|
||||||
server->sconf.ssl_versions = -1;
|
server->sconf.ssl_versions = -1;
|
||||||
|
@ -2729,6 +2740,8 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
|
||||||
server->pass_cmd = nfstrdup( cfg->val );
|
server->pass_cmd = nfstrdup( cfg->val );
|
||||||
else if (!strcasecmp( "Port", cfg->cmd ))
|
else if (!strcasecmp( "Port", cfg->cmd ))
|
||||||
server->sconf.port = parse_int( cfg );
|
server->sconf.port = parse_int( cfg );
|
||||||
|
else if (!strcasecmp( "Timeout", cfg->cmd ))
|
||||||
|
server->sconf.timeout = parse_int( cfg );
|
||||||
else if (!strcasecmp( "PipelineDepth", cfg->cmd )) {
|
else if (!strcasecmp( "PipelineDepth", cfg->cmd )) {
|
||||||
if ((server->max_in_progress = parse_int( cfg )) < 1) {
|
if ((server->max_in_progress = parse_int( cfg )) < 1) {
|
||||||
error( "%s:%d: PipelineDepth must be at least 1\n", cfg->file, cfg->line );
|
error( "%s:%d: PipelineDepth must be at least 1\n", cfg->file, cfg->line );
|
||||||
|
|
|
@ -277,6 +277,12 @@ Specify the TCP port number of the IMAP server. (Default: 143 for IMAP,
|
||||||
If \fBTunnel\fR is used, this setting is ignored.
|
If \fBTunnel\fR is used, this setting is ignored.
|
||||||
..
|
..
|
||||||
.TP
|
.TP
|
||||||
|
\fBTimeout\fR \fItimeout\fR
|
||||||
|
Specify the connect and data timeout for the IMAP server in seconds.
|
||||||
|
Zero means unlimited.
|
||||||
|
(Default: \fI20\fR)
|
||||||
|
..
|
||||||
|
.TP
|
||||||
\fBUser\fR \fIusername\fR
|
\fBUser\fR \fIusername\fR
|
||||||
Specify the login name on the IMAP server.
|
Specify the login name on the IMAP server.
|
||||||
..
|
..
|
||||||
|
|
30
src/socket.c
30
src/socket.c
|
@ -260,6 +260,7 @@ socket_start_tls( conn_t *conn, void (*cb)( int ok, void *aux ) )
|
||||||
conn->ssl = SSL_new( ((server_conf_t *)conn->conf)->SSLContext );
|
conn->ssl = SSL_new( ((server_conf_t *)conn->conf)->SSLContext );
|
||||||
SSL_set_fd( conn->ssl, conn->fd );
|
SSL_set_fd( conn->ssl, conn->fd );
|
||||||
SSL_set_mode( conn->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER );
|
SSL_set_mode( conn->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER );
|
||||||
|
socket_expect_read( conn, 1 );
|
||||||
conn->state = SCK_STARTTLS;
|
conn->state = SCK_STARTTLS;
|
||||||
start_tls_p2( conn );
|
start_tls_p2( conn );
|
||||||
}
|
}
|
||||||
|
@ -279,6 +280,7 @@ start_tls_p2( conn_t *conn )
|
||||||
|
|
||||||
static void start_tls_p3( conn_t *conn, int ok )
|
static void start_tls_p3( conn_t *conn, int ok )
|
||||||
{
|
{
|
||||||
|
socket_expect_read( conn, 0 );
|
||||||
conn->state = SCK_READY;
|
conn->state = SCK_READY;
|
||||||
conn->callbacks.starttls( ok, conn->callback_aux );
|
conn->callbacks.starttls( ok, conn->callback_aux );
|
||||||
}
|
}
|
||||||
|
@ -324,6 +326,7 @@ socket_start_deflate( conn_t *conn )
|
||||||
|
|
||||||
static void socket_fd_cb( int, void * );
|
static void socket_fd_cb( int, void * );
|
||||||
static void socket_fake_cb( void * );
|
static void socket_fake_cb( void * );
|
||||||
|
static void socket_timeout_cb( void * );
|
||||||
|
|
||||||
static void socket_connect_one( conn_t * );
|
static void socket_connect_one( conn_t * );
|
||||||
static void socket_connect_failed( conn_t * );
|
static void socket_connect_failed( conn_t * );
|
||||||
|
@ -337,6 +340,7 @@ socket_open_internal( conn_t *sock, int fd )
|
||||||
fcntl( fd, F_SETFL, O_NONBLOCK );
|
fcntl( fd, F_SETFL, O_NONBLOCK );
|
||||||
init_notifier( &sock->notify, fd, socket_fd_cb, sock );
|
init_notifier( &sock->notify, fd, socket_fd_cb, sock );
|
||||||
init_wakeup( &sock->fd_fake, socket_fake_cb, sock );
|
init_wakeup( &sock->fd_fake, socket_fake_cb, sock );
|
||||||
|
init_wakeup( &sock->fd_timeout, socket_timeout_cb, sock );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -344,6 +348,7 @@ socket_close_internal( conn_t *sock )
|
||||||
{
|
{
|
||||||
wipe_notifier( &sock->notify );
|
wipe_notifier( &sock->notify );
|
||||||
wipe_wakeup( &sock->fd_fake );
|
wipe_wakeup( &sock->fd_fake );
|
||||||
|
wipe_wakeup( &sock->fd_timeout );
|
||||||
close( sock->fd );
|
close( sock->fd );
|
||||||
sock->fd = -1;
|
sock->fd = -1;
|
||||||
}
|
}
|
||||||
|
@ -482,6 +487,7 @@ socket_connect_one( conn_t *sock )
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
conf_notifier( &sock->notify, 0, POLLOUT );
|
conf_notifier( &sock->notify, 0, POLLOUT );
|
||||||
|
socket_expect_read( sock, 1 );
|
||||||
sock->state = SCK_CONNECTING;
|
sock->state = SCK_CONNECTING;
|
||||||
info( "\v\n" );
|
info( "\v\n" );
|
||||||
return;
|
return;
|
||||||
|
@ -512,6 +518,7 @@ socket_connected( conn_t *conn )
|
||||||
freeaddrinfo( conn->addrs );
|
freeaddrinfo( conn->addrs );
|
||||||
#endif
|
#endif
|
||||||
conf_notifier( &conn->notify, 0, POLLIN );
|
conf_notifier( &conn->notify, 0, POLLIN );
|
||||||
|
socket_expect_read( conn, 0 );
|
||||||
conn->state = SCK_READY;
|
conn->state = SCK_READY;
|
||||||
conn->callbacks.connect( 1, conn->callback_aux );
|
conn->callbacks.connect( 1, conn->callback_aux );
|
||||||
}
|
}
|
||||||
|
@ -579,6 +586,8 @@ do_read( conn_t *sock, char *buf, int len )
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
assert( sock->fd >= 0 );
|
assert( sock->fd >= 0 );
|
||||||
|
if (pending_wakeup( &sock->fd_timeout ))
|
||||||
|
conf_wakeup( &sock->fd_timeout, sock->conf->timeout );
|
||||||
#ifdef HAVE_LIBSSL
|
#ifdef HAVE_LIBSSL
|
||||||
if (sock->ssl) {
|
if (sock->ssl) {
|
||||||
if ((n = ssl_return( "read from", sock, SSL_read( sock->ssl, buf, len ) )) <= 0)
|
if ((n = ssl_return( "read from", sock, SSL_read( sock->ssl, buf, len ) )) <= 0)
|
||||||
|
@ -662,6 +671,13 @@ socket_fill( conn_t *sock )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
socket_expect_read( conn_t *conn, int expect )
|
||||||
|
{
|
||||||
|
if (conn->conf->timeout > 0 && expect != pending_wakeup( &conn->fd_timeout ))
|
||||||
|
conf_wakeup( &conn->fd_timeout, expect ? conn->conf->timeout : -1 );
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
socket_read( conn_t *conn, char *buf, int len )
|
socket_read( conn_t *conn, char *buf, int len )
|
||||||
{
|
{
|
||||||
|
@ -970,6 +986,20 @@ socket_fake_cb( void *aux )
|
||||||
do_queued_write( conn );
|
do_queued_write( conn );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
socket_timeout_cb( void *aux )
|
||||||
|
{
|
||||||
|
conn_t *conn = (conn_t *)aux;
|
||||||
|
|
||||||
|
if (conn->state == SCK_CONNECTING) {
|
||||||
|
errno = ETIMEDOUT;
|
||||||
|
socket_connect_failed( conn );
|
||||||
|
} else {
|
||||||
|
error( "Socket error on %s: timeout.\n", conn->name );
|
||||||
|
socket_fail( conn );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef HAVE_LIBZ
|
#ifdef HAVE_LIBZ
|
||||||
static void
|
static void
|
||||||
z_fake_cb( void *aux )
|
z_fake_cb( void *aux )
|
||||||
|
|
|
@ -47,6 +47,7 @@ typedef struct server_conf {
|
||||||
char *tunnel;
|
char *tunnel;
|
||||||
char *host;
|
char *host;
|
||||||
int port;
|
int port;
|
||||||
|
int timeout;
|
||||||
#ifdef HAVE_LIBSSL
|
#ifdef HAVE_LIBSSL
|
||||||
char *cert_file;
|
char *cert_file;
|
||||||
char system_certs;
|
char system_certs;
|
||||||
|
@ -96,6 +97,7 @@ typedef struct {
|
||||||
|
|
||||||
notifier_t notify;
|
notifier_t notify;
|
||||||
wakeup_t fd_fake;
|
wakeup_t fd_fake;
|
||||||
|
wakeup_t fd_timeout;
|
||||||
|
|
||||||
/* writing */
|
/* writing */
|
||||||
buff_chunk_t *append_buf; /* accumulating buffer */
|
buff_chunk_t *append_buf; /* accumulating buffer */
|
||||||
|
@ -137,6 +139,7 @@ void socket_connect( conn_t *conn, void (*cb)( int ok, void *aux ) );
|
||||||
void socket_start_tls(conn_t *conn, void (*cb)( int ok, void *aux ) );
|
void socket_start_tls(conn_t *conn, void (*cb)( int ok, void *aux ) );
|
||||||
void socket_start_deflate( conn_t *conn );
|
void socket_start_deflate( conn_t *conn );
|
||||||
void socket_close( conn_t *sock );
|
void socket_close( conn_t *sock );
|
||||||
|
void socket_expect_read( conn_t *sock, int expect );
|
||||||
int socket_read( conn_t *sock, char *buf, int len ); /* never waits */
|
int socket_read( conn_t *sock, char *buf, int len ); /* never waits */
|
||||||
char *socket_read_line( conn_t *sock ); /* don't free return value; never waits */
|
char *socket_read_line( conn_t *sock ); /* don't free return value; never waits */
|
||||||
typedef enum { KeepOwn = 0, GiveOwn } ownership_t;
|
typedef enum { KeepOwn = 0, GiveOwn } ownership_t;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user