director-connection.c revision dc1bc1685e4a0d58ae7bacaecc282d0ebde2d7da
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen#define DIRECTOR_CONNECTION_PING_TIMEOUT_MSECS (2*1000)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* for incoming connections the director host isn't known until
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen ME-line is received */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic void director_connection_ping(struct director_connection *conn);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainendirector_args_parse_ip_port(struct director_connection *conn,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen const char *const *args,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("director(%s): Command has invalid IP address: %s",
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("director(%s): Command has invalid port: %s",
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic bool director_cmd_me(struct director_connection *conn,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen const char *const *args)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen unsigned int port;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (!director_args_parse_ip_port(conn, args, &ip, &port))
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (!conn->in && (!net_ip_compare(&conn->host->ip, &ip) ||
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("Remote director thinks it's someone else "
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen "(connected to %s:%u, remote says it's %s:%u)",
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen net_ip2addr(&conn->host->ip), conn->host->port,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen connect_str = t_strdup_printf("CONNECT\t%s\t%u\n",
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* make sure this is the correct incoming connection */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* probably we're trying to find our own ip. it's no */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("director(%s): Connection from self, dropping",
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* no conflicts yet */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_warning("director(%s): Dropping existing connection "
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen "in favor of its new connection", host->name);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (director_host_cmp_to_self(dir->left->host, host,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* the old connection is the correct one.
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen refer the client there. */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen director_connection_send(conn, t_strdup_printf(
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen "CONNECT\t%s\t%u\n",
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* also make sure that the connection is alive */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* this new connection is the correct one. disconnect the old
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen one, but before that tell it to connect to the new one.
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen that message might not reach it, so also send the same
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen message to right side. */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen director_connection_send(dir->left, connect_str);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* tell the ring's right side to connect to this new director. */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen director_connection_send(dir->right, connect_str);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* there are only two directors */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* looks like we're the right side. */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainendirector_user_refresh(struct director *dir, unsigned int username_hash,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen user = user_directory_lookup(dir->users, username_hash);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen *user_r = user_directory_add(dir->users, username_hash,
dc1bc1685e4a0d58ae7bacaecc282d0ebde2d7daTimo Sirainen if (timestamp == ioloop_time && (time_t)user->timestamp != timestamp) {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("User hash %u is being redirected to two hosts: "
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainendirector_handshake_cmd_user(struct director_connection *conn,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen const char *const *args)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("director(%s): Invalid USER handshake args",
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("director(%s): USER used unknown host %s in handshake",
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen director_user_refresh(conn->dir, username_hash, host, timestamp, &user);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic bool director_cmd_director(struct director_connection *conn,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen const char *const *args)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen unsigned int port;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (!director_args_parse_ip_port(conn, args, &ip, &port))
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen host = director_host_lookup(conn->dir, &ip, port);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* already have this, skip */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* save the director and forward it */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen t_strdup_printf("DIRECTOR\t%s\t%u\n", net_ip2addr(&ip), port));
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainendirector_cmd_host(struct director_connection *conn, const char *const *args)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("director(%s): Invalid HOST args", conn->name);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* FIXME: 1) shouldn't be unconditional, 2) if we're not
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen handshaking, we should do SYNC before making it visible */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen director_update_host(conn->dir, conn->host, host);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainendirector_cmd_host_remove(struct director_connection *conn,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen const char *const *args)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("director(%s): Invalid HOST-REMOVE args", conn->name);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen director_remove_host(conn->dir, conn->host, host);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic void director_handshake_cmd_done(struct director_connection *conn)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* handshaked to left side. tell it we've received the
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen whole handshake. */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* tell the right director about the left one */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (dir->left != NULL && dir->right != NULL) {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* we're connected to both directors. see if the ring is
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen finished by sending a SYNC. if we get it back, it's done. */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainendirector_connection_handle_handshake(struct director_connection *conn,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen unsigned int port;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* both incoming and outgoing connections get VERSION and ME */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (strcmp(cmd, "VERSION") == 0 && str_array_length(args) >= 3) {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (strcmp(args[0], DIRECTOR_VERSION_NAME) != 0) {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("director(%s): Wrong protocol in socket "
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen "(%s vs %s)",
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen } else if (atoi(args[1]) != DIRECTOR_VERSION_MAJOR) {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("director(%s): Incompatible protocol version: "
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("director(%s): Incompatible protocol", conn->name);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (strcmp(cmd, "ME") == 0 && !conn->me_received &&
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* only outgoing connections get a CONNECT reference */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (!conn->in && strcmp(cmd, "CONNECT") == 0 &&
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* remote wants us to connect elsewhere */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (!director_args_parse_ip_port(conn, args, &ip, &port))
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen host = director_host_get(conn->dir, &ip, port);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* only incoming connections get DIRECTOR and HOST lists */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (conn->in && strcmp(cmd, "DIRECTOR") == 0 && conn->me_received)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (conn->in && strcmp(cmd, "HOST") == 0 && conn->me_received)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* only incoming connections get a USER list */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (conn->in && strcmp(cmd, "USER") == 0 && conn->me_received)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen return director_handshake_cmd_user(conn, args);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* both get DONE */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (strcmp(cmd, "DONE") == 0 && !conn->handshake_received) {
4fe3f07477bae6da3fb8d8fa9bab10ab82ada2bdTimo Sirainen i_error("director(%s): Invalid handshake command: %s",
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainendirector_cmd_user(struct director_connection *conn, const char *const *args)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("director(%s): Invalid USER args", conn->name);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* we probably just removed this host. */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (director_user_refresh(conn->dir, username_hash,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen director_update_user(conn->dir, conn->host, user);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic bool director_connection_sync(struct director_connection *conn,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen director_args_parse_ip_port(conn, args, &ip, &port) < 0 ||
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("director(%s): Invalid SYNC args", conn->name);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* find the originating director. if we don't see it, it was already
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen removed and we can ignore this sync. */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen host = director_host_lookup(conn->dir, &ip, port);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* stale SYNC event */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* the ring is handshaked */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* forward it to the connection on right */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainendirector_connection_handle_line(struct director_connection *conn,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("director(%s): Received empty line", conn->name);
4fe3f07477bae6da3fb8d8fa9bab10ab82ada2bdTimo Sirainen if (!director_connection_handle_handshake(conn, cmd, args)) {
4fe3f07477bae6da3fb8d8fa9bab10ab82ada2bdTimo Sirainen /* invalid commands during handshake,
4fe3f07477bae6da3fb8d8fa9bab10ab82ada2bdTimo Sirainen we probably don't want to reconnect here */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen return director_connection_sync(conn, args, line);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("director(%s): Unknown command (in this state): %s",
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic void director_connection_input(struct director_connection *conn)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* disconnected */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* buffer full */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("BUG: Director sent us more than %d bytes",
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen while ((line = i_stream_next_line(conn->input)) != NULL) {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen ret = director_connection_handle_line(conn, line);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic void director_connection_send_directors(struct director_connection *conn,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic void director_connection_send_hosts(string_t *str)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen net_ip2addr(&(*hostp)->ip), (*hostp)->vhost_count);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic int director_connection_send_users(struct director_connection *conn)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen while ((user = user_directory_iter_next(conn->user_iter)) != NULL) {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (o_stream_get_buffer_used_size(conn->output) >= OUTBUF_FLUSH_THRESHOLD) {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if ((ret = o_stream_flush(conn->output)) <= 0) {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* continue later */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen conn->io = io_add(conn->fd, IO_READ, director_connection_input, conn);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic int director_connection_output(struct director_connection *conn)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainendirector_connection_init_common(struct director *dir, int fd)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen conn->input = i_stream_create_fd(conn->fd, MAX_INBUF_SIZE, FALSE);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen conn->output = o_stream_create_fd(conn->fd, MAX_OUTBUF_SIZE, FALSE);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic void director_connection_send_handshake(struct director_connection *conn)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen director_connection_send(conn, t_strdup_printf(
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen "ME\t%s\t%u\n",
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen DIRECTOR_VERSION_MAJOR, DIRECTOR_VERSION_MINOR,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen net_ip2addr(&conn->dir->self_ip), conn->dir->self_port));
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainendirector_connection_init_in(struct director *dir, int fd)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen conn = director_connection_init_common(dir, fd);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen conn->io = io_add(conn->fd, IO_READ, director_connection_input, conn);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic void director_connection_connected(struct director_connection *conn)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("director(%s): connect() failed: %s", conn->name,
4fe3f07477bae6da3fb8d8fa9bab10ab82ada2bdTimo Sirainen /* try connecting to next server */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen director_connection_send_directors(conn, str);
4fe3f07477bae6da3fb8d8fa9bab10ab82ada2bdTimo Sirainen conn->user_iter = user_directory_iter_init(dir->users);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainendirector_connection_init_out(struct director *dir, int fd,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen conn = director_connection_init_common(dir, fd);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenvoid director_connection_deinit(struct director_connection **_conn)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("close(director connection) failed: %m");
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic void director_connection_timeout(struct director_connection *conn)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenvoid director_connection_send(struct director_connection *conn,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("director(%s): write() failed: %m", conn->name);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen conn->to = timeout_add(0, director_connection_timeout, conn);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenvoid director_connection_send_except(struct director_connection *conn,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic void director_connection_ping_timeout(struct director_connection *conn)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen i_error("director(%s): Ping timed out, disconnecting", conn->name);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic void director_connection_ping(struct director_connection *conn)