]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
login-common: Add support for multiplex iostreams
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Mon, 8 Apr 2024 19:55:59 +0000 (22:55 +0300)
committertimo.sirainen <timo.sirainen@open-xchange.com>
Tue, 18 Jun 2024 08:31:38 +0000 (08:31 +0000)
This is an internal protocol between two trusted Dovecots.

src/login-common/client-common.c
src/login-common/client-common.h
src/login-common/login-proxy.c
src/login-common/login-proxy.h

index dec970c74c382664f70ecbfcaffca21f2750b889..3ef60a7f1b145c1d5d3b8dda13af2a5f8e7d2399 100644 (file)
@@ -7,6 +7,8 @@
 #include "istream.h"
 #include "md5.h"
 #include "ostream.h"
+#include "istream-multiplex.h"
+#include "ostream-multiplex.h"
 #include "iostream.h"
 #include "iostream-ssl.h"
 #include "iostream-proxy.h"
@@ -578,6 +580,7 @@ bool client_unref(struct client **_client)
        client->list_type = CLIENT_LIST_TYPE_NONE;
        i_stream_unref(&client->input);
        o_stream_unref(&client->output);
+       o_stream_unref(&client->multiplex_orig_output);
        i_close_fd(&client->fd);
        event_unref(&client->event);
        event_unref(&client->event_auth);
@@ -758,6 +761,13 @@ int client_init_ssl(struct client *client)
 
 static void client_start_tls(struct client *client)
 {
+       bool add_multiplex_ostream = FALSE;
+
+       if (client->multiplex_output != NULL) {
+               /* restart multiplexing after TLS iostreams are set up */
+               client_multiplex_output_stop(client);
+               add_multiplex_ostream = TRUE;
+       }
        client->connection_used_starttls = TRUE;
        if (client_init_ssl(client) < 0) {
                client_notify_disconnect(client,
@@ -768,6 +778,8 @@ static void client_start_tls(struct client *client)
        }
        login_refresh_proctitle();
 
+       if (add_multiplex_ostream)
+               client_multiplex_output_start(client);
        client->v.starttls(client);
 }
 
@@ -818,6 +830,39 @@ void client_cmd_starttls(struct client *client)
        }
 }
 
+void client_multiplex_output_start(struct client *client)
+{
+       if (client->v.iostream_change_pre != NULL)
+               client->v.iostream_change_pre(client);
+
+       client->multiplex_output =
+               o_stream_create_multiplex(client->output, LOGIN_MAX_OUTBUF_SIZE,
+                                         OSTREAM_MULTIPLEX_FORMAT_STREAM);
+       client->multiplex_orig_output = client->output;
+       client->output = client->multiplex_output;
+
+       if (client->v.iostream_change_post != NULL)
+               client->v.iostream_change_post(client);
+}
+
+void client_multiplex_output_stop(struct client *client)
+{
+       i_assert(client->multiplex_output != NULL);
+       i_assert(client->multiplex_orig_output != NULL);
+
+       if (client->v.iostream_change_pre != NULL)
+               client->v.iostream_change_pre(client);
+
+       i_assert(client->output == client->multiplex_output);
+       o_stream_unref(&client->output);
+       client->output = client->multiplex_orig_output;
+       client->multiplex_output = NULL;
+       client->multiplex_orig_output = NULL;
+
+       if (client->v.iostream_change_post != NULL)
+               client->v.iostream_change_post(client);
+}
+
 static void
 iostream_fd_proxy_finished(enum iostream_proxy_side side ATTR_UNUSED,
                           enum iostream_proxy_status status ATTR_UNUSED,
@@ -834,7 +879,7 @@ int client_get_plaintext_fd(struct client *client, int *fd_r, bool *close_fd_r)
 {
        int fds[2];
 
-       if (client->ssl_iostream == NULL) {
+       if (client->ssl_iostream == NULL && client->multiplex_output == NULL) {
                /* Plaintext connection - We can send the fd directly to
                   the post-login process without any proxying. */
                *fd_r = client->fd;
@@ -858,11 +903,18 @@ int client_get_plaintext_fd(struct client *client, int *fd_r, bool *close_fd_r)
        o_stream_set_no_error_handling(output, TRUE);
 
        i_assert(client->io == NULL);
+       struct ostream *client_output = client->output;
+       if (client->multiplex_output != NULL) {
+               /* The post-login process takes over handling the multiplex
+                  stream. */
+               i_assert(client_output == client->multiplex_output);
+               client_output = client->multiplex_orig_output;
+       }
 
        client_ref(client);
        client->iostream_fd_proxy =
                iostream_proxy_create(input, output,
-                                     client->input, client->output);
+                                     client->input, client_output);
        i_stream_unref(&input);
        o_stream_unref(&output);
 
index 8fb29e20abf9e85aaa228f5f64d930621595a4ba..065159139e7c8703b74d090e4fb96beaca642238 100644 (file)
@@ -196,6 +196,11 @@ struct client {
        int fd;
        struct istream *input;
        struct ostream *output;
+       /* If non-NULL, this is the multiplex ostream. It is usually the same
+          as the output pointer, but some plugins may make them different.
+          This isn't holding a reference, so it must not be unreferenced. */
+       struct ostream *multiplex_output;
+       struct ostream *multiplex_orig_output;
        struct io *io;
        struct iostream_proxy *iostream_fd_proxy;
        struct timeout *to_auth_waiting;
@@ -340,6 +345,9 @@ bool client_unref(struct client **client) ATTR_NOWARN_UNUSED_RESULT;
 int client_init_ssl(struct client *client);
 void client_cmd_starttls(struct client *client);
 
+void client_multiplex_output_start(struct client *client);
+void client_multiplex_output_stop(struct client *client);
+
 int client_get_plaintext_fd(struct client *client, int *fd_r, bool *close_fd_r);
 
 unsigned int clients_get_count(void) ATTR_PURE;
index eddd28669d55a4cb0cf2ba365f7a399cbb52c37c..725538e58c41fbf4fe48d8e976272e07a1344df0 100644 (file)
@@ -5,6 +5,8 @@
 #include "ioloop.h"
 #include "istream.h"
 #include "ostream.h"
+#include "iostream.h"
+#include "istream-multiplex.h"
 #include "iostream-proxy.h"
 #include "iostream-rawlog.h"
 #include "iostream-ssl.h"
@@ -62,6 +64,7 @@ struct login_proxy {
        struct io *client_wait_io, *server_io;
        struct istream *client_input, *server_input;
        struct ostream *client_output, *server_output;
+       struct istream *multiplex_input, *multiplex_orig_input;
        struct iostream_proxy *iostream_proxy;
        struct ssl_iostream *server_ssl_iostream;
        guid_128_t anvil_conn_guid;
@@ -521,6 +524,8 @@ static void login_proxy_disconnect(struct login_proxy *proxy)
        ssl_iostream_destroy(&proxy->server_ssl_iostream);
 
        io_remove(&proxy->server_io);
+       i_stream_destroy(&proxy->multiplex_orig_input);
+       proxy->multiplex_input = NULL;
        i_stream_destroy(&proxy->server_input);
        o_stream_destroy(&proxy->server_output);
        if (proxy->server_fd != -1) {
@@ -1010,11 +1015,27 @@ void login_proxy_detach(struct login_proxy *proxy)
        proxy->detached = TRUE;
        proxy->client_input = client->input;
        proxy->client_output = client->output;
-
-       o_stream_set_max_buffer_size(client->output, PROXY_MAX_OUTBUF_SIZE);
        client->input = NULL;
        client->output = NULL;
 
+       if (proxy->multiplex_orig_input != NULL &&
+           client->multiplex_output == proxy->client_output) {
+               /* both sides of the proxy want multiplexing and there are no
+                  plugins hooking into the ostream. We can just step out of
+                  the way and let the two sides multiplex directly. */
+               i_stream_unref(&proxy->server_input);
+               proxy->server_input = proxy->multiplex_orig_input;
+               proxy->multiplex_input = NULL;
+               proxy->multiplex_orig_input = NULL;
+
+               o_stream_unref(&proxy->client_output);
+               proxy->client_output = client->multiplex_orig_output;
+               client->multiplex_output = NULL;
+               client->multiplex_orig_output = NULL;
+       }
+       o_stream_set_max_buffer_size(proxy->client_output,
+                                    PROXY_MAX_OUTBUF_SIZE);
+
        /* from now on, just do dummy proxying */
        proxy->iostream_proxy =
                iostream_proxy_create(proxy->client_input, proxy->client_output,
@@ -1059,6 +1080,7 @@ int login_proxy_starttls(struct login_proxy *proxy)
        struct ssl_iostream_context *ssl_ctx;
        struct ssl_iostream_settings ssl_set;
        const char *error;
+       bool add_multiplex_istream = FALSE;
 
        master_service_ssl_client_settings_to_iostream_set(
                proxy->client->ssl_set, pool_datastack_create(), &ssl_set);
@@ -1080,6 +1102,16 @@ int login_proxy_starttls(struct login_proxy *proxy)
                return -1;
        }
 
+       if (proxy->multiplex_orig_input != NULL) {
+               /* restart multiplexing after TLS iostreams are set up */
+               i_assert(proxy->server_input == proxy->multiplex_input);
+               i_stream_unref(&proxy->server_input);
+               proxy->server_input = proxy->multiplex_orig_input;
+               proxy->multiplex_input = NULL;
+               proxy->multiplex_orig_input = NULL;
+               add_multiplex_istream = TRUE;
+       }
+
        if (io_stream_create_ssl_client(ssl_ctx, proxy->host, &ssl_set,
                                        proxy->event,
                                        &proxy->server_input,
@@ -1106,9 +1138,28 @@ int login_proxy_starttls(struct login_proxy *proxy)
 
        proxy->server_io = io_add_istream(proxy->server_input,
                                          proxy_prelogin_input, proxy);
+       if (add_multiplex_istream)
+               login_proxy_multiplex_input_start(proxy);
        return 0;
 }
 
+void login_proxy_multiplex_input_start(struct login_proxy *proxy)
+{
+       struct istream *input = i_stream_create_multiplex(proxy->server_input,
+                                                         LOGIN_MAX_INBUF_SIZE);
+       i_assert(proxy->multiplex_orig_input == NULL);
+       proxy->multiplex_orig_input = proxy->server_input;
+       proxy->multiplex_input = input;
+       proxy->server_input = input;
+
+       io_remove(&proxy->server_io);
+       proxy->server_io = io_add_istream(proxy->server_input,
+                                         proxy_prelogin_input, proxy);
+       /* caller needs to break out of the proxy_input() loop and get it
+          called again to update the istream. */
+       i_stream_set_input_pending(input, TRUE);
+}
+
 static void proxy_kill_idle(struct login_proxy *proxy)
 {
        login_proxy_free_full(&proxy,
index c91dec2d47c2e5a2d753d877a216bee31b2777fb..6ef72675d07c51a7cf4f5b24c364eff00daf9144 100644 (file)
@@ -104,6 +104,8 @@ void login_proxy_detach(struct login_proxy *proxy);
 
 /* STARTTLS command was issued. */
 int login_proxy_starttls(struct login_proxy *proxy);
+/* MULTIPLEX input was started. */
+void login_proxy_multiplex_input_start(struct login_proxy *proxy);
 
 struct istream *login_proxy_get_istream(struct login_proxy *proxy);
 struct ostream *login_proxy_get_ostream(struct login_proxy *proxy);