doveadm-connection.c revision 550d2fe097e95f12e8fa60ef52753ea7fe53d4ea
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik/* Copyright (c) 2010-2014 Dovecot authors, see the included COPYING file */
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina#include "lib.h"
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik#include "ioloop.h"
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina#include "net.h"
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik#include "istream.h"
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik#include "ostream.h"
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik#include "array.h"
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik#include "str.h"
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik#include "llist.h"
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik#include "master-service.h"
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik#include "user-directory.h"
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik#include "mail-host.h"
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik#include "director.h"
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik#include "director-host.h"
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik#include "director-request.h"
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik#include "director-connection.h"
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik#include "doveadm-connection.h"
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik#include <unistd.h>
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik#define DOVEADM_PROTOCOL_VERSION_MAJOR 1
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik#define DOVEADM_HANDSHAKE "VERSION\tdirector-doveadm\t1\t0\n"
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina
a2057618f30a3c64bdffb35a2ef3c2ba148c8a03Pavel Březina#define MAX_VALID_VHOST_COUNT 1000
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březinastruct doveadm_connection {
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina struct doveadm_connection *prev, *next;
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina int fd;
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina struct io *io;
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina struct istream *input;
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina struct ostream *output;
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina struct director *dir;
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina unsigned int handshaked:1;
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina};
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březinastatic struct doveadm_connection *doveadm_connections;
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březinastatic void doveadm_connection_deinit(struct doveadm_connection **_conn);
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březinastatic void doveadm_cmd_host_list(struct doveadm_connection *conn)
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina{
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina struct mail_host *const *hostp;
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina string_t *str = t_str_new(1024);
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina array_foreach(mail_hosts_get(conn->dir->mail_hosts), hostp) {
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina str_printfa(str, "%s\t%u\t%u\n",
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina net_ip2addr(&(*hostp)->ip), (*hostp)->vhost_count,
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina (*hostp)->user_count);
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina }
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina str_append_c(str, '\n');
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina o_stream_nsend(conn->output, str_data(str), str_len(str));
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina}
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březinastatic void doveadm_cmd_host_list_removed(struct doveadm_connection *conn)
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina{
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina struct mail_host_list *orig_hosts_list;
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina struct mail_host *const *orig_hosts, *const *cur_hosts;
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina unsigned int i, j, orig_hosts_count, cur_hosts_count;
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina string_t *str = t_str_new(1024);
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina int ret;
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina orig_hosts_list = mail_hosts_init();
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina (void)mail_hosts_parse_and_add(orig_hosts_list,
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina conn->dir->set->director_mail_servers);
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina orig_hosts = array_get(mail_hosts_get(orig_hosts_list),
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina &orig_hosts_count);
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina cur_hosts = array_get(mail_hosts_get(conn->dir->mail_hosts),
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina &cur_hosts_count);
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina /* the hosts are sorted by IP */
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina for (i = j = 0; i < orig_hosts_count && j < cur_hosts_count; ) {
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina ret = net_ip_cmp(&orig_hosts[i]->ip, &cur_hosts[j]->ip);
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina if (ret == 0)
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina i++, j++;
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina else if (ret > 0)
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina j++;
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina else {
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina str_printfa(str, "%s\n",
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina net_ip2addr(&orig_hosts[i]->ip));
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina i++;
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina }
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina }
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina for (; i < orig_hosts_count; i++)
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina str_printfa(str, "%s\n", net_ip2addr(&orig_hosts[i]->ip));
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina str_append_c(str, '\n');
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina o_stream_nsend(conn->output, str_data(str), str_len(str));
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina mail_hosts_deinit(&orig_hosts_list);
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina}
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březinastatic void doveadm_cmd_director_list(struct doveadm_connection *conn)
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina{
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina struct director *dir = conn->dir;
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina struct director_host *const *hostp;
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina string_t *str = t_str_new(1024);
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina const char *type;
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina bool left, right;
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina time_t last_failed;
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina array_foreach(&dir->dir_hosts, hostp) {
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina const struct director_host *host = *hostp;
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina left = dir->left != NULL &&
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina director_connection_get_host(dir->left) == host;
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina right = dir->right != NULL &&
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina director_connection_get_host(dir->right) == host;
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina if (host->removed)
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina type = "removed";
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina else if (dir->self_host == host)
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina type = "self";
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina else if (left)
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina type = right ? "l+r" : "left";
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina else if (right)
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina type = "right";
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina else
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina type = "";
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina last_failed = I_MAX(host->last_network_failure,
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina host->last_protocol_failure);
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina str_printfa(str, "%s\t%u\t%s\t%lu\n",
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina net_ip2addr(&host->ip), host->port, type,
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina (unsigned long)last_failed);
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina }
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina str_append_c(str, '\n');
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina o_stream_nsend(conn->output, str_data(str), str_len(str));
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina}
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březinastatic bool
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březinadoveadm_cmd_director_add(struct doveadm_connection *conn, const char *line)
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina{
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina const char *const *args;
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina struct director_host *host;
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina struct ip_addr ip;
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina unsigned int port = conn->dir->self_port;
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina args = t_strsplit_tab(line);
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina if (args[0] == NULL ||
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina net_addr2ip(line, &ip) < 0 ||
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina (args[1] != NULL && str_to_uint(args[1], &port) < 0)) {
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina i_error("doveadm sent invalid DIRECTOR-ADD parameters");
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina return FALSE;
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina }
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina if (director_host_lookup(conn->dir, &ip, port) == NULL) {
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina host = director_host_add(conn->dir, &ip, port);
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina director_notify_ring_added(host, conn->dir->self_host);
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina }
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina o_stream_nsend(conn->output, "OK\n", 3);
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina return TRUE;
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina}
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březinastatic bool
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březinadoveadm_cmd_director_remove(struct doveadm_connection *conn, const char *line)
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina{
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina const char *const *args;
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina struct director_host *host;
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina struct ip_addr ip;
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik unsigned int port = 0;
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik args = t_strsplit_tab(line);
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik if (args[0] == NULL ||
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik net_addr2ip(line, &ip) < 0 ||
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina (args[1] != NULL && str_to_uint(args[1], &port) < 0)) {
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina i_error("doveadm sent invalid DIRECTOR-REMOVE parameters");
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik return FALSE;
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina }
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina host = port != 0 ?
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina director_host_lookup(conn->dir, &ip, port) :
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina director_host_lookup_ip(conn->dir, &ip);
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik if (host == NULL)
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina o_stream_nsend_str(conn->output, "NOTFOUND\n");
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina else {
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik director_ring_remove(host, conn->dir->self_host);
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik o_stream_nsend(conn->output, "OK\n", 3);
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik }
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina return TRUE;
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina}
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březinastatic bool
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březinadoveadm_cmd_host_set(struct doveadm_connection *conn, const char *line)
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina{
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina struct director *dir = conn->dir;
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina const char *const *args;
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina struct mail_host *host;
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina struct ip_addr ip;
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina unsigned int vhost_count = UINT_MAX;
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina args = t_strsplit_tab(line);
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina if (args[0] == NULL ||
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina net_addr2ip(args[0], &ip) < 0 ||
0f04241fc90f134af0272eb0999e75fb6749b595Pavel Březina (args[1] != NULL && str_to_uint(args[1], &vhost_count) < 0)) {
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik i_error("doveadm sent invalid HOST-SET parameters: %s", line);
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik return FALSE;
b24e4bec819b29f1ec8e77083d4e7610c5dd9c77Lukas Slebodnik }
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina if (vhost_count > MAX_VALID_VHOST_COUNT && vhost_count != UINT_MAX) {
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina o_stream_nsend_str(conn->output, "vhost count too large\n");
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina return TRUE;
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina }
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina host = mail_host_lookup(dir->mail_hosts, &ip);
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina if (host == NULL)
host = mail_host_add_ip(dir->mail_hosts, &ip);
if (vhost_count != UINT_MAX)
mail_host_set_vhost_count(dir->mail_hosts, host, vhost_count);
director_update_host(dir, dir->self_host, NULL, host);
o_stream_nsend(conn->output, "OK\n", 3);
return TRUE;
}
static bool
doveadm_cmd_host_remove(struct doveadm_connection *conn, const char *line)
{
struct mail_host *host;
struct ip_addr ip;
if (net_addr2ip(line, &ip) < 0) {
i_error("doveadm sent invalid HOST-REMOVE parameters");
return FALSE;
}
host = mail_host_lookup(conn->dir->mail_hosts, &ip);
if (host == NULL)
o_stream_nsend_str(conn->output, "NOTFOUND\n");
else {
director_remove_host(conn->dir, conn->dir->self_host,
NULL, host);
o_stream_nsend(conn->output, "OK\n", 3);
}
return TRUE;
}
static void
doveadm_cmd_host_flush_all(struct doveadm_connection *conn)
{
struct mail_host *const *hostp;
array_foreach(mail_hosts_get(conn->dir->mail_hosts), hostp) {
director_flush_host(conn->dir, conn->dir->self_host,
NULL, *hostp);
}
o_stream_nsend(conn->output, "OK\n", 3);
}
static bool
doveadm_cmd_host_flush(struct doveadm_connection *conn, const char *line)
{
struct mail_host *host;
struct ip_addr ip;
if (*line == '\0') {
doveadm_cmd_host_flush_all(conn);
return TRUE;
}
if (net_addr2ip(line, &ip) < 0) {
i_error("doveadm sent invalid HOST-FLUSH parameters");
return FALSE;
}
host = mail_host_lookup(conn->dir->mail_hosts, &ip);
if (host == NULL)
o_stream_nsend_str(conn->output, "NOTFOUND\n");
else {
director_flush_host(conn->dir, conn->dir->self_host,
NULL, host);
o_stream_nsend(conn->output, "OK\n", 3);
}
return TRUE;
}
static bool
doveadm_cmd_user_lookup(struct doveadm_connection *conn, const char *line)
{
struct user *user;
struct mail_host *host;
unsigned int username_hash;
string_t *str = t_str_new(256);
if (str_to_uint(line, &username_hash) < 0)
username_hash = user_directory_get_username_hash(conn->dir->users, line);
/* get user's current host */
user = user_directory_lookup(conn->dir->users, username_hash);
if (user == NULL)
str_append(str, "\t0");
else {
str_printfa(str, "%s\t%u", net_ip2addr(&user->host->ip),
user->timestamp +
conn->dir->set->director_user_expire);
}
/* get host if it wasn't in user directory */
host = mail_host_get_by_hash(conn->dir->mail_hosts, username_hash);
if (host == NULL)
str_append(str, "\t");
else
str_printfa(str, "\t%s", net_ip2addr(&host->ip));
/* get host with default configuration */
host = mail_host_get_by_hash(conn->dir->orig_config_hosts,
username_hash);
if (host == NULL)
str_append(str, "\t");
else
str_printfa(str, "\t%s\n", net_ip2addr(&host->ip));
o_stream_nsend(conn->output, str_data(str), str_len(str));
return TRUE;
}
static bool
doveadm_cmd_user_list(struct doveadm_connection *conn, const char *line)
{
struct user_directory_iter *iter;
struct user *user;
struct ip_addr ip;
if (*line != '\0') {
if (net_addr2ip(line, &ip) < 0) {
i_error("doveadm sent invalid USER-LIST parameters");
return FALSE;
}
} else {
ip.family = 0;
}
iter = user_directory_iter_init(conn->dir->users);
while ((user = user_directory_iter_next(iter)) != NULL) {
if (ip.family == 0 ||
net_ip_compare(&ip, &user->host->ip)) T_BEGIN {
unsigned int expire_time = user->timestamp +
conn->dir->set->director_user_expire;
o_stream_nsend_str(conn->output, t_strdup_printf(
"%u\t%u\t%s\n",
user->username_hash, expire_time,
net_ip2addr(&user->host->ip)));
} T_END;
}
user_directory_iter_deinit(&iter);
o_stream_nsend(conn->output, "\n", 1);
return TRUE;
}
static bool
doveadm_cmd_user_move(struct doveadm_connection *conn, const char *line)
{
unsigned int username_hash;
const char *const *args;
struct user *user;
struct mail_host *host;
struct ip_addr ip;
args = t_strsplit_tab(line);
if (args[0] == NULL || args[1] == NULL ||
net_addr2ip(args[1], &ip) < 0) {
i_error("doveadm sent invalid USER-MOVE parameters: %s", line);
return FALSE;
}
host = mail_host_lookup(conn->dir->mail_hosts, &ip);
if (host == NULL) {
o_stream_nsend_str(conn->output, "NOTFOUND\n");
return TRUE;
}
if (str_to_uint(args[0], &username_hash) < 0)
username_hash = user_directory_get_username_hash(conn->dir->users, line);
user = user_directory_lookup(conn->dir->users, username_hash);
if (user != NULL && user->kill_state != USER_KILL_STATE_NONE) {
o_stream_nsend_str(conn->output, "TRYAGAIN\n");
return TRUE;
}
director_move_user(conn->dir, conn->dir->self_host, NULL,
username_hash, host);
o_stream_nsend(conn->output, "OK\n", 3);
return TRUE;
}
static bool
doveadm_cmd_user_kick(struct doveadm_connection *conn, const char *line)
{
const char *const *args;
args = t_strsplit_tab(line);
if (args[0] == NULL) {
i_error("doveadm sent invalid USER-KICK parameters: %s", line);
return FALSE;
}
director_kick_user(conn->dir, conn->dir->self_host, NULL, args[0]);
o_stream_nsend(conn->output, "OK\n", 3);
return TRUE;
}
static void doveadm_connection_input(struct doveadm_connection *conn)
{
const char *line, *cmd, *args;
bool ret = TRUE;
if (!conn->handshaked) {
if ((line = i_stream_read_next_line(conn->input)) == NULL) {
if (conn->input->eof || conn->input->stream_errno != 0)
doveadm_connection_deinit(&conn);
return;
}
if (!version_string_verify(line, "director-doveadm",
DOVEADM_PROTOCOL_VERSION_MAJOR)) {
i_error("doveadm not compatible with this server "
"(mixed old and new binaries?)");
doveadm_connection_deinit(&conn);
return;
}
conn->handshaked = TRUE;
}
while ((line = i_stream_read_next_line(conn->input)) != NULL && ret) {
args = strchr(line, '\t');
if (args == NULL) {
cmd = line;
args = "";
} else {
cmd = t_strdup_until(line, args);
args++;
}
if (strcmp(cmd, "HOST-LIST") == 0)
doveadm_cmd_host_list(conn);
else if (strcmp(cmd, "HOST-LIST-REMOVED") == 0)
doveadm_cmd_host_list_removed(conn);
else if (strcmp(cmd, "DIRECTOR-LIST") == 0)
doveadm_cmd_director_list(conn);
else if (strcmp(cmd, "DIRECTOR-ADD") == 0)
ret = doveadm_cmd_director_add(conn, args);
else if (strcmp(cmd, "DIRECTOR-REMOVE") == 0)
ret = doveadm_cmd_director_remove(conn, args);
else if (strcmp(cmd, "HOST-SET") == 0)
ret = doveadm_cmd_host_set(conn, args);
else if (strcmp(cmd, "HOST-REMOVE") == 0)
ret = doveadm_cmd_host_remove(conn, args);
else if (strcmp(cmd, "HOST-FLUSH") == 0)
ret = doveadm_cmd_host_flush(conn, args);
else if (strcmp(cmd, "USER-LOOKUP") == 0)
ret = doveadm_cmd_user_lookup(conn, args);
else if (strcmp(cmd, "USER-LIST") == 0)
ret = doveadm_cmd_user_list(conn, args);
else if (strcmp(cmd, "USER-MOVE") == 0)
ret = doveadm_cmd_user_move(conn, args);
else if (strcmp(cmd, "USER-KICK") == 0)
ret = doveadm_cmd_user_kick(conn, args);
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);
o_stream_set_no_error_handling(conn->output, TRUE);
conn->io = io_add(conn->fd, IO_READ, doveadm_connection_input, conn);
o_stream_nsend_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);
i_stream_unref(&conn->input);
o_stream_unref(&conn->output);
if (close(conn->fd) < 0)
i_error("close(doveadm connection) failed: %m");
i_free(conn);
master_service_client_connection_destroyed(master_service);
}
void doveadm_connections_deinit(void)
{
while (doveadm_connections != NULL) {
struct doveadm_connection *conn = doveadm_connections;
doveadm_connection_deinit(&conn);
}
}