director-connection.c revision 4aab01f4eade3d278b61471516c062ce30a84b5f
/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "ioloop.h"
#include "array.h"
#include "network.h"
#include "istream.h"
#include "ostream.h"
#include "str.h"
#include "mail-host.h"
#include "director.h"
#include "director-host.h"
#include "director-request.h"
#include "user-directory.h"
#include "director-connection.h"
#include <stdlib.h>
#include <unistd.h>
#define DIRECTOR_VERSION_NAME "director"
#define DIRECTOR_VERSION_MAJOR 1
#define DIRECTOR_VERSION_MINOR 0
#define MAX_INBUF_SIZE 1024
struct director_connection {
const char *name;
/* for incoming connections the director host isn't known until
ME-line is received */
struct director_host *host;
int fd;
struct user_directory_iter *user_iter;
unsigned int in:1;
unsigned int connected:1;
unsigned int version_received:1;
unsigned int me_received:1;
unsigned int handshake_received:1;
};
static bool
const char *const *args,
{
i_error("director(%s): Command has invalid IP address: %s",
return FALSE;
}
i_error("director(%s): Command has invalid port: %s",
return FALSE;
}
return TRUE;
}
const char *const *args)
{
struct director_host *host;
const char *connect_str;
unsigned int port;
return FALSE;
i_error("Remote director thinks it's someone else "
"(connected to %s:%u, remote says it's %s:%u)",
return FALSE;
}
return TRUE;
/* make sure this is the correct incoming connection */
/* probably we're trying to find our own ip. it's no */
i_error("director(%s): Connection from self, dropping",
return FALSE;
/* no conflicts yet */
i_warning("director(%s): Dropping existing connection "
} else {
/* the old connection is the correct one.
refer the client there. */
"CONNECT\t%s\t%u\n",
/* also make sure that the connection is alive */
return FALSE;
}
/* this new connection is the correct one. disconnect the old
one, but before that tell it to connect to the new one.
that message might not reach it, so also send the same
message to right side. */
}
/* tell the ring's right side to connect to this new director. */
else {
/* there are only two directors */
}
} else {
/* looks like we're the right side. */
}
return TRUE;
}
static bool
{
return TRUE;
}
}
i_error("User hash %u is being redirected to two hosts: "
"%s and %s", username_hash,
}
return ret;
}
static bool
const char *const *args)
{
unsigned int username_hash, timestamp;
i_error("director(%s): Invalid USER handshake args",
return FALSE;
}
i_error("director(%s): USER used unknown host %s in handshake",
return FALSE;
}
return TRUE;
}
const char *const *args)
{
struct director_host *host;
unsigned int port;
return FALSE;
/* already have this, skip */
return TRUE;
}
/* save the director and forward it */
return TRUE;
}
static bool
{
unsigned int vhost_count;
bool update;
return FALSE;
}
} else {
}
if (update) {
/* FIXME: 1) shouldn't be unconditional, 2) if we're not
handshaking, we should do SYNC before making it visible */
}
return TRUE;
}
static bool
const char *const *args)
{
return FALSE;
}
return TRUE;
}
{
/* handshaked to left side. tell it we've received the
whole handshake. */
/* tell the right director about the left one */
t_strdup_printf("DIRECTOR\t%s\t%u\n",
}
}
/* we're connected to both directors. see if the ring is
finished by sending a SYNC. if we get it back, it's done. */
t_strdup_printf("SYNC\t%s\t%u\t%u\n",
}
}
static bool
{
struct director_host *host;
unsigned int port;
/* both incoming and outgoing connections get VERSION and ME */
i_error("director(%s): Wrong protocol in socket "
"(%s vs %s)",
return FALSE;
i_error("director(%s): Incompatible protocol version: "
return FALSE;
}
return TRUE;
}
if (!conn->version_received) {
return FALSE;
}
/* only outgoing connections get a CONNECT reference */
/* remote wants us to connect elsewhere */
return FALSE;
return FALSE;
}
/* only incoming connections get DIRECTOR and HOST lists */
/* only incoming connections get a USER list */
/* both get DONE */
return TRUE;
}
i_error("director(%s): Invalid handshake command: %s",
return FALSE;
}
static bool
{
unsigned int username_hash;
return FALSE;
}
/* we probably just removed this host. */
return TRUE;
}
return TRUE;
}
{
struct director_host *host;
return FALSE;
}
/* find the originating director. if we don't see it, it was already
removed and we can ignore this sync. */
return TRUE;
/* stale SYNC event */
return TRUE;
}
return TRUE;
/* the ring is handshaked */
return TRUE;
}
/* forward it to the connection on right */
}
return TRUE;
}
static bool
const char *line)
{
return FALSE;
}
if (!conn->handshake_received) {
/* invalid commands during handshake,
we probably don't want to reconnect here */
return FALSE;
}
return TRUE;
}
return TRUE;
}
return TRUE;
}
i_error("director(%s): Unknown command (in this state): %s",
return FALSE;
}
{
char *line;
bool ret;
case 0:
return;
case -1:
/* disconnected */
return;
case -2:
/* buffer full */
i_error("BUG: Director sent us more than %d bytes",
return;
}
T_BEGIN {
} T_END;
if (!ret) {
break;
}
}
}
{
struct director_host *const *hostp;
}
}
{
}
}
{
int ret;
T_BEGIN {
const char *line;
} T_END;
/* continue later */
return ret;
}
}
}
return ret;
}
{
return director_connection_send_users(conn);
else
}
static struct director_connection *
{
struct director_connection *conn;
return conn;
}
{
"ME\t%s\t%u\n",
}
struct director_connection *
{
struct director_connection *conn;
return conn;
}
{
int err;
/* try connecting to next server */
return;
}
(void)director_connection_send_users(conn);
}
struct director_connection *
struct director_host *host)
{
struct director_connection *conn;
return conn;
}
{
i_error("close(director connection) failed: %m");
}
{
}
const char *data)
{
return;
if (ret < 0)
else {
i_error("director(%s): Output buffer full, "
}
}
}
struct director_host *skip_host,
const char *data)
{
}
{
}
{
return;
}