]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
anvil: Add handshake with service name and PID for non-FIFO connections
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Wed, 22 Dec 2021 21:20:57 +0000 (23:20 +0200)
committerTimo Sirainen <timo.sirainen@open-xchange.com>
Tue, 8 Feb 2022 09:48:24 +0000 (10:48 +0100)
This is in preparation for assuming that incoming anvil connections can also
be used to send admin commands for processes. Instead of connecting to the
process's admin UNIX socket, anvil can instead use the existing connection.
The PID is used to identify which admin connections are available.
When the handshake is used, multiplex iostream is enabled.

src/anvil/anvil-connection.c
src/doveadm/doveadm-penalty.c
src/doveadm/doveadm-who.c
src/lib-master/anvil-client.c

index 0353e309700fbadc972ccc27f94649e22ab19535..7079d785afecb1d20332a46a35721fe33a9ad736 100644 (file)
@@ -4,6 +4,8 @@
 #include "llist.h"
 #include "istream.h"
 #include "ostream.h"
+#include "istream-multiplex.h"
+#include "ostream-multiplex.h"
 #include "connection.h"
 #include "strescape.h"
 #include "master-service.h"
@@ -22,6 +24,7 @@
 struct anvil_connection {
        struct connection conn;
 
+       char *service;
        bool master:1;
        bool fifo:1;
 };
@@ -215,6 +218,49 @@ anvil_connection_request(struct anvil_connection *conn,
        return 0;
 }
 
+static int
+anvil_connection_handshake(struct anvil_connection *conn,
+                          const char *const *args)
+{
+       /* UNIX socket connections contain a handshake. It contains a PID of
+          the connecting process, which is verified with UNIX credentials if
+          they're available. */
+       pid_t pid;
+
+       if (args[0] == NULL) {
+               /* No service/pid. The client doesn't support admin-commands
+                  via anvil socket. */
+               return 0;
+       }
+
+       conn->service = i_strdup(args[0]);
+       if (args[1] == NULL || str_to_pid(args[1], &pid) < 0) {
+               e_error(conn->conn.event, "Invalid handshake pid: %s", args[1]);
+               return -1;
+       }
+       if (pid != conn->conn.remote_pid &&
+           conn->conn.remote_pid != (pid_t)-1) {
+               e_error(conn->conn.event,
+                       "Handshake PID %ld doesn't match UNIX credentials PID %ld",
+                       (long)pid, (long)conn->conn.remote_pid);
+               return -1;
+       }
+
+       /* Switch input and output to use multiplex stream. The main
+          input/output contains the first channel. */
+       struct istream *orig_input = conn->conn.input;
+       conn->conn.input = i_stream_create_multiplex(orig_input, MAX_INBUF_SIZE);
+       i_stream_unref(&orig_input);
+
+       struct ostream *orig_output = conn->conn.output;
+       conn->conn.output = o_stream_create_multiplex(orig_output, SIZE_MAX);
+       o_stream_set_no_error_handling(conn->conn.output, TRUE);
+       o_stream_unref(&orig_output);
+
+       connection_streams_changed(&conn->conn);
+       return 0;
+}
+
 static int
 anvil_connection_input_line(struct connection *_conn, const char *line)
 {
@@ -239,6 +285,13 @@ anvil_connection_input_line(struct connection *_conn, const char *line)
        }
 
        args = t_strsplit_tabescaped(line);
+       if (!conn->conn.handshake_received && !conn->fifo) {
+               if (anvil_connection_handshake(conn, args) < 0)
+                       return -1;
+               conn->conn.handshake_received = TRUE;
+               return 1;
+       }
+
        if (args[0] == NULL) {
                i_error("Anvil client sent empty line");
                return -1;
@@ -274,6 +327,7 @@ static void anvil_connection_destroy(struct connection *_conn)
 
        connection_deinit(&conn->conn);
        o_stream_destroy(&conn->conn.output);
+       i_free(conn->service);
        i_free(conn);
 
        if (!fifo)
index cbdd69779f445817c225e34013eff7f659b4f727..d0ab4f30a6249af176e8f609f2d93dc41cb293f5 100644 (file)
@@ -59,7 +59,7 @@ penalty_print_line(struct penalty_context *ctx,
 
 static void penalty_lookup(struct penalty_context *ctx)
 {
-#define ANVIL_HANDSHAKE "VERSION\tanvil\t2\t0\n"
+#define ANVIL_HANDSHAKE "VERSION\tanvil\t2\t0\n\n"
 #define ANVIL_CMD ANVIL_HANDSHAKE"PENALTY-DUMP\n"
        struct istream *input;
        const char *line;
index d478bcdb3c2ed83dc22c29d5c389a0f1b6107a9e..98a4832e7da002af6feaabdd151e76235a8f0831 100644 (file)
@@ -183,7 +183,7 @@ int who_parse_args(struct who_context *ctx, const char *const *masks)
 
 struct doveadm_who_iter *doveadm_who_iter_init(const char *anvil_path)
 {
-#define ANVIL_HANDSHAKE "VERSION\tanvil\t2\t0\n"
+#define ANVIL_HANDSHAKE "VERSION\tanvil\t2\t0\n\n"
 #define ANVIL_CMD ANVIL_HANDSHAKE"CONNECT-DUMP\n"
        struct doveadm_who_iter *iter;
        const char *line;
index aa539126418c2a76c5cd7fcf2dc19273aa4e4afb..78cf0a769d5e2ab460a796cfba0914bc4f45e8f6 100644 (file)
@@ -5,8 +5,12 @@
 #include "net.h"
 #include "istream.h"
 #include "ostream.h"
+#include "istream-multiplex.h"
+#include "ostream-multiplex.h"
+#include "hostpid.h"
 #include "array.h"
 #include "aqueue.h"
+#include "master-service.h"
 #include "anvil-client.h"
 
 struct anvil_query {
@@ -32,7 +36,7 @@ struct anvil_client {
        enum anvil_client_flags flags;
 };
 
-#define ANVIL_HANDSHAKE "VERSION\tanvil\t2\t0\n"
+#define ANVIL_HANDSHAKE "VERSION\tanvil\t2\t0\n%s\t%s\n"
 #define ANVIL_INBUF_SIZE 1024
 #define ANVIL_RECONNECT_MIN_SECS 5
 #define ANVIL_QUERY_TIMEOUT_MSECS (1000*5)
@@ -69,6 +73,22 @@ void anvil_client_deinit(struct anvil_client **_client)
        i_free(client);
 }
 
+static void anvil_client_start_multiplex_input(struct anvil_client *client)
+{
+       struct istream *orig_input = client->input;
+       client->input = i_stream_create_multiplex(orig_input, ANVIL_INBUF_SIZE);
+       i_stream_unref(&orig_input);
+}
+
+static void
+anvil_client_start_multiplex_output(struct anvil_client *client)
+{
+       struct ostream *orig_output = client->output;
+       client->output = o_stream_create_multiplex(orig_output, SIZE_MAX);
+       o_stream_set_no_error_handling(client->output, TRUE);
+       o_stream_unref(&orig_output);
+}
+
 static void anvil_reconnect(struct anvil_client *client)
 {
        anvil_client_disconnect(client);
@@ -150,12 +170,19 @@ int anvil_client_connect(struct anvil_client *client, bool retry)
        client->input = i_stream_create_fd(fd, ANVIL_INBUF_SIZE);
        client->output = o_stream_create_fd(fd, SIZE_MAX);
        client->io = io_add(fd, IO_READ, anvil_input, client);
-       if (o_stream_send_str(client->output, ANVIL_HANDSHAKE) < 0) {
+       const char *anvil_handshake =
+               t_strdup_printf(ANVIL_HANDSHAKE,
+                               master_service_get_name(master_service),
+                               my_pid);
+       if (o_stream_send_str(client->output, anvil_handshake) < 0) {
                i_error("write(%s) failed: %s", client->path,
                        o_stream_get_error(client->output));
                anvil_reconnect(client);
                return -1;
        }
+
+       anvil_client_start_multiplex_input(client);
+       anvil_client_start_multiplex_output(client);
        return 0;
 }