director-test.c revision 5f1d689131a75c39f064cbd4202373e7edf78f18
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen/*
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen This program accepts incoming unauthenticated IMAP connections from
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen port 14300. If the same user is connecting to multiple different local IPs,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen it logs an error (i.e. director is not working right then).
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen This program also accepts incoming director connections on port 9091 and
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen forwards them to local_ip:9090. To make this work properly, director
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen executable must be given -t 9091 parameter. The idea is that this test tool
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen hooks between all director connections and can then add delays or break the
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen connections.
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen Finally, this program connects to director-admin socket where it adds
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen and removes mail hosts.
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen*/
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#include "lib.h"
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#include "ioloop.h"
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#include "istream.h"
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen#include "ostream.h"
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen#include "write-full.h"
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen#include "hash.h"
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen#include "llist.h"
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen#include "strescape.h"
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen#include "imap-parser.h"
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#include "master-service.h"
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#include "master-service-settings.h"
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#include "director-settings.h"
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#include <unistd.h>
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#define IMAP_PORT 14300
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#define DIRECTOR_IN_PORT 9091
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#define DIRECTOR_OUT_PORT 9090
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#define USER_TIMEOUT_MSECS (1000*10) /* FIXME: this should be based on director_user_expire */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#define ADMIN_RANDOM_TIMEOUT_MSECS 500
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#define DIRECTOR_CONN_MAX_DELAY_MSECS 100
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#define DIRECTOR_DISCONNECT_TIMEOUT_SECS 10
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstruct host {
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen int refcount;
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct ip_addr ip;
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen unsigned int vhost_count;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen};
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstruct user {
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen char *username;
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen struct host *host;
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen time_t last_seen;
cf64e39cef453f5a4704ea52b81b188ca411cb15Timo Sirainen unsigned int connections;
cf64e39cef453f5a4704ea52b81b188ca411cb15Timo Sirainen
cf64e39cef453f5a4704ea52b81b188ca411cb15Timo Sirainen struct timeout *to;
cf64e39cef453f5a4704ea52b81b188ca411cb15Timo Sirainen};
cf64e39cef453f5a4704ea52b81b188ca411cb15Timo Sirainen
cf64e39cef453f5a4704ea52b81b188ca411cb15Timo Sirainenstruct imap_client {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct imap_client *prev, *next;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen int fd;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct io *io;
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen struct istream *input;
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen struct ostream *output;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct imap_parser *parser;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct user *user;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen char *username;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen};
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstruct director_connection {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct director_connection *prev, *next;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen int in_fd, out_fd;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct io *in_io, *out_io;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct istream *in_input, *out_input;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct ostream *in_output, *out_output;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct timeout *to_delay;
4307c886579381dbb1897ea1388ae6978c96f560Timo Sirainen};
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstruct admin_connection {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen char *path;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen int fd;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct io *io;
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen struct istream *input;
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen struct timeout *to_random;
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen bool pending_command;
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen};
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainenstatic struct imap_client *imap_clients;
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainenstatic struct director_connection *director_connections;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic HASH_TABLE(char *, struct user *) users;
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainenstatic HASH_TABLE(struct ip_addr *, struct host *) hosts;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainenstatic ARRAY(struct host *) hosts_array;
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainenstatic struct admin_connection *admin;
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainenstatic struct timeout *to_disconnect;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainenstatic void imap_client_destroy(struct imap_client **client);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic void director_connection_destroy(struct director_connection **conn);
d11111fc5356108b6408a58b4ff1811d5b5678edTimo Sirainenstatic void director_connection_timeout(struct director_connection *conn);
d11111fc5356108b6408a58b4ff1811d5b5678edTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic void host_unref(struct host **_host)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct host *host = *_host;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen *_host = NULL;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_assert(host->refcount > 0);
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen if (--host->refcount > 0)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_free(host);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainenstatic void client_username_check(struct imap_client *client)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct user *user;
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen struct host *host;
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen struct ip_addr local_ip;
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen if (net_getsockname(client->fd, &local_ip, NULL) < 0)
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen i_fatal("net_getsockname() failed: %m");
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen host = hash_table_lookup(hosts, &local_ip);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (host == NULL) {
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen i_error("User logging into unknown host %s",
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen net_ip2addr(&local_ip));
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen host = i_new(struct host, 1);
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen host->refcount = 1;
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen host->ip = local_ip;
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen host->vhost_count = 100;
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen hash_table_insert(hosts, &host->ip, host);
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen array_append(&hosts_array, &host, 1);
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen }
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen user = hash_table_lookup(users, client->username);
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen if (user == NULL) {
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen user = i_new(struct user, 1);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen user->username = i_strdup(client->username);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen hash_table_insert(users, user->username, user);
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen } else if (user->host != host) {
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen i_error("user %s: old connection from %s, new from %s. "
9ba3ecf529e681688bbf728fc3cb5101bd68d671Timo Sirainen "%u old connections, last was %u secs ago",
9ba3ecf529e681688bbf728fc3cb5101bd68d671Timo Sirainen user->username, net_ip2addr(&user->host->ip),
9ba3ecf529e681688bbf728fc3cb5101bd68d671Timo Sirainen net_ip2addr(&host->ip), user->connections,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen (unsigned int)(ioloop_time - user->last_seen));
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen host_unref(&user->host);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen }
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen client->user = user;
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen user->host = host;
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen user->connections++;
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen user->last_seen = ioloop_time;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen user->host->refcount++;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (user->to != NULL)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen timeout_remove(&user->to);
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen}
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic void user_free(struct user *user)
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen{
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen host_unref(&user->host);
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen if (user->to != NULL)
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen timeout_remove(&user->to);
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen hash_table_remove(users, user->username);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_free(user->username);
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen i_free(user);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic int imap_client_parse_input(struct imap_client *client)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen const char *tag, *cmd, *str;
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen const struct imap_arg *args;
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen int ret;
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen ret = imap_parser_read_args(client->parser, 0, 0, &args);
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen if (ret < 0) {
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen if (ret == -2)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return 0;
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen return -1;
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen }
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen if (!imap_arg_get_atom(args, &tag))
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return -1;
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen args++;
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen if (!imap_arg_get_atom(args, &cmd))
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen return -1;
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen args++;
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen if (strcasecmp(cmd, "login") == 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (client->username != NULL)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return -1;
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen if (!imap_arg_get_astring(args, &str))
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return -1;
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen o_stream_nsend_str(client->output,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen t_strconcat(tag, " OK Logged in.\r\n", NULL));
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen client->username = i_strdup(str);
0f3d4fbcf88e2ffd674893aed8cc1288fe17d290Timo Sirainen client_username_check(client);
0f3d4fbcf88e2ffd674893aed8cc1288fe17d290Timo Sirainen } else if (strcasecmp(cmd, "logout") == 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen o_stream_nsend_str(client->output, t_strconcat(
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen "* BYE Out.\r\n",
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen tag, " OK Logged out.\r\n", NULL));
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen imap_client_destroy(&client);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return 0;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen } else if (strcasecmp(cmd, "capability") == 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen o_stream_nsend_str(client->output,
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen t_strconcat("* CAPABILITY IMAP4rev1\r\n",
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen tag, " OK Done.\r\n", NULL));
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen } else {
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen o_stream_nsend_str(client->output,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen t_strconcat(tag, " BAD Not supported.\r\n", NULL));
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen }
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen (void)i_stream_read_next_line(client->input); /* eat away LF */
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen imap_parser_reset(client->parser);
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen return 1;
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen}
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainenstatic void imap_client_input(struct imap_client *client)
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen int ret;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen switch (i_stream_read(client->input)) {
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen case -2:
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen i_error("imap: Too much input");
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen imap_client_destroy(&client);
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen return;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen case -1:
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen imap_client_destroy(&client);
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen return;
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen default:
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen break;
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen }
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen while ((ret = imap_client_parse_input(client)) > 0) ;
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen if (ret < 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_error("imap: Invalid input");
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen imap_client_destroy(&client);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic void imap_client_create(int fd)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen struct imap_client *client;
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen client = i_new(struct imap_client, 1);
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen client->fd = fd;
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen client->input = i_stream_create_fd(fd, 4096);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen client->output = o_stream_create_fd(fd, (size_t)-1);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen o_stream_set_no_error_handling(client->output, TRUE);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen client->io = io_add(fd, IO_READ, imap_client_input, client);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen client->parser =
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen imap_parser_create(client->input, client->output, 4096);
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen o_stream_nsend_str(client->output,
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen "* OK [CAPABILITY IMAP4rev1] director-test ready.\r\n");
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen DLLIST_PREPEND(&imap_clients, client);
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen}
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic void imap_client_destroy(struct imap_client **_client)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct imap_client *client = *_client;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct user *user = client->user;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen *_client = NULL;
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen if (user != NULL) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_assert(user->connections > 0);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (--user->connections == 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_assert(user->to == NULL);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen user->to = timeout_add(USER_TIMEOUT_MSECS, user_free,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen user);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen user->last_seen = ioloop_time;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen DLLIST_REMOVE(&imap_clients, client);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen imap_parser_unref(&client->parser);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen io_remove(&client->io);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen i_stream_destroy(&client->input);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen o_stream_destroy(&client->output);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen net_disconnect(client->fd);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_free(client->username);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_free(client);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen master_service_client_connection_destroyed(master_service);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic void
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainendirector_connection_input(struct director_connection *conn,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct istream *input, struct ostream *output)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen const unsigned char *data;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen size_t size;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen if (i_stream_read_more(input, &data, &size) == -1) {
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen director_connection_destroy(&conn);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen return;
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen }
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen o_stream_nsend(output, data, size);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen i_stream_skip(input, size);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen if (i_rand_limit(3) == 0 && conn->to_delay == NULL) {
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen conn->to_delay =
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen timeout_add(i_rand_limit(DIRECTOR_CONN_MAX_DELAY_MSECS),
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen director_connection_timeout, conn);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen io_remove(&conn->in_io);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen io_remove(&conn->out_io);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen }
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen}
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainenstatic void director_connection_in_input(struct director_connection *conn)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen{
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen director_connection_input(conn, conn->in_input, conn->out_output);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen}
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainenstatic void director_connection_out_input(struct director_connection *conn)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen{
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen director_connection_input(conn, conn->out_input, conn->in_output);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen}
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainenstatic void director_connection_timeout(struct director_connection *conn)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen{
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen timeout_remove(&conn->to_delay);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen conn->in_io = io_add(conn->in_fd, IO_READ,
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen director_connection_in_input, conn);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen conn->out_io = io_add(conn->out_fd, IO_READ,
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen director_connection_out_input, conn);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen}
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainenstatic void
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainendirector_connection_create(int in_fd, const struct ip_addr *local_ip,
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen const struct ip_addr *remote_ip)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen{
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen struct director_connection *conn;
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen int out_fd;
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen out_fd = net_connect_ip(local_ip, DIRECTOR_OUT_PORT, remote_ip);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen if (out_fd == -1) {
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen i_close_fd(&in_fd);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen return;
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen }
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen conn = i_new(struct director_connection, 1);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen conn->in_fd = in_fd;
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen conn->in_input = i_stream_create_fd(conn->in_fd, (size_t)-1);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen conn->in_output = o_stream_create_fd(conn->in_fd, (size_t)-1);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen o_stream_set_no_error_handling(conn->in_output, TRUE);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen conn->in_io = io_add(conn->in_fd, IO_READ,
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen director_connection_in_input, conn);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen conn->out_fd = out_fd;
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen conn->out_input = i_stream_create_fd(conn->out_fd, (size_t)-1);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen conn->out_output = o_stream_create_fd(conn->out_fd, (size_t)-1);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen o_stream_set_no_error_handling(conn->out_output, TRUE);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen conn->out_io = io_add(conn->out_fd, IO_READ,
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen director_connection_out_input, conn);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen DLLIST_PREPEND(&director_connections, conn);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen}
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainenstatic void director_connection_destroy(struct director_connection **_conn)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen{
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen struct director_connection *conn = *_conn;
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen DLLIST_REMOVE(&director_connections, conn);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen if (conn->to_delay != NULL)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen timeout_remove(&conn->to_delay);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen io_remove(&conn->in_io);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen i_stream_unref(&conn->in_input);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen o_stream_unref(&conn->in_output);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen net_disconnect(conn->in_fd);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen io_remove(&conn->out_io);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen i_stream_unref(&conn->out_input);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen o_stream_unref(&conn->out_output);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen net_disconnect(conn->out_fd);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen i_free(conn);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen}
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainenstatic void client_connected(struct master_service_connection *conn)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen{
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen struct ip_addr local_ip, remote_ip;
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen in_port_t local_port;
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen if (net_getsockname(conn->fd, &local_ip, &local_port) < 0)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen i_fatal("net_getsockname() failed: %m");
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen if (net_getpeername(conn->fd, &remote_ip, NULL) < 0)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen i_fatal("net_getsockname() failed: %m");
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen if (local_port == IMAP_PORT)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen imap_client_create(conn->fd);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen else if (local_port == DIRECTOR_IN_PORT)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen director_connection_create(conn->fd, &local_ip, &remote_ip);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen else {
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen i_error("Connection to unknown port %u", local_port);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen return;
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen }
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen master_service_client_connection_accept(conn);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen}
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainenstatic void
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainenadmin_send(struct admin_connection *conn, const char *data)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen{
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen if (write_full(i_stream_get_fd(conn->input), data, strlen(data)) < 0)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen i_fatal("write(%s) failed: %m", conn->path);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen}
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainenstatic void admin_input(struct admin_connection *conn)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen{
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen const char *line;
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen while ((line = i_stream_read_next_line(conn->input)) != NULL) {
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen if (strcmp(line, "OK") != 0)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen i_error("director-doveadm: Unexpected input: %s", line);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen conn->pending_command = FALSE;
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen }
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen if (conn->input->stream_errno != 0 || conn->input->eof)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen i_fatal("director-doveadm: Connection lost");
fffb5431af081379c169d0d96aafe2a9a7352be2Timo Sirainen}
fffb5431af081379c169d0d96aafe2a9a7352be2Timo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainenstatic void admin_random_action(struct admin_connection *conn)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen{
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen struct host *const *hosts;
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen unsigned int i, count;
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen if (conn->pending_command)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen return;
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen hosts = array_get(&hosts_array, &count);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen i = i_rand_limit(count);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen hosts[i]->vhost_count = i_rand_limit(20) * 10;
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen admin_send(conn, t_strdup_printf("HOST-SET\t%s\t%u\n",
08a4f9396a805b2ac70d55fd494637321ada6516Timo Sirainen net_ip2addr(&hosts[i]->ip), hosts[i]->vhost_count));
08a4f9396a805b2ac70d55fd494637321ada6516Timo Sirainen conn->pending_command = TRUE;
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen}
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainenstatic struct admin_connection *admin_connect(const char *path)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen{
#define DIRECTOR_ADMIN_HANDSHAKE "VERSION\tdirector-doveadm\t1\t0\n"
struct admin_connection *conn;
const char *line;
conn = i_new(struct admin_connection, 1);
conn->path = i_strdup(path);
conn->fd = net_connect_unix(path);
if (conn->fd == -1)
i_fatal("net_connect_unix(%s) failed: %m", path);
conn->io = io_add(conn->fd, IO_READ, admin_input, conn);
conn->to_random = timeout_add_short(ADMIN_RANDOM_TIMEOUT_MSECS,
admin_random_action, conn);
net_set_nonblock(conn->fd, FALSE);
conn->input = i_stream_create_fd(conn->fd, (size_t)-1);
admin_send(conn, DIRECTOR_ADMIN_HANDSHAKE);
line = i_stream_read_next_line(conn->input);
if (line == NULL)
i_fatal("%s disconnected", conn->path);
if (!version_string_verify(line, "director-doveadm", 1)) {
i_fatal("%s not a compatible director-doveadm socket",
conn->path);
}
net_set_nonblock(conn->fd, TRUE);
return conn;
}
static void admin_disconnect(struct admin_connection **_conn)
{
struct admin_connection *conn = *_conn;
*_conn = NULL;
if (conn->to_random != NULL)
timeout_remove(&conn->to_random);
i_stream_destroy(&conn->input);
io_remove(&conn->io);
net_disconnect(conn->fd);
i_free(conn->path);
i_free(conn);
}
static void admin_read_hosts(struct admin_connection *conn)
{
const char *line;
net_set_nonblock(admin->fd, FALSE);
while ((line = i_stream_read_next_line(conn->input)) != NULL) {
if (*line == '\0')
break;
/* ip vhost-count user-count */
T_BEGIN {
const char *const *args = t_strsplit_tabescaped(line);
struct host *host;
host = i_new(struct host, 1);
host->refcount = 1;
if (net_addr2ip(args[0], &host->ip) < 0 ||
str_to_uint(args[1], &host->vhost_count) < 0)
i_fatal("host list broken");
hash_table_insert(hosts, &host->ip, host);
array_append(&hosts_array, &host, 1);
} T_END;
}
if (line == NULL)
i_fatal("Couldn't read hosts list");
net_set_nonblock(admin->fd, TRUE);
}
static void ATTR_NULL(1)
director_connection_disconnect_timeout(void *context ATTR_UNUSED)
{
struct director_connection *conn;
unsigned int i, count = 0;
for (conn = director_connections; conn != NULL; conn = conn->next)
count++;
if (count != 0) {
i = 0; count = i_rand() % count;
for (conn = director_connections; i < count; conn = conn->next) {
i_assert(conn != NULL);
i++;
}
i_assert(conn != NULL);
director_connection_destroy(&conn);
}
}
static void main_init(const char *admin_path)
{
hash_table_create(&users, default_pool, 0, str_hash, strcmp);
hash_table_create(&hosts, default_pool, 0, net_ip_hash, net_ip_cmp);
i_array_init(&hosts_array, 256);
admin = admin_connect(admin_path);
admin_send(admin, "HOST-LIST\n");
admin_read_hosts(admin);
to_disconnect =
timeout_add(1000 * i_rand_minmax(5, 5 + DIRECTOR_DISCONNECT_TIMEOUT_SECS - 1),
director_connection_disconnect_timeout, (void *)NULL);
}
static void main_deinit(void)
{
struct hash_iterate_context *iter;
char *username;
struct ip_addr *ip;
struct user *user;
struct host *host;
while (imap_clients != NULL) {
struct imap_client *client = imap_clients;
imap_client_destroy(&client);
}
timeout_remove(&to_disconnect);
while (director_connections != NULL) {
struct director_connection *conn = director_connections;
director_connection_destroy(&conn);
}
iter = hash_table_iterate_init(users);
while (hash_table_iterate(iter, users, &username, &user))
user_free(user);
hash_table_iterate_deinit(&iter);
hash_table_destroy(&users);
iter = hash_table_iterate_init(hosts);
while (hash_table_iterate(iter, hosts, &ip, &host))
host_unref(&host);
hash_table_iterate_deinit(&iter);
hash_table_destroy(&hosts);
array_free(&hosts_array);
admin_disconnect(&admin);
}
int main(int argc, char *argv[])
{
const char *admin_path;
master_service = master_service_init("director-test", 0,
&argc, &argv, "");
if (master_getopt(master_service) > 0)
return FATAL_DEFAULT;
admin_path = argv[optind];
if (admin_path == NULL)
i_fatal("director-doveadm socket path missing");
master_service_init_log(master_service, "director-test: ");
main_init(admin_path);
master_service_init_finish(master_service);
master_service_run(master_service, client_connected);
main_deinit();
master_service_deinit(&master_service);
return 0;
}