refactor socket EOF handling

handling EOF already at the socket level isn't a very good idea - it
breaks the abstraction, and makes implementing sane semantics hard.
This commit is contained in:
Oswald Buddenhagen 2015-02-15 12:15:46 +01:00
parent 13c742529c
commit 9e15ab4a5a
3 changed files with 47 additions and 23 deletions

View File

@ -114,6 +114,8 @@ typedef struct imap_store {
/* Used during sequential operations like connect */ /* Used during sequential operations like connect */
enum { GreetingPending = 0, GreetingBad, GreetingOk, GreetingPreauth } greeting; enum { GreetingPending = 0, GreetingBad, GreetingOk, GreetingPreauth } greeting;
int expectBYE; /* LOGOUT is in progress */
int expectEOF; /* received LOGOUT's OK or unsolicited BYE */
int canceling; /* imap_cancel() is in progress */ int canceling; /* imap_cancel() is in progress */
union { union {
void (*imap_open)( store_t *srv, void *aux ); void (*imap_open)( store_t *srv, void *aux );
@ -674,7 +676,7 @@ parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
{ {
list_t *cur, **curp; list_t *cur, **curp;
char *s = *sp, *d, *p; char *s = *sp, *d, *p;
int bytes; int n, bytes;
char c; char c;
assert( sts ); assert( sts );
@ -724,7 +726,13 @@ parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
s[cur->len] = 0; s[cur->len] = 0;
getbytes: getbytes:
bytes -= socket_read( &ctx->conn, s, bytes ); n = socket_read( &ctx->conn, s, bytes );
if (n < 0) {
badeof:
error( "IMAP error: unexpected EOF from %s\n", ctx->conn.name );
goto bail;
}
bytes -= n;
if (bytes > 0) if (bytes > 0)
goto postpone; goto postpone;
@ -738,6 +746,8 @@ parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
getline: getline:
if (!(s = socket_read_line( &ctx->conn ))) if (!(s = socket_read_line( &ctx->conn )))
goto postpone; goto postpone;
if (s == (void *)~0)
goto badeof;
if (DFlags & VERBOSE) { if (DFlags & VERBOSE) {
printf( "%s%s\n", ctx->label, s ); printf( "%s%s\n", ctx->label, s );
fflush( stdout ); fflush( stdout );
@ -1218,6 +1228,12 @@ imap_socket_read( void *aux )
} }
if (!(cmd = socket_read_line( &ctx->conn ))) if (!(cmd = socket_read_line( &ctx->conn )))
return; return;
if (cmd == (void *)~0) {
if (!ctx->expectEOF)
error( "IMAP error: unexpected EOF from %s\n", ctx->conn.name );
/* A clean shutdown sequence ends with bad_callback as well (see imap_cleanup()). */
break;
}
if (DFlags & VERBOSE) { if (DFlags & VERBOSE) {
printf( "%s%s\n", ctx->label, cmd ); printf( "%s%s\n", ctx->label, cmd );
fflush( stdout ); fflush( stdout );
@ -1250,12 +1266,14 @@ imap_socket_read( void *aux )
goto dogreet; goto dogreet;
} }
} else if (!strcmp( "BYE", arg )) { } else if (!strcmp( "BYE", arg )) {
if (ctx->conn.state != SCK_CLOSING) { if (!ctx->expectBYE) {
ctx->conn.state = SCK_CLOSING;
ctx->greeting = GreetingBad; ctx->greeting = GreetingBad;
error( "IMAP error: unexpected BYE response: %s\n", cmd ); error( "IMAP error: unexpected BYE response: %s\n", cmd );
}
/* We just wait for the server to close the connection now. */ /* We just wait for the server to close the connection now. */
ctx->expectEOF = 1;
} else {
/* We still need to wait for the LOGOUT's tagged OK. */
}
} else if (ctx->greeting == GreetingPending) { } else if (ctx->greeting == GreetingPending) {
error( "IMAP error: bogus greeting response %s\n", arg ); error( "IMAP error: bogus greeting response %s\n", arg );
break; break;
@ -1470,7 +1488,7 @@ imap_cleanup( void )
for (ctx = unowned; ctx; ctx = nctx) { for (ctx = unowned; ctx; ctx = nctx) {
nctx = ctx->next; nctx = ctx->next;
set_bad_callback( ctx, (void (*)(void *))imap_cancel_store, ctx ); set_bad_callback( ctx, (void (*)(void *))imap_cancel_store, ctx );
((imap_store_t *)ctx)->conn.state = SCK_CLOSING; ((imap_store_t *)ctx)->expectBYE = 1;
imap_exec( (imap_store_t *)ctx, 0, imap_cleanup_p2, "LOGOUT" ); imap_exec( (imap_store_t *)ctx, 0, imap_cleanup_p2, "LOGOUT" );
} }
} }
@ -1481,6 +1499,8 @@ imap_cleanup_p2( imap_store_t *ctx,
{ {
if (response == RESP_NO) if (response == RESP_NO)
imap_cancel_store( &ctx->gen ); imap_cancel_store( &ctx->gen );
else if (response == RESP_OK)
ctx->expectEOF = 1;
} }
/******************* imap_open_store *******************/ /******************* imap_open_store *******************/

View File

@ -43,6 +43,15 @@
# include <openssl/x509v3.h> # include <openssl/x509v3.h>
#endif #endif
enum {
SCK_CONNECTING,
#ifdef HAVE_LIBSSL
SCK_STARTTLS,
#endif
SCK_READY,
SCK_EOF
};
static void static void
socket_fail( conn_t *conn ) socket_fail( conn_t *conn )
{ {
@ -67,11 +76,12 @@ ssl_return( const char *func, conn_t *conn, int ret )
case SSL_ERROR_SSL: case SSL_ERROR_SSL:
if (!(err = ERR_get_error())) { if (!(err = ERR_get_error())) {
if (ret == 0) { if (ret == 0) {
if (conn->state != SCK_CLOSING) /* Callers take the short path out, so signal higher layers from here. */
error( "Socket error: secure %s %s: unexpected EOF\n", func, conn->name ); conn->state = SCK_EOF;
} else { conn->read_callback( conn->callback_aux );
sys_error( "Socket error: secure %s %s", func, conn->name ); return 0;
} }
sys_error( "Socket error: secure %s %s", func, conn->name );
} else { } else {
error( "Socket error: secure %s %s: %s\n", func, conn->name, ERR_error_string( err, 0 ) ); error( "Socket error: secure %s %s: %s\n", func, conn->name, ERR_error_string( err, 0 ) );
} }
@ -582,10 +592,9 @@ do_read( conn_t *sock, char *buf, int len )
sys_error( "Socket error: read from %s", sock->name ); sys_error( "Socket error: read from %s", sock->name );
socket_fail( sock ); socket_fail( sock );
} else if (!n) { } else if (!n) {
if (sock->state != SCK_CLOSING) /* EOF. Callers take the short path out, so signal higher layers from here. */
error( "Socket error: read from %s: unexpected EOF\n", sock->name ); sock->state = SCK_EOF;
socket_fail( sock ); sock->read_callback( sock->callback_aux );
return -1;
} }
} }
@ -656,6 +665,8 @@ int
socket_read( conn_t *conn, char *buf, int len ) socket_read( conn_t *conn, char *buf, int len )
{ {
int n = conn->bytes; int n = conn->bytes;
if (!n && conn->state == SCK_EOF)
return -1;
if (n > len) if (n > len)
n = len; n = len;
memcpy( buf, conn->buf + conn->offset, n ); memcpy( buf, conn->buf + conn->offset, n );
@ -680,6 +691,8 @@ socket_read_line( conn_t *b )
memmove( b->buf, b->buf + b->offset, b->bytes ); memmove( b->buf, b->buf + b->offset, b->bytes );
b->offset = 0; b->offset = 0;
} }
if (b->state == SCK_EOF)
return (void *)~0;
return 0; return 0;
} }
n = p + 1 - s; n = p + 1 - s;

View File

@ -29,15 +29,6 @@
#include <zlib.h> #include <zlib.h>
#endif #endif
enum {
SCK_CONNECTING,
#ifdef HAVE_LIBSSL
SCK_STARTTLS,
#endif
SCK_READY,
SCK_CLOSING
};
#ifdef HAVE_LIBSSL #ifdef HAVE_LIBSSL
typedef struct ssl_st SSL; typedef struct ssl_st SSL;
typedef struct ssl_ctx_st SSL_CTX; typedef struct ssl_ctx_st SSL_CTX;