Skip to content

Commit

Permalink
lib-smtp: client: Allow receiving replies before command data stream …
Browse files Browse the repository at this point in the history
…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.
  • Loading branch information
stephanbosch committed Jan 22, 2019
1 parent f9f9d5e commit 4f5f68c
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 21 deletions.
35 changes: 21 additions & 14 deletions src/lib-smtp/smtp-client-command.c
Expand Up @@ -626,32 +626,39 @@ 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) {
if (ret < 0)
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);
}
Expand Down
25 changes: 19 additions & 6 deletions src/lib-smtp/smtp-client-connection.c
Expand Up @@ -1093,20 +1093,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 */
Expand Down Expand Up @@ -1817,6 +1829,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 *
Expand Down
4 changes: 3 additions & 1 deletion src/lib-smtp/smtp-client-private.h
Expand Up @@ -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;
Expand Down

0 comments on commit 4f5f68c

Please sign in to comment.