From: Timo Sirainen Date: Wed, 22 Dec 2021 21:20:57 +0000 (+0200) Subject: anvil: Add handshake with service name and PID for non-FIFO connections X-Git-Tag: 2.4.0~4507 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=93f85196ada0742b3d859168b74a0b9b10d54974;p=thirdparty%2Fdovecot%2Fcore.git anvil: Add handshake with service name and PID for non-FIFO connections 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. --- diff --git a/src/anvil/anvil-connection.c b/src/anvil/anvil-connection.c index 0353e30970..7079d785af 100644 --- a/src/anvil/anvil-connection.c +++ b/src/anvil/anvil-connection.c @@ -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) diff --git a/src/doveadm/doveadm-penalty.c b/src/doveadm/doveadm-penalty.c index cbdd69779f..d0ab4f30a6 100644 --- a/src/doveadm/doveadm-penalty.c +++ b/src/doveadm/doveadm-penalty.c @@ -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; diff --git a/src/doveadm/doveadm-who.c b/src/doveadm/doveadm-who.c index d478bcdb3c..98a4832e7d 100644 --- a/src/doveadm/doveadm-who.c +++ b/src/doveadm/doveadm-who.c @@ -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; diff --git a/src/lib-master/anvil-client.c b/src/lib-master/anvil-client.c index aa53912641..78cf0a769d 100644 --- a/src/lib-master/anvil-client.c +++ b/src/lib-master/anvil-client.c @@ -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; }