director-test.c revision a10ed8c47534b4c6b6bf2711ccfe577e720a47b4
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2005-2012 Dovecot authors, see the included COPYING file */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen This program accepts incoming unauthenticated IMAP connections from
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen port 14300. If the same user is connecting to multiple different local IPs,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen it logs an error (i.e. director is not working right then).
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen This program also accepts incoming director connections on port 9091 and
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen forwards them to local_ip:9090. To make this work properly, director
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen executable must be given -t 9091 parameter. The idea is that this test tool
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen hooks between all director connections and can then add delays or break the
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen Finally, this program connects to director-admin socket where it adds
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen and removes mail hosts.
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic struct director_connection *director_connections;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainenstatic ARRAY_DEFINE(hosts_array, struct host *);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic void imap_client_destroy(struct imap_client **client);
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainenstatic void director_connection_destroy(struct director_connection **conn);
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainenstatic void director_connection_timeout(struct director_connection *conn);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic void client_username_check(struct imap_client *client)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (net_getsockname(client->fd, &local_ip, NULL) < 0)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen user = hash_table_lookup(users, client->username);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen hash_table_insert(users, user->username, user);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen i_error("user %s: old connection from %s, new from %s. "
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen "%u old connections, last was %u secs ago",
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen (unsigned int)(ioloop_time - user->last_seen));
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic int imap_client_parse_input(struct imap_client *client)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen ret = imap_parser_read_args(client->parser, 0, 0, &args);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen t_strconcat(tag, " OK Logged in.\r\n", NULL));
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen o_stream_send_str(client->output, t_strconcat(
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen "* BYE Out.\r\n",
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen } else if (strcasecmp(cmd, "capability") == 0) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen t_strconcat(tag, " BAD Not supported.\r\n", NULL));
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen (void)i_stream_read_next_line(client->input); /* eat away LF */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic void imap_client_input(struct imap_client *client)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen while ((ret = imap_client_parse_input(client)) > 0) ;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen client->input = i_stream_create_fd(fd, 4096, FALSE);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen client->output = o_stream_create_fd(fd, (size_t)-1, FALSE);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen client->io = io_add(fd, IO_READ, imap_client_input, client);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen imap_parser_create(client->input, client->output, 4096);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen "* OK [CAPABILITY IMAP4rev1] director-test ready.\r\n");
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic void imap_client_destroy(struct imap_client **_client)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen user->to = timeout_add(USER_TIMEOUT_MSECS, user_free,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen master_service_client_connection_destroyed(master_service);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainendirector_connection_input(struct director_connection *conn,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen struct istream *input, struct ostream *output)
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen const unsigned char *data;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen if (i_stream_read_data(input, &data, &size, 0) == -1) {
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen if (rand() % 3 == 0 && conn->to_delay == NULL) {
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen timeout_add(rand() % DIRECTOR_CONN_MAX_DELAY_MSECS,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic void director_connection_in_input(struct director_connection *conn)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen director_connection_input(conn, conn->in_input, conn->out_output);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic void director_connection_out_input(struct director_connection *conn)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen director_connection_input(conn, conn->out_input, conn->in_output);
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainenstatic void director_connection_timeout(struct director_connection *conn)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainendirector_connection_create(int in_fd, const struct ip_addr *local_ip)
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen out_fd = net_connect_ip(local_ip, DIRECTOR_OUT_PORT, NULL);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen conn->in_input = i_stream_create_fd(conn->in_fd, (size_t)-1, FALSE);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen conn->in_output = o_stream_create_fd(conn->in_fd, (size_t)-1, FALSE);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen conn->out_input = i_stream_create_fd(conn->out_fd, (size_t)-1, FALSE);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen conn->out_output = o_stream_create_fd(conn->out_fd, (size_t)-1, FALSE);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic void director_connection_destroy(struct director_connection **_conn)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic void client_connected(struct master_service_connection *conn)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (net_getsockname(conn->fd, &local_ip, &local_port) < 0)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen director_connection_create(conn->fd, &local_ip);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen i_error("Connection to unknown port %u", local_port);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen master_service_client_connection_accept(conn);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenadmin_send(struct admin_connection *conn, const char *data)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (write_full(i_stream_get_fd(conn->input), data, strlen(data)) < 0)
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainenstatic void admin_input(struct admin_connection *conn)
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen while ((line = i_stream_read_next_line(conn->input)) != NULL) {
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen i_error("director-doveadm: Unexpected input: %s", line);
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen if (conn->input->stream_errno != 0 || conn->input->eof)
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainenstatic void admin_random_action(struct admin_connection *conn)
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen unsigned int i, count;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen admin_send(conn, t_strdup_printf("HOST-SET\t%s\t%u\n",
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen net_ip2addr(&hosts[i]->ip), hosts[i]->vhost_count));
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic struct admin_connection *admin_connect(const char *path)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen#define DIRECTOR_ADMIN_HANDSHAKE "VERSION\tdirector-doveadm\t1\t0\n"
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen i_fatal("net_connect_unix(%s) failed: %m", path);
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen conn->io = io_add(conn->fd, IO_READ, admin_input, conn);
4b335788eb41dec2de5f78459d96387fcc710010Timo Sirainen conn->to_random = timeout_add_short(ADMIN_RANDOM_TIMEOUT_MSECS,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen conn->input = i_stream_create_fd(conn->fd, (size_t)-1, TRUE);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (!version_string_verify(line, "director-doveadm", 1)) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen i_fatal("%s not a compatible director-doveadm socket",
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic void admin_disconnect(struct admin_connection **_conn)
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainenstatic void admin_read_hosts(struct admin_connection *conn)
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen while ((line = i_stream_read_next_line(conn->input)) != NULL) {
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen /* ip vhost-count user-count */
3c296d819c54e21ce05c3d2eeeedc79be42ac593Timo Sirainen const char *const *args = t_strsplit_tab(line);
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainendirector_connection_disconnect_timeout(void *context ATTR_UNUSED)
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen unsigned int i, count = 0;
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen for (conn = director_connections; conn != NULL; conn = conn->next)
6c961309a97ee0c86b85fa336f5f51662bd3d515Timo Sirainen for (conn = director_connections; i < count; conn = conn->next) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen users = hash_table_create(default_pool, default_pool, 0,
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen hosts = hash_table_create(default_pool, default_pool, 0,
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen timeout_add(1000*(1 + rand()%DIRECTOR_DISCONNECT_TIMEOUT_SECS),
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen director_connection_disconnect_timeout, NULL);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic void main_deinit(void)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen struct director_connection *conn = director_connections;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen while (hash_table_iterate(iter, &key, &value)) {
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen while (hash_table_iterate(iter, &key, &value)) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen master_service = master_service_init("director-test", 0,
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen i_fatal("director-doveadm socket path missing");
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen master_service_init_log(master_service, "director-test: ");