director-connection.c revision 1df39b899804fd1dbc560f75382364822935c857
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2010-2012 Dovecot authors, see the included COPYING file */
377dd19a90436b8f96902af741a3ea130bc3fe75Timo Sirainen Incoming director connections send:
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen <wait for DONE from remote handshake>
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen <make this connection our "left" connection, potentially disconnecting
377dd19a90436b8f96902af741a3ea130bc3fe75Timo Sirainen Outgoing director connections send:
377dd19a90436b8f96902af741a3ea130bc3fe75Timo Sirainen [0..n] DIRECTOR
377dd19a90436b8f96902af741a3ea130bc3fe75Timo Sirainen HOST-HAND-START
377dd19a90436b8f96902af741a3ea130bc3fe75Timo Sirainen HOST-HAND-END
377dd19a90436b8f96902af741a3ea130bc3fe75Timo Sirainen <possibly other non-handshake commands between USERs>
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen <wait for DONE from remote>
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen <make this connection our "right" connection, potentially disconnecting
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen/* Max idling time before "ME" command must have been received,
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen or we'll disconnect. */
fe7f9298fb789717d26dc4cb6317a9d376acd8fcTimo Sirainen#define DIRECTOR_CONNECTION_ME_TIMEOUT_MSECS (10*1000)
3574bab52a67dfe1291f6306e707c6199e777043Timo Sirainen/* Max time to wait for USERs in handshake to be sent. With a lot of users the
3574bab52a67dfe1291f6306e707c6199e777043Timo Sirainen kernel may quickly eat up everything we send, while the receiver is busy
3574bab52a67dfe1291f6306e707c6199e777043Timo Sirainen parsing the data. */
5a5b39ce7c6ba6d6ff2218ae1679e0485bf43b47Timo Sirainen#define DIRECTOR_CONNECTION_SEND_USERS_TIMEOUT_MSECS (30*1000)
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen/* Max idling time before "DONE" command must have been received,
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen or we'll disconnect. */
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen#define DIRECTOR_CONNECTION_DONE_TIMEOUT_MSECS (30*1000)
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen/* How long to wait for PONG for an idling connection */
fe7f9298fb789717d26dc4cb6317a9d376acd8fcTimo Sirainen#define DIRECTOR_CONNECTION_PING_IDLE_TIMEOUT_MSECS (10*1000)
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen/* Maximum time to wait for PONG reply */
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen#define DIRECTOR_CONNECTION_PONG_TIMEOUT_MSECS (60*1000)
03fa2d644be0a9274e7e94fb4835cc374c539264Timo Sirainen/* How long to wait to send PING when connection is idle */
03fa2d644be0a9274e7e94fb4835cc374c539264Timo Sirainen#define DIRECTOR_CONNECTION_PING_INTERVAL_MSECS (15*1000)
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen/* How long to wait before sending PING while waiting for SYNC reply */
f64b5bc9e73bedc63ba3c072c286542c29c69e43Timo Sirainen#define DIRECTOR_CONNECTION_PING_SYNC_INTERVAL_MSECS 1000
4bc4042782c465636eff2c713bc85f5a1d773d91Timo Sirainen/* If outgoing director connection exists for less than this many seconds,
4bc4042782c465636eff2c713bc85f5a1d773d91Timo Sirainen mark the host as failed so we won't try to reconnect to it immediately */
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen#define DIRECTOR_HANDSHAKE_BYTES_LOG_MIN_SECS (60*30)
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen#if DIRECTOR_CONNECTION_DONE_TIMEOUT_MSECS <= DIRECTOR_CONNECTION_PING_TIMEOUT_MSECS
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen# error DIRECTOR_CONNECTION_DONE_TIMEOUT_MSECS is too low
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen#if DIRECTOR_CONNECTION_PONG_TIMEOUT_MSECS <= DIRECTOR_CONNECTION_PING_IDLE_TIMEOUT_MSECS
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen# error DIRECTOR_CONNECTION_PONG_TIMEOUT_MSECS is too low
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* for incoming connections the director host isn't known until
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen ME-line is received */
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen /* this is set only for wrong connections: */
6cb8e7d726a7e9d157e87fb379982d52100b283fTimo Sirainen struct timeout *to_disconnect, *to_ping, *to_pong;
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainen /* set during command execution */
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainenstatic void director_connection_disconnected(struct director_connection **conn);
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainenstatic void director_connection_reconnect(struct director_connection **conn,
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen const char *reason);
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainenstatic void director_connection_log_disconnect(struct director_connection *conn);
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainendirector_cmd_error(struct director_connection *conn, const char *fmt, ...)
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainen i_error("director(%s): Command %s: %s (input: %s)", conn->name,
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainen conn->cur_cmd, t_strdup_vprintf(fmt, args), conn->cur_line);
4847d74a7442a3efabe76a8ad18dd464082d6581Timo Sirainen conn->host->last_protocol_failure = ioloop_time;
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainendirector_connection_init_timeout(struct director_connection *conn)
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen unsigned int secs = ioloop_time - conn->created;
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen i_error("director(%s): Connect timed out (%u secs)",
cc22ca265b5b355c4a029155e074f36b5baf2e60Timo Sirainen i_error("director(%s): Sending handshake (%u secs)",
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen i_error("director(%s): Handshaking ME timed out (%u secs)",
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen i_error("director(%s): Handshaking DONE timed out (%u secs)",
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainendirector_connection_set_ping_timeout(struct director_connection *conn)
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen unsigned int msecs;
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen msecs = conn->synced || !conn->handshake_received ?
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen conn->to_ping = timeout_add(msecs, director_connection_ping, conn);
6cb8e7d726a7e9d157e87fb379982d52100b283fTimo Sirainenstatic void director_connection_wait_timeout(struct director_connection *conn)
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen "Timeout waiting for disconnect after CONNECT");
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainenstatic void director_connection_send_connect(struct director_connection *conn,
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen connect_str = t_strdup_printf("CONNECT\t%s\t%u\n",
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen /* wait for a while for the remote to disconnect, so it will hopefully
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen see our CONNECT command. we'll also log the warning later to avoid
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen multiple log lines about it. */
9a656df90290a5fef45b3a1191ae75864f17602dTimo Sirainen timeout_add(DIRECTOR_WAIT_DISCONNECT_SECS*1000,
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainenstatic void director_connection_assigned(struct director_connection *conn)
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen if (dir->left != NULL && dir->right != NULL) {
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen /* we're connected to both directors. see if the ring is
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen finished by sending a SYNC. if we get it back, it's done. */
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen director_sync_send(dir, dir->self_host, dir->sync_seq,
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainenstatic bool director_connection_assign_left(struct director_connection *conn)
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen /* make sure this is the correct incoming connection */
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen /* no conflicts yet */
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen i_warning("Replacing left director connection %s with %s",
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen director_connection_deinit(&dir->left, t_strdup_printf(
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen /* we're waiting to verify if our current left is still
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen working. if we don't receive a PONG, the current left
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen gets disconnected and a new left gets assigned. if we do
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen receive a PONG, we'll wait until the current left
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen disconnects us and then reassign the new left. */
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen } else if (director_host_cmp_to_self(dir->left->host, conn->host,
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen /* the old connection is the correct one.
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen refer the client there (FIXME: do we ever get here?) */
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen director_connection_send_connect(conn, dir->left->host);
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen /* this new connection is the correct one, but wait until the
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen old connection gets disconnected before using this one.
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen that guarantees that the director inserting itself into
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen the ring has finished handshaking its left side, so the
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen switch will be fast. */
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen conn->name = i_strdup_printf("%s/left", conn->host->name);
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainenstatic void director_assign_left(struct director *dir)
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen struct director_connection *conn, *const *connp;
6cb8e7d726a7e9d157e87fb379982d52100b283fTimo Sirainen conn->to_disconnect == NULL && conn != dir->left) {
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen /* either use this or disconnect it */
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen /* we don't want this */
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen "Unwanted incoming connection");
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainenstatic bool director_has_outgoing_connections(struct director *dir)
6cb8e7d726a7e9d157e87fb379982d52100b283fTimo Sirainen if (!(*connp)->in && (*connp)->to_disconnect == NULL)
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainenstatic bool director_connection_assign_right(struct director_connection *conn)
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen /* see if we should disconnect or keep the existing
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen connection. */
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen if (director_host_cmp_to_self(conn->host, dir->right->host,
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen /* the old connection is the correct one */
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen i_warning("Aborting incorrect outgoing connection to %s "
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen "(already connected to correct one: %s)",
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen i_warning("Replacing right director connection %s with %s",
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen director_connection_deinit(&dir->right, t_strdup_printf(
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen conn->name = i_strdup_printf("%s/right", conn->host->name);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainendirector_args_parse_ip_port(struct director_connection *conn,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen const char *const *args,
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainen director_cmd_error(conn, "Missing IP+port parameters");
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainen director_cmd_error(conn, "Invalid IP address: %s", args[0]);
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainen director_cmd_error(conn, "Invalid port: %s", args[1]);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic bool director_cmd_me(struct director_connection *conn,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen const char *const *args)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen unsigned int port;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (!director_args_parse_ip_port(conn, args, &ip, &port))
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (!conn->in && (!net_ip_compare(&conn->host->ip, &ip) ||
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("Remote director thinks it's someone else "
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen "(connected to %s:%u, remote says it's %s:%u)",
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen net_ip2addr(&conn->host->ip), conn->host->port,
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen conn->to_ping = timeout_add(DIRECTOR_CONNECTION_DONE_TIMEOUT_MSECS,
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen /* Incoming connection:
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen a) we don't have an established ring yet. make sure we're connecting
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen to our right side (which might become our left side).
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen b) it's our current "left" connection. the previous connection
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen is most likely dead.
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen c) we have an existing ring. tell our current "left" to connect to
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen it with CONNECT command.
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen d) the incoming connection doesn't belong to us at all, refer it
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen elsewhere with CONNECT. however, before disconnecting it verify
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen first that our left side is actually still functional.
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen conn->host = director_host_get(dir, &ip, port);
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainen /* the host shouldn't be removed at this point, but if for some
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainen reason it is we don't want to crash */
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen /* make sure we don't keep old sequence values across restarts */
8c2b4a45f17a5cb13bb01058ca37798cf48d91baTimo Sirainen next_comm_attempt = conn->host->last_protocol_failure +
8c2b4a45f17a5cb13bb01058ca37798cf48d91baTimo Sirainen /* the director recently sent invalid protocol data,
8c2b4a45f17a5cb13bb01058ca37798cf48d91baTimo Sirainen don't try retrying yet */
8c2b4a45f17a5cb13bb01058ca37798cf48d91baTimo Sirainen i_error("director(%s): Remote sent invalid protocol data recently, "
8c2b4a45f17a5cb13bb01058ca37798cf48d91baTimo Sirainen "waiting %u secs before allowing further communication",
8c2b4a45f17a5cb13bb01058ca37798cf48d91baTimo Sirainen conn->name, (unsigned int)(next_comm_attempt-ioloop_time));
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen /* a) - just in case the left is also our right side reset
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen its failed state, so we can connect to it */
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen "Replacing with new incoming connection");
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen } else if (director_host_cmp_to_self(conn->host, dir->left->host,
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen connect_str = t_strdup_printf("CONNECT\t%s\t%u\n",
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen director_connection_send(dir->left, connect_str);
4f2b533808371b3b8a8187819cd4c3d90e1ca8eeTimo Sirainendirector_user_refresh(struct director_connection *conn,
4f2b533808371b3b8a8187819cd4c3d90e1ca8eeTimo Sirainen unsigned int username_hash, struct mail_host *host,
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen time_t timestamp, bool weak, struct user **user_r)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen user = user_directory_lookup(dir->users, username_hash);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen *user_r = user_directory_add(dir->users, username_hash,
1df39b899804fd1dbc560f75382364822935c857Timo Sirainen dir_debug("user refresh: %u added", username_hash);
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen /* removing user's weakness */
1df39b899804fd1dbc560f75382364822935c857Timo Sirainen dir_debug("user refresh: %u weakness removed",
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen /* weak user marked again as weak */
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen } else if (weak &&
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen !user_directory_user_is_recently_updated(dir->users, user)) {
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen /* mark the user as weak */
1df39b899804fd1dbc560f75382364822935c857Timo Sirainen dir_debug("user refresh: %u set weak", username_hash);
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen /* non-weak user received a non-weak update with
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen conflicting host. this shouldn't happen. */
4f2b533808371b3b8a8187819cd4c3d90e1ca8eeTimo Sirainen "is being redirected to two hosts: %s and %s",
4f2b533808371b3b8a8187819cd4c3d90e1ca8eeTimo Sirainen str_printfa(str, " (old_ts=%ld", (long)user->timestamp);
4f2b533808371b3b8a8187819cd4c3d90e1ca8eeTimo Sirainen str_printfa(str, ",kill_state=%d", user->kill_state);
242abe3ad2423776e9cf05e1304eb8fda4831b23Timo Sirainen /* we want all the directors to redirect the user to same
242abe3ad2423776e9cf05e1304eb8fda4831b23Timo Sirainen server, but we don't want two directors fighting over which
242abe3ad2423776e9cf05e1304eb8fda4831b23Timo Sirainen server it belongs to, so always use the lower IP address */
242abe3ad2423776e9cf05e1304eb8fda4831b23Timo Sirainen if (net_ip_cmp(&user->host->ip, &host->ip) > 0) {
242abe3ad2423776e9cf05e1304eb8fda4831b23Timo Sirainen /* change the host. we'll also need to remove the user
242abe3ad2423776e9cf05e1304eb8fda4831b23Timo Sirainen from the old host's user_count, because we can't
242abe3ad2423776e9cf05e1304eb8fda4831b23Timo Sirainen keep track of the user for more than one host */
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen /* keep the host */
4f2b533808371b3b8a8187819cd4c3d90e1ca8eeTimo Sirainen if (timestamp == ioloop_time && (time_t)user->timestamp != timestamp) {
1df39b899804fd1dbc560f75382364822935c857Timo Sirainen dir_debug("user refresh: %u refreshed timeout to %ld",
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen /* user is no longer weak. handle pending requests for
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen this user if there are any */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainendirector_handshake_cmd_user(struct director_connection *conn,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen const char *const *args)
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainen director_cmd_error(conn, "Invalid parameters");
cf9d67e4a9bfee31cf3be05244555d51a3d1b9feTimo Sirainen host = mail_host_lookup(conn->dir->mail_hosts, &ip);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("director(%s): USER used unknown host %s in handshake",
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen director_user_refresh(conn, username_hash, host, timestamp, weak, &user);
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainendirector_cmd_user(struct director_connection *conn,
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen const char *const *args)
377dd19a90436b8f96902af741a3ea130bc3fe75Timo Sirainen /* NOTE: if more parameters are added, update also
377dd19a90436b8f96902af741a3ea130bc3fe75Timo Sirainen CMD_IS_USER_HANDHAKE() macro */
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainen director_cmd_error(conn, "Invalid parameters");
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen host = mail_host_lookup(conn->dir->mail_hosts, &ip);
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen /* we probably just removed this host. */
4f2b533808371b3b8a8187819cd4c3d90e1ca8eeTimo Sirainen if (director_user_refresh(conn, username_hash,
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen director_update_user(conn->dir, conn->host, user);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic bool director_cmd_director(struct director_connection *conn,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen const char *const *args)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen unsigned int port;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (!director_args_parse_ip_port(conn, args, &ip, &port))
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen host = director_host_lookup(conn->dir, &ip, port);
c9b08dc8d71bd655e5648daf8a09ff4b728cae81Timo Sirainen /* ignore updates to ourself */
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainen /* ignore re-adds of removed directors */
8c2b4a45f17a5cb13bb01058ca37798cf48d91baTimo Sirainen /* already have this. just reset its last_network_failure
8c2b4a45f17a5cb13bb01058ca37798cf48d91baTimo Sirainen timestamp, since it might be up now. */
c9b08dc8d71bd655e5648daf8a09ff4b728cae81Timo Sirainen /* it also may have been restarted, reset last_seq */
c9b08dc8d71bd655e5648daf8a09ff4b728cae81Timo Sirainen /* save the director and forward it */
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainen host = director_host_add(conn->dir, &ip, port);
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainenstatic bool director_cmd_director_remove(struct director_connection *conn,
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainen const char *const *args)
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainen unsigned int port;
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainen if (!director_args_parse_ip_port(conn, args, &ip, &port))
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainen host = director_host_lookup(conn->dir, &ip, port);
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainen director_ring_remove(host, director_connection_get_host(conn));
8d63b0ab4dea920a4dd6a4469289950eef50a063Timo Sirainendirector_cmd_host_hand_start(struct director_connection *conn,
8d63b0ab4dea920a4dd6a4469289950eef50a063Timo Sirainen const char *const *args)
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainen str_to_uint(args[0], &remote_ring_completed) < 0) {
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainen director_cmd_error(conn, "Invalid parameters");
8d63b0ab4dea920a4dd6a4469289950eef50a063Timo Sirainen if (remote_ring_completed && !conn->dir->ring_handshaked) {
8d63b0ab4dea920a4dd6a4469289950eef50a063Timo Sirainen /* clear everything we have and use only what remote sends us */
cf9d67e4a9bfee31cf3be05244555d51a3d1b9feTimo Sirainen hosts = mail_hosts_get(conn->dir->mail_hosts);
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen director_remove_host(conn->dir, NULL, NULL, *hostp);
8d63b0ab4dea920a4dd6a4469289950eef50a063Timo Sirainen } else if (!remote_ring_completed && conn->dir->ring_handshaked) {
8d63b0ab4dea920a4dd6a4469289950eef50a063Timo Sirainen /* ignore whatever remote sends */
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainendirector_cmd_is_seen(struct director_connection *conn,
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen const char *const **_args,
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainen director_cmd_error(conn, "Invalid parameters");
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen host = director_host_lookup(conn->dir, &ip, port);
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen /* director is already gone, but we can't be sure if this
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen command was sent everywhere. re-send it as if it was from
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen /* already seen this */
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainendirector_cmd_user_weak(struct director_connection *conn,
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen const char *const *args)
d3211a8014c08677e1c1bbd84e98ad51b5744448Timo Sirainen /* note that unlike other commands we don't want to just ignore
d3211a8014c08677e1c1bbd84e98ad51b5744448Timo Sirainen duplicate commands */
d3211a8014c08677e1c1bbd84e98ad51b5744448Timo Sirainen if ((ret = director_cmd_is_seen(conn, &args, &dir_host)) < 0)
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainen director_cmd_error(conn, "Invalid parameters");
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen host = mail_host_lookup(conn->dir->mail_hosts, &ip);
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen /* we probably just removed this host. */
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen /* The entire ring has seen this USER-WEAK.
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen make it non-weak now. */
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen if (director_user_refresh(conn, username_hash,
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen director_update_user(conn->dir, src_host, user);
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen director_update_user_weak(conn->dir, src_host,
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainendirector_cmd_host_int(struct director_connection *conn, const char *const *args,
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainen director_cmd_error(conn, "Invalid parameters");
8d63b0ab4dea920a4dd6a4469289950eef50a063Timo Sirainen /* remote is sending hosts in a handshake, but it doesn't have
8d63b0ab4dea920a4dd6a4469289950eef50a063Timo Sirainen a completed ring and we do. */
cf9d67e4a9bfee31cf3be05244555d51a3d1b9feTimo Sirainen host = mail_host_lookup(conn->dir->mail_hosts, &ip);
cf9d67e4a9bfee31cf3be05244555d51a3d1b9feTimo Sirainen host = mail_host_add_ip(conn->dir->mail_hosts, &ip);
242abe3ad2423776e9cf05e1304eb8fda4831b23Timo Sirainen mail_host_set_vhost_count(conn->dir->mail_hosts,
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen director_update_host(conn->dir, conn->host, dir_host, host);
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainendirector_cmd_host_handshake(struct director_connection *conn,
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen const char *const *args)
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen return director_cmd_host_int(conn, args, NULL);
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainendirector_cmd_host(struct director_connection *conn, const char *const *args)
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen if ((ret = director_cmd_is_seen(conn, &args, &dir_host)) != 0)
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen return director_cmd_host_int(conn, args, dir_host);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainendirector_cmd_host_remove(struct director_connection *conn,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen const char *const *args)
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen if ((ret = director_cmd_is_seen(conn, &args, &dir_host)) != 0)
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainen director_cmd_error(conn, "Invalid parameters");
cf9d67e4a9bfee31cf3be05244555d51a3d1b9feTimo Sirainen host = mail_host_lookup(conn->dir->mail_hosts, &ip);
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen director_remove_host(conn->dir, conn->host, dir_host, host);
5e9bb72de1209cd39fdf3e95bdb26e047cc5594eTimo Sirainendirector_cmd_host_flush(struct director_connection *conn,
5e9bb72de1209cd39fdf3e95bdb26e047cc5594eTimo Sirainen const char *const *args)
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen if ((ret = director_cmd_is_seen(conn, &args, &dir_host)) != 0)
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainen director_cmd_error(conn, "Invalid parameters");
5e9bb72de1209cd39fdf3e95bdb26e047cc5594eTimo Sirainen host = mail_host_lookup(conn->dir->mail_hosts, &ip);
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen director_flush_host(conn->dir, conn->host, dir_host, host);
15ab2452b0220a115f4351ad9d7fd5ec70ae7966Timo Sirainendirector_cmd_user_move(struct director_connection *conn,
15ab2452b0220a115f4351ad9d7fd5ec70ae7966Timo Sirainen const char *const *args)
15ab2452b0220a115f4351ad9d7fd5ec70ae7966Timo Sirainen if ((ret = director_cmd_is_seen(conn, &args, &dir_host)) != 0)
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainen director_cmd_error(conn, "Invalid parameters");
15ab2452b0220a115f4351ad9d7fd5ec70ae7966Timo Sirainen host = mail_host_lookup(conn->dir->mail_hosts, &ip);
15ab2452b0220a115f4351ad9d7fd5ec70ae7966Timo Sirainen director_move_user(conn->dir, conn->host, dir_host,
15ab2452b0220a115f4351ad9d7fd5ec70ae7966Timo Sirainendirector_cmd_user_killed(struct director_connection *conn,
15ab2452b0220a115f4351ad9d7fd5ec70ae7966Timo Sirainen const char *const *args)
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainen director_cmd_error(conn, "Invalid parameters");
15ab2452b0220a115f4351ad9d7fd5ec70ae7966Timo Sirainen director_user_killed(conn->dir, username_hash);
15ab2452b0220a115f4351ad9d7fd5ec70ae7966Timo Sirainendirector_cmd_user_killed_everywhere(struct director_connection *conn,
15ab2452b0220a115f4351ad9d7fd5ec70ae7966Timo Sirainen const char *const *args)
15ab2452b0220a115f4351ad9d7fd5ec70ae7966Timo Sirainen if ((ret = director_cmd_is_seen(conn, &args, &dir_host)) != 0)
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainen director_cmd_error(conn, "Invalid parameters");
15ab2452b0220a115f4351ad9d7fd5ec70ae7966Timo Sirainen director_user_killed_everywhere(conn->dir, conn->host,
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainenstatic bool director_handshake_cmd_done(struct director_connection *conn)
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen unsigned int handshake_secs = time(NULL) - conn->created;
1df39b899804fd1dbc560f75382364822935c857Timo Sirainen if (handshake_secs >= DIRECTOR_HANDSHAKE_WARN_SECS || director_debug) {
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen str_printfa(str, "director(%s): Handshake took %u secs, "
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen conn->name, handshake_secs, conn->input->v_offset,
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen if (handshake_secs >= DIRECTOR_HANDSHAKE_WARN_SECS)
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen /* the host is up now, make sure we can connect to it immediately
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* handshaked to left side. tell it we've received the
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen whole handshake. */
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen /* tell the "right" director about the "left" one */
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen director_update_send(dir, director_connection_get_host(conn),
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen /* this is our "left" side. */
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen /* handshaked to "right" side. */
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen return director_connection_assign_right(conn);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainendirector_connection_handle_handshake(struct director_connection *conn,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* both incoming and outgoing connections get VERSION and ME */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (strcmp(cmd, "VERSION") == 0 && str_array_length(args) >= 3) {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (strcmp(args[0], DIRECTOR_VERSION_NAME) != 0) {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("director(%s): Wrong protocol in socket "
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen "(%s vs %s)",
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen } else if (atoi(args[1]) != DIRECTOR_VERSION_MAJOR) {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("director(%s): Incompatible protocol version: "
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainen director_cmd_error(conn, "Incompatible protocol");
377dd19a90436b8f96902af741a3ea130bc3fe75Timo Sirainen director_cmd_error(conn, "Expecting ME command first");
377dd19a90436b8f96902af741a3ea130bc3fe75Timo Sirainen /* incoming connections get a HOST list */
377dd19a90436b8f96902af741a3ea130bc3fe75Timo Sirainen return director_cmd_host_handshake(conn, args) ? 1 : -1;
377dd19a90436b8f96902af741a3ea130bc3fe75Timo Sirainen director_cmd_error(conn, "Unexpected command during host list");
377dd19a90436b8f96902af741a3ea130bc3fe75Timo Sirainen "Host list is only for incoming connections");
377dd19a90436b8f96902af741a3ea130bc3fe75Timo Sirainen return director_cmd_host_hand_start(conn, args) ? 1 : -1;
377dd19a90436b8f96902af741a3ea130bc3fe75Timo Sirainen if (conn->in && strcmp(cmd, "USER") == 0 && CMD_IS_USER_HANDHAKE(args))
377dd19a90436b8f96902af741a3ea130bc3fe75Timo Sirainen return director_handshake_cmd_user(conn, args) ? 1 : -1;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* both get DONE */
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen return director_handshake_cmd_done(conn) ? 1 : -1;
fe201fb1819622ab33c51ef5a0a1f672e906b21aTimo Sirainendirector_connection_sync_host(struct director_connection *conn,
aa797403d51ff047727b77d64532001d6b6cc21aTimo Sirainen /* we're not up to date */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* stale SYNC event */
377dd19a90436b8f96902af741a3ea130bc3fe75Timo Sirainen /* sync_seq increases when we get disconnected, so we must be
377dd19a90436b8f96902af741a3ea130bc3fe75Timo Sirainen successfully connected to both directions */
377dd19a90436b8f96902af741a3ea130bc3fe75Timo Sirainen i_assert(dir->left != NULL && dir->right != NULL);
242abe3ad2423776e9cf05e1304eb8fda4831b23Timo Sirainen /* the ring is handshaked */
fe201fb1819622ab33c51ef5a0a1f672e906b21aTimo Sirainen /* duplicate SYNC (which was sent just in case the
fe201fb1819622ab33c51ef5a0a1f672e906b21aTimo Sirainen previous one got lost) */
fe201fb1819622ab33c51ef5a0a1f672e906b21aTimo Sirainen /* forward it to the connection on right */
aa797403d51ff047727b77d64532001d6b6cc21aTimo Sirainen director_sync_send(dir, host, seq, minor_version);
fe201fb1819622ab33c51ef5a0a1f672e906b21aTimo Sirainenstatic bool director_connection_sync(struct director_connection *conn,
aa797403d51ff047727b77d64532001d6b6cc21aTimo Sirainen const char *const *args)
fe201fb1819622ab33c51ef5a0a1f672e906b21aTimo Sirainen !director_args_parse_ip_port(conn, args, &ip, &port) ||
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainen director_cmd_error(conn, "Invalid parameters");
fe201fb1819622ab33c51ef5a0a1f672e906b21aTimo Sirainen /* find the originating director. if we don't see it, it was already
fe201fb1819622ab33c51ef5a0a1f672e906b21aTimo Sirainen removed and we can ignore this sync. */
aa797403d51ff047727b77d64532001d6b6cc21aTimo Sirainen director_connection_sync_host(conn, host, seq,
1055d8038122c4f4190d37d98fdff6791d1306f8Timo Sirainenstatic bool director_cmd_connect(struct director_connection *conn,
1055d8038122c4f4190d37d98fdff6791d1306f8Timo Sirainen const char *const *args)
1055d8038122c4f4190d37d98fdff6791d1306f8Timo Sirainen unsigned int port;
ef44f827db33c2f8181d110802db1aebcd15120bTimo Sirainen !director_args_parse_ip_port(conn, args, &ip, &port)) {
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainen director_cmd_error(conn, "Invalid parameters");
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen host = director_host_get(conn->dir, &ip, port);
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen /* reset failure timestamp so we'll actually try to connect there. */
1055d8038122c4f4190d37d98fdff6791d1306f8Timo Sirainen /* remote suggests us to connect elsewhere */
1055d8038122c4f4190d37d98fdff6791d1306f8Timo Sirainen director_host_cmp_to_self(host, dir->right->host,
1055d8038122c4f4190d37d98fdff6791d1306f8Timo Sirainen /* the old connection is the correct one */
1df39b899804fd1dbc560f75382364822935c857Timo Sirainen dir_debug("Ignoring CONNECT request to %s (current right is %s)",
1df39b899804fd1dbc560f75382364822935c857Timo Sirainen "replacing current right %s",
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen /* connect here */
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainenstatic void director_disconnect_wrong_lefts(struct director *dir)
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen struct director_connection *const *connp, *conn;
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen if (conn->in && conn != dir->left && conn->me_received &&
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen director_host_cmp_to_self(dir->left->host, conn->host,
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen director_connection_send_connect(conn, dir->left->host);
03fa2d644be0a9274e7e94fb4835cc374c539264Timo Sirainenstatic bool director_cmd_pong(struct director_connection *conn)
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen /* our left side is functional. tell all the wrong
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen incoming connections to connect to it instead. */
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainendirector_connection_handle_cmd(struct director_connection *conn,
377dd19a90436b8f96902af741a3ea130bc3fe75Timo Sirainen ret = director_connection_handle_handshake(conn, cmd, args);
4fe3f07477bae6da3fb8d8fa9bab10ab82ada2bdTimo Sirainen /* invalid commands during handshake,
4fe3f07477bae6da3fb8d8fa9bab10ab82ada2bdTimo Sirainen we probably don't want to reconnect here */
377dd19a90436b8f96902af741a3ea130bc3fe75Timo Sirainen /* allow also other commands during handshake */
15ab2452b0220a115f4351ad9d7fd5ec70ae7966Timo Sirainen if (strcmp(cmd, "USER-KILLED-EVERYWHERE") == 0)
15ab2452b0220a115f4351ad9d7fd5ec70ae7966Timo Sirainen return director_cmd_user_killed_everywhere(conn, args);
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainen return director_cmd_director_remove(conn, args);
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen i_warning("Director %s disconnected us with reason: %s",
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainen director_cmd_error(conn, "Unknown command %s", cmd);
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainendirector_connection_handle_line(struct director_connection *conn,
4847d74a7442a3efabe76a8ad18dd464082d6581Timo Sirainen director_cmd_error(conn, "Received empty line");
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainen ret = director_connection_handle_cmd(conn, cmd, args);
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainendirector_connection_log_disconnect(struct director_connection *conn)
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen unsigned int secs = ioloop_time - conn->created;
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen i_warning("Director %s tried to connect to us, "
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen "should use %s instead",
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen str_printfa(str, "Director %s disconnected: ", conn->name);
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen secs, conn->input->v_offset, conn->output->offset);
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen str_append(str, ", handshake ME not received");
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen str_append(str, ", handshake DONE not received");
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic void director_connection_input(struct director_connection *conn)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* disconnected */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* buffer full */
4847d74a7442a3efabe76a8ad18dd464082d6581Timo Sirainen director_cmd_error(conn, "Director sent us more than %d bytes",
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen director_connection_reconnect(&conn, "Too long input line");
6cb8e7d726a7e9d157e87fb379982d52100b283fTimo Sirainen /* just read everything the remote sends, and wait for it
6cb8e7d726a7e9d157e87fb379982d52100b283fTimo Sirainen to disconnect. we mainly just want the remote to read the
6cb8e7d726a7e9d157e87fb379982d52100b283fTimo Sirainen CONNECT we sent it. */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen while ((line = i_stream_next_line(conn->input)) != NULL) {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen ret = director_connection_handle_line(conn, line);
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen director_connection_reconnect(&conn, t_strdup_printf(
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic void director_connection_send_directors(struct director_connection *conn,
8d63b0ab4dea920a4dd6a4469289950eef50a063Timo Sirainendirector_connection_send_hosts(struct director_connection *conn, string_t *str)
8d63b0ab4dea920a4dd6a4469289950eef50a063Timo Sirainen str_printfa(str, "HOST-HAND-START\t%u\n", conn->dir->ring_handshaked);
cf9d67e4a9bfee31cf3be05244555d51a3d1b9feTimo Sirainen array_foreach(mail_hosts_get(conn->dir->mail_hosts), hostp) {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen net_ip2addr(&(*hostp)->ip), (*hostp)->vhost_count);
8d63b0ab4dea920a4dd6a4469289950eef50a063Timo Sirainen str_printfa(str, "HOST-HAND-END\t%u\n", conn->dir->ring_handshaked);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic int director_connection_send_users(struct director_connection *conn)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen while ((user = user_directory_iter_next(conn->user_iter)) != NULL) {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (o_stream_get_buffer_used_size(conn->output) >= OUTBUF_FLUSH_THRESHOLD) {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if ((ret = o_stream_flush(conn->output)) <= 0) {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* continue later */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic int director_connection_output(struct director_connection *conn)
377dd19a90436b8f96902af741a3ea130bc3fe75Timo Sirainen /* still handshaking USER list */
b695e4700d0953031205ad4411182c4bb207605cTimo Sirainen o_stream_set_flush_pending(conn->output, TRUE);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainendirector_connection_init_common(struct director *dir, int fd)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen conn->input = i_stream_create_fd(conn->fd, MAX_INBUF_SIZE, FALSE);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen conn->output = o_stream_create_fd(conn->fd, MAX_OUTBUF_SIZE, FALSE);
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen conn->to_ping = timeout_add(DIRECTOR_CONNECTION_ME_TIMEOUT_MSECS,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic void director_connection_send_handshake(struct director_connection *conn)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen director_connection_send(conn, t_strdup_printf(
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen "ME\t%s\t%u\n",
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen DIRECTOR_VERSION_MAJOR, DIRECTOR_VERSION_MINOR,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen net_ip2addr(&conn->dir->self_ip), conn->dir->self_port));
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainendirector_connection_init_in(struct director *dir, int fd,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen conn = director_connection_init_common(dir, fd);
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen conn->name = i_strdup_printf("%s/in", net_ip2addr(ip));
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen conn->io = io_add(conn->fd, IO_READ, director_connection_input, conn);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic void director_connection_connected(struct director_connection *conn)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("director(%s): connect() failed: %s", conn->name,
a70216512b55a4f6d633defcead6fcb081b03c16Timo Sirainen conn->io = io_add(conn->fd, IO_READ, director_connection_input, conn);
3574bab52a67dfe1291f6306e707c6199e777043Timo Sirainen conn->to_ping = timeout_add(DIRECTOR_CONNECTION_SEND_USERS_TIMEOUT_MSECS,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen director_connection_send_directors(conn, str);
4fe3f07477bae6da3fb8d8fa9bab10ab82ada2bdTimo Sirainen conn->user_iter = user_directory_iter_init(dir->users);
f37684a44ce2371e04cf40ba3d26630fee4630f6Timo Sirainen if (director_connection_send_users(conn) == 0)
f37684a44ce2371e04cf40ba3d26630fee4630f6Timo Sirainen o_stream_set_flush_pending(conn->output, TRUE);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainendirector_connection_init_out(struct director *dir, int fd,
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen /* make sure we don't keep old sequence values across restarts */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen conn = director_connection_init_common(dir, fd);
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen conn->name = i_strdup_printf("%s/out", host->name);
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainenvoid director_connection_deinit(struct director_connection **_conn,
caae18c876f81e261350e4957471efa453c0ea9fTimo Sirainen struct director_connection *const *conns, *conn = *_conn;
caae18c876f81e261350e4957471efa453c0ea9fTimo Sirainen unsigned int i, count;
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen conn->minor_version >= DIRECTOR_VERSION_QUIT) {
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen o_stream_send_str(conn->output, t_strdup_printf(
caae18c876f81e261350e4957471efa453c0ea9fTimo Sirainen for (i = 0; i < count; i++) {
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen /* if there is already another handshaked incoming connection,
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen use it as the new "left" */
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen director_host_unref(conn->connect_request_to);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("close(director connection) failed: %m");
6ffc2a3e61636ba8ad50a3be260885bb6b041a3dTimo Sirainen master_service_client_connection_destroyed(master_service);
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen if (dir->left == NULL || dir->right == NULL) {
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen /* we aren't synced until we're again connected to a ring */
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainenvoid director_connection_disconnected(struct director_connection **_conn)
93cc8cf70b8df74972268d4b9ce25e9cb98ca40cTimo Sirainen if (conn->created + DIRECTOR_SUCCESS_MIN_CONNECT_SECS > ioloop_time &&
8c2b4a45f17a5cb13bb01058ca37798cf48d91baTimo Sirainen /* connection didn't exist for very long, assume it has a
8c2b4a45f17a5cb13bb01058ca37798cf48d91baTimo Sirainen network problem */
8c2b4a45f17a5cb13bb01058ca37798cf48d91baTimo Sirainen conn->host->last_network_failure = ioloop_time;
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainenstatic void director_connection_reconnect(struct director_connection **_conn,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenvoid director_connection_send(struct director_connection *conn,
1df39b899804fd1dbc560f75382364822935c857Timo Sirainen const char *const *lines = t_strsplit(data, "\n");
1df39b899804fd1dbc560f75382364822935c857Timo Sirainen dir_debug("output: %s: %s", conn->name, *lines);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("director(%s): write() failed: %m", conn->name);
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainendirector_connection_ping_idle_timeout(struct director_connection *conn)
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen i_error("director(%s): Ping timed out, disconnecting", conn->name);
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainenstatic void director_connection_pong_timeout(struct director_connection *conn)
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen i_error("director(%s): PONG reply not received although other "
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen "input keeps coming, disconnecting", conn->name);
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainenvoid director_connection_ping(struct director_connection *conn)
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen conn->to_ping = timeout_add(DIRECTOR_CONNECTION_PING_IDLE_TIMEOUT_MSECS,
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainen conn->to_pong = timeout_add(DIRECTOR_CONNECTION_PONG_TIMEOUT_MSECS,
242abe3ad2423776e9cf05e1304eb8fda4831b23Timo Sirainenconst char *director_connection_get_name(struct director_connection *conn)
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainendirector_connection_get_host(struct director_connection *conn)
377dd19a90436b8f96902af741a3ea130bc3fe75Timo Sirainenbool director_connection_is_handshaked(struct director_connection *conn)
caae18c876f81e261350e4957471efa453c0ea9fTimo Sirainenbool director_connection_is_incoming(struct director_connection *conn)
ee3362f3b78827a2c9a7e9ddee83f5a429c06213Timo Sirainendirector_connection_get_minor_version(struct director_connection *conn)
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainenvoid director_connection_cork(struct director_connection *conn)
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainenvoid director_connection_uncork(struct director_connection *conn)
f64b5bc9e73bedc63ba3c072c286542c29c69e43Timo Sirainenvoid director_connection_set_synced(struct director_connection *conn,
f64b5bc9e73bedc63ba3c072c286542c29c69e43Timo Sirainen /* switch ping timeout, unless we're already waiting for PONG */