]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
submission: Restructure handling of QUIT command.
authorStephan Bosch <stephan.bosch@dovecot.fi>
Sun, 24 Dec 2017 14:12:36 +0000 (15:12 +0100)
committerVille Savolainen <ville.savolainen@dovecot.fi>
Wed, 31 Jan 2018 14:05:57 +0000 (16:05 +0200)
- Avoid explicitly proxying QUIT command when the proxy connection is not ready: in that case the SMTP client connection will just send QUIT if appropriate, without waiting for reply.
- Add timeout for proxied QUIT command, so that there are no problems when the relay server hangs after QUIT (addresses FIXME).

src/submission/cmd-quit.c
src/submission/submission-client.c
src/submission/submission-client.h
src/submission/submission-common.h

index 5744d7e06f9b33400273ed9fd005c3319975b72f..7912a9abecaee17ff035df4bdbf8b72636f853ba 100644 (file)
@@ -16,13 +16,69 @@ struct cmd_quit_context {
        struct smtp_client_command *cmd_proxied;
 };
 
+static void cmd_quit_finish(struct cmd_quit_context *quit_cmd)
+{
+       struct client *client = quit_cmd->client;
+       struct smtp_server_cmd_ctx *cmd = quit_cmd->cmd;
+
+       timeout_remove(&client->to_quit);
+       if (quit_cmd->cmd_proxied != NULL)
+               smtp_client_command_abort(&quit_cmd->cmd_proxied);
+       smtp_server_reply_quit(cmd);
+}
+
 static void cmd_quit_proxy_cb(
        const struct smtp_reply *proxy_reply ATTR_UNUSED,
        struct cmd_quit_context *quit_cmd)
 {
+       quit_cmd->cmd_proxied = NULL;
+       cmd_quit_finish(quit_cmd);
+}
+
+static void cmd_quit_proxy(struct cmd_quit_context *quit_cmd)
+{
+       struct client *client = quit_cmd->client;
        struct smtp_server_cmd_ctx *cmd = quit_cmd->cmd;
 
-       smtp_server_reply_quit(cmd);
+       if (quit_cmd->cmd_proxied != NULL)
+               return;
+
+       if (smtp_client_connection_get_state(client->proxy_conn)
+               < SMTP_CLIENT_CONNECTION_STATE_READY) {
+               /* Don't bother proxying QUIT command when proxy is not
+                  fully initialized. */
+               smtp_server_reply_quit(cmd);
+               return;
+       }
+
+       /* RFC 5321, Section 4.1.1.10:
+
+          The sender MUST NOT intentionally close the transmission channel
+          until it sends a QUIT command, and it SHOULD wait until it receives
+          the reply (even if there was an error response to a previous
+          command). */
+       quit_cmd->cmd_proxied =
+               smtp_client_command_new(client->proxy_conn, 0,
+                                       cmd_quit_proxy_cb, quit_cmd);
+       smtp_client_command_write(quit_cmd->cmd_proxied, "QUIT");
+       smtp_client_command_submit(quit_cmd->cmd_proxied);
+}
+
+static void cmd_quit_next(struct smtp_server_cmd_ctx *cmd)
+{
+       struct cmd_quit_context *quit_cmd = cmd->context;
+       struct client *client = quit_cmd->client;
+
+       /* QUIT command is next to reply */
+
+       cmd_quit_proxy(quit_cmd);
+
+       if (quit_cmd->cmd_proxied != NULL) {
+               /* give relay server brief interval to reply */
+               client->to_quit = timeout_add(
+                       SUBMISSION_MAX_WAIT_QUIT_REPLY_MSECS,
+                       cmd_quit_finish, quit_cmd);
+       }
 }
 
 int cmd_quit(void *conn_ctx, struct smtp_server_cmd_ctx *cmd)
@@ -33,11 +89,12 @@ int cmd_quit(void *conn_ctx, struct smtp_server_cmd_ctx *cmd)
        quit_cmd = p_new(cmd->pool, struct cmd_quit_context, 1);
        quit_cmd->client = client;
        quit_cmd->cmd = cmd;
+
+       cmd->hook_next = cmd_quit_next;
        cmd->context = quit_cmd;
 
-       quit_cmd->cmd_proxied = smtp_client_command_new
-               (client->proxy_conn, 0, cmd_quit_proxy_cb, quit_cmd);
-       smtp_client_command_write(quit_cmd->cmd_proxied, "QUIT");
-       smtp_client_command_submit(quit_cmd->cmd_proxied); // FIXME: timeout
+       if (smtp_client_connection_get_state(client->proxy_conn)
+               >= SMTP_CLIENT_CONNECTION_STATE_READY)
+               cmd_quit_proxy(quit_cmd);
        return 0;
 }
index b04031ca34af0a8687552aa26d7e775087282e46..549a34571ef1de86c388396796393bee2d568c2f 100644 (file)
@@ -367,6 +367,7 @@ void client_disconnect(struct client *client, const char *enh_code,
                return;
        client->disconnected = TRUE;
 
+       timeout_remove(&client->to_quit);
        if (client->proxy_conn != NULL)
                smtp_client_connection_close(&client->proxy_conn);
 
index 9cb7600518e2f78b30bf38a36ef4d403afbe15a5..d12b579f868918b994990a88de0f956d6574315a 100644 (file)
@@ -32,6 +32,7 @@ struct client {
        struct imap_urlauth_context *urlauth_ctx;
 
        struct smtp_client_connection *proxy_conn;
+       struct timeout *to_quit;
 
        struct smtp_server_stats stats;
 
index bb2830139c11ff3a3046d2e5d831673ffc26bc95..49c97f8feb4d09403d7cf8d1312724d433973774 100644 (file)
@@ -16,6 +16,9 @@
 #define SUBMISSION_MAX_ADDITIONAL_MAIL_SIZE 1024
 #define SUBMISSION_MAIL_DATA_MAX_INMEMORY_SIZE (1024*128)
 
+/* Maximum time to wait for QUIT reply from relay server */
+#define SUBMISSION_MAX_WAIT_QUIT_REPLY_MSECS 2000
+
 typedef void submission_client_created_func_t(struct client **client);
 
 extern submission_client_created_func_t *hook_client_created;