director-test.c revision 5f1d689131a75c39f064cbd4202373e7edf78f18
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */
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 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
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen Finally, this program connects to director-admin socket where it adds
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen and removes mail hosts.
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#define USER_TIMEOUT_MSECS (1000*10) /* FIXME: this should be based on director_user_expire */
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;
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);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainenstatic void client_username_check(struct imap_client *client)
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen if (net_getsockname(client->fd, &local_ip, NULL) < 0)
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen user = hash_table_lookup(users, client->username);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen hash_table_insert(users, user->username, user);
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen i_error("user %s: old connection from %s, new from %s. "
9ba3ecf529e681688bbf728fc3cb5101bd68d671Timo Sirainen "%u old connections, last was %u secs ago",
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen (unsigned int)(ioloop_time - user->last_seen));
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic int imap_client_parse_input(struct imap_client *client)
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen ret = imap_parser_read_args(client->parser, 0, 0, &args);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen t_strconcat(tag, " OK Logged in.\r\n", NULL));
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen o_stream_nsend_str(client->output, t_strconcat(
338088fe2875e8039d2b3df32cbd7a8396b24ea6Timo Sirainen "* BYE Out.\r\n",
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen } else if (strcasecmp(cmd, "capability") == 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen t_strconcat(tag, " BAD Not supported.\r\n", NULL));
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen (void)i_stream_read_next_line(client->input); /* eat away LF */
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainenstatic void imap_client_input(struct imap_client *client)
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen while ((ret = imap_client_parse_input(client)) > 0) ;
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);
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen imap_parser_create(client->input, client->output, 4096);
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen "* OK [CAPABILITY IMAP4rev1] director-test ready.\r\n");
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic void imap_client_destroy(struct imap_client **_client)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen user->to = timeout_add(USER_TIMEOUT_MSECS, user_free,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen master_service_client_connection_destroyed(master_service);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainendirector_connection_input(struct director_connection *conn,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct istream *input, struct ostream *output)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen const unsigned char *data;
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen if (i_stream_read_more(input, &data, &size) == -1) {
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen if (i_rand_limit(3) == 0 && conn->to_delay == NULL) {
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen timeout_add(i_rand_limit(DIRECTOR_CONN_MAX_DELAY_MSECS),
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainenstatic void director_connection_in_input(struct director_connection *conn)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen director_connection_input(conn, conn->in_input, conn->out_output);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainenstatic void director_connection_out_input(struct director_connection *conn)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen director_connection_input(conn, conn->out_input, conn->in_output);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainenstatic void director_connection_timeout(struct director_connection *conn)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainendirector_connection_create(int in_fd, const struct ip_addr *local_ip,
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen out_fd = net_connect_ip(local_ip, DIRECTOR_OUT_PORT, remote_ip);
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->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 Sirainenstatic void director_connection_destroy(struct director_connection **_conn)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainenstatic void client_connected(struct master_service_connection *conn)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen if (net_getsockname(conn->fd, &local_ip, &local_port) < 0)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen if (net_getpeername(conn->fd, &remote_ip, NULL) < 0)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen director_connection_create(conn->fd, &local_ip, &remote_ip);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen i_error("Connection to unknown port %u", local_port);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen master_service_client_connection_accept(conn);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainenadmin_send(struct admin_connection *conn, const char *data)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen if (write_full(i_stream_get_fd(conn->input), data, strlen(data)) < 0)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainenstatic void admin_input(struct admin_connection *conn)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen while ((line = i_stream_read_next_line(conn->input)) != NULL) {
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen i_error("director-doveadm: Unexpected input: %s", line);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen if (conn->input->stream_errno != 0 || conn->input->eof)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainenstatic void admin_random_action(struct admin_connection *conn)
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen unsigned int i, count;
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen hosts[i]->vhost_count = i_rand_limit(20) * 10;
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));
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainenstatic struct admin_connection *admin_connect(const char *path)
const char *line;
return conn;
const char *line;
T_BEGIN {
} T_END;
unsigned int i, count = 0;
count++;
if (count != 0) {
static void main_deinit(void)
char *username;
const char *admin_path;
return FATAL_DEFAULT;
main_deinit();