From: Stephan Bosch Date: Wed, 15 Aug 2018 16:29:11 +0000 (+0200) Subject: lib-smtp: client: Allow receiving replies before command data stream is sent completely. X-Git-Tag: 2.3.6~85 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=0de66545558d31d74278d3de2ad8de47a37a5bd5;p=thirdparty%2Fdovecot%2Fcore.git lib-smtp: client: Allow receiving replies before command data stream is sent completely. This avoids unnecessary problems with servers that reply somewhat early to DATA and BDAT commands. For one, early failure replies are now handled properly. Also, race conditions at the client between sending the closing CRLF.CRLF and receiving the DATA reply are no longer causing problems. --- diff --git a/src/lib-smtp/smtp-client-command.c b/src/lib-smtp/smtp-client-command.c index 0feb17012e..6cbed57445 100644 --- a/src/lib-smtp/smtp-client-command.c +++ b/src/lib-smtp/smtp-client-command.c @@ -620,18 +620,27 @@ static int smtp_client_command_do_send_more(struct smtp_client_connection *conn) int ret; for (;;) { - /* check whether we can send anything */ - cmd = conn->cmd_send_queue_head; - if (cmd == NULL) - return 0; - if (!smtp_client_command_pipeline_is_open(conn)) - return 0; + if (conn->cmd_streaming != NULL) { + cmd = conn->cmd_streaming; + i_assert(cmd->stream != NULL); + } else { + /* check whether we can send anything */ + cmd = conn->cmd_send_queue_head; + if (cmd == NULL) + return 0; + if (!smtp_client_command_pipeline_is_open(conn)) + return 0; - cmd->state = SMTP_CLIENT_COMMAND_STATE_SENDING; - conn->sending_command = TRUE; + cmd->state = SMTP_CLIENT_COMMAND_STATE_SENDING; + conn->sending_command = TRUE; - if ((ret=smtp_client_command_send_line(cmd)) <= 0) - return ret; + if ((ret=smtp_client_command_send_line(cmd)) <= 0) + return ret; + + /* command line sent. move command to wait list. */ + smtp_cient_command_wait(cmd); + cmd->state = SMTP_CLIENT_COMMAND_STATE_WAITING; + } if (cmd->stream != NULL && (ret=smtp_client_command_send_stream(cmd)) <= 0) { @@ -639,13 +648,11 @@ static int smtp_client_command_do_send_more(struct smtp_client_connection *conn) return -1; smtp_client_command_debug(cmd, "Blocked while sending payload"); + conn->cmd_streaming = cmd; return 0; } - /* everything sent. move command to wait list. */ - smtp_cient_command_wait(cmd); - cmd->state = SMTP_CLIENT_COMMAND_STATE_WAITING; - + conn->cmd_streaming = NULL; conn->sending_command = FALSE; smtp_client_command_sent(cmd); } diff --git a/src/lib-smtp/smtp-client-connection.c b/src/lib-smtp/smtp-client-connection.c index 6b6396f657..83dfdc4d1c 100644 --- a/src/lib-smtp/smtp-client-connection.c +++ b/src/lib-smtp/smtp-client-connection.c @@ -1091,20 +1091,32 @@ smtp_client_connection_input_reply(struct smtp_client_connection *conn, return 1; } + if (reply->status == SMTP_CLIENT_COMMAND_ERROR_CONNECTION_CLOSED) { + smtp_client_connection_fail_reply(conn, reply); + return -1; + } + /* unexpected reply? */ if (conn->cmd_wait_list_head == NULL) { smtp_client_connection_debug(conn, "Unexpected reply: %s", smtp_reply_log(reply)); + smtp_client_connection_fail(conn, + SMTP_CLIENT_COMMAND_ERROR_BAD_REPLY, + "Got unexpected reply"); + return -1; + } - if (reply->status == - SMTP_CLIENT_COMMAND_ERROR_CONNECTION_CLOSED) { - smtp_client_connection_fail_reply(conn, reply); - } else { + /* replied early? */ + if (conn->cmd_wait_list_head == conn->cmd_streaming && + !conn->cmd_wait_list_head->stream_finished) { + smtp_client_connection_debug(conn, + "Early reply: %s", smtp_reply_log(reply)); + if (smtp_reply_is_success(reply)) { smtp_client_connection_fail(conn, SMTP_CLIENT_COMMAND_ERROR_BAD_REPLY, - "Got unexpected reply"); + "Got early success reply"); + return -1; } - return -1; } /* command reply */ @@ -1815,6 +1827,7 @@ void smtp_client_connection_disconnect(struct smtp_client_connection *conn) SMTP_CLIENT_COMMAND_ERROR_ABORTED, "Disconnected from server"); } + conn->cmd_streaming = NULL; } static struct smtp_client_connection * diff --git a/src/lib-smtp/smtp-client-private.h b/src/lib-smtp/smtp-client-private.h index 4ecc2e018e..7ef852b22a 100644 --- a/src/lib-smtp/smtp-client-private.h +++ b/src/lib-smtp/smtp-client-private.h @@ -186,9 +186,11 @@ struct smtp_client_connection { /* commands pending in queue to be sent */ struct smtp_client_command *cmd_send_queue_head, *cmd_send_queue_tail; unsigned int cmd_send_queue_count; - /* commands that have been sent, waiting for response */ + /* commands that have been (mostly) sent, waiting for response */ struct smtp_client_command *cmd_wait_list_head, *cmd_wait_list_tail; unsigned int cmd_wait_list_count; + /* command sending data stream */ + struct smtp_client_command *cmd_streaming; /* active transactions */ struct smtp_client_transaction *transactions_head, *transactions_tail;