doveadm-connection.c revision 2670cd577aa57eb9f915a4f4220ae48c9b4fc5fb
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */
d82741b1a8ada493ca74efa5d5c8b731412d035cLukas Slebodnik
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov#include "lib.h"
4e17c050dac8f2c6e2d278c4c4a27001c8d7d164Jakub Hrozek#include "ioloop.h"
1921d739ff7b028baa591272cc8969e330c8f872Jakub Hrozek#include "network.h"
38b07019861240cf5107f5d51fc0027519e21619Lukas Slebodnik#include "istream.h"
b8946a5dbde01a87465de707092716349a35248bJakub Hrozek#include "ostream.h"
b4633e73067d7bf3b0dbaf212569c123de88f306Lukas Slebodnik#include "array.h"
8578fba1500d43ad9632784462c255bf8bb360feJakub Hrozek#include "str.h"
3728db53ac32da51fcaae96b132e8e56ebbaebfaJakub Hrozek#include "llist.h"
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov#include "user-directory.h"
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov#include "mail-host.h"
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov#include "director.h"
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov#include "director-host.h"
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov#include "director-request.h"
8d1dcb6af723f2968410c4b088d06d63d02b4feaPavel Reichl#include "doveadm-connection.h"
586f512ab8b6e5a03349598846141f43c1d505b8Michal Židek
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov#include <unistd.h>
1f4dc2971bac4ceb0803b18f86a746656a0f1990Lukas Slebodnik
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik#define DOVEADM_HANDSHAKE_EXPECTED "VERSION\tdirector-doveadm\t1\t"
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik#define DOVEADM_HANDSHAKE DOVEADM_HANDSHAKE_EXPECTED"0\n"
53a4219e2f51cd0443931aa931505bf0b4bf5a45Nikolai Kondrashov
b8946a5dbde01a87465de707092716349a35248bJakub Hrozek#define MAX_VALID_VHOST_COUNT 1000
05457ed0e399aaacc919b7aacee5d8210e1c1072Petr Cech
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozekstruct doveadm_connection {
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek struct doveadm_connection *prev, *next;
35ecfab87a24031e55798b22975e02832ee0f2adMichal Židek
8bdb8c0970dc9acb5b0a54dab0bae306ca964944Jakub Hrozek int fd;
8bdb8c0970dc9acb5b0a54dab0bae306ca964944Jakub Hrozek struct io *io;
8bdb8c0970dc9acb5b0a54dab0bae306ca964944Jakub Hrozek struct istream *input;
0700118d8388c38b8cb28279510b206b76a3a411Jakub Hrozek struct ostream *output;
0700118d8388c38b8cb28279510b206b76a3a411Jakub Hrozek struct director *dir;
0700118d8388c38b8cb28279510b206b76a3a411Jakub Hrozek
1f331476e7d33bb03cc35a2a9064ee1cc5bed6cfSumit Bose unsigned int handshaked:1;
36df33cd44774a5b5eab52ab222bcd3240b3ca5aLukas Slebodnik};
36df33cd44774a5b5eab52ab222bcd3240b3ca5aLukas Slebodnik
da7a3c347dd630085839afa7ec245ee9d36f6ce2Lukas Slebodnikstatic struct doveadm_connection *doveadm_connections;
0df79781d78973e5462dbef1e89d8fd6001da05cLukas Slebodnik
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashovstatic void doveadm_connection_deinit(struct doveadm_connection **_conn);
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnikstatic void doveadm_cmd_host_list(struct doveadm_connection *conn)
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik{
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik struct mail_host *const *hostp;
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik string_t *str = t_str_new(1024);
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik array_foreach(mail_hosts_get(), hostp) {
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik str_printfa(str, "%s\t%u\t%u\n",
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik net_ip2addr(&(*hostp)->ip), (*hostp)->vhost_count,
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik (*hostp)->user_count);
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik }
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik str_append_c(str, '\n');
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik o_stream_send(conn->output, str_data(str), str_len(str));
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik}
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnikstatic void doveadm_cmd_director_list(struct doveadm_connection *conn)
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik{
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik struct director_host *const *hostp;
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik string_t *str = t_str_new(1024);
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik array_foreach(&conn->dir->dir_hosts, hostp) {
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik str_printfa(str, "%s\t%u\n",
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik net_ip2addr(&(*hostp)->ip), (*hostp)->port);
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik }
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik str_append_c(str, '\n');
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik o_stream_send(conn->output, str_data(str), str_len(str));
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik}
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnikstatic bool
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnikdoveadm_cmd_host_set(struct doveadm_connection *conn, const char *line)
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik{
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik const char *const *args;
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik struct mail_host *host;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov struct ip_addr ip;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov unsigned int vhost_count = -1U;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov args = t_strsplit(line, "\t");
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov if (args[0] == NULL ||
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov net_addr2ip(args[0], &ip) < 0 ||
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov (args[1] != NULL && str_to_uint(args[1], &vhost_count) < 0)) {
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek i_error("doveadm sent invalid HOST-SET parameters");
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek return FALSE;
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek }
91b0592cdab22915dff27ceae6d8e49c608aea4aJakub Hrozek if (vhost_count > MAX_VALID_VHOST_COUNT && vhost_count != -1U) {
53a4219e2f51cd0443931aa931505bf0b4bf5a45Nikolai Kondrashov o_stream_send_str(conn->output, "vhost count too large\n");
a3bed9df5a47bfc84b82341f0f7e693e2b14a67aLukas Slebodnik return TRUE;
a3bed9df5a47bfc84b82341f0f7e693e2b14a67aLukas Slebodnik }
a3bed9df5a47bfc84b82341f0f7e693e2b14a67aLukas Slebodnik host = mail_host_lookup(&ip);
a3bed9df5a47bfc84b82341f0f7e693e2b14a67aLukas Slebodnik if (host == NULL)
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov host = mail_host_add_ip(&ip);
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov if (vhost_count != -1U)
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov mail_host_set_vhost_count(host, vhost_count);
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov director_update_host(conn->dir, conn->dir->self_host, host);
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov o_stream_send(conn->output, "OK\n", 3);
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov return TRUE;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov}
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashovstatic bool
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashovdoveadm_cmd_host_remove(struct doveadm_connection *conn, const char *line)
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov{
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov struct mail_host *host;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov struct ip_addr ip;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov if (net_addr2ip(line, &ip) < 0) {
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov i_error("doveadm sent invalid HOST-SET parameters");
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik return FALSE;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov }
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov host = mail_host_lookup(&ip);
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov if (host == NULL)
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov o_stream_send_str(conn->output, "NOTFOUND\n");
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov else {
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov director_remove_host(conn->dir, conn->dir->self_host, host);
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov o_stream_send(conn->output, "OK\n", 3);
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov }
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov return TRUE;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov}
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashovstatic void doveadm_connection_input(struct doveadm_connection *conn)
82c36227e36de155b13e6eb7cfa3e80a25774157Lukas Slebodnik{
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov const char *line;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov bool ret = TRUE;
841bcb5e1f66bb9c41e1884a2ab1dae654def13eLukas Slebodnik
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov if (!conn->handshaked) {
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov if ((line = i_stream_read_next_line(conn->input)) == NULL)
f75ba99fc8dd64e45af2f642d9fb7660860fd28fLukas Slebodnik return;
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov if (strncmp(line, DOVEADM_HANDSHAKE_EXPECTED,
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov strlen(DOVEADM_HANDSHAKE_EXPECTED)) != 0) {
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov i_error("doveadm not compatible with this server "
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov "(mixed old and new binaries?)");
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov doveadm_connection_deinit(&conn);
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov return;
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik }
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik conn->handshaked = TRUE;
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik }
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov while ((line = i_stream_read_next_line(conn->input)) != NULL && ret) {
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov if (strcmp(line, "HOST-LIST") == 0)
doveadm_cmd_host_list(conn);
else if (strcmp(line, "DIRECTOR-LIST") == 0)
doveadm_cmd_director_list(conn);
else if (strncmp(line, "HOST-SET\t", 9) == 0)
ret = doveadm_cmd_host_set(conn, line + 9);
else if (strncmp(line, "HOST-REMOVE\t", 12) == 0)
ret = doveadm_cmd_host_remove(conn, line + 12);
else {
i_error("doveadm sent unknown command: %s", line);
ret = FALSE;
}
}
if (conn->input->eof || conn->input->stream_errno != 0 || !ret)
doveadm_connection_deinit(&conn);
}
struct doveadm_connection *
doveadm_connection_init(struct director *dir, int fd)
{
struct doveadm_connection *conn;
conn = i_new(struct doveadm_connection, 1);
conn->fd = fd;
conn->dir = dir;
conn->input = i_stream_create_fd(conn->fd, 1024, FALSE);
conn->output = o_stream_create_fd(conn->fd, (size_t)-1, FALSE);
conn->io = io_add(conn->fd, IO_READ, doveadm_connection_input, conn);
o_stream_send_str(conn->output, DOVEADM_HANDSHAKE);
DLLIST_PREPEND(&doveadm_connections, conn);
return conn;
}
static void doveadm_connection_deinit(struct doveadm_connection **_conn)
{
struct doveadm_connection *conn = *_conn;
*_conn = NULL;
DLLIST_REMOVE(&doveadm_connections, conn);
io_remove(&conn->io);
o_stream_unref(&conn->output);
if (close(conn->fd) < 0)
i_error("close(doveadm connection) failed: %m");
i_free(conn);
}
void doveadm_connections_deinit(void)
{
while (doveadm_connections != NULL) {
struct doveadm_connection *conn = doveadm_connections;
doveadm_connection_deinit(&conn);
}
}