bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2010-2018 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
5a530f778cc3eae05e70d13b8e5d7d501e7ba0b3Timo Sirainen/* Max time to wait for connect() to finish before aborting */
5a530f778cc3eae05e70d13b8e5d7d501e7ba0b3Timo Sirainen#define DIRECTOR_CONNECTION_CONNECT_TIMEOUT_MSECS (10*1000)
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,
5c8dec7f648cb0de2293a13265873baa640aa0cfTimo Sirainen or we'll disconnect. Use a slightly larger value than for _SEND_USERS_ so
5c8dec7f648cb0de2293a13265873baa640aa0cfTimo Sirainen that we'll get a better error if the sender decides to disconnect. */
5c8dec7f648cb0de2293a13265873baa640aa0cfTimo Sirainen#define DIRECTOR_CONNECTION_DONE_TIMEOUT_MSECS (40*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
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen/* Log a warning if PING reply or PONG response takes longer than this */
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen#define DIRECTOR_CONNECTION_PINGPONG_WARN_MSECS (5*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 */
edaa9f5c32594e7af8c0b862cd3a0c188d55ac7bTimo Sirainen/* If USER request doesn't have a timestamp, user isn't refreshed if it was
edaa9f5c32594e7af8c0b862cd3a0c188d55ac7bTimo Sirainen already refreshed director_user_expire/4 seconds ago. This value is the
edaa9f5c32594e7af8c0b862cd3a0c188d55ac7bTimo Sirainen hardcoded maximum for that value. */
edaa9f5c32594e7af8c0b862cd3a0c188d55ac7bTimo Sirainen#define DIRECTOR_SKIP_RECENT_REFRESH_MAX_SECS 15
d8a93844f0b25be82da9c0ab79c321c110f5fb5fTimo Sirainen#define DIRECTOR_RECONNECT_AFTER_WRONG_CONNECT_MSECS 1000
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen#define DIRECTOR_HANDSHAKE_BYTES_LOG_MIN_SECS (60*30)
dfbd56c81cb8fb7fe70393c4682cc99e68fe06b6Timo Sirainen/* If we receive SYNCs with a timestamp this many seconds higher than the last
dfbd56c81cb8fb7fe70393c4682cc99e68fe06b6Timo Sirainen valid received SYNC timestamp, assume that we lost the director's restart
dfbd56c81cb8fb7fe70393c4682cc99e68fe06b6Timo Sirainen notification and reset the last_sync_seq */
dfbd56c81cb8fb7fe70393c4682cc99e68fe06b6Timo Sirainen#define DIRECTOR_SYNC_STALE_TIMESTAMP_RESET_SECS (60*2)
ad394c531ca4b7ca85f3741fa01f96c36c008c70Timo Sirainen/* How many USER entries to send during handshake before going back to ioloop
ad394c531ca4b7ca85f3741fa01f96c36c008c70Timo Sirainen to see if there's other work to be done as well. */
ad394c531ca4b7ca85f3741fa01f96c36c008c70Timo Sirainen#define DIRECTOR_HANDSHAKE_MAX_USERS_SENT_PER_FLUSH 10000
05b7b8f14426b5fe5d016940eb5916033f0bc841Timo Sirainen#define CMD_IS_USER_HANDSHAKE(minor_version, args) \
05b7b8f14426b5fe5d016940eb5916033f0bc841Timo Sirainen ((minor_version) < DIRECTOR_VERSION_HANDSHAKE_U_CMD && \
eb209d12e3b1cfed564c35cf19fdb1bf7fcc6811Timo Sirainen#define DIRECTOR_OPT_CONSISTENT_HASHING "consistent-hashing"
7fd22d5521c8ecf84c40fbef553e70bf2553a663Timo Sirainen struct timeval created, connected_time, me_received_time;
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen uoff_t ping_sent_input_offset, ping_sent_output_offset;
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;
58a102564ba58e2713e02ab86054978eb611cb81Timo Sirainen unsigned int users_received, handshake_users_received;
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainen /* set during command execution */
312213260e384239ac93c77951c2f1f5f3d3611eTimo Sirainenstatic bool director_connection_unref(struct director_connection *conn);
a9ade104616bbb81c34cc6f8bfde5dab0571afacTimo Sirainenstatic void director_finish_sending_handshake(struct director_connection *conn);
d3ee83b4c24529fac4be5c1f30e254295e7addd9Timo Sirainenstatic void director_connection_disconnected(struct director_connection **conn,
d3ee83b4c24529fac4be5c1f30e254295e7addd9Timo Sirainen const char *reason);
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainenstatic void director_connection_reconnect(struct director_connection **conn,
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen const char *reason);
d3ee83b4c24529fac4be5c1f30e254295e7addd9Timo Sirainendirector_connection_log_disconnect(struct director_connection *conn, int err,
d3ee83b4c24529fac4be5c1f30e254295e7addd9Timo Sirainen const char *errstr);
eb209d12e3b1cfed564c35cf19fdb1bf7fcc6811Timo Sirainenstatic int director_connection_send_done(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,
34115224152b94328ffe3ec4ff4f30927c8f9aa1Timo Sirainen conn->host->last_protocol_failure = ioloop_time;
1ef9754a5169dc886d15089e59b45a7017d647d7Timo Sirainendirector_connection_append_stats(struct director_connection *conn, string_t *str)
1ef9754a5169dc886d15089e59b45a7017d647d7Timo Sirainen int input_msecs = timeval_diff_msecs(&ioloop_timeval, &conn->last_input);
1ef9754a5169dc886d15089e59b45a7017d647d7Timo Sirainen int output_msecs = timeval_diff_msecs(&ioloop_timeval, &conn->last_output);
1ef9754a5169dc886d15089e59b45a7017d647d7Timo Sirainen int connected_msecs = timeval_diff_msecs(&ioloop_timeval, &conn->connected_time);
8a513c80e95a51c29f5af5c702fbf71ecbad41f4Timo Sirainen str_printfa(str, "bytes in=%"PRIuUOFF_T", bytes out=%"PRIuUOFF_T,
58a102564ba58e2713e02ab86054978eb611cb81Timo Sirainen conn->handshake_users_received, conn->users_received);
e5ef6fa616e0b0ed1bf545b39f0d9e8dea48c970Timo Sirainen str_printfa(str, ", %u USERs sent in handshake",
8a513c80e95a51c29f5af5c702fbf71ecbad41f4Timo Sirainen str_printfa(str, ", last input %u.%03u s ago",
8a513c80e95a51c29f5af5c702fbf71ecbad41f4Timo Sirainen str_printfa(str, ", last output %u.%03u s ago",
1ef9754a5169dc886d15089e59b45a7017d647d7Timo Sirainen if (o_stream_get_buffer_used_size(conn->output) > 0) {
1ef9754a5169dc886d15089e59b45a7017d647d7Timo Sirainen str_printfa(str, ", %"PRIuSIZE_T" bytes in output buffer",
ef996cb0e68ea5474716058137308a98cfa0c324Timo Sirainen str_printfa(str, ", %zu peak output buffer size",
f534eb971e3e40087ad81dcd080c1d69424df417Timo Sirainen /* this isn't measuring the CPU usage used by the connection
f534eb971e3e40087ad81dcd080c1d69424df417Timo Sirainen itself, but it can still be a useful measurement */
f534eb971e3e40087ad81dcd080c1d69424df417Timo Sirainen int diff = timeval_diff_msecs(&usage.ru_utime,
f534eb971e3e40087ad81dcd080c1d69424df417Timo Sirainen str_printfa(str, ", %d.%03d CPU secs since connected",
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainendirector_connection_init_timeout(struct director_connection *conn)
1ef9754a5169dc886d15089e59b45a7017d647d7Timo Sirainen str_append(reason, "Handshaking ME timed out");
1ef9754a5169dc886d15089e59b45a7017d647d7Timo Sirainen str_append(reason, "Sending handshake timed out");
1ef9754a5169dc886d15089e59b45a7017d647d7Timo Sirainen str_append(reason, "Handshaking DONE timed out");
1ef9754a5169dc886d15089e59b45a7017d647d7Timo Sirainen int msecs = timeval_diff_msecs(&ioloop_timeval, &start_time);
1ef9754a5169dc886d15089e59b45a7017d647d7Timo Sirainen str_printfa(reason, " (%u.%03u secs, ", msecs/1000, msecs%1000);
1ef9754a5169dc886d15089e59b45a7017d647d7Timo Sirainen director_connection_append_stats(conn, reason);
1ef9754a5169dc886d15089e59b45a7017d647d7Timo Sirainen i_error("director(%s): %s", conn->name, str_c(reason));
d3ee83b4c24529fac4be5c1f30e254295e7addd9Timo Sirainen director_connection_disconnected(&conn, "Handshake timeout");
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainendirector_connection_set_ping_timeout(struct director_connection *conn)
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)
d3ee83b4c24529fac4be5c1f30e254295e7addd9Timo Sirainen director_connection_log_disconnect(conn, ETIMEDOUT, "");
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)
67cb14c7fb54a031818228522dc7255d5cd00f0aTimo Sirainenstatic void director_send_delayed_syncs(struct director *dir)
67cb14c7fb54a031818228522dc7255d5cd00f0aTimo Sirainen dir_debug("director(%s): Sending delayed SYNCs", dir->right->name);
67cb14c7fb54a031818228522dc7255d5cd00f0aTimo Sirainen director_sync_send(dir, *hostp, (*hostp)->delayed_sync_seq,
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 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)",
9054b5f92a7e5666c6beaa04916699a1408bf021Timo Sirainen director_cmd_error(conn, "Invalid ME timestamp");
9054b5f92a7e5666c6beaa04916699a1408bf021Timo Sirainen if (diff > DIRECTOR_MAX_CLOCK_DIFF_WARN_SECS ||
9054b5f92a7e5666c6beaa04916699a1408bf021Timo Sirainen (diff < 0 && -diff > DIRECTOR_MAX_CLOCK_DIFF_WARN_SECS)) {
9054b5f92a7e5666c6beaa04916699a1408bf021Timo Sirainen i_warning("Director %s clock differs from ours by %d secs",
5a530f778cc3eae05e70d13b8e5d7d501e7ba0b3Timo Sirainen conn->to_ping = timeout_add(DIRECTOR_CONNECTION_DONE_TIMEOUT_MSECS,
5a530f778cc3eae05e70d13b8e5d7d501e7ba0b3Timo Sirainen conn->to_ping = timeout_add(DIRECTOR_CONNECTION_SEND_USERS_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);
edaa9f5c32594e7af8c0b862cd3a0c188d55ac7bTimo Sirainenstatic inline bool
edaa9f5c32594e7af8c0b862cd3a0c188d55ac7bTimo Sirainenuser_need_refresh(struct director *dir, struct user *user,
edaa9f5c32594e7af8c0b862cd3a0c188d55ac7bTimo Sirainen /* we already have this timestamp */
edaa9f5c32594e7af8c0b862cd3a0c188d55ac7bTimo Sirainen /* Old director sent USER command without timestamp. We don't
edaa9f5c32594e7af8c0b862cd3a0c188d55ac7bTimo Sirainen know what it is exactly, but we can assume that it's very
edaa9f5c32594e7af8c0b862cd3a0c188d55ac7bTimo Sirainen close to the current time (which timestamp parameter is
edaa9f5c32594e7af8c0b862cd3a0c188d55ac7bTimo Sirainen already set to). However, try to break USER loops here when
edaa9f5c32594e7af8c0b862cd3a0c188d55ac7bTimo Sirainen director ring latency is >1sec, but below skip_recent_secs
edaa9f5c32594e7af8c0b862cd3a0c188d55ac7bTimo Sirainen by just not refreshing the user. */
edaa9f5c32594e7af8c0b862cd3a0c188d55ac7bTimo Sirainen if ((time_t)user->timestamp + skip_recent_secs >= timestamp)
4f2b533808371b3b8a8187819cd4c3d90e1ca8eeTimo Sirainendirector_user_refresh(struct director_connection *conn,
4f2b533808371b3b8a8187819cd4c3d90e1ca8eeTimo Sirainen unsigned int username_hash, struct mail_host *host,
edaa9f5c32594e7af8c0b862cd3a0c188d55ac7bTimo Sirainen bool unknown_timestamp = (timestamp == (time_t)-1);
edaa9f5c32594e7af8c0b862cd3a0c188d55ac7bTimo Sirainen /* Old director version sent USER without timestamp. */
154f91726624265fce15097eb4bbbf6e55f8c477Timo Sirainen if (timestamp + (time_t)dir->set->director_user_expire <= ioloop_time) {
0bd3eb7c8139cfb80f46dc596c5fe7cdbb1d2aeeTimo Sirainen /* Ignore this refresh entirely, regardless of whether the
0bd3eb7c8139cfb80f46dc596c5fe7cdbb1d2aeeTimo Sirainen user already exists or not. */
154f91726624265fce15097eb4bbbf6e55f8c477Timo Sirainen dir_debug("user refresh: %u has expired timestamp %"PRIdTIME_T,
68d87d8fb8f23ffed031ddfd9c410f3c929777faTimo Sirainen user = user_directory_lookup(users, username_hash);
68d87d8fb8f23ffed031ddfd9c410f3c929777faTimo Sirainen *user_r = user_directory_add(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 &&
68d87d8fb8f23ffed031ddfd9c410f3c929777faTimo Sirainen !user_directory_user_is_recently_updated(users, user)) {
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen /* mark the user as weak */
1df39b899804fd1dbc560f75382364822935c857Timo Sirainen dir_debug("user refresh: %u set weak", username_hash);
c6a5305674d2aa59ee52dc101ef87bbcb04f04efTimo Sirainen } else if (weak) {
c6a5305674d2aa59ee52dc101ef87bbcb04f04efTimo Sirainen dir_debug("user refresh: %u weak update to %s ignored, "
c6a5305674d2aa59ee52dc101ef87bbcb04f04efTimo Sirainen "we recently changed it to %s",
c6a5305674d2aa59ee52dc101ef87bbcb04f04efTimo Sirainen /* update to the same host */
68d87d8fb8f23ffed031ddfd9c410f3c929777faTimo Sirainen } else if (user_directory_user_is_near_expiring(users, user)) {
c6a5305674d2aa59ee52dc101ef87bbcb04f04efTimo Sirainen /* host conflict for a user that is already near expiring. we can
c6a5305674d2aa59ee52dc101ef87bbcb04f04efTimo Sirainen assume that the other director had already dropped this user
c6a5305674d2aa59ee52dc101ef87bbcb04f04efTimo Sirainen and we should have as well. use the new host. */
c6a5305674d2aa59ee52dc101ef87bbcb04f04efTimo Sirainen dir_debug("user refresh: %u is nearly expired, "
5c1733e9e572e242598b8b2f12a0068897caf5b7Timo Sirainen /* user is still being moved - ignore conflicting host updates
5c1733e9e572e242598b8b2f12a0068897caf5b7Timo Sirainen from other directors who don't yet know about the move. */
5c1733e9e572e242598b8b2f12a0068897caf5b7Timo Sirainen "preserve its host %s instead of replacing with %s",
bd4bbe6478a97e3fab77b05257dd1397c7c090eaTimo Sirainen username_hash, user->host->ip_str, host->ip_str);
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",
bd4bbe6478a97e3fab77b05257dd1397c7c090eaTimo Sirainen username_hash, user->host->ip_str, host->ip_str);
4f2b533808371b3b8a8187819cd4c3d90e1ca8eeTimo Sirainen str_printfa(str, " (old_ts=%ld", (long)user->timestamp);
1b7cd57585d8c2f133dd612d2d5d9c775595659fTimo Sirainen user_kill_state_names[user->kill_ctx->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
fb9dfa9ea15abdbf248021cfb7bf2846410116e6Timo Sirainen keep track of the user for more than one host.
fb9dfa9ea15abdbf248021cfb7bf2846410116e6Timo Sirainen send the updated USER back to the sender as well. */
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen /* keep the host */
36e091dc733c6cd690c5aae6e411e41adb1eca73Timo Sirainen /* especially IMAP connections can take a long time to die.
36e091dc733c6cd690c5aae6e411e41adb1eca73Timo Sirainen make sure we kill off the connections in the wrong
36e091dc733c6cd690c5aae6e411e41adb1eca73Timo Sirainen director_kick_user_hash(dir, dir->self_host, NULL,
b3e36790ca9e16d022118012b46ed50f73a45046Timo Sirainen /* Update user's timestamp if it's higher than the current one. Note
b3e36790ca9e16d022118012b46ed50f73a45046Timo Sirainen that we'll preserve the original timestamp. This is important when
b3e36790ca9e16d022118012b46ed50f73a45046Timo Sirainen the director ring is slow and a single USER can traverse through
b3e36790ca9e16d022118012b46ed50f73a45046Timo Sirainen the ring more than a second. We don't want to get into a loop where
b3e36790ca9e16d022118012b46ed50f73a45046Timo Sirainen the same USER goes through the ring forever. */
edaa9f5c32594e7af8c0b862cd3a0c188d55ac7bTimo Sirainen if (user_need_refresh(dir, user, timestamp, unknown_timestamp)) {
b3e36790ca9e16d022118012b46ed50f73a45046Timo Sirainen /* NOTE: This makes the users list somewhat out-of-order.
b3e36790ca9e16d022118012b46ed50f73a45046Timo Sirainen It's not a big problem - most likely it's only a few seconds
b3e36790ca9e16d022118012b46ed50f73a45046Timo Sirainen difference. The worst that can happen is that some users
b3e36790ca9e16d022118012b46ed50f73a45046Timo Sirainen take up memory that should have been freed already. */
b3e36790ca9e16d022118012b46ed50f73a45046Timo Sirainen dir_debug("user refresh: %u refreshed timestamp from %u to %"PRIdTIME_T,
b3e36790ca9e16d022118012b46ed50f73a45046Timo Sirainen dir_debug("user refresh: %u ignored timestamp %"PRIdTIME_T" (we have %u)",
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",
94fb15fffc814d7dcecabf8d90691502311d4b88Timo Sirainen /* The other director's clock seems to be into the future
94fb15fffc814d7dcecabf8d90691502311d4b88Timo Sirainen compared to us. Don't set any of our users' timestamps into
94fb15fffc814d7dcecabf8d90691502311d4b88Timo Sirainen future though. It's most likely only 1 second difference. */
0bd3eb7c8139cfb80f46dc596c5fe7cdbb1d2aeeTimo Sirainen if (director_user_refresh(conn, username_hash, host,
0bd3eb7c8139cfb80f46dc596c5fe7cdbb1d2aeeTimo Sirainen /* user expired - ignore */
94fb15fffc814d7dcecabf8d90691502311d4b88Timo Sirainen /* Possibilities:
94fb15fffc814d7dcecabf8d90691502311d4b88Timo Sirainen a) The user didn't exist yet, and it was added with the given
94fb15fffc814d7dcecabf8d90691502311d4b88Timo Sirainen b) The user existed, but with an older timestamp. The timestamp
94fb15fffc814d7dcecabf8d90691502311d4b88Timo Sirainen wasn't yet updated, so do it here below.
94fb15fffc814d7dcecabf8d90691502311d4b88Timo Sirainen c) The user existed with a newer timestamp. This is either because
94fb15fffc814d7dcecabf8d90691502311d4b88Timo Sirainen we already received a non-handshake USER update for this user, or
94fb15fffc814d7dcecabf8d90691502311d4b88Timo Sirainen our director saw a login for this user. Ignore this update.
94fb15fffc814d7dcecabf8d90691502311d4b88Timo Sirainen (We never want to change the user's timestamp to be older, because
94fb15fffc814d7dcecabf8d90691502311d4b88Timo Sirainen that could result in directors going to a loop fighting each others
94fb15fffc814d7dcecabf8d90691502311d4b88Timo Sirainen over a flipping timestamp.) */
94fb15fffc814d7dcecabf8d90691502311d4b88Timo Sirainen /* always sort users after handshaking to make sure the order
94fb15fffc814d7dcecabf8d90691502311d4b88Timo Sirainen is correct */
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainendirector_cmd_user(struct director_connection *conn,
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen const char *const *args)
b3e36790ca9e16d022118012b46ed50f73a45046Timo Sirainen (args[2] != NULL && str_to_time(args[2], ×tamp) < 0)) {
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainen director_cmd_error(conn, "Invalid parameters");
dd19de9b6382c9a47b65df6b2396789df37a19fbTimo Sirainen /* could this before it's potentially ignored */
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,
0bd3eb7c8139cfb80f46dc596c5fe7cdbb1d2aeeTimo Sirainen host, timestamp, FALSE, &forced, &user) > 0) {
0bd3eb7c8139cfb80f46dc596c5fe7cdbb1d2aeeTimo Sirainen /* user changed - forward the USER in the ring */
fb9dfa9ea15abdbf248021cfb7bf2846410116e6Timo Sirainen director_update_user(conn->dir, src_host, user);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic bool director_cmd_director(struct director_connection *conn,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen const char *const *args)
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
e588e0b3f3119ba2cc98b338ee9868f8f0297f49Timo Sirainen timestamp, since it might be up now, but only if this
e588e0b3f3119ba2cc98b338ee9868f8f0297f49Timo Sirainen isn't part of the handshake. (if it was, reseting the
e588e0b3f3119ba2cc98b338ee9868f8f0297f49Timo Sirainen timestamp could cause us to rapidly keep trying to connect
88cb1595bf0ad84846b9c611a1afbca35fcfe460Timo Sirainen /* it also may have been restarted, reset its state */
c9b08dc8d71bd655e5648daf8a09ff4b728cae81Timo Sirainen /* save the director and forward it */
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainen host = director_host_add(conn->dir, &ip, port);
88cb1595bf0ad84846b9c611a1afbca35fcfe460Timo Sirainen /* just forward this to the entire ring until it reaches back to
88cb1595bf0ad84846b9c611a1afbca35fcfe460Timo Sirainen itself. some hosts may see this twice, but that's the only way to
19557f192d37cd54a1a090a8a26d9d47265e4413Aki Tuomi guarantee that it gets seen by everyone. resetting the host multiple
88cb1595bf0ad84846b9c611a1afbca35fcfe460Timo Sirainen times may cause us to handle its commands multiple times, but the
336cdc9993838d967bbaf214a671975c2e7e5942Timo Sirainen commands can handle that. however, we need to also handle a
336cdc9993838d967bbaf214a671975c2e7e5942Timo Sirainen situation where the added director never comes back - we don't want
336cdc9993838d967bbaf214a671975c2e7e5942Timo Sirainen to send the director information in a loop forever. */
336cdc9993838d967bbaf214a671975c2e7e5942Timo Sirainen director_host_cmp_to_self(host, conn->dir->right->host,
336cdc9993838d967bbaf214a671975c2e7e5942Timo Sirainen dir_debug("Received DIRECTOR update for a host where we should be connected to. "
336cdc9993838d967bbaf214a671975c2e7e5942Timo Sirainen "Not forwarding it since it's probably crashed.");
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainenstatic bool director_cmd_director_remove(struct director_connection *conn,
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainen const char *const *args)
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");
23bdbb7b1831785c6ba6df190f6369da882d2b9dTimo Sirainen if (remote_ring_completed != 0 && !conn->dir->ring_handshaked) {
8d63b0ab4dea920a4dd6a4469289950eef50a063Timo Sirainen /* clear everything we have and use only what remote sends us */
0874d7a4fa15322318c71291f0134ff7cc49bbb8Timo Sirainen dir_debug("%s: We're joining a ring - replace all hosts",
cf9d67e4a9bfee31cf3be05244555d51a3d1b9feTimo Sirainen hosts = mail_hosts_get(conn->dir->mail_hosts);
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen director_remove_host(conn->dir, NULL, NULL, *hostp);
23bdbb7b1831785c6ba6df190f6369da882d2b9dTimo Sirainen } else if (remote_ring_completed == 0 && conn->dir->ring_handshaked) {
8d63b0ab4dea920a4dd6a4469289950eef50a063Timo Sirainen /* ignore whatever remote sends */
0874d7a4fa15322318c71291f0134ff7cc49bbb8Timo Sirainen dir_debug("%s: Merge rings' hosts", conn->name);
0874d7a4fa15322318c71291f0134ff7cc49bbb8Timo Sirainendirector_cmd_is_seen_full(struct director_connection *conn,
0874d7a4fa15322318c71291f0134ff7cc49bbb8Timo Sirainen const char *const **_args, unsigned int *seq_r,
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 */
0874d7a4fa15322318c71291f0134ff7cc49bbb8Timo Sirainendirector_cmd_is_seen(struct director_connection *conn,
0874d7a4fa15322318c71291f0134ff7cc49bbb8Timo Sirainen const char *const **_args,
0874d7a4fa15322318c71291f0134ff7cc49bbb8Timo Sirainen return director_cmd_is_seen_full(conn, _args, &seq, host_r);
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainendirector_cmd_user_weak(struct director_connection *conn,
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen const char *const *args)
fb9dfa9ea15abdbf248021cfb7bf2846410116e6Timo Sirainen bool weak = TRUE, weak_forward = FALSE, forced;
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)
dd19de9b6382c9a47b65df6b2396789df37a19fbTimo Sirainen /* could this before it's potentially ignored */
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. */
f8c57c39abd8d987fdbc90dff5289b420017a700Timo Sirainen /* First time we're seeing this - forward it to others also.
f8c57c39abd8d987fdbc90dff5289b420017a700Timo Sirainen We'll want to do it even if the user was already marked as
f8c57c39abd8d987fdbc90dff5289b420017a700Timo Sirainen weak, because otherwise if two directors mark the user weak
f8c57c39abd8d987fdbc90dff5289b420017a700Timo Sirainen at the same time both the USER-WEAK notifications reach
f8c57c39abd8d987fdbc90dff5289b420017a700Timo Sirainen only half the directors until they collide and neither one
f8c57c39abd8d987fdbc90dff5289b420017a700Timo Sirainen finishes going through the whole ring marking the user
f8c57c39abd8d987fdbc90dff5289b420017a700Timo Sirainen } else if (dir_host == conn->dir->self_host) {
c6a5305674d2aa59ee52dc101ef87bbcb04f04efTimo Sirainen /* We originated this USER-WEAK request. The entire ring has seen
c6a5305674d2aa59ee52dc101ef87bbcb04f04efTimo Sirainen it and there weren't any conflicts. Make the user non-weak. */
c6a5305674d2aa59ee52dc101ef87bbcb04f04efTimo Sirainen dir_debug("user refresh: %u Our USER-WEAK seen by the entire ring",
c6a5305674d2aa59ee52dc101ef87bbcb04f04efTimo Sirainen /* The original USER-WEAK sender will send a new non-weak USER
c6a5305674d2aa59ee52dc101ef87bbcb04f04efTimo Sirainen update saying what really happened. We'll still need to forward
c6a5305674d2aa59ee52dc101ef87bbcb04f04efTimo Sirainen this around the ring to the origin so it also knows it has
c6a5305674d2aa59ee52dc101ef87bbcb04f04efTimo Sirainen travelled through the ring. */
c6a5305674d2aa59ee52dc101ef87bbcb04f04efTimo Sirainen dir_debug("user refresh: %u Remote USER-WEAK from %s seen by the entire ring, ignoring",
0bd3eb7c8139cfb80f46dc596c5fe7cdbb1d2aeeTimo Sirainen ret = director_user_refresh(conn, username_hash,
0bd3eb7c8139cfb80f46dc596c5fe7cdbb1d2aeeTimo Sirainen /* user is refreshed with ioloop_time, it can't be expired already */
0bd3eb7c8139cfb80f46dc596c5fe7cdbb1d2aeeTimo Sirainen /* user changed, or we've decided that we need to forward
0bd3eb7c8139cfb80f46dc596c5fe7cdbb1d2aeeTimo Sirainen the weakness notification to the rest of the ring even
0bd3eb7c8139cfb80f46dc596c5fe7cdbb1d2aeeTimo Sirainen though we already knew it. */
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen director_update_user(conn->dir, src_host, user);
ab779efe68458cf6fdcaa4f99527685d5563df0aTimo Sirainen director_update_user_weak(conn->dir, src_host, conn,
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainendirector_cmd_host_int(struct director_connection *conn, const char *const *args,
093b42b11c1236a687d3da564b26a324e2189ae6Timo Sirainen const char *tag = "", *host_tag, *hostname = NULL;
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainen director_cmd_error(conn, "Invalid parameters");
c5279d575d40f7c4c6cd4b44dbf8fba55e156d90Timo Sirainen if ((args[3][0] != 'D' && args[3][0] != 'U') ||
c5279d575d40f7c4c6cd4b44dbf8fba55e156d90Timo Sirainen str_to_time(args[3]+1, &last_updown_change) < 0) {
c5279d575d40f7c4c6cd4b44dbf8fba55e156d90Timo Sirainen director_cmd_error(conn, "Invalid updown parameters");
8d63b0ab4dea920a4dd6a4469289950eef50a063Timo Sirainen /* remote is sending hosts in a handshake, but it doesn't have
8d63b0ab4dea920a4dd6a4469289950eef50a063Timo Sirainen a completed ring and we do. */
ae32667c54480d329eed994b3defab89cd76c077Timo Sirainen if (tag[0] != '\0' && conn->minor_version < DIRECTOR_VERSION_TAGS_V2) {
ae32667c54480d329eed994b3defab89cd76c077Timo Sirainen director_cmd_error(conn, "Received a host tag from older director version with incompatible tagging support");
cf9d67e4a9bfee31cf3be05244555d51a3d1b9feTimo Sirainen host = mail_host_lookup(conn->dir->mail_hosts, &ip);
9de5eb9e1ac3a07c4197a60fdefd412d6cc78eb2Timo Sirainen host = mail_host_add_hostname(conn->dir->mail_hosts,
09060303d565e15d54e42b4ef722f9d3c26f5336Timo Sirainen i_error("director(%s): Host %s changed tag from '%s' to '%s'",
78a6431465ac6ee6e870352a68ea1d7a8170376aTimo Sirainen str_printfa(str, "director(%s): Host %s is being updated before previous update had finished (",
12f8ffba02e99b53ee71cd44f9947c6a59ff9ea4Timo Sirainen host->last_updown_change > last_updown_change) {
12f8ffba02e99b53ee71cd44f9947c6a59ff9ea4Timo Sirainen /* our host has a newer change. preserve it. */
8752573c44bcd139ae3ecc6d8e917c2c60bcb89fTimo Sirainen vhost_count = I_MIN(vhost_count, host->vhost_count);
8752573c44bcd139ae3ecc6d8e917c2c60bcb89fTimo Sirainen last_updown_change = I_MAX(last_updown_change,
78a6431465ac6ee6e870352a68ea1d7a8170376aTimo Sirainen str_printfa(str, "setting to state=%s vhosts=%u",
8752573c44bcd139ae3ecc6d8e917c2c60bcb89fTimo Sirainen /* make the change appear to come from us, so it
8752573c44bcd139ae3ecc6d8e917c2c60bcb89fTimo Sirainen reaches the full ring */
b3abfd0ac256ffa0cbfd74c3793eac4e83d41f78Martti Rannanjärvi const char *log_prefix = t_strdup_printf("director(%s): ",
b3abfd0ac256ffa0cbfd74c3793eac4e83d41f78Martti Rannanjärvi mail_host_set_down(host, down, last_updown_change, log_prefix);
b3abfd0ac256ffa0cbfd74c3793eac4e83d41f78Martti Rannanjärvi mail_host_set_vhost_count(host, vhost_count, log_prefix);
8752573c44bcd139ae3ecc6d8e917c2c60bcb89fTimo Sirainen director_update_host(conn->dir, src_host, dir_host, host);
1574df6b0bc965212f1152e480e7a762cdaa8226Timo Sirainen dir_debug("Ignoring host %s update vhost_count=%u "
1574df6b0bc965212f1152e480e7a762cdaa8226Timo Sirainen "down=%d last_updown_change=%ld (hosts_hash=%u)",
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,
550d2fe097e95f12e8fa60ef52753ea7fe53d4eaTimo Sirainendirector_cmd_user_kick(struct director_connection *conn,
550d2fe097e95f12e8fa60ef52753ea7fe53d4eaTimo Sirainen const char *const *args)
550d2fe097e95f12e8fa60ef52753ea7fe53d4eaTimo Sirainen if ((ret = director_cmd_is_seen(conn, &args, &dir_host)) != 0)
550d2fe097e95f12e8fa60ef52753ea7fe53d4eaTimo Sirainen director_cmd_error(conn, "Invalid parameters");
550d2fe097e95f12e8fa60ef52753ea7fe53d4eaTimo Sirainen director_kick_user(conn->dir, conn->host, dir_host, args[0]);
bfef6891565ff9018ac92add6eae401e9352c657Timo Sirainendirector_cmd_user_kick_alt(struct director_connection *conn,
bfef6891565ff9018ac92add6eae401e9352c657Timo Sirainen const char *const *args)
bfef6891565ff9018ac92add6eae401e9352c657Timo Sirainen if ((ret = director_cmd_is_seen(conn, &args, &dir_host)) != 0)
bfef6891565ff9018ac92add6eae401e9352c657Timo Sirainen director_cmd_error(conn, "Invalid parameters");
bfef6891565ff9018ac92add6eae401e9352c657Timo Sirainen director_kick_user_alt(conn->dir, conn->host, dir_host, args[0], args[1]);
36e091dc733c6cd690c5aae6e411e41adb1eca73Timo Sirainendirector_cmd_user_kick_hash(struct director_connection *conn,
36e091dc733c6cd690c5aae6e411e41adb1eca73Timo Sirainen const char *const *args)
36e091dc733c6cd690c5aae6e411e41adb1eca73Timo Sirainen if ((ret = director_cmd_is_seen(conn, &args, &dir_host)) != 0)
36e091dc733c6cd690c5aae6e411e41adb1eca73Timo Sirainen director_cmd_error(conn, "Invalid parameters");
36e091dc733c6cd690c5aae6e411e41adb1eca73Timo Sirainen director_kick_user_hash(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)
0874d7a4fa15322318c71291f0134ff7cc49bbb8Timo Sirainen if ((ret = director_cmd_is_seen_full(conn, &args, &seq, &dir_host)) < 0)
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainen director_cmd_error(conn, "Invalid parameters");
0874d7a4fa15322318c71291f0134ff7cc49bbb8Timo Sirainen dir_debug("User %u - ignoring already seen USER-KILLED-EVERYWHERE "
0874d7a4fa15322318c71291f0134ff7cc49bbb8Timo Sirainen "with seq=%u <= %s.last_seq=%u", username_hash,
15ab2452b0220a115f4351ad9d7fd5ec70ae7966Timo Sirainen director_user_killed_everywhere(conn->dir, conn->host,
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainenstatic bool director_handshake_cmd_done(struct director_connection *conn)
7fd22d5521c8ecf84c40fbef553e70bf2553a663Timo Sirainen int handshake_msecs = timeval_diff_msecs(&ioloop_timeval, &conn->connected_time);
8621be3846dc097420cce325ad36d1b646f72a09Timo Sirainen if (conn->users_unsorted && conn->user_iter == NULL) {
8621be3846dc097420cce325ad36d1b646f72a09Timo Sirainen /* we sent our user list before receiving remote's */
1ef9754a5169dc886d15089e59b45a7017d647d7Timo Sirainen str_printfa(str, "director(%s): Handshake finished in %u.%03u secs (",
1ef9754a5169dc886d15089e59b45a7017d647d7Timo Sirainen conn->name, handshake_msecs/1000, handshake_msecs%1000);
7fd22d5521c8ecf84c40fbef553e70bf2553a663Timo Sirainen if (handshake_msecs >= DIRECTOR_HANDSHAKE_WARN_SECS*1000)
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);
eb209d12e3b1cfed564c35cf19fdb1bf7fcc6811Timo Sirainendirector_handshake_cmd_options(struct director_connection *conn,
eb209d12e3b1cfed564c35cf19fdb1bf7fcc6811Timo Sirainen const char *const *args)
eb209d12e3b1cfed564c35cf19fdb1bf7fcc6811Timo Sirainen unsigned int i;
eb209d12e3b1cfed564c35cf19fdb1bf7fcc6811Timo Sirainen if (strcmp(args[i], DIRECTOR_OPT_CONSISTENT_HASHING) == 0)
00cc7eb569710722af0e0af652034b9fc22c57dfMartti Rannanjärvi i_error("director(%s): director_consistent_hashing settings "
00cc7eb569710722af0e0af652034b9fc22c57dfMartti Rannanjärvi "differ between directors. Set "
00cc7eb569710722af0e0af652034b9fc22c57dfMartti Rannanjärvi "director_consistent_hashing=yes on old directors",
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)",
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch } else if (str_to_uint(args[1], &major_version) < 0 ||
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch str_to_uint(args[2], &conn->minor_version) < 0) {
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch i_error("director(%s): Invalid protocol version: "
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch } else if (major_version != DIRECTOR_VERSION_MAJOR) {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("director(%s): Incompatible protocol version: "
ae32667c54480d329eed994b3defab89cd76c077Timo Sirainen if (conn->minor_version < DIRECTOR_VERSION_TAGS_V2 &&
ae32667c54480d329eed994b3defab89cd76c077Timo Sirainen mail_hosts_have_tags(conn->dir->mail_hosts)) {
ae32667c54480d329eed994b3defab89cd76c077Timo Sirainen i_error("director(%s): Director version supports incompatible tags", conn->name);
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");
eb209d12e3b1cfed564c35cf19fdb1bf7fcc6811Timo Sirainen return director_handshake_cmd_options(conn, args);
377dd19a90436b8f96902af741a3ea130bc3fe75Timo Sirainen "Host list is only for incoming connections");
377dd19a90436b8f96902af741a3ea130bc3fe75Timo Sirainen return director_cmd_host_hand_start(conn, args) ? 1 : -1;
05b7b8f14426b5fe5d016940eb5916033f0bc841Timo Sirainen CMD_IS_USER_HANDSHAKE(conn->minor_version, 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,
abe29107f5dce932d28a00912d2d75a01021bef1Timo Sirainen unsigned int timestamp, unsigned int hosts_hash)
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);
abe29107f5dce932d28a00912d2d75a01021bef1Timo Sirainen hosts_hash != mail_hosts_hash(conn->dir->mail_hosts)) {
abe29107f5dce932d28a00912d2d75a01021bef1Timo Sirainen i_error("director(%s): Hosts unexpectedly changed during SYNC reply - resending"
abe29107f5dce932d28a00912d2d75a01021bef1Timo Sirainen "(seq=%u, old hosts_hash=%u, new hosts_hash=%u)",
242abe3ad2423776e9cf05e1304eb8fda4831b23Timo Sirainen /* the ring is handshaked */
fe201fb1819622ab33c51ef5a0a1f672e906b21aTimo Sirainen /* duplicate SYNC (which was sent just in case the
fe201fb1819622ab33c51ef5a0a1f672e906b21aTimo Sirainen previous one got lost) */
1574df6b0bc965212f1152e480e7a762cdaa8226Timo Sirainen dir_debug("Ring is synced (%s sent seq=%u, hosts_hash=%u)",
3dc72a40e457658caa3c033fb6b3418d16e9fd21Timo Sirainen timeval_diff_msecs(&ioloop_timeval, &dir->last_sync_start_time);
468c28dfb03613ab8d487b5aebc985a969193aceTimo Sirainen /* stale SYNC event */
9d2575d99e43d08898c44d861f1a2e1377043c1eTimo Sirainen "(seq %u < %u, timestamp=%u)",
688653dfb53411a9ca66dc5647363b53e4f2c997Aki Tuomi i_warning("Last SYNC seq for %s appears to be stale, resetting "
dfbd56c81cb8fb7fe70393c4682cc99e68fe06b6Timo Sirainen "(seq=%u, timestamp=%u -> seq=%u, timestamp=%u)",
dfbd56c81cb8fb7fe70393c4682cc99e68fe06b6Timo Sirainen "(seq=%u, timestamp=%u -> seq=%u, timestamp=%u)",
468c28dfb03613ab8d487b5aebc985a969193aceTimo Sirainen /* we've received this too many times already */
7e656f6f504c93f91af8b6eb290da1a52c11d01bTimo Sirainen dir_debug("Ignore duplicate #%u SYNC event for %s "
7e656f6f504c93f91af8b6eb290da1a52c11d01bTimo Sirainen "(seq=%u, timestamp %u <= %u)",
abe29107f5dce932d28a00912d2d75a01021bef1Timo Sirainen hosts_hash != mail_hosts_hash(conn->dir->mail_hosts)) {
abe29107f5dce932d28a00912d2d75a01021bef1Timo Sirainen if (host->desynced_hosts_hash != hosts_hash) {
abe29107f5dce932d28a00912d2d75a01021bef1Timo Sirainen dir_debug("Ignore director %s stale SYNC request whose hosts don't match us "
abe29107f5dce932d28a00912d2d75a01021bef1Timo Sirainen "(seq=%u, remote hosts_hash=%u, my hosts_hash=%u)",
abe29107f5dce932d28a00912d2d75a01021bef1Timo Sirainen /* we'll get here only if we received a SYNC twice
abe29107f5dce932d28a00912d2d75a01021bef1Timo Sirainen with the same wrong hosts_hash. FIXME: this gets
abe29107f5dce932d28a00912d2d75a01021bef1Timo Sirainen triggered unnecessarily sometimes if hosts are
abe29107f5dce932d28a00912d2d75a01021bef1Timo Sirainen changing rapidly. */
abe29107f5dce932d28a00912d2d75a01021bef1Timo Sirainen i_error("director(%s): Director %s SYNC request hosts don't match us - resending hosts "
abe29107f5dce932d28a00912d2d75a01021bef1Timo Sirainen "(seq=%u, remote hosts_hash=%u, my hosts_hash=%u)",
abe29107f5dce932d28a00912d2d75a01021bef1Timo Sirainen hosts_hash, mail_hosts_hash(dir->mail_hosts));
468c28dfb03613ab8d487b5aebc985a969193aceTimo Sirainen /* forward it to the connection on right */
468c28dfb03613ab8d487b5aebc985a969193aceTimo Sirainen director_sync_send(dir, host, seq, minor_version,
67cb14c7fb54a031818228522dc7255d5cd00f0aTimo Sirainen dir_debug("director(%s): We have no right connection - "
67cb14c7fb54a031818228522dc7255d5cd00f0aTimo Sirainen "delay replying to SYNC until finished", conn->name);
67cb14c7fb54a031818228522dc7255d5cd00f0aTimo Sirainen host->delayed_sync_minor_version = minor_version;
fe201fb1819622ab33c51ef5a0a1f672e906b21aTimo Sirainenstatic bool director_connection_sync(struct director_connection *conn,
aa797403d51ff047727b77d64532001d6b6cc21aTimo Sirainen const char *const *args)
dd81d88575909f2bd99eafffb13c48c3b8cf9529Timo Sirainen unsigned int arg_count, seq, minor_version = 0, timestamp = ioloop_time;
fe201fb1819622ab33c51ef5a0a1f672e906b21aTimo Sirainen !director_args_parse_ip_port(conn, args, &ip, &port) ||
98d5941dc28754f32432edc38578b946ba71dd0bTimo Sirainen director_cmd_error(conn, "Invalid parameters");
dd81d88575909f2bd99eafffb13c48c3b8cf9529Timo Sirainen if (arg_count >= 4 && str_to_uint(args[3], &minor_version) < 0) {
dd81d88575909f2bd99eafffb13c48c3b8cf9529Timo Sirainen director_cmd_error(conn, "Invalid parameters");
dd81d88575909f2bd99eafffb13c48c3b8cf9529Timo Sirainen if (arg_count >= 5 && str_to_uint(args[4], ×tamp) < 0) {
dd81d88575909f2bd99eafffb13c48c3b8cf9529Timo Sirainen director_cmd_error(conn, "Invalid parameters");
abe29107f5dce932d28a00912d2d75a01021bef1Timo Sirainen if (arg_count >= 6 && str_to_uint(args[5], &hosts_hash) < 0) {
abe29107f5dce932d28a00912d2d75a01021bef1Timo 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. */
468c28dfb03613ab8d487b5aebc985a969193aceTimo Sirainen if (!director_connection_sync_host(conn, host, seq,
58b8a301b7b36047f10a592751094fbed86d6f0cTimo Sirainen /* If directors got disconnected while we were waiting a SYNC reply,
58b8a301b7b36047f10a592751094fbed86d6f0cTimo Sirainen it might have gotten lost. If we've received a DIRECTOR update since
58b8a301b7b36047f10a592751094fbed86d6f0cTimo Sirainen the last time we sent a SYNC, retry sending it here to make sure
58b8a301b7b36047f10a592751094fbed86d6f0cTimo Sirainen it doesn't get stuck. We don't want to do this too eagerly because
58b8a301b7b36047f10a592751094fbed86d6f0cTimo Sirainen it may trigger desynced_hosts_hash != hosts_hash mismatch, which
58b8a301b7b36047f10a592751094fbed86d6f0cTimo Sirainen causes unnecessary error logging and hosts-resending. */
58b8a301b7b36047f10a592751094fbed86d6f0cTimo Sirainen dir->last_sync_sent_ring_change_counter != dir->ring_change_counter &&
61b0eeb704039e9837ef3dc7d133096851517d0fTimo Sirainen (time_t)dir->self_host->last_sync_timestamp != ioloop_time)
d8a93844f0b25be82da9c0ab79c321c110f5fb5fTimo Sirainenstatic void director_disconnect_timeout(struct director_connection *conn)
d8a93844f0b25be82da9c0ab79c321c110f5fb5fTimo Sirainen director_connection_deinit(&conn, "CONNECT requested");
d8a93844f0b25be82da9c0ab79c321c110f5fb5fTimo Sirainendirector_reconnect_after_wrong_connect_timeout(struct director_connection *conn)
d8a93844f0b25be82da9c0ab79c321c110f5fb5fTimo Sirainen director_connection_deinit(&conn, "Wrong CONNECT requested");
d8a93844f0b25be82da9c0ab79c321c110f5fb5fTimo Sirainen director_connect(dir, "Reconnecting after wrong CONNECT request");
d8a93844f0b25be82da9c0ab79c321c110f5fb5fTimo Sirainendirector_reconnect_after_wrong_connect(struct director_connection *conn)
d8a93844f0b25be82da9c0ab79c321c110f5fb5fTimo Sirainen timeout_add_short(DIRECTOR_RECONNECT_AFTER_WRONG_CONNECT_MSECS,
d8a93844f0b25be82da9c0ab79c321c110f5fb5fTimo Sirainen director_reconnect_after_wrong_connect_timeout, conn);
1055d8038122c4f4190d37d98fdff6791d1306f8Timo Sirainenstatic bool director_cmd_connect(struct director_connection *conn,
1055d8038122c4f4190d37d98fdff6791d1306f8Timo Sirainen const char *const *args)
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);
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)",
69ea755e44ae1ec65971acc737b2641a4917a60eTimo Sirainen dir_debug("Ignoring CONNECT request to %s (director is removed)",
002eaedb419e67eec1b518f520eca133a6ee27dbTimo Sirainen /* reset failure timestamp so we'll actually try to connect there. */
002eaedb419e67eec1b518f520eca133a6ee27dbTimo Sirainen /* reset removed-flag, so we don't crash */
e2fdcdb4ee53ab769123e27997713aaea34910e1Timo Sirainen right_state = t_strdup_printf("replacing current right %s",
d8a93844f0b25be82da9c0ab79c321c110f5fb5fTimo Sirainen /* disconnect from right side immediately - it's not accepting
d8a93844f0b25be82da9c0ab79c321c110f5fb5fTimo Sirainen any further commands from us. */
d8a93844f0b25be82da9c0ab79c321c110f5fb5fTimo Sirainen director_connection_deinit(&conn->dir->right, "CONNECT requested");
d8a93844f0b25be82da9c0ab79c321c110f5fb5fTimo Sirainen timeout_add_short(0, director_disconnect_timeout, conn);
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen /* connect here */
e2fdcdb4ee53ab769123e27997713aaea34910e1Timo Sirainen (void)director_connect_host(dir, host, t_strdup_printf(
e2fdcdb4ee53ab769123e27997713aaea34910e1Timo Sirainen "Received CONNECT request from %s - %s",
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);
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainenstatic bool director_cmd_ping(struct director_connection *conn,
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen const char *const *args)
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen str_to_uintmax(args[1], &send_buffer_size) == 0) {
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen if (diff_secs*1000+500 > DIRECTOR_CONNECTION_PINGPONG_WARN_MSECS) {
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen i_warning("director(%s): PING response took %d secs to receive "
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen "(send buffer was %ju bytes)",
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen ioloop_time, o_stream_get_buffer_used_size(conn->output)));
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainendirector_ping_append_extra(struct director_connection *conn, string_t *str,
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen str_printfa(str, "buffer size at PING was %zu bytes", conn->ping_sent_buffer_size);
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen str_printfa(str, ", remote sent it %"PRIdTIME_T" secs ago",
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen str_printfa(str, ", remote buffer size at PONG was %ju bytes",
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen if (conn->ping_sent_user_cpu.tv_sec != (time_t)-1 &&
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen int diff = timeval_diff_msecs(&usage.ru_utime,
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen str_printfa(str, ", %u.%03u CPU secs since PING was sent",
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen str_printfa(str, ", %"PRIuUOFF_T" bytes input",
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen conn->input->v_offset - conn->ping_sent_input_offset);
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen str_printfa(str, ", %"PRIuUOFF_T" bytes output",
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen conn->output->offset - conn->ping_sent_output_offset);
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainenstatic bool director_cmd_pong(struct director_connection *conn,
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen const char *const *args)
b2117031bf2c3e35f6efad43b78caaecb6ebdad9Timo Sirainen str_to_uintmax(args[1], &send_buffer_size) < 0) {
f1551f4a50a471f0adecd92dd1f94702beeed72dTimo Sirainen int ping_msecs = timeval_diff_msecs(&ioloop_timeval, &conn->ping_sent_time);
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen if (ping_msecs > DIRECTOR_CONNECTION_PINGPONG_WARN_MSECS) {
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen director_ping_append_extra(conn, extra, sent_time, send_buffer_size);
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen i_warning("director(%s): PONG response took %u.%03u secs (%s)",
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 */
bfef6891565ff9018ac92add6eae401e9352c657Timo Sirainen return director_cmd_user_kick_alt(conn, args);
36e091dc733c6cd690c5aae6e411e41adb1eca73Timo Sirainen return director_cmd_user_kick_hash(conn, args);
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");
e8d44b65aab0a6723e7906756d0825e22ee85a82Timo Sirainen ret = director_connection_handle_cmd(conn, cmd, args+1);
d3ee83b4c24529fac4be5c1f30e254295e7addd9Timo Sirainendirector_connection_log_disconnect(struct director_connection *conn, int err,
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 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 */
d3ee83b4c24529fac4be5c1f30e254295e7addd9Timo Sirainen director_connection_log_disconnect(conn, conn->input->stream_errno,
d3ee83b4c24529fac4be5c1f30e254295e7addd9Timo Sirainen director_connection_disconnected(&conn, i_stream_get_error(conn->input));
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. */
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen i_stream_skip(conn->input, i_stream_get_data_size(conn->input));
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen while ((line = i_stream_next_line(conn->input)) != NULL) {
ece0a20249ce26208db3415ba2e79423678856f8Timo Sirainen dir->ring_traffic_input += conn->input->v_offset - prev_offset;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen ret = director_connection_handle_line(conn, line);
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen director_connection_reconnect(&conn, t_strdup_printf(
a9ade104616bbb81c34cc6f8bfde5dab0571afacTimo Sirainenstatic void director_connection_send_directors(struct director_connection *conn)
a9ade104616bbb81c34cc6f8bfde5dab0571afacTimo Sirainendirector_connection_send_hosts(struct director_connection *conn)
a5ddfd7a8b473f73135b93d5e081e470a87f0f7eTimo Sirainen send_updowns = conn->minor_version >= DIRECTOR_VERSION_UPDOWN;
cf9d67e4a9bfee31cf3be05244555d51a3d1b9feTimo Sirainen array_foreach(mail_hosts_get(conn->dir->mail_hosts), hostp) {
093b42b11c1236a687d3da564b26a324e2189ae6Timo Sirainen const char *host_tag = mail_host_get_tag(host);
9de5eb9e1ac3a07c4197a60fdefd412d6cc78eb2Timo Sirainen str_printfa(str, "\t%c%ld\t", host->down ? 'D' : 'U',
eb209d12e3b1cfed564c35cf19fdb1bf7fcc6811Timo Sirainenstatic int director_connection_send_done(struct director_connection *conn)
00cc7eb569710722af0e0af652034b9fc22c57dfMartti Rannanjärvi if (conn->minor_version >= DIRECTOR_VERSION_OPTIONS) {
eb209d12e3b1cfed564c35cf19fdb1bf7fcc6811Timo Sirainen "OPTIONS\t"DIRECTOR_OPT_CONSISTENT_HASHING"\n");
eb209d12e3b1cfed564c35cf19fdb1bf7fcc6811Timo Sirainen i_error("director(%s): Director version is too old for supporting director_consistent_hashing=yes",
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic int director_connection_send_users(struct director_connection *conn)
05b7b8f14426b5fe5d016940eb5916033f0bc841Timo Sirainen /* with new versions use "U" for sending the handshake users, because
05b7b8f14426b5fe5d016940eb5916033f0bc841Timo Sirainen otherwise their parameters may look identical and can't be
05b7b8f14426b5fe5d016940eb5916033f0bc841Timo Sirainen distinguished. */
05b7b8f14426b5fe5d016940eb5916033f0bc841Timo Sirainen if (director_connection_get_minor_version(conn) >= DIRECTOR_VERSION_HANDSHAKE_U_CMD)
05b7b8f14426b5fe5d016940eb5916033f0bc841Timo Sirainen while ((user = director_iterate_users_next(conn->user_iter)) != NULL) {
6b740bf2d35b25dcc607cfd16593fe912ec49812Timo Sirainen str_append(str, dec2str_buf(dec_buf, user->username_hash));
6b740bf2d35b25dcc607cfd16593fe912ec49812Timo Sirainen str_append(str, dec2str_buf(dec_buf, user->timestamp));
ad394c531ca4b7ca85f3741fa01f96c36c008c70Timo Sirainen if (++sent_count >= DIRECTOR_HANDSHAKE_MAX_USERS_SENT_PER_FLUSH) {
ad394c531ca4b7ca85f3741fa01f96c36c008c70Timo Sirainen /* Don't send too much at once to avoid hangs */
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 */
1f7f4294207557edf83171642ef62ce4922ffc9dTimo Sirainen director_iterate_users_deinit(&conn->user_iter);
8621be3846dc097420cce325ad36d1b646f72a09Timo Sirainen if (conn->users_unsorted && conn->handshake_received) {
8621be3846dc097420cce325ad36d1b646f72a09Timo Sirainen /* we received remote's list of users before sending ours */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic int director_connection_output(struct director_connection *conn)
377dd19a90436b8f96902af741a3ea130bc3fe75Timo Sirainen /* still handshaking USER list */
4ccb6182274da8e95e8dfb940ef9c03755381cd4Timo Sirainen director_connection_log_disconnect(conn, conn->output->stream_errno,
b695e4700d0953031205ad4411182c4bb207605cTimo Sirainen o_stream_set_flush_pending(conn->output, TRUE);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainendirector_connection_init_common(struct director *dir, int fd)
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi conn->input = i_stream_create_fd(conn->fd, MAX_INBUF_SIZE);
311cf367b949f360c9a90822f06f39df31ec69e3Timo Sirainen conn->output = o_stream_create_fd(conn->fd, dir->set->director_output_buffer_size);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_set_no_error_handling(conn->output, TRUE);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic void director_connection_send_handshake(struct director_connection *conn)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen director_connection_send(conn, t_strdup_printf(
9054b5f92a7e5666c6beaa04916699a1408bf021Timo Sirainen "ME\t%s\t%u\t%lld\n",
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen DIRECTOR_VERSION_MAJOR, DIRECTOR_VERSION_MINOR,
9054b5f92a7e5666c6beaa04916699a1408bf021Timo Sirainen net_ip2addr(&conn->dir->self_ip), conn->dir->self_port,
c83e45c826c93c5122340a37a6faf99ce6078627Timo Sirainenstatic void director_connection_set_connected(struct director_connection *conn)
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);
5a530f778cc3eae05e70d13b8e5d7d501e7ba0b3Timo Sirainen conn->to_ping = timeout_add(DIRECTOR_CONNECTION_ME_TIMEOUT_MSECS,
e2fdcdb4ee53ab769123e27997713aaea34910e1Timo Sirainen i_info("Incoming connection from director %s", conn->name);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic void director_connection_connected(struct director_connection *conn)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("director(%s): connect() failed: %s", conn->name,
d3ee83b4c24529fac4be5c1f30e254295e7addd9Timo Sirainen director_connection_disconnected(&conn, strerror(err));
a70216512b55a4f6d633defcead6fcb081b03c16Timo Sirainen conn->io = io_add(conn->fd, IO_READ, director_connection_input, conn);
5a530f778cc3eae05e70d13b8e5d7d501e7ba0b3Timo Sirainen conn->to_ping = timeout_add(DIRECTOR_CONNECTION_ME_TIMEOUT_MSECS,
a9ade104616bbb81c34cc6f8bfde5dab0571afacTimo Sirainen /* send the rest of the handshake after we've received the remote's
a9ade104616bbb81c34cc6f8bfde5dab0571afacTimo Sirainen version number */
a9ade104616bbb81c34cc6f8bfde5dab0571afacTimo Sirainenstatic void director_finish_sending_handshake(struct director_connection *conn)
a9ade104616bbb81c34cc6f8bfde5dab0571afacTimo Sirainen /* only outgoing connections send hosts & users */
14660f677e16a5c36f3c43e9e64f5e021fda627bTimo Sirainen /* Iterate only through users that aren't refreshed since the
14660f677e16a5c36f3c43e9e64f5e021fda627bTimo Sirainen iteration started. The refreshed users will already be sent as
14660f677e16a5c36f3c43e9e64f5e021fda627bTimo Sirainen regular USER updates, so they don't need to be sent again.
14660f677e16a5c36f3c43e9e64f5e021fda627bTimo Sirainen We especially don't want to send these users again, because
14660f677e16a5c36f3c43e9e64f5e021fda627bTimo Sirainen otherwise in a rapidly changing director we might never end up
14660f677e16a5c36f3c43e9e64f5e021fda627bTimo Sirainen sending all the users when they constantly keep being added to the
14660f677e16a5c36f3c43e9e64f5e021fda627bTimo Sirainen end of the list. (The iteration lists users in order from older to
14660f677e16a5c36f3c43e9e64f5e021fda627bTimo Sirainen conn->user_iter = director_iterate_users_init(conn->dir, TRUE);
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);
5a530f778cc3eae05e70d13b8e5d7d501e7ba0b3Timo Sirainen conn->to_ping = timeout_add(DIRECTOR_CONNECTION_CONNECT_TIMEOUT_MSECS,
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainenvoid director_connection_deinit(struct director_connection **_conn,
caae18c876f81e261350e4957471efa453c0ea9fTimo Sirainen struct director_connection *const *conns, *conn = *_conn;
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" */
0e94f6c27bffd58953b3479415532bff2bc04732Timo Sirainen /* Users were received, but handshake didn't finish.
0e94f6c27bffd58953b3479415532bff2bc04732Timo Sirainen Finish sorting so the users won't stay in wrong order. */
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainen director_host_unref(conn->connect_request_to);
1f7f4294207557edf83171642ef62ce4922ffc9dTimo Sirainen director_iterate_users_deinit(&conn->user_iter);
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 */
312213260e384239ac93c77951c2f1f5f3d3611eTimo Sirainenstatic bool director_connection_unref(struct director_connection *conn)
ff060dcf76798701c0d1386717096062ff5cf9a8Phil Carmodystatic void director_connection_disconnected(struct director_connection **_conn,
1707f8b66faf2dcd1fe2d1b07d421f9d006c9432Timo Sirainen conn->connected_time.tv_sec + 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;
e2fdcdb4ee53ab769123e27997713aaea34910e1Timo Sirainen director_connect(dir, "Reconnecting after disconnection");
dff32d11a411a24f3b76003c1ae22c5a960f180eTimo Sirainenstatic void director_connection_reconnect(struct director_connection **_conn,
e2fdcdb4ee53ab769123e27997713aaea34910e1Timo Sirainen director_connect(dir, "Reconnecting after error");
1511fc56b80709183cfa1201bfddd09d8a804c99Timo Sirainenstatic void director_disconnect_write_error(struct director_connection *conn)
1511fc56b80709183cfa1201bfddd09d8a804c99Timo Sirainen director_connection_deinit(&conn, "write failure");
3a7a8b3685eb946a2153b9af92bc545800935a95Timo Sirainen director_connect(dir, "Reconnecting after write failure");
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);
4ccb6182274da8e95e8dfb940ef9c03755381cd4Timo Sirainen director_connection_log_disconnect(conn, EINVAL,
4ccb6182274da8e95e8dfb940ef9c03755381cd4Timo Sirainen o_stream_get_buffer_used_size(conn->output)));
1511fc56b80709183cfa1201bfddd09d8a804c99Timo Sirainen /* closing the stream when output buffer is full doesn't cause
1511fc56b80709183cfa1201bfddd09d8a804c99Timo Sirainen disconnection itself. */
1511fc56b80709183cfa1201bfddd09d8a804c99Timo Sirainen timeout_add_short(0, director_disconnect_write_error, conn);
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainendirector_connection_ping_idle_timeout(struct director_connection *conn)
8506e63fd90332f32d991337b48e06fd3db5b928Timo Sirainen int diff = timeval_diff_msecs(&ioloop_timeval, &conn->ping_sent_time);
1a85ac6b42b73c48143ef7b76f95c15e89d3ca4dTimo Sirainen str_printfa(str, "Ping timed out in %u.%03u secs: ",
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen director_ping_append_extra(conn, str, 0, (uintmax_t)-1);
4ccb6182274da8e95e8dfb940ef9c03755381cd4Timo Sirainen director_connection_log_disconnect(conn, EINVAL, str_c(str));
d3ee83b4c24529fac4be5c1f30e254295e7addd9Timo Sirainen director_connection_disconnected(&conn, "Ping timeout");
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainenstatic void director_connection_pong_timeout(struct director_connection *conn)
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen int diff = timeval_diff_msecs(&ioloop_timeval, &conn->ping_sent_time);
1a85ac6b42b73c48143ef7b76f95c15e89d3ca4dTimo Sirainen "PONG reply not received in %u.%03u secs, "
1a85ac6b42b73c48143ef7b76f95c15e89d3ca4dTimo Sirainen "although other input keeps coming",
4ccb6182274da8e95e8dfb940ef9c03755381cd4Timo Sirainen director_connection_log_disconnect(conn, EINVAL, errstr);
d3ee83b4c24529fac4be5c1f30e254295e7addd9Timo Sirainen director_connection_disconnected(&conn, "Pong timeout");
5f5713d6468dca1acf3d350dd8a33057331f78c5Timo Sirainenvoid director_connection_ping(struct director_connection *conn)
2adcf55dd8b0380cb2f1d2fd53accf448053d5d3Timo Sirainen conn->to_ping = timeout_add(conn->dir->set->director_ping_idle_timeout*1000,
2adcf55dd8b0380cb2f1d2fd53accf448053d5d3Timo Sirainen conn->to_pong = timeout_add(conn->dir->set->director_ping_max_timeout*1000,
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen conn->ping_sent_buffer_size = o_stream_get_buffer_used_size(conn->output);
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen conn->ping_sent_input_offset = conn->input->v_offset;
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen conn->ping_sent_output_offset = conn->output->offset;
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen /* send it after getting the buffer size */
91b0bd6e7277646032d5001822f65575be75f062Timo Sirainen director_connection_send(conn, t_strdup_printf(
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)
ad404d294fedf792619aed432ed8de5174e9ce7cTimo Sirainenbool director_connection_is_synced(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 */
56139ffc94e481ca1940ffd8d8f7166879150665Timo Sirainenvoid director_connection_get_status(struct director_connection *conn,
56139ffc94e481ca1940ffd8d8f7166879150665Timo Sirainen status_r->bytes_buffered = o_stream_get_buffer_used_size(conn->output);
56139ffc94e481ca1940ffd8d8f7166879150665Timo Sirainen status_r->peak_bytes_buffered = conn->peak_bytes_buffered;
f1551f4a50a471f0adecd92dd1f94702beeed72dTimo Sirainen status_r->last_ping_msecs = conn->last_ping_msecs;
5cdaaf2ecfed02503572935266f55123923ba00aTimo Sirainen status_r->handshake_users_sent = conn->handshake_users_sent;