director-connection.c revision 2acc1162990b5be76d2b4923a0fcfcfdcdc65d10
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen/* Max idling time while connecting/handshaking before disconnecting */
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen#define DIRECTOR_CONNECTION_INIT_TIMEOUT_MSECS (2*1000)
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen/* How long to wait for PONG after PING request */
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen#define DIRECTOR_CONNECTION_PING_TIMEOUT_MSECS (2*1000)
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen/* How long to wait to send PING when connection is idle */
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen#define DIRECTOR_CONNECTION_PING_INTERVAL_MSECS (15*1000)
da985034a708db2f61394b30d117050ae6829ee5Timo Sirainen/* How long to wait before sending PING while waiting for SYNC reply */
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen#define DIRECTOR_CONNECTION_SYNC_TIMEOUT_MSECS 1000
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen /* for incoming connections the director host isn't known until
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen ME-line is received */
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainenstatic void director_connection_ping(struct director_connection *conn);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainenstatic void director_connection_disconnected(struct director_connection **conn);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainendirector_args_parse_ip_port(struct director_connection *conn,
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen const char *const *args,
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen i_error("director(%s): Command has invalid IP address: %s",
9315dd69233d554452df0c12bc57002d2042a8f4Timo Sirainen i_error("director(%s): Command has invalid port: %s",
c27f03fa8fd2ef4acd1db814fae7d90e0eb9d3aeTimo Sirainenstatic bool director_cmd_me(struct director_connection *conn,
c27f03fa8fd2ef4acd1db814fae7d90e0eb9d3aeTimo Sirainen const char *const *args)
c27f03fa8fd2ef4acd1db814fae7d90e0eb9d3aeTimo Sirainen unsigned int port;
de12ff295bb3d0873b4dced5840612cbacd635efTimo Sirainen if (!director_args_parse_ip_port(conn, args, &ip, &port))
da985034a708db2f61394b30d117050ae6829ee5Timo Sirainen if (!conn->in && (!net_ip_compare(&conn->host->ip, &ip) ||
c27f03fa8fd2ef4acd1db814fae7d90e0eb9d3aeTimo Sirainen i_error("Remote director thinks it's someone else "
c27f03fa8fd2ef4acd1db814fae7d90e0eb9d3aeTimo Sirainen "(connected to %s:%u, remote says it's %s:%u)",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen net_ip2addr(&conn->host->ip), conn->host->port,
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen conn->name = i_strdup_printf("%s/left", host->name);
b2105c78f0fd58281317e6d777ded860f33153a3Timo Sirainen /* make sure we don't keep old sequence values across restarts */
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen connect_str = t_strdup_printf("CONNECT\t%s\t%u\n",
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen /* make sure this is the correct incoming connection */
41bb0aa8e357876bc9a1916a37c9e3e78e5f8185Timo Sirainen /* probably we're trying to find our own ip. it's no */
41bb0aa8e357876bc9a1916a37c9e3e78e5f8185Timo Sirainen i_error("director(%s): Connection from self, dropping",
190237ce467d2389dfb809874b0fec86d3c7968dTimo Sirainen /* no conflicts yet */
41bb0aa8e357876bc9a1916a37c9e3e78e5f8185Timo Sirainen i_warning("director(%s): Dropping existing connection "
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "in favor of its new connection", host->name);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (director_host_cmp_to_self(dir->left->host, host,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* the old connection is the correct one.
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen refer the client there. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen director_connection_send(conn, t_strdup_printf(
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "CONNECT\t%s\t%u\n",
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen /* also make sure that the connection is alive */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* this new connection is the correct one. disconnect the old
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen one, but before that tell it to connect to the new one.
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen that message might not reach it, so also send the same
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen message to right side. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_warning("Replacing director connection %s with %s",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen director_connection_send(dir->left, connect_str);
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen /* tell the ring's right side to connect to this new director. */
e86d0d34fe365da4c7ca4312d575bfcbf3a01c0eTimo Sirainen director_connection_send(dir->right, connect_str);
e86d0d34fe365da4c7ca4312d575bfcbf3a01c0eTimo Sirainen /* there are only two directors, and we already have
e86d0d34fe365da4c7ca4312d575bfcbf3a01c0eTimo Sirainen a connection to this server. */
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen /* there are only two directors. connect to the other one. */
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainendirector_user_refresh(struct director *dir, unsigned int username_hash,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen user = user_directory_lookup(dir->users, username_hash);
369a1084c500a9df7448ffa9409ce32e42060bc2Timo Sirainen *user_r = user_directory_add(dir->users, username_hash,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (timestamp == ioloop_time && (time_t)user->timestamp != timestamp) {
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen i_error("User hash %u is being redirected to two hosts: "
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen /* we want all the directors to redirect the user to same
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen server, but we don't want two directors fighting over which
5626ae5e3316eced244adb6485c0927f1c7fdc41Timo Sirainen server it belongs to, so always use the lower IP address */
5626ae5e3316eced244adb6485c0927f1c7fdc41Timo Sirainen if (net_ip_cmp(&user->host->ip, &host->ip) > 0) {
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen /* change the host. we'll also need to remove the user
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen from the old host's user_count, because we can't
f23298fea47eecbeded985ee2537a34c4c4ef56bTimo Sirainen keep track of the user for more than one host */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainendirector_handshake_cmd_user(struct director_connection *conn,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *const *args)
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen i_error("director(%s): Invalid USER handshake args",
abbe0657a4d6271740d41cf1de55e8686f5769fcTimo Sirainen host = mail_host_lookup(conn->dir->mail_hosts, &ip);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_error("director(%s): USER used unknown host %s in handshake",
75ef04fc62a3955d3a5310410e09735cbd4e972bTimo Sirainen director_user_refresh(conn->dir, username_hash, host, timestamp, &user);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainendirector_cmd_user(struct director_connection *conn, const char *const *args)
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen i_error("director(%s): Invalid USER args", conn->name);
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen host = mail_host_lookup(conn->dir->mail_hosts, &ip);
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen /* we probably just removed this host. */
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen if (director_user_refresh(conn->dir, username_hash,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen director_update_user(conn->dir, conn->host, user);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic bool director_cmd_director(struct director_connection *conn,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *const *args)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int port;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (!director_args_parse_ip_port(conn, args, &ip, &port))
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen host = director_host_lookup(conn->dir, &ip, port);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* already have this, skip */
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen /* save the director and forward it */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen t_strdup_printf("DIRECTOR\t%s\t%u\n", net_ip2addr(&ip), port));
return TRUE;
const char *const *args)
unsigned int remote_ring_completed;
return FALSE;
return TRUE;
const char *const **_args,
unsigned int vhost_count;
bool update;
return FALSE;
return TRUE;
if (update) {
return TRUE;
const char *const *args)
int ret;
return ret > 0;
const char *const *args)
int ret;
return ret > 0;
return FALSE;
return TRUE;
const char *const *args)
unsigned int seq;
int ret;
return ret > 0;
return FALSE;
return TRUE;
unsigned int port;
return FALSE;
return FALSE;
return TRUE;
return FALSE;
return FALSE;
return FALSE;
return TRUE;
return TRUE;
return FALSE;
return FALSE;
return TRUE;
return TRUE;
return TRUE;
return TRUE;
return TRUE;
const char *const *args)
unsigned int port;
return FALSE;
return TRUE;
return TRUE;
return TRUE;
return TRUE;
return TRUE;
const char *line)
return FALSE;
return TRUE;
return FALSE;
return TRUE;
return FALSE;
char *line;
bool ret;
T_BEGIN {
} T_END;
if (!ret) {
int ret;
user)) {
T_BEGIN {
const char *line;
} T_END;
return ret;
return ret;
static struct director_connection *
return conn;
struct director_connection *
return conn;
int err;
struct director_connection *
return conn;
const char *data)
if (ret < 0)
const char *data)
struct director_host *
struct director_connection *
return conn;
return NULL;