[dovecot-cvs] dovecot/src/auth Makefile.am, 1.43, 1.44 auth-client-connection.c, 1.33, 1.34 auth-master-connection.c, 1.25, 1.26 auth-master-connection.h, 1.8, 1.9 auth-request-balancer-child.c, NONE, 1.1 auth-request-balancer-worker.c, NONE, 1.1 auth-request-balancer.h, NONE, 1.1 auth-request-handler-balancer.c, NONE, 1.1 auth-request-handler-default.c, NONE, 1.1 auth-request-handler.c, 1.1, 1.2 auth-request-handler.h, 1.1, 1.2 auth.c, 1.12, 1.13 auth.h, 1.10, 1.11 common.h, 1.10, 1.11 main.c, 1.38, 1.39 mech.h, 1.34, 1.35

cras at dovecot.org cras at dovecot.org
Sun Jan 9 18:54:51 EET 2005


Update of /var/lib/cvs/dovecot/src/auth
In directory talvi:/tmp/cvs-serv30683/auth

Modified Files:
	Makefile.am auth-client-connection.c auth-master-connection.c 
	auth-master-connection.h auth-request-handler.c 
	auth-request-handler.h auth.c auth.h common.h main.c mech.h 
Added Files:
	auth-request-balancer-child.c auth-request-balancer-worker.c 
	auth-request-balancer.h auth-request-handler-balancer.c 
	auth-request-handler-default.c 
Log Message:
Changed the way multiple auth processes are handled. It no longer uses a pid
appended to socket name but instead there's a balancer process which
proxies the requests to worker processes.



Index: Makefile.am
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/Makefile.am,v
retrieving revision 1.43
retrieving revision 1.44
diff -u -d -r1.43 -r1.44
--- Makefile.am	9 Jan 2005 00:49:18 -0000	1.43
+++ Makefile.am	9 Jan 2005 16:54:48 -0000	1.44
@@ -38,7 +38,11 @@
 	auth-master-connection.c \
 	auth-module.c \
 	auth-request.c \
+	auth-request-balancer-child.c \
+	auth-request-balancer-worker.c \
 	auth-request-handler.c \
+	auth-request-handler-balancer.c \
+	auth-request-handler-default.c \
 	db-ldap.c \
 	db-sql.c \
 	db-passwd-file.c \
@@ -81,6 +85,7 @@
 	auth-master-connection.h \
 	auth-module.h \
 	auth-request.h \
+	auth-request-balancer.h \
 	auth-request-handler.h \
 	db-ldap.h \
 	db-sql.h \

Index: auth-client-connection.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/auth-client-connection.c,v
retrieving revision 1.33
retrieving revision 1.34
diff -u -d -r1.33 -r1.34
--- auth-client-connection.c	9 Jan 2005 00:49:18 -0000	1.33
+++ auth-client-connection.c	9 Jan 2005 16:54:48 -0000	1.34
@@ -98,16 +98,17 @@
         conn->refcount++;
 	if (!AUTH_MASTER_IS_DUMMY(conn->master)) {
 		conn->request_handler =
-			auth_request_handler_create(conn->auth,
-				conn->connect_uid, pid, auth_callback, conn,
+			auth_request_handler_create(conn->auth, FALSE,
+				auth_callback, conn,
 				auth_master_request_callback, conn->master);
 	} else {
 		conn->request_handler =
-			auth_request_handler_create(conn->auth,
-						    conn->connect_uid, pid,
+			auth_request_handler_create(conn->auth, FALSE,
 						    auth_callback, conn,
 						    NULL, NULL);
 	}
+	auth_request_handler_set(conn->request_handler, conn->connect_uid, pid);
+
 	conn->pid = pid;
 	return TRUE;
 }
@@ -338,6 +339,8 @@
 		master->clients = next;
 	}
 
-	timeout_remove(master->to_clients);
-	master->to_clients = NULL;
+	if (master->to_clients != NULL) {
+		timeout_remove(master->to_clients);
+		master->to_clients = NULL;
+	}
 }

Index: auth-master-connection.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/auth-master-connection.c,v
retrieving revision 1.25
retrieving revision 1.26
diff -u -d -r1.25 -r1.26
--- auth-master-connection.c	9 Jan 2005 00:49:18 -0000	1.25
+++ auth-master-connection.c	9 Jan 2005 16:54:48 -0000	1.26
@@ -10,6 +10,7 @@
 #include "network.h"
 #include "userdb.h"
 #include "auth-request-handler.h"
+#include "auth-request-balancer.h"
 #include "auth-master-interface.h"
 #include "auth-client-connection.h"
 #include "auth-master-connection.h"
@@ -22,7 +23,7 @@
 
 struct auth_listener {
 	struct auth_master_connection *master;
-	int client_listener;
+	enum listener_type type;
 	int fd;
 	char *path;
 	struct io *io;
@@ -37,6 +38,7 @@
 static int master_output(void *context);
 static void auth_master_connection_close(struct auth_master_connection *conn);
 static int auth_master_connection_unref(struct auth_master_connection *conn);
+static void auth_listener_destroy(struct auth_listener *l);
 
 void auth_master_request_callback(const char *reply, void *context)
 {
@@ -233,7 +235,6 @@
 void auth_master_connection_destroy(struct auth_master_connection *conn)
 {
 	struct auth_listener **l;
-	size_t i, size;
 
 	if (conn->destroyed)
 		return;
@@ -244,16 +245,9 @@
 	if (conn->fd != -1)
 		auth_master_connection_close(conn);
 
-	l = buffer_get_modifyable_data(conn->listeners_buf, &size);
-	size /= sizeof(*l);
-	for (i = 0; i < size; i++) {
-		net_disconnect(l[i]->fd);
-		io_remove(l[i]->io);
-		if (l[i]->path != NULL) {
-			(void)unlink(l[i]->path);
-			i_free(l[i]->path);
-		}
-		i_free(l[i]);
+	while (conn->listeners_buf->used > 0) {
+		l = buffer_get_modifyable_data(conn->listeners_buf, NULL);
+		auth_listener_destroy(*l);
 	}
 	buffer_free(conn->listeners_buf);
 	conn->listeners_buf = NULL;
@@ -280,30 +274,64 @@
 	fd = net_accept(l->fd, NULL, NULL);
 	if (fd < 0) {
 		if (fd < -1)
-			i_fatal("accept() failed: %m");
+			i_fatal("accept(type %d) failed: %m", l->type);
 	} else {
 		net_set_nonblock(fd, TRUE);
-		if (l->client_listener)
+		switch (l->type) {
+		case LISTENER_CLIENT:
 			(void)auth_client_connection_create(l->master, fd);
-		else {
+			break;
+		case LISTENER_MASTER:
 			/* we'll just replace the previous master.. */
 			auth_master_connection_set_fd(l->master, fd);
-                        auth_master_connection_send_handshake(l->master);
+			auth_master_connection_send_handshake(l->master);
+			break;
+		case LISTENER_BALANCER:
+			/* worker process connected to us */
+			auth_request_balancer_add_child(fd);
+			auth_listener_destroy(l);
+			break;
 		}
 	}
 }
 
 void auth_master_connection_add_listener(struct auth_master_connection *conn,
-					 int fd, const char *path, int client)
+					 int fd, const char *path,
+					 enum listener_type type)
 {
 	struct auth_listener *l;
 
 	l = i_new(struct auth_listener, 1);
 	l->master = conn;
-	l->client_listener = client;
+	l->type = type;
 	l->fd = fd;
 	l->path = i_strdup(path);
 	l->io = io_add(fd, IO_READ, auth_accept, l);
 
 	buffer_append(conn->listeners_buf, &l, sizeof(l));
 }
+
+static void auth_listener_destroy(struct auth_listener *l)
+{
+	struct auth_listener **lp;
+	size_t i, size;
+
+	lp = buffer_get_modifyable_data(l->master->listeners_buf, &size);
+	size /= sizeof(*lp);
+
+	for (i = 0; i < size; i++) {
+		if (lp[i] == l) {
+			buffer_delete(l->master->listeners_buf,
+				      i * sizeof(l), sizeof(l));
+			break;
+		}
+	}
+
+	net_disconnect(l->fd);
+	io_remove(l->io);
+	if (l->path != NULL) {
+		(void)unlink(l->path);
+		i_free(l->path);
+	}
+	i_free(l);
+}

Index: auth-master-connection.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/auth-master-connection.h,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -d -r1.8 -r1.9
--- auth-master-connection.h	9 Jan 2005 00:49:18 -0000	1.8
+++ auth-master-connection.h	9 Jan 2005 16:54:48 -0000	1.9
@@ -1,6 +1,12 @@
 #ifndef __AUTH_MASTER_CONNECTION_H
 #define __AUTH_MASTER_CONNECTION_H
 
+enum listener_type {
+	LISTENER_MASTER,
+	LISTENER_CLIENT,
+	LISTENER_BALANCER
+};
+
 struct auth_master_connection {
 	struct auth *auth;
 
@@ -30,6 +36,7 @@
 void auth_master_request_callback(const char *reply, void *context);
 
 void auth_master_connection_add_listener(struct auth_master_connection *conn,
-					 int fd, const char *path, int client);
+					 int fd, const char *path,
+					 enum listener_type type);
 
 #endif

--- NEW FILE: auth-request-balancer-child.c ---
/* Copyright (C) 2002-2005 Timo Sirainen */

#include "common.h"
#include "ioloop.h"
#include "network.h"
#include "buffer.h"
#include "hash.h"
#include "istream.h"
#include "ostream.h"
#include "auth-request-balancer.h"

#include <stdlib.h>
#include <unistd.h>

struct auth_balancer_child {
	unsigned int id;
	int fd;

	struct io *io;
	struct istream *input;
	struct ostream *output;
};

static buffer_t *balancer_children;
static struct hash_table *balancer_handlers;
static unsigned int balancer_next_idx = 0;

static void
auth_request_balancer_remove_child(struct auth_balancer_child *child);

static void balancer_input(void *context)
{
	struct auth_balancer_child *child = context;
	struct auth_request_handler *handler;
	const char *line, *id_str;
	unsigned int id;

	switch (i_stream_read(child->input)) {
	case 0:
		return;
	case -1:
		/* disconnected */
		auth_request_balancer_remove_child(child);
		return;
	case -2:
		/* buffer full */
		i_error("BUG: Auth balancer child sent us more than %d bytes",
			(int)AUTH_BALANCER_MAX_LINE_LENGTH);
		auth_request_balancer_remove_child(child);
		return;
	}

	while ((line = i_stream_next_line(child->input)) != NULL) {
		id_str = line;
		line = strchr(line, '\t');
		if (line == NULL)
			continue;

		t_push();
		id = (unsigned int)strtoul(t_strcut(id_str, '\t'), NULL, 10);
		handler = hash_lookup(balancer_handlers, POINTER_CAST(id));
		t_pop();

		if (handler != NULL) {
			auth_request_handler_balancer_reply(handler,
							    line + 1);
		}
	}
}

static int balancer_output(void *context)
{
	struct auth_balancer_child *child = context;

	if (o_stream_flush(child->output) < 0) {
		auth_request_balancer_remove_child(child);
		return 1;
	}

	/* FIXME: throttle control.. */
	return 1;
}

void auth_request_balancer_add_child(int fd)
{
	static unsigned int balancer_id_counter = 0;
	struct auth_balancer_child *child;

	net_set_nonblock(fd, TRUE);

	child = i_new(struct auth_balancer_child, 1);
	child->id = ++balancer_id_counter;
	child->fd = fd;
	child->input =
		i_stream_create_file(fd, default_pool,
				     AUTH_BALANCER_MAX_LINE_LENGTH, FALSE);
	child->output =
		o_stream_create_file(fd, default_pool, (size_t)-1, FALSE);
	o_stream_set_flush_callback(child->output, balancer_output, child);
	child->io = io_add(fd, IO_READ, balancer_input, child);

	buffer_append(balancer_children, &child, sizeof(child));
}

static void
auth_request_balancer_remove_child(struct auth_balancer_child *child)
{
	struct auth_balancer_child **children;
	size_t i, size;

	children = buffer_get_modifyable_data(balancer_children, &size);
	size /= sizeof(*children);

	for (i = 0; i < size; i++) {
		if (children[i] == child) {
			buffer_delete(balancer_children,
				      i * sizeof(child), sizeof(child));
			break;
		}
	}
	i_assert(i != size);

	if (child->io != NULL)
		io_remove(child->io);

	i_stream_unref(child->input);
	o_stream_unref(child->output);

	if (close(child->fd) < 0)
		i_error("close(balancer) failed: %m");
	i_free(child);
}

static void balancer_send(struct auth_balancer_child *child, const char *line)
{
	struct const_iovec iov[2];

	iov[0].iov_base = line;
	iov[0].iov_len = strlen(line);
	iov[1].iov_base = "\n";
	iov[1].iov_len = 1;

	(void)o_stream_sendv(child->output, iov, 2);
	/* FIXME: throttle control */
}

unsigned int auth_request_balancer_send(const char *line)
{
	struct auth_balancer_child **child, *min_child;
	size_t size, used_size, min_size;
	unsigned int i, start;

	child = buffer_get_modifyable_data(balancer_children, &size);
	size /= sizeof(*child);

	start = i = balancer_next_idx % size;
	balancer_next_idx++;

	min_size = (size_t)-1;
	min_child = NULL;
	do {
		used_size = o_stream_get_buffer_used_size(child[i]->output);
		if (used_size == 0) {
			/* nothing in output buffer, use this */
			balancer_send(child[i], line);
			return child[i]->id;
		}
		if (used_size < min_size) {
			min_size = used_size;
			min_child = child[i];
		}
	} while (++i != start);

	/* min_child has the smallest amount of data in output buffer */
	balancer_send(min_child, line);
	return min_child->id;
}

void auth_request_balancer_send_to(unsigned int id, const char *line)
{
	struct auth_balancer_child **child;
	size_t i, size;

	child = buffer_get_modifyable_data(balancer_children, &size);
	size /= sizeof(*child);

	for (i = 0; i < size; i++) {
		if (child[i]->id == id) {
			balancer_send(child[i], line);
			return;
		}
	}

	// FIXME: ?
}

void auth_request_balancer_add_handler(struct auth_request_handler *handler,
				       unsigned int connect_uid)
{
	hash_insert(balancer_handlers, POINTER_CAST(connect_uid), handler);
}

void auth_request_balancer_remove_handler(unsigned int connect_uid)
{
	hash_remove(balancer_handlers, POINTER_CAST(connect_uid));
}

void auth_request_balancer_child_init(void)
{
	balancer_children = buffer_create_dynamic(default_pool, 32);
	balancer_handlers =
		hash_create(default_pool, default_pool, 0, NULL, NULL);
}

void auth_request_balancer_child_deinit(void)
{
	while (balancer_children->used > 0) {
		struct auth_balancer_child **child;

		child = buffer_get_modifyable_data(balancer_children, NULL);
		auth_request_balancer_remove_child(*child);
	}
	buffer_free(balancer_children);
	hash_destroy(balancer_handlers);
}

--- NEW FILE: auth-request-balancer-worker.c ---
/* Copyright (C) 2002-2005 Timo Sirainen */

#include "common.h"
#include "ioloop.h"
#include "network.h"
#include "istream.h"
#include "ostream.h"
#include "safe-memset.h"
#include "auth-request-handler.h"
#include "auth-request-balancer.h"

#include <stdlib.h>
#include <unistd.h>

struct auth_balancer_worker {
	int fd;

	struct auth_request_handler *request_handler;

	struct io *io;
	struct istream *input;
	struct ostream *output;
};

static unsigned int next_uint(const char **line)
{
	const char *p, *value = *line;

	p = strchr(*line, '\t');
	if (p == NULL)
		*line += strlen(value);
	else {
		value = t_strdup_until(value, p);
		*line = p + 1;
	}
	return (unsigned int)strtoul(value, NULL, 10);
}

static char *balancer_socket_path;
static struct timeout *to_connect;

static void
auth_client_handle_line(struct auth_balancer_worker *worker, const char *line)
{
	struct auth_request_handler *rh = worker->request_handler;
	unsigned int connect_uid, client_pid, id;

	connect_uid = next_uint(&line);
	client_pid = next_uint(&line);

        auth_request_handler_set(rh, connect_uid, client_pid);

	if (strncmp(line, "AUTH\t", 5) == 0)
		(void)auth_request_handler_auth_begin(rh, line + 5);
	else if (strncmp(line, "CONT\t", 5) == 0)
		(void)auth_request_handler_auth_continue(rh, line + 5);
	else if (strncmp(line, "REQUEST\t", 8) == 0) {
		id = (unsigned int)strtoul(line + 8, NULL, 10);
		(void)auth_request_handler_master_request(rh, id, id);
	}
}

static void balancer_worker_input(void *context)
{
	struct auth_balancer_worker *worker = context;
	char *line;

	switch (i_stream_read(worker->input)) {
	case 0:
		return;
	case -1:
		/* disconnected */
		auth_request_balancer_worker_destroy(worker);
		return;
	case -2:
		/* buffer full */
		i_error("BUG: Auth balancer server sent us more than %d bytes",
			(int)AUTH_BALANCER_MAX_LINE_LENGTH);
		auth_request_balancer_worker_destroy(worker);
		return;
	}

	while ((line = i_stream_next_line(worker->input)) != NULL) {
		t_push();

		auth_client_handle_line(worker, line);
		safe_memset(line, 0, strlen(line));

		t_pop();
	}
}

static int balancer_worker_output(void *context)
{
	struct auth_balancer_worker *worker = context;

	if (o_stream_flush(worker->output) < 0) {
		auth_request_balancer_worker_destroy(worker);
		return 1;
	}

	/* FIXME: throttle control.. */
	return 1;
}

static void auth_callback(const char *reply, void *context)
{
	struct auth_balancer_worker *worker = context;
	struct const_iovec iov[2];

	if (reply == NULL) {
		/* request handler was destroyed */
		return;
	}

	iov[0].iov_base = reply;
	iov[0].iov_len = strlen(reply);
	iov[1].iov_base = "\n";
	iov[1].iov_len = 1;

	(void)o_stream_sendv(worker->output, iov, 2);
}

void auth_request_balancer_add_worker(struct auth *auth, int fd)
{
	struct auth_balancer_worker *worker;

	net_set_nonblock(fd, TRUE);

	worker = i_new(struct auth_balancer_worker, 1);
	worker->fd = fd;
	worker->input =
		i_stream_create_file(fd, default_pool,
				     AUTH_BALANCER_MAX_LINE_LENGTH, FALSE);
	worker->output =
		o_stream_create_file(fd, default_pool, (size_t)-1, FALSE);
	o_stream_set_flush_callback(worker->output, balancer_worker_output,
				    worker);
	worker->io = io_add(fd, IO_READ, balancer_worker_input, worker);

	worker->request_handler =
		auth_request_handler_create(auth, TRUE, auth_callback, worker,
					    auth_callback, worker);

	i_assert(auth->balancer_worker == NULL);
        auth->balancer_worker = worker;
}

void auth_request_balancer_worker_destroy(struct auth_balancer_worker *worker)
{
	io_loop_stop(ioloop);
	auth_request_handler_unref(worker->request_handler);

	if (worker->io != NULL)
		io_remove(worker->io);

	i_stream_unref(worker->input);
	o_stream_unref(worker->output);

	if (close(worker->fd) < 0)
		i_error("close(balancer) failed: %m");
	i_free(worker);
}

static int auth_request_balancer_connect(struct auth *auth)
{
	int fd;

	fd = net_connect_unix(balancer_socket_path);
	if (fd < 0) {
		if (errno != EAGAIN) {
			i_fatal("net_connect_unix(%s) failed: %m",
				balancer_socket_path);
		}
		/* busy */
		return FALSE;
	}

	auth_request_balancer_add_worker(auth, fd);
	return TRUE;
}

static void balancer_connect_timeout(void *context)
{
	struct auth *auth = context;

	if (auth_request_balancer_connect(auth)) {
		timeout_remove(to_connect);
		to_connect = NULL;
	}
}

void auth_request_balancer_worker_init(struct auth *auth)
{
	const char *name;

	name = getenv("AUTH_NAME");
	if (name == NULL) name = "auth";
	balancer_socket_path = i_strconcat(name, "-balancer", NULL);

	if (!auth_request_balancer_connect(auth)) {
		/* couldn't connect to balancer yet, it's probably still
		   starting. try again later. */
		to_connect = timeout_add(1000, balancer_connect_timeout, auth);
	}
}

void auth_request_balancer_worker_deinit(void)
{
	if (to_connect != NULL)
		timeout_remove(to_connect);
	i_free(balancer_socket_path);
}

--- NEW FILE: auth-request-balancer.h ---
#ifndef __AUTH_REQUEST_BALANCER_H
#define __AUTH_REQUEST_BALANCER_H

#include "auth-client-interface.h"

#define AUTH_BALANCER_MAX_LINE_LENGTH \
        (AUTH_CLIENT_MAX_LINE_LENGTH + 64)

struct auth_request_handler;

void auth_request_balancer_add_child(int fd);
void auth_request_balancer_add_worker(struct auth *auth, int fd);
void auth_request_balancer_worker_destroy(struct auth_balancer_worker *worker);

void auth_request_balancer_add_handler(struct auth_request_handler *handler,
				       unsigned int connect_uid);
void auth_request_balancer_remove_handler(unsigned int connect_uid);

unsigned int auth_request_balancer_send(const char *line);
void auth_request_balancer_send_to(unsigned int id, const char *line);

void auth_request_handler_balancer_reply(struct auth_request_handler *handler,
					 const char *line);

void auth_request_balancer_child_init(void);
void auth_request_balancer_child_deinit(void);

void auth_request_balancer_worker_init(struct auth *auth);
void auth_request_balancer_worker_deinit(void);

#endif

--- NEW FILE: auth-request-handler-balancer.c ---
/* Copyright (C) 2005 Timo Sirainen */

#include "common.h"
#include "ioloop.h"
#include "hash.h"
#include "str.h"
#include "str-sanitize.h"
#include "mech.h"
#include "auth-client-interface.h"
#include "auth-request-handler.h"
#include "auth-request-balancer.h"

#include <stdlib.h>

struct auth_balancer_request {
	unsigned int client_id;
	unsigned int master_id;
	unsigned int balancer_pid;
	time_t created;
};

struct auth_request_handler {
	int refcount;
	pool_t pool;
	struct hash_table *client_requests;

        struct auth *auth;
	unsigned int connect_uid, client_pid;

	auth_request_callback_t *callback;
	void *context;

	auth_request_callback_t *master_callback;
	void *master_context;
};

static struct auth_request_handler *
_create(struct auth *auth, int prepend_connect_uid,
	auth_request_callback_t *callback, void *context,
	auth_request_callback_t *master_callback, void *master_context)
{
	struct auth_request_handler *handler;
	pool_t pool;

	i_assert(!prepend_connect_uid);

	pool = pool_alloconly_create("auth request handler", 4096);

	handler = p_new(pool, struct auth_request_handler, 1);
	handler->refcount = 1;
	handler->pool = pool;
	handler->client_requests =
		hash_create(default_pool, pool, 0, NULL, NULL);
	handler->auth = auth;
	handler->callback = callback;
	handler->context = context;
	handler->master_callback = master_callback;
	handler->master_context = master_context;

	return handler;
}

static void _set(struct auth_request_handler *handler,
		 unsigned int connect_uid, unsigned int client_pid)
{
	i_assert(handler->connect_uid == 0);

	handler->connect_uid = connect_uid;
	handler->client_pid = client_pid;

	auth_request_balancer_add_handler(handler, connect_uid);
}

static void _unref(struct auth_request_handler *handler)
{
	struct hash_iterate_context *iter;
	void *key, *value;

	i_assert(handler->refcount > 0);
	if (--handler->refcount > 0)
		return;

	auth_request_balancer_remove_handler(handler->connect_uid);

	iter = hash_iterate_init(handler->client_requests);
	while (hash_iterate(iter, &key, &value))
		i_free(value);
	hash_iterate_deinit(iter);

	/* notify parent that we're done with all requests */
	handler->callback(NULL, handler->context);

	hash_destroy(handler->client_requests);
	pool_unref(handler->pool);
}

static void auth_request_handler_remove(struct auth_request_handler *handler,
					struct auth_balancer_request *request)
{
	hash_remove(handler->client_requests, POINTER_CAST(request->client_id));
	i_free(request);
}

static void _check_timeouts(struct auth_request_handler *handler)
{
	struct hash_iterate_context *iter;
	void *key, *value;

	iter = hash_iterate_init(handler->client_requests);
	while (hash_iterate(iter, &key, &value)) {
		struct auth_balancer_request *request = value;

		if (request->created + AUTH_REQUEST_TIMEOUT < ioloop_time)
			auth_request_handler_remove(handler, request);
	}
	hash_iterate_deinit(iter);
}

static int _auth_begin(struct auth_request_handler *handler, const char *args)
{
	struct auth_balancer_request *request;
	struct mech_module *mech;
	const char *mech_name;
	string_t *str;
	unsigned int id;

	/* <id> <mechanism> [...] */
	id = (unsigned int)strtoul(t_strcut(args, '\t'), NULL, 10);
	mech_name = strchr(args, '\t');
	if (mech_name == NULL) {
		i_error("BUG: Authentication client %u "
			"sent broken AUTH request", handler->client_pid);
		return FALSE;
	}
	args = strchr(++mech_name, '\t');
	mech_name = t_strcut(mech_name, '\t');

	mech = mech_module_find(mech_name);
	if (mech == NULL) {
		/* unsupported mechanism */
		i_error("BUG: Authentication client %u requested unsupported "
			"authentication mechanism %s", handler->client_pid,
			str_sanitize(mech_name, MAX_MECH_NAME_LEN));
		return FALSE;
	}

	request = i_new(struct auth_balancer_request, 1);
	request->created = ioloop_time;
	request->client_id = id;

	str = t_str_new(256);
	str_printfa(str, "%u\t%u\tAUTH\t%u\t%s%s",
		    handler->connect_uid, handler->client_pid,
		    id, mech->mech_name, args == NULL ? "" : args);
	request->balancer_pid = auth_request_balancer_send(str_c(str));

	hash_insert(handler->client_requests, POINTER_CAST(id), request);
	return TRUE;
}

static int
_auth_continue(struct auth_request_handler *handler, const char *args)
{
	struct auth_balancer_request *request;
	const char *data;
	string_t *str;
	unsigned int id;

	data = strchr(args, '\t');
	if (data++ == NULL) {
		i_error("BUG: Authentication client sent broken CONT request");
		return FALSE;
	}

	id = (unsigned int)strtoul(args, NULL, 10);

	request = hash_lookup(handler->client_requests, POINTER_CAST(id));
	if (request == NULL) {
		data = t_strdup_printf("FAIL\t%u\treason=Timeouted", id);
		handler->callback(data, handler->context);
		return TRUE;
	}

	str = t_str_new(128);
	str_printfa(str, "%u\t%u\tCONT\t%u\t%s",
		    handler->connect_uid, handler->client_pid, id, data);
	auth_request_balancer_send_to(request->balancer_pid, str_c(str));
	return TRUE;
}

static void _master_request(struct auth_request_handler *handler,
			    unsigned int id, unsigned int client_id)
{
	struct auth_balancer_request *request;
	const char *reply;
	string_t *str;

	request = hash_lookup(handler->client_requests,
			      POINTER_CAST(client_id));
	if (request == NULL || request->balancer_pid == 0) {
		i_error("Master request %u.%u not found from balancer",
			handler->client_pid, client_id);
		reply = t_strdup_printf("NOTFOUND\t%u", id);
		handler->master_callback(reply, handler->master_context);
		return;
	}

	request->master_id = id;

	str = t_str_new(128);
	str_printfa(str, "%u\t%u\tREQUEST\t%u",
		    handler->connect_uid, handler->client_pid, client_id);
	auth_request_balancer_send_to(request->balancer_pid, str_c(str));
}

static void _flush_failures(void)
{
}

static void _init(void)
{
        auth_request_balancer_child_init();
}

static void _deinit(void)
{
        auth_request_balancer_child_deinit();
}

struct auth_request_handler_api auth_request_handler_balancer = {
	_create,
	_unref,
	_set,
	_check_timeouts,
	_auth_begin,
	_auth_continue,
	_master_request,
	_flush_failures,
	_init,
	_deinit
};

void auth_request_handler_balancer_reply(struct auth_request_handler *handler,
					 const char *line)
{
	struct auth_balancer_request *request;
	const char *cmd, *id_str, *args;
	unsigned int id;

	/* <cmd> <id> [...] */
	args = strchr(line, '\t');
	if (args == NULL) {
		i_error("Balancer worker sent invalid reply: %s", line);
		return;
	}

	cmd = t_strdup_until(line, args);
	id_str = args + 1;
	args = strchr(id_str, '\t');
	if (args == NULL)
		args = "";
	else
		id_str = t_strdup_until(id_str, args);

	id = (unsigned int)strtoul(id_str, NULL, 10);
	request = hash_lookup(handler->client_requests, POINTER_CAST(id));
	if (request == NULL) {
		i_error("Balancer worker sent unknown request %u", id);
		return;
	}

	if (request->master_id == 0) {
		handler->callback(line, handler->context);
		if (strcmp(cmd, "CONT") != 0 &&
		    (strcmp(cmd, "OK") != 0 ||
		     strstr(line, "\tnologin") != NULL ||
		     handler->master_callback == NULL)) {
			/* this request doesn't have to wait for master
			   process to pick it up. delete it */
			auth_request_handler_remove(handler, request);
		}
	} else {
		/* replace client id with master id */
		line = t_strdup_printf("%s\t%u%s", cmd,
				       request->master_id, args);
		handler->master_callback(line, handler->master_context);
		auth_request_handler_remove(handler, request);
	}
}

--- NEW FILE: auth-request-handler-default.c ---
/* Copyright (C) 2005 Timo Sirainen */

#include "common.h"
#include "ioloop.h"
#include "buffer.h"
#include "base64.h"
#include "hash.h"
#include "str.h"
#include "str-sanitize.h"
#include "auth-request.h"
#include "auth-request-handler.h"

#include <stdlib.h>

struct auth_request_handler {
	int refcount;
	pool_t pool;
	struct hash_table *requests;

        struct auth *auth;
        unsigned int connect_uid, client_pid;

	auth_request_callback_t *callback;
	void *context;

	auth_request_callback_t *master_callback;
	void *master_context;

	unsigned int prepend_connect_uid:1;
};

static buffer_t *auth_failures_buf;
static struct timeout *to_auth_failures;

static struct auth_request_handler *
_create(struct auth *auth, int prepend_connect_uid,
	auth_request_callback_t *callback, void *context,
	auth_request_callback_t *master_callback, void *master_context)
{
	struct auth_request_handler *handler;
	pool_t pool;

	pool = pool_alloconly_create("auth request handler", 4096);

	handler = p_new(pool, struct auth_request_handler, 1);
	handler->refcount = 1;
	handler->pool = pool;
	handler->requests = hash_create(default_pool, pool, 0, NULL, NULL);
	handler->auth = auth;
	handler->callback = callback;
	handler->context = context;
	handler->master_callback = master_callback;
	handler->master_context = master_context;
	handler->prepend_connect_uid = prepend_connect_uid;
	return handler;
}

static void _set(struct auth_request_handler *handler,
		 unsigned int connect_uid, unsigned int client_pid)
{
	handler->connect_uid = connect_uid;
	handler->client_pid = client_pid;
}

static void _unref(struct auth_request_handler *handler)
{
	struct hash_iterate_context *iter;
	void *key, *value;

	i_assert(handler->refcount > 0);
	if (--handler->refcount > 0)
		return;

	iter = hash_iterate_init(handler->requests);
	while (hash_iterate(iter, &key, &value))
		auth_request_unref(value);
	hash_iterate_deinit(iter);

	/* notify parent that we're done with all requests */
	handler->callback(NULL, handler->context);

	hash_destroy(handler->requests);
	pool_unref(handler->pool);
}

static void auth_request_handler_remove(struct auth_request_handler *handler,
					struct auth_request *request)
{
	hash_remove(handler->requests, POINTER_CAST(request->id));
	auth_request_unref(request);
}

static void _check_timeouts(struct auth_request_handler *handler)
{
	struct hash_iterate_context *iter;
	void *key, *value;

	iter = hash_iterate_init(handler->requests);
	while (hash_iterate(iter, &key, &value)) {
		struct auth_request *request = value;

		if (request->created + AUTH_REQUEST_TIMEOUT < ioloop_time)
			auth_request_handler_remove(handler, request);
	}
	hash_iterate_deinit(iter);
}

static const char *get_client_extra_fields(struct auth_request *request)
{
	const char **fields;
	unsigned int src, dest;

	if (request->extra_fields == NULL)
		return NULL;

	/* we only wish to remove all fields prefixed with "userdb_" */
	if (strstr(request->extra_fields, "userdb_") == NULL)
		return request->extra_fields;

	fields = t_strsplit(request->extra_fields, "\t");
	for (src = dest = 0; fields[src] != NULL; src++) {
		if (strncmp(fields[src], "userdb_", 7) == 0)
			fields[dest++] = fields[src];
	}
	fields[dest] = NULL;
	return t_strarray_join(fields, "\t");
}

static void auth_callback(struct auth_request *request,
			  enum auth_client_result result,
			  const void *reply, size_t reply_size)
{
        struct auth_request_handler *handler = request->context;
	string_t *str;
	const char *fields;

	t_push();

	str = t_str_new(128 + MAX_BASE64_ENCODED_SIZE(reply_size));
	if (handler->prepend_connect_uid)
		str_printfa(str, "%u\t", request->connect_uid);

	switch (result) {
	case AUTH_CLIENT_RESULT_CONTINUE:
		str_printfa(str, "CONT\t%u\t", request->id);
		base64_encode(reply, reply_size, str);
                request->accept_input = TRUE;
		handler->callback(str_c(str), handler->context);
		break;
	case AUTH_CLIENT_RESULT_SUCCESS:
		str_printfa(str, "OK\t%u\tuser=%s", request->id, request->user);
		if (reply_size > 0) {
			str_append(str, "\tresp=");
			base64_encode(reply, reply_size, str);
		}
		fields = get_client_extra_fields(request);
		if (fields != NULL) {
			str_append_c(str, '\t');
			str_append(str, fields);
		}

		if (request->no_login || handler->master_callback == NULL) {
			/* this request doesn't have to wait for master
			   process to pick it up. delete it */
			auth_request_handler_remove(handler, request);
		}
		handler->callback(str_c(str), handler->context);
		break;
	case AUTH_CLIENT_RESULT_FAILURE:
		str_printfa(str, "FAIL\t%u", request->id);
		if (request->user != NULL)
			str_printfa(str, "\tuser=%s", request->user);
		if (request->internal_failure)
			str_append(str, "\ttemp");
		fields = get_client_extra_fields(request);
		if (fields != NULL) {
			str_append_c(str, '\t');
			str_append(str, fields);
		}

		if (request->delayed_failure) {
			/* we came here from flush_failures() */
			handler->callback(str_c(str), handler->context);
			break;
		}

		/* remove the request from requests-list */
		auth_request_ref(request);
		auth_request_handler_remove(handler, request);

		if (request->no_failure_delay) {
			/* passdb specifically requested not to delay the
			   reply. */
			handler->callback(str_c(str), handler->context);
		} else {
			/* failure. don't announce it immediately to avoid
			   a) timing attacks, b) flooding */
			request->delayed_failure = TRUE;
			handler->refcount++;
			buffer_append(auth_failures_buf,
				      &request, sizeof(request));
		}
		break;
	}
	/* NOTE: request may be destroyed now */

        auth_request_handler_unref(handler);

	t_pop();
}

static void auth_request_handler_auth_fail(struct auth_request_handler *handler,
					   struct auth_request *request,
					   const char *reason)
{
	string_t *reply = t_str_new(64);

	auth_request_log_info(request, request->mech->mech_name, "%s", reason);

	if (handler->prepend_connect_uid)
		str_printfa(reply, "%u\t", request->connect_uid);
	str_printfa(reply, "FAIL\t%u\treason=%s", request->id, reason);
	handler->callback(str_c(reply), handler->context);

	auth_request_handler_remove(handler, request);
}

static int _auth_begin(struct auth_request_handler *handler, const char *args)
{
	struct mech_module *mech;
	struct auth_request *request;
	const char *const *list, *name, *arg, *initial_resp;
	const void *initial_resp_data;
	size_t initial_resp_len;
	unsigned int id;
	buffer_t *buf;
	int valid_client_cert;

	/* <id> <mechanism> [...] */
	list = t_strsplit(args, "\t");
	if (list[0] == NULL || list[1] == NULL) {
		i_error("BUG: Authentication client %u "
			"sent broken AUTH request", handler->client_pid);
		return FALSE;
	}

	id = (unsigned int)strtoul(list[0], NULL, 10);

	mech = mech_module_find(list[1]);
	if (mech == NULL) {
		/* unsupported mechanism */
		i_error("BUG: Authentication client %u requested unsupported "
			"authentication mechanism %s", handler->client_pid,
			str_sanitize(list[1], MAX_MECH_NAME_LEN));
		return FALSE;
	}

	request = auth_request_new(handler->auth, mech, auth_callback, handler);
	request->connect_uid = handler->connect_uid;
	request->client_pid = handler->client_pid;
	request->id = id;

	/* parse optional parameters */
	initial_resp = NULL;
	valid_client_cert = FALSE;
	for (list += 2; *list != NULL; list++) {
		arg = strchr(*list, '=');
		if (arg == NULL) {
			name = *list;
			arg = "";
		} else {
			name = t_strdup_until(*list, arg);
			arg++;
		}

		if (strcmp(name, "lip") == 0)
			(void)net_addr2ip(arg, &request->local_ip);
		else if (strcmp(name, "rip") == 0)
			(void)net_addr2ip(arg, &request->remote_ip);
		else if (strcmp(name, "service") == 0)
			request->service = p_strdup(request->pool, arg);
		else if (strcmp(name, "resp") == 0)
			initial_resp = arg;
		else if (strcmp(name, "valid-client-cert") == 0)
			valid_client_cert = TRUE;
	}

	if (request->service == NULL) {
		i_error("BUG: Authentication client %u "
			"didn't specify service in request",
			handler->client_pid);
		auth_request_unref(request);
		return FALSE;
	}

	hash_insert(handler->requests, POINTER_CAST(id), request);

	if (request->auth->ssl_require_client_cert && !valid_client_cert) {
		/* we fail without valid certificate */
                auth_request_handler_auth_fail(handler, request,
			"Client didn't present valid SSL certificate");
		return TRUE;
	}

	if (initial_resp == NULL) {
		initial_resp_data = NULL;
		initial_resp_len = 0;
	} else {
		size_t len = strlen(initial_resp);
		buf = buffer_create_dynamic(pool_datastack_create(),
					    MAX_BASE64_DECODED_SIZE(len));
		if (base64_decode(initial_resp, len, NULL, buf) < 0) {
                        auth_request_handler_auth_fail(handler, request,
				"Invalid base64 data in initial response");
			return TRUE;
		}
		initial_resp_data = buf->data;
		initial_resp_len = buf->used;
	}

	/* handler is referenced until auth_callback is called. */
	handler->refcount++;
	auth_request_initial(request, initial_resp_data, initial_resp_len);
	return TRUE;
}

static int
_auth_continue(struct auth_request_handler *handler, const char *args)
{
	struct auth_request *request;
	const char *data;
	size_t data_len;
	buffer_t *buf;
	unsigned int id;

	data = strchr(args, '\t');
	if (data++ == NULL) {
		i_error("BUG: Authentication client sent broken CONT request");
		return FALSE;
	}

	id = (unsigned int)strtoul(args, NULL, 10);

	request = hash_lookup(handler->requests, POINTER_CAST(id));
	if (request == NULL) {
		string_t *reply = t_str_new(64);

		if (handler->prepend_connect_uid)
			str_printfa(reply, "%u\t", handler->connect_uid);
		str_printfa(reply, "FAIL\t%u\treason=Timeouted", id);
		handler->callback(str_c(reply), handler->context);
		return TRUE;
	}

	/* accept input only once after mechanism has sent a CONT reply */
	if (!request->accept_input) {
		auth_request_handler_auth_fail(handler, request,
					       "Unexpected continuation");
		return TRUE;
	}
	request->accept_input = FALSE;

	data_len = strlen(data);
	buf = buffer_create_dynamic(pool_datastack_create(),
				    MAX_BASE64_DECODED_SIZE(data_len));
	if (base64_decode(data, data_len, NULL, buf) < 0) {
		auth_request_handler_auth_fail(handler, request,
			"Invalid base64 data in continued response");
		return TRUE;
	}

	/* handler is referenced until auth_callback is called. */
	handler->refcount++;
	auth_request_continue(request, buf->data, buf->used);
	return TRUE;
}

static void append_user_reply(string_t *str, const struct user_data *user)
{
	const char *p;

	str_printfa(str, "%s\tuid=%s\tgid=%s", user->virtual_user,
		    dec2str(user->uid), dec2str(user->gid));

	if (user->system_user != NULL)
		str_printfa(str, "\tsystem_user=%s", user->system_user);
	if (user->mail != NULL)
		str_printfa(str, "\tmail=%s", user->mail);

	p = user->home != NULL ? strstr(user->home, "/./") : NULL;
	if (p == NULL) {
		if (user->home != NULL)
			str_printfa(str, "\thome=%s", user->home);
	} else {
		/* wu-ftpd like <chroot>/./<home> */
		str_printfa(str, "\thome=%s\tchroot=%s",
			    p + 3, t_strdup_until(user->home, p));
	}
}

static void userdb_callback(const struct user_data *user, void *context)
{
        struct auth_request *request = context;
        struct auth_request_handler *handler = request->context;
	string_t *reply;

	if (user != NULL) {
		auth_request_log_debug(request, "userdb",
				       "uid=%s gid=%s home=%s mail=%s",
				       dec2str(user->uid), dec2str(user->gid),
				       user->home != NULL ? user->home : "",
				       user->mail != NULL ? user->mail : "");
	}

	reply = t_str_new(256);
	if (handler->prepend_connect_uid)
		str_printfa(reply, "%u\t", request->connect_uid);
	if (user == NULL)
		str_printfa(reply, "NOTFOUND\t%u", request->id);
	else {
		str_printfa(reply, "USER\t%u\t", request->id);
		append_user_reply(reply, user);
	}
	handler->master_callback(str_c(reply), handler->master_context);

	auth_request_unref(request);
        auth_request_handler_unref(handler);
}

static void _master_request(struct auth_request_handler *handler,
			    unsigned int id, unsigned int client_id)
{
	struct auth_request *request;
	string_t *reply;

	reply = t_str_new(64);
	if (handler->prepend_connect_uid)
		str_printfa(reply, "%u\t", handler->connect_uid);

	request = hash_lookup(handler->requests, POINTER_CAST(client_id));
	if (request == NULL) {
		i_error("Master request %u.%u not found",
			handler->client_pid, client_id);
		str_printfa(reply, "NOTFOUND\t%u", id);
		handler->master_callback(str_c(reply), handler->master_context);
		return;
	}

	auth_request_ref(request);
	auth_request_handler_remove(handler, request);

	if (!request->successful) {
		i_error("Master requested unfinished authentication request "
			"%u.%u", handler->client_pid, client_id);
		str_printfa(reply, "NOTFOUND\t%u", id);
		handler->master_callback(str_c(reply), handler->master_context);
	} else {
		/* the request isn't being referenced anywhere anymore,
		   so we can do a bit of kludging.. replace the request's
		   old client_id with master's id. */
		request->id = id;
		request->context = handler;

		/* handler is referenced until userdb_callback is called. */
		handler->refcount++;
		auth_request_lookup_user(request, userdb_callback, request);
	}
}

static void _flush_failures(void)
{
	struct auth_request **auth_request;
	size_t i, size;

	auth_request = buffer_get_modifyable_data(auth_failures_buf, &size);
	size /= sizeof(*auth_request);

	for (i = 0; i < size; i++) {
		auth_request[i]->callback(auth_request[i],
					  AUTH_CLIENT_RESULT_FAILURE, NULL, 0);
		auth_request_unref(auth_request[i]);
	}
	buffer_set_used_size(auth_failures_buf, 0);
}

static void auth_failure_timeout(void *context __attr_unused__)
{
	_flush_failures();
}

static void _init(void)
{
	auth_failures_buf = buffer_create_dynamic(default_pool, 1024);
        to_auth_failures = timeout_add(2000, auth_failure_timeout, NULL);
}

static void _deinit(void)
{
	buffer_free(auth_failures_buf);
	timeout_remove(to_auth_failures);
}

struct auth_request_handler_api auth_request_handler_default = {
	_create,
	_unref,
	_set,
	_check_timeouts,
	_auth_begin,
	_auth_continue,
	_master_request,
	_flush_failures,
	_init,
	_deinit
};

Index: auth-request-handler.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/auth-request-handler.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- auth-request-handler.c	9 Jan 2005 00:49:18 -0000	1.1
+++ auth-request-handler.c	9 Jan 2005 16:54:48 -0000	1.2
@@ -1,486 +1,73 @@
 /* Copyright (C) 2005 Timo Sirainen */
 
 #include "common.h"
-#include "ioloop.h"
-#include "buffer.h"
-#include "base64.h"
-#include "hash.h"
-#include "str.h"
-#include "str-sanitize.h"
-#include "auth-request.h"
 #include "auth-request-handler.h"
+#include "auth-request-balancer.h"
 
-#include <stdlib.h>
-
-/* Used only for string sanitization. */
-#define MAX_MECH_NAME_LEN 64
-
-struct auth_request_handler {
-	int refcount;
-	pool_t pool;
-	struct hash_table *requests;
-
-        struct auth *auth;
-        unsigned int connect_uid, client_pid;
-
-	auth_request_callback_t *callback;
-	void *context;
-
-	auth_request_callback_t *master_callback;
-	void *master_context;
-};
-
-static buffer_t *auth_failures_buf;
-static struct timeout *to_auth_failures;
+struct auth_request_handler_api *auth_request_handler_api;
 
 struct auth_request_handler *
-auth_request_handler_create(struct auth *auth,
-			    unsigned int connect_uid, unsigned int client_pid,
+auth_request_handler_create(struct auth *auth, int prepend_connect_uid,
 			    auth_request_callback_t *callback, void *context,
 			    auth_request_callback_t *master_callback,
 			    void *master_context)
 {
-	struct auth_request_handler *handler;
-	pool_t pool;
-
-	pool = pool_alloconly_create("auth request handler", 4096);
-
-	handler = p_new(pool, struct auth_request_handler, 1);
-	handler->refcount = 1;
-	handler->pool = pool;
-	handler->requests = hash_create(default_pool, pool, 0, NULL, NULL);
-	handler->auth = auth;
-	handler->connect_uid = connect_uid;
-	handler->client_pid = client_pid;
-	handler->callback = callback;
-	handler->context = context;
-	handler->master_callback = master_callback;
-	handler->master_context = master_context;
-	return handler;
-}
-
-void auth_request_handler_unref(struct auth_request_handler *handler)
-{
-	struct hash_iterate_context *iter;
-	void *key, *value;
-
-	i_assert(handler->refcount > 0);
-	if (--handler->refcount > 0)
-		return;
-
-	iter = hash_iterate_init(handler->requests);
-	while (hash_iterate(iter, &key, &value))
-		auth_request_unref(value);
-	hash_iterate_deinit(iter);
-
-	/* notify parent that we're done with all requests */
-	handler->callback(NULL, handler->context);
-
-	hash_destroy(handler->requests);
-	pool_unref(handler->pool);
-}
-
-void auth_request_handler_check_timeouts(struct auth_request_handler *handler)
-{
-	struct hash_iterate_context *iter;
-	void *key, *value;
-
-	iter = hash_iterate_init(handler->requests);
-	while (hash_iterate(iter, &key, &value)) {
-		struct auth_request *auth_request = value;
-
-		if (auth_request->created + AUTH_REQUEST_TIMEOUT < ioloop_time)
-			hash_remove(handler->requests, key);
-	}
-	hash_iterate_deinit(iter);
-}
-
-static void auth_request_handler_remove(struct auth_request_handler *handler,
-					struct auth_request *request)
-{
-	hash_remove(handler->requests, POINTER_CAST(request->id));
-	auth_request_unref(request);
+	return auth_request_handler_api->
+		create(auth, prepend_connect_uid, callback, context,
+		       master_callback, master_context);
 }
 
-static const char *get_client_extra_fields(struct auth_request *request)
+void auth_request_handler_set(struct auth_request_handler *handler,
+			      unsigned int connect_uid,
+			      unsigned int client_pid)
 {
-	const char **fields;
-	unsigned int src, dest;
-
-	if (request->extra_fields == NULL)
-		return NULL;
-
-	/* we only wish to remove all fields prefixed with "userdb_" */
-	if (strstr(request->extra_fields, "userdb_") == NULL)
-		return request->extra_fields;
-
-	fields = t_strsplit(request->extra_fields, "\t");
-	for (src = dest = 0; fields[src] != NULL; src++) {
-		if (strncmp(fields[src], "userdb_", 7) == 0)
-			fields[dest++] = fields[src];
-	}
-	fields[dest] = NULL;
-	return t_strarray_join(fields, "\t");
+	auth_request_handler_api->set(handler, connect_uid, client_pid);
 }
 
-static void auth_callback(struct auth_request *request,
-			  enum auth_client_result result,
-			  const void *reply, size_t reply_size)
+void auth_request_handler_unref(struct auth_request_handler *handler)
 {
-        struct auth_request_handler *handler = request->context;
-	string_t *str = NULL;
-	const char *fields;
-
-	t_push();
-
-	switch (result) {
-	case AUTH_CLIENT_RESULT_CONTINUE:
-		str = t_str_new(32 + MAX_BASE64_ENCODED_SIZE(reply_size));
-		str_printfa(str, "CONT\t%u\t", request->id);
-		base64_encode(reply, reply_size, str);
-                request->accept_input = TRUE;
-		handler->callback(str_c(str), handler->context);
-		break;
-	case AUTH_CLIENT_RESULT_SUCCESS:
-		str = t_str_new(128 + MAX_BASE64_ENCODED_SIZE(reply_size));
-		str_printfa(str, "OK\t%u\tuser=%s", request->id, request->user);
-		if (reply_size > 0) {
-			str_append(str, "\tresp=");
-			base64_encode(reply, reply_size, str);
-		}
-		fields = get_client_extra_fields(request);
-		if (fields != NULL) {
-			str_append_c(str, '\t');
-			str_append(str, fields);
-		}
-
-		if (request->no_login || handler->master_callback == NULL) {
-			/* this request doesn't have to wait for master
-			   process to pick it up. delete it */
-			auth_request_handler_remove(handler, request);
-		}
-		handler->callback(str_c(str), handler->context);
-		break;
-	case AUTH_CLIENT_RESULT_FAILURE:
-		str = t_str_new(128);
-		str_printfa(str, "FAIL\t%u", request->id);
-		if (request->user != NULL)
-			str_printfa(str, "\tuser=%s", request->user);
-		if (request->internal_failure)
-			str_append(str, "\ttemp");
-		fields = get_client_extra_fields(request);
-		if (fields != NULL) {
-			str_append_c(str, '\t');
-			str_append(str, fields);
-		}
-
-		if (request->delayed_failure) {
-			/* we came here from flush_failures() */
-			handler->callback(str_c(str), handler->context);
-			break;
-		}
-
-		/* remove the request from requests-list */
-		auth_request_ref(request);
-		auth_request_handler_remove(handler, request);
-
-		if (request->no_failure_delay) {
-			/* passdb specifically requested not to delay the
-			   reply. */
-			handler->callback(str_c(str), handler->context);
-		} else {
-			/* failure. don't announce it immediately to avoid
-			   a) timing attacks, b) flooding */
-			request->delayed_failure = TRUE;
-			handler->refcount++;
-			buffer_append(auth_failures_buf,
-				      &request, sizeof(request));
-		}
-		break;
-	}
-	/* NOTE: request may be destroyed now */
-
-        auth_request_handler_unref(handler);
-
-	t_pop();
+	auth_request_handler_api->unref(handler);
 }
 
-static void auth_request_handler_auth_fail(struct auth_request_handler *handler,
-					   struct auth_request *request,
-					   const char *reason)
+void auth_request_handler_check_timeouts(struct auth_request_handler *handler)
 {
-	const char *reply;
-
-	auth_request_log_info(request, request->mech->mech_name, "%s", reason);
-
-	reply = t_strdup_printf("FAIL\t%u\treason=%s", request->id, reason);
-	handler->callback(reply, handler->context);
-	auth_request_handler_remove(handler, request);
+        auth_request_handler_api->check_timeouts(handler);
 }
 
 int auth_request_handler_auth_begin(struct auth_request_handler *handler,
 				    const char *args)
 {
-	struct mech_module *mech;
-	struct auth_request *request;
-	const char *const *list, *name, *arg, *initial_resp;
-	const void *initial_resp_data;
-	size_t initial_resp_len;
-	unsigned int id;
-	buffer_t *buf;
-	int valid_client_cert;
-
-	/* <id> <mechanism> [...] */
-	list = t_strsplit(args, "\t");
-	if (list[0] == NULL || list[1] == NULL) {
-		i_error("BUG: Authentication client %u "
-			"sent broken AUTH request", handler->client_pid);
-		return FALSE;
-	}
-
-	id = (unsigned int)strtoul(list[0], NULL, 10);
-
-	mech = mech_module_find(list[1]);
-	if (mech == NULL) {
-		/* unsupported mechanism */
-		i_error("BUG: Authentication client %u requested unsupported "
-			"authentication mechanism %s", handler->client_pid,
-			str_sanitize(list[1], MAX_MECH_NAME_LEN));
-		return FALSE;
-	}
-
-	request = auth_request_new(handler->auth, mech, auth_callback, handler);
-	request->connect_uid = handler->connect_uid;
-	request->client_pid = handler->client_pid;
-	request->id = id;
-
-	/* parse optional parameters */
-	initial_resp = NULL;
-	valid_client_cert = FALSE;
-	for (list += 2; *list != NULL; list++) {
-		arg = strchr(*list, '=');
-		if (arg == NULL) {
-			name = *list;
-			arg = "";
-		} else {
-			name = t_strdup_until(*list, arg);
-			arg++;
-		}
-
-		if (strcmp(name, "lip") == 0)
-			(void)net_addr2ip(arg, &request->local_ip);
-		else if (strcmp(name, "rip") == 0)
-			(void)net_addr2ip(arg, &request->remote_ip);
-		else if (strcmp(name, "service") == 0)
-			request->service = p_strdup(request->pool, arg);
-		else if (strcmp(name, "resp") == 0)
-			initial_resp = arg;
-		else if (strcmp(name, "valid-client-cert") == 0)
-			valid_client_cert = TRUE;
-	}
-
-	if (request->service == NULL) {
-		i_error("BUG: Authentication client %u "
-			"didn't specify service in request",
-			handler->client_pid);
-		auth_request_unref(request);
-		return FALSE;
-	}
-
-	hash_insert(handler->requests, POINTER_CAST(id), request);
-
-	if (request->auth->ssl_require_client_cert && !valid_client_cert) {
-		/* we fail without valid certificate */
-                auth_request_handler_auth_fail(handler, request,
-			"Client didn't present valid SSL certificate");
-		return TRUE;
-	}
-
-	if (initial_resp == NULL) {
-		initial_resp_data = NULL;
-		initial_resp_len = 0;
-	} else {
-		size_t len = strlen(initial_resp);
-		buf = buffer_create_dynamic(pool_datastack_create(),
-					    MAX_BASE64_DECODED_SIZE(len));
-		if (base64_decode(initial_resp, len, NULL, buf) < 0) {
-                        auth_request_handler_auth_fail(handler, request,
-				"Invalid base64 data in initial response");
-			return TRUE;
-		}
-		initial_resp_data = buf->data;
-		initial_resp_len = buf->used;
-	}
-
-	/* handler is referenced until auth_callback is called. */
-	handler->refcount++;
-	auth_request_initial(request, initial_resp_data, initial_resp_len);
-	return TRUE;
+        return auth_request_handler_api->auth_begin(handler, args);
 }
 
 int auth_request_handler_auth_continue(struct auth_request_handler *handler,
 				       const char *args)
 {
-	struct auth_request *request;
-	const char *data;
-	size_t data_len;
-	buffer_t *buf;
-	unsigned int id;
-
-	data = strchr(args, '\t');
-	if (data++ == NULL) {
-		i_error("BUG: Authentication client sent broken CONT request");
-		return FALSE;
-	}
-
-	id = (unsigned int)strtoul(args, NULL, 10);
-
-	request = hash_lookup(handler->requests, POINTER_CAST(id));
-	if (request == NULL) {
-		data = t_strdup_printf("FAIL\t%u\treason=Timeouted", id);
-		handler->callback(data, handler->context);
-		return TRUE;
-	}
-
-	/* accept input only once after mechanism has sent a CONT reply */
-	if (!request->accept_input) {
-		auth_request_handler_auth_fail(handler, request,
-					       "Unexpected continuation");
-		return TRUE;
-	}
-	request->accept_input = FALSE;
-
-	data_len = strlen(data);
-	buf = buffer_create_dynamic(pool_datastack_create(),
-				    MAX_BASE64_DECODED_SIZE(data_len));
-	if (base64_decode(data, data_len, NULL, buf) < 0) {
-		auth_request_handler_auth_fail(handler, request,
-			"Invalid base64 data in continued response");
-		return TRUE;
-	}
-
-	/* handler is referenced until auth_callback is called. */
-	handler->refcount++;
-	auth_request_continue(request, buf->data, buf->used);
-	return TRUE;
-}
-
-static void append_user_reply(string_t *str, const struct user_data *user)
-{
-	const char *p;
-
-	str_printfa(str, "%s\tuid=%s\tgid=%s", user->virtual_user,
-		    dec2str(user->uid), dec2str(user->gid));
-
-	if (user->system_user != NULL)
-		str_printfa(str, "\tsystem_user=%s", user->system_user);
-	if (user->mail != NULL)
-		str_printfa(str, "\tmail=%s", user->mail);
-
-	p = user->home != NULL ? strstr(user->home, "/./") : NULL;
-	if (p == NULL) {
-		if (user->home != NULL)
-			str_printfa(str, "\thome=%s", user->home);
-	} else {
-		/* wu-ftpd like <chroot>/./<home> */
-		str_printfa(str, "\thome=%s\tchroot=%s",
-			    p + 3, t_strdup_until(user->home, p));
-	}
-}
-
-static void userdb_callback(const struct user_data *user, void *context)
-{
-        struct auth_request *request = context;
-        struct auth_request_handler *handler = request->context;
-	string_t *reply;
-
-	if (user != NULL) {
-		auth_request_log_debug(request, "userdb",
-				       "uid=%s gid=%s home=%s mail=%s",
-				       dec2str(user->uid), dec2str(user->gid),
-				       user->home != NULL ? user->home : "",
-				       user->mail != NULL ? user->mail : "");
-	}
-
-	reply = t_str_new(256);
-	if (user == NULL)
-		str_printfa(reply, "NOTFOUND\t%u", request->id);
-	else {
-		str_printfa(reply, "USER\t%u\t", request->id);
-		append_user_reply(reply, user);
-	}
-	handler->master_callback(str_c(reply), handler->master_context);
-
-	auth_request_unref(request);
-        auth_request_handler_unref(handler);
+	return auth_request_handler_api->auth_continue(handler, args);
 }
 
 void auth_request_handler_master_request(struct auth_request_handler *handler,
 					 unsigned int id,
 					 unsigned int client_id)
 {
-	struct auth_request *request;
-	const char *reply;
-
-	request = hash_lookup(handler->requests, POINTER_CAST(client_id));
-	if (request == NULL) {
-		i_error("Master request %u.%u not found",
-			handler->client_pid, client_id);
-		reply = t_strdup_printf("NOTFOUND\t%u", id);
-		handler->master_callback(reply, handler->master_context);
-		return;
-	}
-
-	auth_request_ref(request);
-	auth_request_handler_remove(handler, request);
-
-	if (!request->successful) {
-		i_error("Master requested unfinished authentication request "
-			"%u.%u", handler->client_pid, client_id);
-		reply = t_strdup_printf("NOTFOUND\t%u", id);
-		handler->master_callback(reply, handler->master_context);
-	} else {
-		/* the request isn't being referenced anywhere anymore,
-		   so we can do a bit of kludging.. replace the request's
-		   old client_id with master's id. */
-		request->id = id;
-		request->context = handler;
-
-		/* handler is referenced until userdb_callback is called. */
-		handler->refcount++;
-		auth_request_lookup_user(request, userdb_callback, request);
-	}
+        auth_request_handler_api->master_request(handler, id, client_id);
 }
 
 void auth_request_handlers_flush_failures(void)
 {
-	struct auth_request **auth_request;
-	size_t i, size;
-
-	auth_request = buffer_get_modifyable_data(auth_failures_buf, &size);
-	size /= sizeof(*auth_request);
-
-	for (i = 0; i < size; i++) {
-		auth_request[i]->callback(auth_request[i],
-					  AUTH_CLIENT_RESULT_FAILURE, NULL, 0);
-		auth_request_unref(auth_request[i]);
-	}
-	buffer_set_used_size(auth_failures_buf, 0);
+        auth_request_handler_api->flush_failures();
 }
 
-static void auth_failure_timeout(void *context __attr_unused__)
+void auth_request_handlers_init(int balancer)
 {
-	auth_request_handlers_flush_failures();
-}
+	/* use balancer if we have it */
+	auth_request_handler_api = balancer ?
+		&auth_request_handler_balancer : &auth_request_handler_default;
 
-void auth_request_handlers_init(void)
-{
-	auth_failures_buf = buffer_create_dynamic(default_pool, 1024);
-        to_auth_failures = timeout_add(2000, auth_failure_timeout, NULL);
+        auth_request_handler_api->init();
 }
 
 void auth_request_handlers_deinit(void)
 {
-	buffer_free(auth_failures_buf);
-	timeout_remove(to_auth_failures);
+        auth_request_handler_api->deinit();
 }

Index: auth-request-handler.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/auth-request-handler.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- auth-request-handler.h	9 Jan 2005 00:49:18 -0000	1.1
+++ auth-request-handler.h	9 Jan 2005 16:54:48 -0000	1.2
@@ -1,16 +1,50 @@
 #ifndef __AUTH_REQUEST_HANDLER_H
 #define __AUTH_REQUEST_HANDLER_H
 
+struct auth_request;
+
 typedef void auth_request_callback_t(const char *reply, void *context);
 
+struct auth_request_handler_api {
+	struct auth_request_handler *
+		(*create)(struct auth *auth, int prepend_connect_uid,
+			  auth_request_callback_t *callback, void *context,
+			  auth_request_callback_t *master_callback,
+			  void *master_context);
+	void (*unref)(struct auth_request_handler *handler);
+
+	void (*set)(struct auth_request_handler *handler,
+		    unsigned int connect_uid, unsigned int client_pid);
+
+	void (*check_timeouts)(struct auth_request_handler *handler);
+	int (*auth_begin)(struct auth_request_handler *handler,
+			  const char *args);
+	int (*auth_continue)(struct auth_request_handler *handler,
+			     const char *args);
+	void (*master_request)(struct auth_request_handler *handler,
+			       unsigned int id, unsigned int client_id);
+
+	void (*flush_failures)(void);
+
+	void (*init)(void);
+	void (*deinit)(void);
+};
+
+extern struct auth_request_handler_api auth_request_handler_default;
+extern struct auth_request_handler_api auth_request_handler_balancer;
+extern struct auth_request_handler_api *auth_request_handler_api;
+
 struct auth_request_handler *
-auth_request_handler_create(struct auth *auth,
-			    unsigned int connect_uid, unsigned int client_pid,
+auth_request_handler_create(struct auth *auth, int prepend_connect_uid,
 			    auth_request_callback_t *callback, void *context,
 			    auth_request_callback_t *master_callback,
 			    void *master_context);
 void auth_request_handler_unref(struct auth_request_handler *handler);
 
+void auth_request_handler_set(struct auth_request_handler *handler,
+			      unsigned int connect_uid,
+			      unsigned int client_pid);
+
 void auth_request_handler_check_timeouts(struct auth_request_handler *handler);
 
 int auth_request_handler_auth_begin(struct auth_request_handler *handler,
@@ -23,7 +57,7 @@
 
 void auth_request_handlers_flush_failures(void);
 
-void auth_request_handlers_init(void);
+void auth_request_handlers_init(int balancer);
 void auth_request_handlers_deinit(void);
 
 #endif

Index: auth.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/auth.c,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -d -r1.12 -r1.13
--- auth.c	8 Jan 2005 16:56:04 -0000	1.12
+++ auth.c	9 Jan 2005 16:54:48 -0000	1.13
@@ -1,13 +1,18 @@
 /* Copyright (C) 2005 Timo Sirainen */
 
 #include "common.h"
+#include "network.h"
+#include "buffer.h"
 #include "str.h"
 #include "mech.h"
 #include "userdb.h"
 #include "passdb.h"
 #include "auth.h"
+#include "auth-request-handler.h"
+#include "auth-request-balancer.h"
 
 #include <stdlib.h>
+#include <unistd.h>
 
 struct auth *auth_preinit(void)
 {
@@ -169,5 +174,8 @@
 	userdb_deinit(auth);
 	passdb_deinit(auth);
 
+	if (auth->balancer_worker != NULL)
+		auth_request_balancer_worker_destroy(auth->balancer_worker);
+
 	str_free(auth->mech_handshake);
 }

Index: auth.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/auth.h,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -d -r1.10 -r1.11
--- auth.h	8 Jan 2005 16:56:04 -0000	1.10
+++ auth.h	9 Jan 2005 16:54:48 -0000	1.11
@@ -1,7 +1,11 @@
 #ifndef __AUTH_H
 #define __AUTH_H
 
+struct auth_balancer_worker;
+
 struct auth {
+	struct auth_balancer_worker *balancer_worker;
+
 	struct mech_module_list *mech_modules;
 	buffer_t *mech_handshake;
 

Index: common.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/common.h,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -d -r1.10 -r1.11
--- common.h	9 Jan 2005 00:48:02 -0000	1.10
+++ common.h	9 Jan 2005 16:54:48 -0000	1.11
@@ -5,7 +5,8 @@
 #include "auth.h"
 
 #define MASTER_SOCKET_FD 0
-#define LOGIN_LISTEN_FD 3
+#define CLIENT_LISTEN_FD 3
+#define BALANCER_LISTEN_FD 4
 
 extern struct ioloop *ioloop;
 extern int standalone;

Index: main.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/main.c,v
retrieving revision 1.38
retrieving revision 1.39
diff -u -d -r1.38 -r1.39
--- main.c	9 Jan 2005 16:51:03 -0000	1.38
+++ main.c	9 Jan 2005 16:54:48 -0000	1.39
@@ -12,6 +12,8 @@
 #include "mech.h"
 #include "auth.h"
 #include "auth-request-handler.h"
+#include "auth-request-balancer.h"
+#include "auth-master-interface.h"
 #include "auth-master-connection.h"
 #include "auth-client-connection.h"
 
@@ -29,6 +31,7 @@
 
 static buffer_t *masters_buf;
 static struct auth *auth;
+static int balancer = FALSE, balancer_worker = FALSE;
 
 static void sig_quit(int signo __attr_unused__)
 {
@@ -156,11 +159,13 @@
 		master = auth_master_connection_create(auth, -1);
 		if (master_fd != -1) {
 			auth_master_connection_add_listener(master, master_fd,
-							    master_path, FALSE);
+							    master_path,
+							    LISTENER_MASTER);
 		}
 		if (client_fd != -1) {
 			auth_master_connection_add_listener(master, client_fd,
-							    client_path, TRUE);
+							    client_path,
+							    LISTENER_CLIENT);
 		}
 		auth_client_connections_init(master);
 		buffer_append(masters_buf, &master, sizeof(master));
@@ -181,7 +186,8 @@
         password_schemes_init();
 
 	masters_buf = buffer_create_dynamic(default_pool, 64);
-	add_extra_listeners();
+	if (!balancer_worker)
+		add_extra_listeners();
 
 	/* Password lookups etc. may require roots, allow it. */
 	restrict_access_by_env(FALSE);
@@ -192,13 +198,11 @@
 	struct auth_master_connection *master, **master_p;
 	size_t i, size;
 
-	process_start_time = ioloop_time;
-
-	process_start_time = ioloop_time;
+        process_start_time = ioloop_time;
 
 	mech_init();
 	auth_init(auth);
-	auth_request_handlers_init();
+	auth_request_handlers_init(balancer);
 
 	lib_init_signals(sig_quit);
 
@@ -228,12 +232,23 @@
 			if (chdir("/") < 0)
 				i_fatal("chdir(/) failed: %m");
 		}
-       } else {
+	} else if (!balancer_worker) {
 		master = auth_master_connection_create(auth, MASTER_SOCKET_FD);
-		auth_master_connection_add_listener(master, LOGIN_LISTEN_FD,
-						    NULL, TRUE);
+		auth_master_connection_add_listener(master, CLIENT_LISTEN_FD,
+						    NULL, LISTENER_CLIENT);
+		if (balancer) {
+			auth_master_connection_add_listener(master,
+							    BALANCER_LISTEN_FD,
+							    NULL,
+							    LISTENER_BALANCER);
+		}
 		auth_client_connections_init(master);
 		buffer_append(masters_buf, &master, sizeof(master));
+	} else {
+		master = auth_master_connection_create(auth, MASTER_SOCKET_FD);
+		buffer_append(masters_buf, &master, sizeof(master));
+
+		auth_request_balancer_worker_init(auth);
 	}
 
 	/* everything initialized, notify masters that all is well */
@@ -253,6 +268,9 @@
 
 	auth_request_handlers_flush_failures();
 
+	if (balancer_worker)
+		auth_request_balancer_worker_deinit();
+
 	master = buffer_get_modifyable_data(masters_buf, &size);
 	size /= sizeof(*master);
 	for (i = 0; i < size; i++)
@@ -268,20 +286,32 @@
 	closelog();
 }
 
-int main(int argc, char *argv[])
+int main(int argc __attr_unused__, char *argv[])
 {
+	int foreground;
+
 #ifdef DEBUG
 	if (getenv("GDB") == NULL)
-		fd_debug_verify_leaks(4, 1024);
+		fd_debug_verify_leaks(BALANCER_LISTEN_FD + 1, 1024);
 #endif
 	/* NOTE: we start rooted, so keep the code minimal until
 	   restrict_access_by_env() is called */
 	lib_init();
 	ioloop = io_loop_create(system_pool);
 
+	while (argv[1] != NULL) {
+		if (strcmp(argv[1], "-F") == 0)
+			foreground = TRUE;
+		else if (strcmp(argv[1], "-b") == 0)
+			balancer = TRUE;
+		else if (strcmp(argv[1], "-bw") == 0)
+			balancer_worker = TRUE;
+		argv++;
+	}
+
 	drop_privileges();
 
-	main_init(argc > 1 && strcmp(argv[1], "-F") == 0);
+	main_init(foreground);
         io_loop_run(ioloop);
 	main_deinit();
 

Index: mech.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/mech.h,v
retrieving revision 1.34
retrieving revision 1.35
diff -u -d -r1.34 -r1.35
--- mech.h	8 Jan 2005 21:37:32 -0000	1.34
+++ mech.h	9 Jan 2005 16:54:48 -0000	1.35
@@ -17,6 +17,9 @@
 
 #include "auth-request.h"
 
+/* Used only for string sanitization. */
+#define MAX_MECH_NAME_LEN 64
+
 struct mech_module {
 	const char *mech_name;
 



More information about the dovecot-cvs mailing list