bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen#include "lib.h"
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen#include "llist.h"
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen#include "istream.h"
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen#include "ostream.h"
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen#include "strescape.h"
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen#include "master-service.h"
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen#include "replicator-queue.h"
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen#include "notify-connection.h"
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen#include <unistd.h>
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen#define MAX_INBUF_SIZE (1024*64)
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen#define NOTIFY_CLIENT_PROTOCOL_MAJOR_VERSION 1
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen#define NOTIFY_CLIENT_PROTOCOL_MINOR_VERSION 0
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainenstruct notify_connection {
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen struct notify_connection *prev, *next;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen int refcount;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen int fd;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen struct io *io;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen struct istream *input;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen struct ostream *output;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen struct replicator_queue *queue;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen bool version_received:1;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen bool destroyed:1;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen};
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainenstruct notify_sync_request {
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen struct notify_connection *conn;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen unsigned int id;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen};
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainenstatic struct notify_connection *connections;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainenstatic void notify_connection_destroy(struct notify_connection *conn);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainenstatic void notify_sync_callback(bool success, void *context)
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen{
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen struct notify_sync_request *request = context;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend_str(request->conn->output, t_strdup_printf(
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen "%c\t%u\n", success ? '+' : '-', request->id));
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen notify_connection_unref(&request->conn);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen i_free(request);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen}
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainenstatic int
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainennotify_connection_input_line(struct notify_connection *conn, const char *line)
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen{
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen struct notify_sync_request *request;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen const char *const *args;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen enum replication_priority priority;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen unsigned int id;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen /* U \t <username> \t <priority> [\t <sync id>] */
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen args = t_strsplit_tabescaped(line);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen if (str_array_length(args) < 2) {
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen i_error("notify client sent invalid input: %s", line);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen return -1;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen }
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen if (strcmp(args[0], "U") != 0) {
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen i_error("notify client sent unknown command: %s", args[0]);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen return -1;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen }
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen if (replication_priority_parse(args[2], &priority) < 0) {
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen i_error("notify client sent invalid priority: %s", args[2]);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen return -1;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen }
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen if (priority != REPLICATION_PRIORITY_SYNC)
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen (void)replicator_queue_add(conn->queue, args[1], priority);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen else if (args[3] == NULL || str_to_uint(args[3], &id) < 0) {
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen i_error("notify client sent invalid sync id: %s", line);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen return -1;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen } else {
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen request = i_new(struct notify_sync_request, 1);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen request->conn = conn;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen request->id = id;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen notify_connection_ref(conn);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen replicator_queue_add_sync(conn->queue, args[1],
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen notify_sync_callback, request);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen }
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen return 0;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen}
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainenstatic void notify_connection_input(struct notify_connection *conn)
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen{
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen const char *line;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen int ret;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen switch (i_stream_read(conn->input)) {
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen case -2:
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen i_error("BUG: Client connection sent too much data");
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen notify_connection_destroy(conn);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen return;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen case -1:
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen notify_connection_destroy(conn);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen return;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen }
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen if (!conn->version_received) {
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen if ((line = i_stream_next_line(conn->input)) == NULL)
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen return;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen if (!version_string_verify(line, "replicator-notify",
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen NOTIFY_CLIENT_PROTOCOL_MAJOR_VERSION)) {
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen i_error("Notify client not compatible with this server "
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen "(mixed old and new binaries?)");
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen notify_connection_destroy(conn);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen return;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen }
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen conn->version_received = TRUE;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen }
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen while ((line = i_stream_next_line(conn->input)) != NULL) {
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen T_BEGIN {
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen ret = notify_connection_input_line(conn, line);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen } T_END;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen if (ret < 0) {
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen notify_connection_destroy(conn);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen break;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen }
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen }
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen}
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainenstruct notify_connection *
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainennotify_connection_create(int fd, struct replicator_queue *queue)
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen{
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen struct notify_connection *conn;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen i_assert(fd >= 0);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen conn = i_new(struct notify_connection, 1);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen conn->refcount = 1;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen conn->queue = queue;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen conn->fd = fd;
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi conn->input = i_stream_create_fd(fd, MAX_INBUF_SIZE);
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi conn->output = o_stream_create_fd(fd, (size_t)-1);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_set_no_error_handling(conn->output, TRUE);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen conn->io = io_add(fd, IO_READ, notify_connection_input, conn);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen conn->queue = queue;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen DLLIST_PREPEND(&connections, conn);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen return conn;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen}
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainenstatic void notify_connection_destroy(struct notify_connection *conn)
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen{
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen if (conn->destroyed)
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen return;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen conn->destroyed = TRUE;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen DLLIST_REMOVE(&connections, conn);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen io_remove(&conn->io);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen i_stream_close(conn->input);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen o_stream_close(conn->output);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen if (close(conn->fd) < 0)
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen i_error("close(notify connection) failed: %m");
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen conn->fd = -1;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen notify_connection_unref(&conn);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen master_service_client_connection_destroyed(master_service);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen}
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainenvoid notify_connection_ref(struct notify_connection *conn)
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen{
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen i_assert(conn->refcount > 0);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen conn->refcount++;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen}
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainenvoid notify_connection_unref(struct notify_connection **_conn)
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen{
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen struct notify_connection *conn = *_conn;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen i_assert(conn->refcount > 0);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen *_conn = NULL;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen if (--conn->refcount > 0)
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen return;
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen notify_connection_destroy(conn);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen i_stream_unref(&conn->input);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen o_stream_unref(&conn->output);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen i_free(conn);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen}
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainenvoid notify_connections_destroy_all(void)
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen{
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen while (connections != NULL)
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen notify_connection_destroy(connections);
447e086422f1ab7cc16833583ed70a4af7a84bc5Timo Sirainen}