changeset 3166:e6a487d80288 HEAD

Restructuring of auth code. Balancer auth processes were a bad idea. Usually the balancer itself took as much CPU as the actual workers because it acted as a proxy. Now auth worker means different thing: they're used to execute blocking passdb and userdb queries. Currently just MySQL (PAM and checkpassword in TODO).
author Timo Sirainen <tss@iki.fi>
date Tue, 01 Mar 2005 00:19:21 +0200
parents 07a1726b7e6b
children 97f53e0cce63
files dovecot-example.conf src/auth/Makefile.am src/auth/auth-master-connection.c src/auth/auth-master-connection.h src/auth/auth-request-balancer-child.c src/auth/auth-request-balancer-worker.c src/auth/auth-request-balancer.h src/auth/auth-request-handler-balancer.c src/auth/auth-request-handler-default.c src/auth/auth-request-handler.c src/auth/auth-request-handler.h src/auth/auth-request.c src/auth/auth-request.h src/auth/auth.c src/auth/auth.h src/auth/common.h src/auth/main.c src/auth/passdb-bsdauth.c src/auth/passdb-checkpassword.c src/auth/passdb-ldap.c src/auth/passdb-pam.c src/auth/passdb-passwd-file.c src/auth/passdb-passwd.c src/auth/passdb-shadow.c src/auth/passdb-sql.c src/auth/passdb-vpopmail.c src/auth/passdb.c src/auth/passdb.h src/auth/userdb-ldap.c src/auth/userdb-passdb.c src/auth/userdb-passwd-file.c src/auth/userdb-passwd.c src/auth/userdb-sql.c src/auth/userdb-static.c src/auth/userdb-vpopmail.c src/auth/userdb.h src/master/auth-process.c src/master/common.h src/master/main.c src/master/master-settings.c src/master/master-settings.h
diffstat 41 files changed, 783 insertions(+), 1541 deletions(-) [+]
line wrap: on
line diff
--- a/dovecot-example.conf	Tue Mar 01 00:15:25 2005 +0200
+++ b/dovecot-example.conf	Tue Mar 01 00:19:21 2005 +0200
@@ -535,6 +535,11 @@
 # queries.
 #auth_debug = no
 
+# Maximum number of dovecot-auth worker processes. They're used to execute
+# blocking passdb and userdb queries (eg. MySQL and PAM). They're
+# automatically created and destroyed as needed.
+#auth_worker_max_count = 30
+
 auth default {
   # Space separated list of wanted authentication mechanisms:
   #   plain digest-md5 cram-md5 apop anonymous
--- a/src/auth/Makefile.am	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/Makefile.am	Tue Mar 01 00:19:21 2005 +0200
@@ -38,11 +38,9 @@
 	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 \
+	auth-worker-client.c \
+	auth-worker-server.c \
 	db-ldap.c \
 	db-sql.c \
 	db-passwd-file.c \
@@ -57,6 +55,7 @@
 	mech-rpa.c \
 	mech-apop.c \
 	passdb.c \
+	passdb-blocking.c \
 	passdb-bsdauth.c \
 	passdb-cache.c \
 	passdb-ldap.c \
@@ -68,6 +67,7 @@
 	passdb-vpopmail.c \
 	passdb-sql.c \
 	userdb.c \
+	userdb-blocking.c \
 	userdb-ldap.c \
 	userdb-passdb.c \
 	userdb-passwd.c \
@@ -85,8 +85,9 @@
 	auth-master-connection.h \
 	auth-module.h \
 	auth-request.h \
-	auth-request-balancer.h \
 	auth-request-handler.h \
+	auth-worker-client.h \
+	auth-worker-server.h \
 	db-ldap.h \
 	db-sql.h \
 	db-passwd-file.h \
@@ -94,9 +95,11 @@
 	mech.h \
 	mycrypt.h \
 	passdb.h \
+	passdb-blocking.h \
 	passdb-cache.h \
 	password-scheme.h \
 	userdb.h \
+	userdb-blocking.h \
 	userdb-vpopmail.h
 
 checkpassword_reply_LDADD = \
--- a/src/auth/auth-master-connection.c	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/auth-master-connection.c	Tue Mar 01 00:19:21 2005 +0200
@@ -10,7 +10,6 @@
 #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"
@@ -292,10 +291,6 @@
 			auth_master_connection_set_fd(l->master, fd);
 			auth_master_connection_send_handshake(l->master);
 			break;
-		case LISTENER_BALANCER:
-			/* worker process connected to us */
-			auth_request_balancer_add_child(fd);
-			break;
 		}
 	}
 }
--- a/src/auth/auth-master-connection.h	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/auth-master-connection.h	Tue Mar 01 00:19:21 2005 +0200
@@ -3,8 +3,7 @@
 
 enum listener_type {
 	LISTENER_MASTER,
-	LISTENER_CLIENT,
-	LISTENER_BALANCER
+	LISTENER_CLIENT
 };
 
 struct auth_master_connection {
--- a/src/auth/auth-request-balancer-child.c	Tue Mar 01 00:15:25 2005 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,227 +0,0 @@
-/* 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);
-
-	i_assert(size > 0);
-
-	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);
-}
--- a/src/auth/auth-request-balancer-worker.c	Tue Mar 01 00:15:25 2005 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,213 +0,0 @@
-/* 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);
-}
--- a/src/auth/auth-request-balancer.h	Tue Mar 01 00:15:25 2005 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-#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
--- a/src/auth/auth-request-handler-balancer.c	Tue Mar 01 00:15:25 2005 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,289 +0,0 @@
-/* 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);
-	}
-}
--- a/src/auth/auth-request-handler-default.c	Tue Mar 01 00:15:25 2005 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,484 +0,0 @@
-/* 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(str_c(request->extra_fields), "userdb_") == NULL)
-		return str_c(request->extra_fields);
-
-	fields = t_strsplit(str_c(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);
-			auth_request_unref(request);
-		} 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 userdb_callback(const char *result, void *context)
-{
-        struct auth_request *request = context;
-        struct auth_request_handler *handler = request->context;
-	string_t *reply;
-
-	reply = t_str_new(256);
-	if (handler->prepend_connect_uid)
-		str_printfa(reply, "%u\t", request->connect_uid);
-	if (result == NULL)
-		str_printfa(reply, "NOTFOUND\t%u", request->id);
-	else {
-		str_printfa(reply, "USER\t%u\t", request->id);
-		str_append(reply, result);
-	}
-	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
-};
--- a/src/auth/auth-request-handler.c	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/auth-request-handler.c	Tue Mar 01 00:19:21 2005 +0200
@@ -1,10 +1,36 @@
 /* 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>
+
+struct auth_request_handler {
+	int refcount;
+	pool_t pool;
+	struct hash_table *requests;
+
+        struct auth *auth;
+        unsigned int connect_uid, client_pid;
 
-struct auth_request_handler_api *auth_request_handler_api;
+	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;
 
 struct auth_request_handler *
 auth_request_handler_create(struct auth *auth, int prepend_connect_uid,
@@ -12,62 +38,437 @@
 			    auth_request_callback_t *master_callback,
 			    void *master_context)
 {
-	return auth_request_handler_api->
-		create(auth, prepend_connect_uid, callback, context,
-		       master_callback, 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;
+}
+
+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_set(struct auth_request_handler *handler,
 			      unsigned int connect_uid,
 			      unsigned int client_pid)
 {
-	auth_request_handler_api->set(handler, connect_uid, client_pid);
+	handler->connect_uid = connect_uid;
+	handler->client_pid = client_pid;
 }
 
-void auth_request_handler_unref(struct auth_request_handler *handler)
+static void auth_request_handler_remove(struct auth_request_handler *handler,
+					struct auth_request *request)
 {
-	auth_request_handler_api->unref(handler);
+	hash_remove(handler->requests, POINTER_CAST(request->id));
+	auth_request_unref(request);
 }
 
 void auth_request_handler_check_timeouts(struct auth_request_handler *handler)
 {
-        auth_request_handler_api->check_timeouts(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(str_c(request->extra_fields), "userdb_") == NULL)
+		return str_c(request->extra_fields);
+
+	fields = t_strsplit(str_c(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);
+			auth_request_unref(request);
+		} 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);
 }
 
 int auth_request_handler_auth_begin(struct auth_request_handler *handler,
 				    const char *args)
 {
-        return auth_request_handler_api->auth_begin(handler, 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;
 }
 
 int auth_request_handler_auth_continue(struct auth_request_handler *handler,
 				       const char *args)
 {
-	return auth_request_handler_api->auth_continue(handler, 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 userdb_callback(const char *result, struct auth_request *request)
+{
+        struct auth_request_handler *handler = request->context;
+	string_t *reply;
+
+	reply = t_str_new(256);
+	if (handler->prepend_connect_uid)
+		str_printfa(reply, "%u\t", request->connect_uid);
+	if (result == NULL)
+		str_printfa(reply, "NOTFOUND\t%u", request->id);
+	else {
+		str_printfa(reply, "USER\t%u\t", request->id);
+		str_append(reply, result);
+	}
+	handler->master_callback(str_c(reply), handler->master_context);
+
+	auth_request_unref(request);
+        auth_request_handler_unref(handler);
 }
 
 void auth_request_handler_master_request(struct auth_request_handler *handler,
 					 unsigned int id,
 					 unsigned int client_id)
 {
-        auth_request_handler_api->master_request(handler, id, 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;
+	}
 
-void auth_request_handlers_flush_failures(void)
-{
-        auth_request_handler_api->flush_failures();
+	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);
+	}
 }
 
-void auth_request_handlers_init(int balancer)
+void auth_request_handler_flush_failures(void)
 {
-	/* use balancer if we have it */
-	auth_request_handler_api = balancer ?
-		&auth_request_handler_balancer : &auth_request_handler_default;
+	struct auth_request **auth_request;
+	size_t i, size;
+
+	auth_request = buffer_get_modifyable_data(auth_failures_buf, &size);
+	size /= sizeof(*auth_request);
 
-        auth_request_handler_api->init();
+	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);
 }
 
-void auth_request_handlers_deinit(void)
+static void auth_failure_timeout(void *context __attr_unused__)
+{
+	auth_request_handler_flush_failures();
+}
+
+void auth_request_handler_init(void)
 {
-        auth_request_handler_api->deinit();
+	auth_failures_buf = buffer_create_dynamic(default_pool, 1024);
+        to_auth_failures = timeout_add(2000, auth_failure_timeout, NULL);
 }
+
+void auth_request_handler_deinit(void)
+{
+	buffer_free(auth_failures_buf);
+	timeout_remove(to_auth_failures);
+}
--- a/src/auth/auth-request-handler.h	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/auth-request-handler.h	Tue Mar 01 00:19:21 2005 +0200
@@ -5,35 +5,6 @@
 
 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, int prepend_connect_uid,
 			    auth_request_callback_t *callback, void *context,
@@ -55,9 +26,9 @@
 					 unsigned int id,
 					 unsigned int client_id);
 
-void auth_request_handlers_flush_failures(void);
+void auth_request_handler_flush_failures(void);
 
-void auth_request_handlers_init(int balancer);
-void auth_request_handlers_deinit(void);
+void auth_request_handler_init(void);
+void auth_request_handler_deinit(void);
 
 #endif
--- a/src/auth/auth-request.c	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/auth-request.c	Tue Mar 01 00:19:21 2005 +0200
@@ -12,6 +12,8 @@
 #include "auth-client-connection.h"
 #include "auth-master-connection.h"
 #include "passdb.h"
+#include "passdb-blocking.h"
+#include "userdb-blocking.h"
 #include "passdb-cache.h"
 
 struct auth_request *
@@ -72,6 +74,18 @@
 	return FALSE;
 }
 
+void auth_request_export(struct auth_request *request, string_t *str)
+{
+	str_append(str, "user=");
+	str_append(str, request->user);
+	str_append(str, "\tservice=");
+	str_append(str, request->service);
+	str_append(str, "\tlip=");
+	str_append(str, net_ip2addr(&request->local_ip));
+	str_append(str, "\trip=");
+	str_append(str, net_ip2addr(&request->remote_ip));
+}
+
 void auth_request_initial(struct auth_request *request,
 			  const unsigned char *data, size_t data_size)
 {
@@ -105,17 +119,21 @@
 		return;
 	}
 
+	if (request->passdb_password == NULL) {
+		/* no password given by passdb, cannot cache this */
+		return;
+	}
+
 	/* save all except the currently given password in cache */
 	str = t_str_new(32 + str_len(request->extra_fields));
-	if (request->passdb_password != NULL) {
-		if (*request->passdb_password != '{') {
-			/* cached passwords must have a known scheme */
-			str_append_c(str, '{');
-			str_append(str, passdb->default_pass_scheme);
-			str_append_c(str, '}');
-		}
-		str_append(str, request->passdb_password);
+	if (*request->passdb_password != '{') {
+		/* cached passwords must have a known scheme */
+		str_append_c(str, '{');
+		str_append(str, passdb->default_pass_scheme);
+		str_append_c(str, '}');
 	}
+	str_append(str, request->passdb_password);
+
 	if (request->extra_fields != NULL) {
 		str_append_c(str, '\t');
 		str_append_str(str, request->extra_fields);
@@ -127,8 +145,8 @@
 	auth_cache_insert(passdb_cache, request, passdb->cache_key, str_c(str));
 }
 
-static void auth_request_verify_plain_callback(enum passdb_result result,
-					       struct auth_request *request)
+void auth_request_verify_plain_callback(enum passdb_result result,
+					struct auth_request *request)
 {
         auth_request_save_cache(request, result);
 
@@ -157,6 +175,7 @@
 	const char *cache_key;
 
 	request->mech_password = p_strdup(request->pool, password);
+	request->private_callback.verify_plain = callback;
 
 	cache_key = passdb_cache == NULL ? NULL : passdb->cache_key;
 	if (cache_key != NULL) {
@@ -167,15 +186,17 @@
 		}
 	}
 
-	request->private_callback.verify_plain = callback;
-	passdb->verify_plain(request, password,
-			     auth_request_verify_plain_callback);
+	if (passdb->blocking)
+		passdb_blocking_verify_plain(request);
+	else {
+		passdb->verify_plain(request, password,
+				     auth_request_verify_plain_callback);
+	}
 }
 
-static void
-auth_request_lookup_credentials_callback(enum passdb_result result,
-					 const char *credentials,
-					 struct auth_request *request)
+void auth_request_lookup_credentials_callback(enum passdb_result result,
+					      const char *credentials,
+					      struct auth_request *request)
 {
         auth_request_save_cache(request, result);
 
@@ -208,15 +229,26 @@
 		}
 	}
 
+	request->credentials = credentials;
 	request->private_callback.lookup_credentials = callback;
-	passdb->lookup_credentials(request, credentials,
-				   auth_request_lookup_credentials_callback);
+
+	if (passdb->blocking)
+		passdb_blocking_lookup_credentials(request);
+	else {
+		passdb->lookup_credentials(request, credentials,
+			auth_request_lookup_credentials_callback);
+	}
 }
 
 void auth_request_lookup_user(struct auth_request *request,
-			      userdb_callback_t *callback, void *context)
+			      userdb_callback_t *callback)
 {
-	request->auth->userdb->lookup(request, callback, context);
+	struct userdb_module *userdb = request->auth->userdb;
+
+	if (userdb->blocking)
+		userdb_blocking_lookup(request, callback);
+	else
+		userdb->lookup(request, callback);
 }
 
 int auth_request_set_username(struct auth_request *request,
--- a/src/auth/auth-request.h	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/auth-request.h	Tue Mar 01 00:19:21 2005 +0200
@@ -30,8 +30,10 @@
 
 	union {
 		verify_plain_callback_t *verify_plain;
-                lookup_credentials_callback_t *lookup_credentials;
+		lookup_credentials_callback_t *lookup_credentials;
+                userdb_callback_t *userdb;
 	} private_callback;
+        enum passdb_credentials credentials;
 
 	mech_callback_t *callback;
 	void *context;
@@ -58,6 +60,8 @@
 void auth_request_fail(struct auth_request *request);
 void auth_request_internal_failure(struct auth_request *request);
 
+void auth_request_export(struct auth_request *request, string_t *str);
+
 void auth_request_initial(struct auth_request *request,
 			  const unsigned char *data, size_t data_size);
 void auth_request_continue(struct auth_request *request,
@@ -70,7 +74,7 @@
 				     enum passdb_credentials credentials,
 				     lookup_credentials_callback_t *callback);
 void auth_request_lookup_user(struct auth_request *request,
-			      userdb_callback_t *callback, void *context);
+			      userdb_callback_t *callback);
 
 int auth_request_set_username(struct auth_request *request,
 			      const char *username, const char **error_r);
@@ -92,4 +96,10 @@
 			    const char *subsystem,
 			    const char *format, ...) __attr_format__(3, 4);
 
+void auth_request_verify_plain_callback(enum passdb_result result,
+					struct auth_request *request);
+void auth_request_lookup_credentials_callback(enum passdb_result result,
+					      const char *credentials,
+					      struct auth_request *request);
+
 #endif
--- a/src/auth/auth.c	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/auth.c	Tue Mar 01 00:19:21 2005 +0200
@@ -9,7 +9,6 @@
 #include "passdb.h"
 #include "auth.h"
 #include "auth-request-handler.h"
-#include "auth-request-balancer.h"
 
 #include <stdlib.h>
 #include <unistd.h>
@@ -174,8 +173,5 @@
 	userdb_deinit(auth);
 	passdb_deinit(auth);
 
-	if (auth->balancer_worker != NULL)
-		auth_request_balancer_worker_destroy(auth->balancer_worker);
-
 	str_free(auth->mech_handshake);
 }
--- a/src/auth/auth.h	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/auth.h	Tue Mar 01 00:19:21 2005 +0200
@@ -1,11 +1,7 @@
 #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;
 
--- a/src/auth/common.h	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/common.h	Tue Mar 01 00:19:21 2005 +0200
@@ -6,10 +6,10 @@
 
 #define MASTER_SOCKET_FD 0
 #define CLIENT_LISTEN_FD 3
-#define BALANCER_LISTEN_FD 4
+#define WORKER_SERVER_FD 4
 
 extern struct ioloop *ioloop;
-extern int standalone;
+extern int standalone, worker;
 extern time_t process_start_time;
 
 #endif
--- a/src/auth/main.c	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/main.c	Tue Mar 01 00:19:21 2005 +0200
@@ -12,7 +12,8 @@
 #include "mech.h"
 #include "auth.h"
 #include "auth-request-handler.h"
-#include "auth-request-balancer.h"
+#include "auth-worker-server.h"
+#include "auth-worker-client.h"
 #include "auth-master-interface.h"
 #include "auth-master-connection.h"
 #include "auth-client-connection.h"
@@ -26,12 +27,12 @@
 #include <sys/stat.h>
 
 struct ioloop *ioloop;
-int standalone = FALSE;
+int standalone = FALSE, worker = FALSE;
 time_t process_start_time;
 
 static buffer_t *masters_buf;
 static struct auth *auth;
-static int balancer = FALSE, balancer_worker = FALSE;
+static struct auth_worker_client *worker_client;
 
 static void sig_quit(int signo __attr_unused__)
 {
@@ -186,7 +187,7 @@
         password_schemes_init();
 
 	masters_buf = buffer_create_dynamic(default_pool, 64);
-	if (!balancer_worker)
+	if (!worker)
 		add_extra_listeners();
 
 	/* Password lookups etc. may require roots, allow it. */
@@ -199,12 +200,17 @@
 	size_t i, size;
 
         process_start_time = ioloop_time;
+	lib_init_signals(sig_quit);
 
 	mech_init();
 	auth_init(auth);
-	auth_request_handlers_init(balancer);
+	auth_request_handler_init();
 
-	lib_init_signals(sig_quit);
+	if (worker) {
+		worker_client =
+			auth_worker_client_create(auth, WORKER_SERVER_FD);
+		return;
+	}
 
 	standalone = getenv("DOVECOT_MASTER") == NULL;
 	if (standalone) {
@@ -232,23 +238,12 @@
 			if (chdir("/") < 0)
 				i_fatal("chdir(/) failed: %m");
 		}
-	} else if (!balancer_worker) {
+	} else {
 		master = auth_master_connection_create(auth, MASTER_SOCKET_FD);
 		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 */
@@ -266,21 +261,23 @@
         if (lib_signal_kill != 0)
 		i_warning("Killed with signal %d", lib_signal_kill);
 
-	auth_request_handlers_flush_failures();
-
-	if (balancer_worker)
-		auth_request_balancer_worker_deinit();
+	if (worker_client != NULL)
+		auth_worker_client_destroy(worker_client);
+	else {
+		auth_request_handler_flush_failures();
 
-	master = buffer_get_modifyable_data(masters_buf, &size);
-	size /= sizeof(*master);
-	for (i = 0; i < size; i++)
-		auth_master_connection_destroy(master[i]);
+		master = buffer_get_modifyable_data(masters_buf, &size);
+		size /= sizeof(*master);
+		for (i = 0; i < size; i++)
+			auth_master_connection_destroy(master[i]);
+	}
 
-        password_schemes_deinit();
-	auth_request_handlers_deinit();
+	auth_request_handler_deinit();
 	auth_deinit(auth);
 	mech_deinit();
 
+        auth_worker_server_deinit();
+        password_schemes_deinit();
 	random_deinit();
 
 	closelog();
@@ -292,7 +289,7 @@
 
 #ifdef DEBUG
 	if (getenv("GDB") == NULL)
-		fd_debug_verify_leaks(BALANCER_LISTEN_FD + 1, 1024);
+		fd_debug_verify_leaks(WORKER_SERVER_FD + 1, 1024);
 #endif
 	/* NOTE: we start rooted, so keep the code minimal until
 	   restrict_access_by_env() is called */
@@ -302,10 +299,8 @@
 	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;
+		else if (strcmp(argv[1], "-w") == 0)
+			worker = TRUE;
 		argv++;
 	}
 
--- a/src/auth/passdb-bsdauth.c	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/passdb-bsdauth.c	Tue Mar 01 00:19:21 2005 +0200
@@ -58,7 +58,7 @@
 
 struct passdb_module passdb_bsdauth = {
 	"bsdauth",
-	"%u", "CRYPT",
+	"%u", "CRYPT", FALSE,
 
 	NULL, NULL,
 	bsdauth_deinit,
--- a/src/auth/passdb-checkpassword.c	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/passdb-checkpassword.c	Tue Mar 01 00:19:21 2005 +0200
@@ -343,7 +343,7 @@
 
 struct passdb_module passdb_checkpassword = {
 	"checkpassword",
-	NULL, NULL,
+	NULL, NULL, FALSE,
 
 	NULL,
 	checkpassword_init,
--- a/src/auth/passdb-ldap.c	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/passdb-ldap.c	Tue Mar 01 00:19:21 2005 +0200
@@ -239,7 +239,7 @@
 
 struct passdb_module passdb_ldap = {
 	"ldap",
-	NULL, NULL,
+	NULL, NULL, FALSE,
 
 	passdb_ldap_preinit,
 	passdb_ldap_init,
--- a/src/auth/passdb-pam.c	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/passdb-pam.c	Tue Mar 01 00:19:21 2005 +0200
@@ -401,7 +401,7 @@
 
 struct passdb_module passdb_pam = {
 	"pam",
-	NULL, NULL,
+	NULL, NULL, FALSE,
 
 	NULL,
 	pam_init,
--- a/src/auth/passdb-passwd-file.c	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/passdb-passwd-file.c	Tue Mar 01 00:19:21 2005 +0200
@@ -84,7 +84,7 @@
 
 struct passdb_module passdb_passwd_file = {
 	"passwd-file",
-	NULL, NULL,
+	NULL, NULL, FALSE,
 
 	NULL,
 	passwd_file_init,
--- a/src/auth/passdb-passwd.c	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/passdb-passwd.c	Tue Mar 01 00:19:21 2005 +0200
@@ -55,7 +55,7 @@
 
 struct passdb_module passdb_passwd = {
 	"passwd",
-	"%u", "CRYPT",
+	"%u", "CRYPT", FALSE,
 
 	NULL, NULL,
 	passwd_deinit,
--- a/src/auth/passdb-shadow.c	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/passdb-shadow.c	Tue Mar 01 00:19:21 2005 +0200
@@ -55,7 +55,7 @@
 
 struct passdb_module passdb_shadow = {
 	"shadow",
-	"%u", "CRYPT",
+	"%u", "CRYPT", FALSE,
 
 	NULL, NULL,
 	shadow_deinit,
--- a/src/auth/passdb-sql.c	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/passdb-sql.c	Tue Mar 01 00:19:21 2005 +0200
@@ -174,7 +174,12 @@
 
 static void passdb_sql_init(const char *args __attr_unused__)
 {
+	enum sql_db_flags flags;
+
 	db_sql_connect(passdb_sql_conn);
+
+	flags = sql_get_flags(passdb_sql_conn->db);
+	passdb_sql.blocking = (flags & SQL_DB_FLAG_BLOCKING) != 0;
 }
 
 static void passdb_sql_deinit(void)
@@ -185,7 +190,7 @@
 
 struct passdb_module passdb_sql = {
 	"sql",
-	NULL, NULL,
+	NULL, NULL, FALSE,
 
 	passdb_sql_preinit,
 	passdb_sql_init,
--- a/src/auth/passdb-vpopmail.c	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/passdb-vpopmail.c	Tue Mar 01 00:19:21 2005 +0200
@@ -98,7 +98,7 @@
 
 struct passdb_module passdb_vpopmail = {
 	"vpopmail",
-	"%u", "CRYPT",
+	"%u", "CRYPT", FALSE,
 
 	NULL, NULL,
 	vpopmail_deinit,
--- a/src/auth/passdb.c	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/passdb.c	Tue Mar 01 00:19:21 2005 +0200
@@ -3,6 +3,7 @@
 #include "common.h"
 #include "auth-module.h"
 #include "password-scheme.h"
+#include "auth-worker-server.h"
 #include "passdb.h"
 #include "passdb-cache.h"
 
@@ -162,6 +163,11 @@
 
 	i_assert(auth->passdb->default_pass_scheme != NULL ||
 		 auth->passdb->cache_key == NULL);
+
+	if (auth->passdb->blocking && !worker) {
+		/* blocking passdb - we need an auth server */
+		auth_worker_server_init();
+	}
 }
 
 void passdb_deinit(struct auth *auth)
--- a/src/auth/passdb.h	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/passdb.h	Tue Mar 01 00:19:21 2005 +0200
@@ -42,6 +42,9 @@
 	/* Default password scheme for this module.
 	   If cache_key is set, must not be NULL. */
 	const char *default_pass_scheme;
+	/* If blocking is set to TRUE, use child processes to access
+	   this passdb. */
+	int blocking;
 
 	void (*preinit)(const char *args);
 	void (*init)(const char *args);
--- a/src/auth/userdb-ldap.c	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/userdb-ldap.c	Tue Mar 01 00:19:21 2005 +0200
@@ -138,7 +138,7 @@
 	if (ret != LDAP_SUCCESS) {
 		auth_request_log_error(auth_request, "ldap",
 			"ldap_search() failed: %s", ldap_err2string(ret));
-		urequest->userdb_callback(NULL, request->context);
+		urequest->userdb_callback(NULL, auth_request);
 		return;
 	}
 
@@ -158,11 +158,11 @@
 		}
 	}
 
-	urequest->userdb_callback(result, request->context);
+	urequest->userdb_callback(result, auth_request);
 }
 
 static void userdb_ldap_lookup(struct auth_request *auth_request,
-			       userdb_callback_t *callback, void *context)
+			       userdb_callback_t *callback)
 {
 	struct ldap_connection *conn = userdb_ldap_conn;
         const struct var_expand_table *vars;
@@ -183,7 +183,6 @@
 
 	request = p_new(auth_request->pool, struct userdb_ldap_request, 1);
 	request->request.callback = handle_request;
-	request->request.context = context;
 	request->auth_request = auth_request;
 	request->userdb_callback = callback;
 
@@ -217,6 +216,7 @@
 
 struct userdb_module userdb_ldap = {
 	"ldap",
+	FALSE,
 
 	userdb_ldap_preinit,
 	userdb_ldap_init,
--- a/src/auth/userdb-passdb.c	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/userdb-passdb.c	Tue Mar 01 00:19:21 2005 +0200
@@ -13,7 +13,7 @@
 #include <stdlib.h>
 
 static void passdb_lookup(struct auth_request *auth_request,
-			  userdb_callback_t *callback, void *context)
+			  userdb_callback_t *callback)
 {
 	const char *const *args;
 	string_t *str;
@@ -24,7 +24,7 @@
 	if (auth_request->extra_fields == NULL) {
 		auth_request_log_error(auth_request, "passdb",
 				       "passdb didn't return userdb entries");
-		callback(NULL, context);
+		callback(NULL, auth_request);
 		return;
 	}
 
@@ -76,14 +76,15 @@
 	}
 
 	if (uid == (uid_t)-1 || gid == (gid_t)-1)
-		callback(NULL, context);
+		callback(NULL, auth_request);
 	else
-		callback(str_c(str), context);
+		callback(str_c(str), auth_request);
 	t_pop();
 }
 
 struct userdb_module userdb_passdb = {
 	"passdb",
+	FALSE,
 
 	NULL,
 	NULL,
--- a/src/auth/userdb-passwd-file.c	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/userdb-passwd-file.c	Tue Mar 01 00:19:21 2005 +0200
@@ -13,14 +13,14 @@
 struct passwd_file *userdb_pwf = NULL;
 
 static void passwd_file_lookup(struct auth_request *auth_request,
-			       userdb_callback_t *callback, void *context)
+			       userdb_callback_t *callback)
 {
 	struct passwd_user *pu;
 	string_t *str;
 
 	pu = db_passwd_file_lookup(userdb_pwf, auth_request);
 	if (pu == NULL) {
-		callback(NULL, context);
+		callback(NULL, auth_request);
 		return;
 	}
 
@@ -33,7 +33,7 @@
 	if (pu->mail != NULL)
 		str_printfa(str, "\tmail=%s", pu->mail);
 
-	callback(str_c(str), context);
+	callback(str_c(str), auth_request);
 }
 
 static void passwd_file_init(const char *args)
@@ -57,6 +57,7 @@
 
 struct userdb_module userdb_passwd_file = {
 	"passwd-file",
+	FALSE,
 
 	NULL,
 	passwd_file_init,
--- a/src/auth/userdb-passwd.c	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/userdb-passwd.c	Tue Mar 01 00:19:21 2005 +0200
@@ -11,7 +11,7 @@
 #include <pwd.h>
 
 static void passwd_lookup(struct auth_request *auth_request,
-			  userdb_callback_t *callback, void *context)
+			  userdb_callback_t *callback)
 {
 	struct passwd *pw;
 	const char *result;
@@ -19,7 +19,7 @@
 	pw = getpwnam(auth_request->user);
 	if (pw == NULL) {
 		auth_request_log_info(auth_request, "passwd", "unknown user");
-		callback(NULL, context);
+		callback(NULL, auth_request);
 		return;
 	}
 
@@ -27,11 +27,12 @@
 				 "home=%s", pw->pw_name, pw->pw_name,
 				 dec2str(pw->pw_uid), dec2str(pw->pw_gid),
 				 pw->pw_dir);
-	callback(result, context);
+	callback(result, auth_request);
 }
 
 struct userdb_module userdb_passwd = {
 	"passwd",
+	FALSE,
 
 	NULL, NULL, NULL,
 	passwd_lookup
--- a/src/auth/userdb-sql.c	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/userdb-sql.c	Tue Mar 01 00:19:21 2005 +0200
@@ -18,9 +18,10 @@
 struct userdb_sql_request {
 	struct auth_request *auth_request;
 	userdb_callback_t *callback;
-	void *context;
 };
 
+extern struct userdb_module userdb_sql;
+
 static struct sql_connection *userdb_sql_conn;
 
 static const char *sql_query_get_result(struct sql_result *result,
@@ -94,12 +95,12 @@
                 user_result = sql_query_get_result(result, auth_request);
 	}
 
-	sql_request->callback(user_result, sql_request->context);
+	sql_request->callback(user_result, auth_request);
 	i_free(sql_request);
 }
 
 static void userdb_sql_lookup(struct auth_request *auth_request,
-			      userdb_callback_t *callback, void *context)
+			      userdb_callback_t *callback)
 {
 	struct userdb_sql_request *sql_request;
 	string_t *query;
@@ -111,7 +112,6 @@
 
 	sql_request = i_new(struct userdb_sql_request, 1);
 	sql_request->callback = callback;
-	sql_request->context = context;
 	sql_request->auth_request = auth_request;
 
 	auth_request_log_debug(auth_request, "sql", "%s", str_c(query));
@@ -127,7 +127,12 @@
 
 static void userdb_sql_init(const char *args __attr_unused__)
 {
+	enum sql_db_flags flags;
+
 	db_sql_connect(userdb_sql_conn);
+
+	flags = sql_get_flags(userdb_sql_conn->db);
+	userdb_sql.blocking = (flags & SQL_DB_FLAG_BLOCKING) != 0;
 }
 
 static void userdb_sql_deinit(void)
@@ -137,6 +142,7 @@
 
 struct userdb_module userdb_sql = {
 	"sql",
+	FALSE,
 
 	userdb_sql_preinit,
 	userdb_sql_init,
--- a/src/auth/userdb-static.c	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/userdb-static.c	Tue Mar 01 00:19:21 2005 +0200
@@ -15,7 +15,7 @@
 static char *static_template;
 
 static void static_lookup(struct auth_request *auth_request,
-			  userdb_callback_t *callback, void *context)
+			  userdb_callback_t *callback)
 {
 	string_t *str;
 
@@ -23,7 +23,7 @@
 	str_append(str, auth_request->user);
 	var_expand(str, static_template,
 		   auth_request_get_var_expand_table(auth_request, NULL));
-	callback(str_c(str), context);
+	callback(str_c(str), auth_request);
 }
 
 static void static_init(const char *args)
@@ -78,6 +78,7 @@
 
 struct userdb_module userdb_static = {
 	"static",
+	FALSE,
 
 	NULL,
 	static_init,
--- a/src/auth/userdb-vpopmail.c	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/userdb-vpopmail.c	Tue Mar 01 00:19:21 2005 +0200
@@ -42,7 +42,7 @@
 #ifdef USERDB_VPOPMAIL
 
 static void vpopmail_lookup(struct auth_request *auth_request,
-			    userdb_callback_t *callback, void *context)
+			    userdb_callback_t *callback)
 {
 	char vpop_user[VPOPMAIL_LIMIT], vpop_domain[VPOPMAIL_LIMIT];
 	struct vqpasswd *vpw;
@@ -52,7 +52,7 @@
 
 	vpw = vpopmail_lookup_vqp(auth_request, vpop_user, vpop_domain);
 	if (vpw == NULL) {
-		callback(NULL, context);
+		callback(NULL, auth_request);
 		return;
 	}
 
@@ -61,7 +61,7 @@
 	if (vget_assign(vpop_domain, NULL, 0, &uid, &gid) == NULL) {
 		auth_request_log_info(auth_request, "vpopmail",
 				      "vget_assign(%s) failed", vpop_domain);
-		callback(NULL, context);
+		callback(NULL, auth_request);
 		return;
 	}
 
@@ -74,14 +74,14 @@
 			auth_request_log_error(auth_request, "vpopmail",
 					       "make_user_dir(%s, %s) failed",
 					       vpop_user, vpop_domain);
-			callback(NULL, context);
+			callback(NULL, auth_request);
 			return;
 		}
 
 		/* get the user again so pw_dir is visible */
 		vpw = vauth_getpw(vpop_user, vpop_domain);
 		if (vpw == NULL) {
-			callback(NULL, context);
+			callback(NULL, auth_request);
 			return;
 		}
 	}
@@ -90,11 +90,12 @@
 				 vpw->pw_name, dec2str(uid), dec2str(gid),
 				 vpw->pw_dir);
 
-	callback(result, context);
+	callback(result, auth_request);
 }
 
 struct userdb_module userdb_vpopmail = {
 	"vpopmail",
+	FALSE,
 
 	NULL, NULL, NULL,
 	vpopmail_lookup
--- a/src/auth/userdb.h	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/auth/userdb.h	Tue Mar 01 00:19:21 2005 +0200
@@ -3,17 +3,22 @@
 
 struct auth_request;
 
-typedef void userdb_callback_t(const char *result, void *context);
+typedef void userdb_callback_t(const char *result,
+			       struct auth_request *request);
 
 struct userdb_module {
 	const char *name;
 
+	/* If blocking is set to TRUE, use child processes to access
+	   this passdb. */
+	int blocking;
+
 	void (*preinit)(const char *args);
 	void (*init)(const char *args);
 	void (*deinit)(void);
 
 	void (*lookup)(struct auth_request *auth_request,
-		       userdb_callback_t *callback, void *context);
+		       userdb_callback_t *callback);
 };
 
 uid_t userdb_parse_uid(struct auth_request *request, const char *str);
--- a/src/master/auth-process.c	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/master/auth-process.c	Tue Mar 01 00:19:21 2005 +0200
@@ -4,6 +4,7 @@
 #include "ioloop.h"
 #include "env-util.h"
 #include "fd-close-on-exec.h"
+#include "unix-socket-create.h"
 #include "network.h"
 #include "istream.h"
 #include "ostream.h"
@@ -18,7 +19,6 @@
 #include <unistd.h>
 #include <pwd.h>
 #include <syslog.h>
-#include <sys/stat.h>
 
 #define MAX_INBUF_SIZE 8192
 #define MAX_OUTBUF_SIZE 65536
@@ -27,13 +27,10 @@
 	struct auth_process_group *next;
 
 	int listen_fd;
-	int balancer_listen_fd;
 	struct auth_settings *set;
 
 	unsigned int process_count;
 	struct auth_process *processes;
-
-	unsigned int need_balancer:1;
 };
 
 struct auth_process {
@@ -46,9 +43,11 @@
 	struct istream *input;
 	struct ostream *output;
 
+	int worker_listen_fd;
+	struct io *worker_io;
+
 	struct hash_table *requests;
 
-	unsigned int balancer:1;
 	unsigned int external:1;
 	unsigned int version_received:1;
 	unsigned int initialized:1;
@@ -60,6 +59,7 @@
 static struct auth_process_group *process_groups;
 
 static void auth_process_destroy(struct auth_process *p);
+static int create_auth_worker(struct auth_process *process, int fd);
 
 void auth_process_request(struct auth_process *process, unsigned int login_pid,
 			  unsigned int login_id, void *context)
@@ -253,12 +253,29 @@
 	}
 }
 
+static void auth_worker_input(void *context)
+{
+	struct auth_process *p = context;
+	int fd;
+
+	fd = net_accept(p->worker_listen_fd, NULL, NULL);
+	if (fd < 0) {
+		if (fd == -2)
+			i_fatal("net_accept(worker) failed: %m");
+		return;
+	}
+
+	net_set_nonblock(fd, TRUE);
+	fd_close_on_exec(fd, TRUE);
+
+	create_auth_worker(p, fd);
+}
+
 static struct auth_process *
-auth_process_new(pid_t pid, int fd, struct auth_process_group *group,
-		 int balancer)
+auth_process_new(pid_t pid, int fd, struct auth_process_group *group)
 {
 	struct auth_process *p;
-	const char *handshake;
+	const char *path, *handshake;
 
 	if (pid != 0)
 		PID_ADD_PROCESS_TYPE(pid, PROCESS_TYPE_AUTH);
@@ -274,12 +291,21 @@
 					 FALSE);
 	p->requests = hash_create(default_pool, default_pool, 0, NULL, NULL);
 
-	if (balancer) {
-		p->balancer = TRUE;
-		group->need_balancer = FALSE;
-	} else {
-		group->process_count++;
-	}
+	group->process_count++;
+
+	path = t_strdup_printf("%s/auth-worker.%s",
+			       group->set->parent->defaults->base_dir,
+			       dec2str(pid));
+	p->worker_listen_fd =
+		unix_socket_create(path, 0600, group->set->uid,
+				   group->set->gid, 16);
+	if (p->worker_listen_fd == -1)
+		i_fatal("Couldn't create auth worker listener");
+
+	net_set_nonblock(p->worker_listen_fd, TRUE);
+	fd_close_on_exec(p->worker_listen_fd, TRUE);
+	p->worker_io = io_add(p->worker_listen_fd, IO_READ,
+			      auth_worker_input, p);
 
 	handshake = t_strdup_printf("VERSION\t%u\t%u\n",
 				    AUTH_MASTER_PROTOCOL_MAJOR_VERSION,
@@ -296,6 +322,7 @@
 	struct hash_iterate_context *iter;
 	void *key, *value;
 	struct auth_process **pos;
+	const char *path;
 
 	if (!p->initialized && io_loop_is_running(ioloop) && !p->external) {
 		i_error("Auth process died too early - shutting down");
@@ -308,12 +335,16 @@
 			break;
 		}
 	}
-	if (p->balancer) {
-		/* the balancer process died, restart it */
-		p->group->need_balancer = TRUE;
-	} else {
-		p->group->process_count--;
-	}
+	p->group->process_count--;
+
+	path = t_strdup_printf("%s/auth-worker.%s",
+			       p->group->set->parent->defaults->base_dir,
+			       dec2str(p->pid));
+	(void)unlink(path);
+
+	if (close(p->worker_listen_fd) < 0)
+		i_error("close(worker_listen) failed: %m");
+	io_remove(p->worker_io);
 
 	iter = hash_iterate_init(p->requests);
 	while (hash_iterate(iter, &key, &value))
@@ -358,48 +389,64 @@
 
 	net_set_nonblock(fd, TRUE);
 	fd_close_on_exec(fd, TRUE);
-	auth = auth_process_new(0, fd, group, FALSE);
+	auth = auth_process_new(0, fd, group);
 	auth->external = TRUE;
 	return 0;
 }
 
-static int unix_socket_create(const char *path, int mode,
-			      uid_t uid, gid_t gid, int backlog)
+static void auth_set_environment(struct auth_settings *set)
 {
-	mode_t old_umask;
-	int fd;
+	struct auth_socket_settings *as;
+	const char *str;
+	int i;
 
-	(void)unlink(path);
-
-	old_umask = umask(0777 ^ mode);
-	fd = net_listen_unix(path, backlog);
-	umask(old_umask);
+	/* setup access environment */
+	restrict_access_set_env(set->user, set->uid, set->gid, set->chroot,
+				0, 0, NULL);
 
-	if (fd < 0)
-		i_fatal("Can't listen in UNIX socket %s: %m", path);
-	net_set_nonblock(fd, TRUE);
-	fd_close_on_exec(fd, TRUE);
+	/* set other environment */
+	env_put("DOVECOT_MASTER=1");
+	env_put(t_strconcat("AUTH_NAME=", set->name, NULL));
+	env_put(t_strconcat("MECHANISMS=", set->mechanisms, NULL));
+	env_put(t_strconcat("REALMS=", set->realms, NULL));
+	env_put(t_strconcat("DEFAULT_REALM=", set->default_realm, NULL));
+	env_put(t_strconcat("USERDB=", set->userdb, NULL));
+	env_put(t_strconcat("PASSDB=", set->passdb, NULL));
+	env_put(t_strconcat("USERNAME_CHARS=", set->username_chars, NULL));
+	env_put(t_strconcat("USERNAME_TRANSLATION=",
+			    set->username_translation, NULL));
+	env_put(t_strconcat("ANONYMOUS_USERNAME=",
+			    set->anonymous_username, NULL));
+	env_put(t_strdup_printf("CACHE_SIZE=%u", set->cache_size));
+	env_put(t_strdup_printf("CACHE_TTL=%u", set->cache_ttl));
 
-	if (uid != (uid_t)-1 || gid != (gid_t)-1) {
-		/* set correct permissions */
-		if (chown(path, uid, gid) < 0) {
-			i_fatal("login: chown(%s, %s, %s) failed: %m",
-				path, dec2str(uid), dec2str(gid));
-		}
+	for (as = set->sockets, i = 1; as != NULL; as = as->next, i++) {
+		if (strcmp(as->type, "listen") != 0)
+			continue;
+
+		str = t_strdup_printf("AUTH_%u", i);
+		socket_settings_env_put(str, &as->client);
+		socket_settings_env_put(t_strconcat(str, "_MASTER", NULL),
+					&as->master);
 	}
-	return fd;
+
+	if (set->verbose)
+		env_put("VERBOSE=1");
+	if (set->debug)
+		env_put("VERBOSE_DEBUG=1");
+	if (set->ssl_require_client_cert)
+		env_put("SSL_REQUIRE_CLIENT_CERT=1");
+
+	restrict_process_size(set->process_size, (unsigned int)-1);
 }
 
-static int create_auth_process(struct auth_process_group *group,
-			       int balancer_worker)
+static int create_auth_process(struct auth_process_group *group)
 {
 	struct auth_socket_settings *as;
-	const char *prefix, *str, *executable;
+	const char *prefix, *executable;
 	struct log_io *log;
 	pid_t pid;
-	int fd[2], log_fd, i, balancer;
-
-	balancer = group->balancer_listen_fd != -1 && !balancer_worker;
+	int fd[2], log_fd, i;
 
 	/* see if this is a connect socket */
 	as = group->set->sockets;
@@ -435,7 +482,7 @@
 
 		net_set_nonblock(fd[0], TRUE);
 		fd_close_on_exec(fd[0], TRUE);
-		auth_process_new(pid, fd[0], group, balancer);
+		auth_process_new(pid, fd[0], group);
 		(void)close(fd[1]);
 		(void)close(log_fd);
 		return 0;
@@ -460,76 +507,94 @@
 
 	child_process_init_env();
 
-	if (!balancer_worker) {
-		i_assert(group->balancer_listen_fd != 3);
-		if (group->listen_fd != 3) {
-			if (dup2(group->listen_fd, 3) < 0)
-				i_fatal("dup2() failed: %m");
-		}
-		fd_close_on_exec(3, FALSE);
+	if (group->listen_fd != 3) {
+		if (dup2(group->listen_fd, 3) < 0)
+			i_fatal("dup2() failed: %m");
 	}
-
-	if (balancer) {
-		if (group->balancer_listen_fd != 4) {
-			if (dup2(group->balancer_listen_fd, 4) < 0)
-				i_fatal("dup2() failed: %m");
-		}
-		fd_close_on_exec(4, FALSE);
-	}
+	fd_close_on_exec(3, FALSE);
 
 	for (i = 0; i <= 2; i++)
 		fd_close_on_exec(i, FALSE);
 
-	/* setup access environment */
-	restrict_access_set_env(group->set->user, group->set->uid,
-				group->set->gid, group->set->chroot,
-				0, 0, NULL);
+        auth_set_environment(group->set);
 
-	/* set other environment */
-	env_put("DOVECOT_MASTER=1");
-	env_put(t_strconcat("AUTH_NAME=", group->set->name, NULL));
-	env_put(t_strconcat("MECHANISMS=", group->set->mechanisms, NULL));
-	env_put(t_strconcat("REALMS=", group->set->realms, NULL));
-	env_put(t_strconcat("DEFAULT_REALM=", group->set->default_realm, NULL));
-	env_put(t_strconcat("USERDB=", group->set->userdb, NULL));
-	env_put(t_strconcat("PASSDB=", group->set->passdb, NULL));
-	env_put(t_strconcat("USERNAME_CHARS=", group->set->username_chars, NULL));
-	env_put(t_strconcat("USERNAME_TRANSLATION=",
-			    group->set->username_translation, NULL));
-	env_put(t_strconcat("ANONYMOUS_USERNAME=",
-			    group->set->anonymous_username, NULL));
-	env_put(t_strdup_printf("CACHE_SIZE=%u", group->set->cache_size));
-	env_put(t_strdup_printf("CACHE_TTL=%u", group->set->cache_ttl));
-
-	for (as = group->set->sockets, i = 1; as != NULL; as = as->next, i++) {
-		if (strcmp(as->type, "listen") != 0)
-			continue;
-
-		str = t_strdup_printf("AUTH_%u", i);
-		socket_settings_env_put(str, &as->client);
-		socket_settings_env_put(t_strconcat(str, "_MASTER", NULL),
-					&as->master);
-	}
-
-	if (group->set->verbose)
-		env_put("VERBOSE=1");
-	if (group->set->debug)
-		env_put("VERBOSE_DEBUG=1");
-	if (group->set->ssl_require_client_cert)
-		env_put("SSL_REQUIRE_CLIENT_CERT=1");
-
-	restrict_process_size(group->set->process_size, (unsigned int)-1);
+	env_put(t_strdup_printf("AUTH_WORKER_PATH=%s/auth-worker.%s",
+				group->set->parent->defaults->base_dir,
+				dec2str(getpid())));
+	env_put(t_strdup_printf("AUTH_WORKER_MAX_COUNT=%u",
+				group->set->worker_max_count));
 
 	/* make sure we don't leak syslog fd, but do it last so that
 	   any errors above will be logged */
 	closelog();
 
 	executable = group->set->executable;
-	if (balancer || balancer_worker) {
-		/* we are running in balancer mode */
-		executable = t_strconcat(executable, balancer_worker ?
-					 " -bw" : " -b", NULL);
+	client_process_exec(executable, "");
+	i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", executable);
+	return -1;
+}
+
+static int create_auth_worker(struct auth_process *process, int fd)
+{
+	struct log_io *log;
+	const char *prefix, *executable;
+	pid_t pid;
+	int log_fd, i;
+
+	log_fd = log_create_pipe(&log, 0);
+	if (log_fd < 0)
+		pid = -1;
+	else {
+		pid = fork();
+		if (pid < 0)
+			i_error("fork() failed: %m");
+	}
+
+	if (pid < 0) {
+		(void)close(log_fd);
+		return -1;
 	}
+
+	if (pid != 0) {
+		/* master */
+		PID_ADD_PROCESS_TYPE(pid, PROCESS_TYPE_AUTH_WORKER);
+		prefix = t_strdup_printf("auth-worker(%s): ",
+					 process->group->set->name);
+		log_set_prefix(log, prefix);
+		(void)close(fd);
+		(void)close(log_fd);
+		return 0;
+	}
+
+	prefix = t_strdup_printf("master-auth-worker(%s): ",
+				 process->group->set->name);
+	log_set_prefix(log, prefix);
+
+	/* set stdin and stdout to /dev/null, so anything written into it
+	   gets ignored. */
+	if (dup2(null_fd, 0) < 0)
+		i_fatal("dup2(stdin) failed: %m");
+	if (dup2(null_fd, 1) < 0)
+		i_fatal("dup2(stdout) failed: %m");
+
+	if (dup2(log_fd, 2) < 0)
+		i_fatal("dup2(stderr) failed: %m");
+
+	if (dup2(fd, 4) < 0)
+		i_fatal("dup2(stdin) failed: %m");
+
+	for (i = 0; i <= 2; i++)
+		fd_close_on_exec(i, FALSE);
+	fd_close_on_exec(4, FALSE);
+
+	child_process_init_env();
+        auth_set_environment(process->group->set);
+
+	/* make sure we don't leak syslog fd, but do it last so that
+	   any errors above will be logged */
+	closelog();
+
+	executable = t_strconcat(process->group->set->executable, " -w", NULL);
 	client_process_exec(executable, "");
 	i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", executable);
 	return -1;
@@ -569,17 +634,11 @@
 			   auth_set->name, NULL);
 	group->listen_fd = unix_socket_create(path, 0660, master_uid,
 					      auth_set->parent->login_gid, 16);
+	if (group->listen_fd == -1)
+		i_fatal("Couldn't create auth process listener");
 
-	if (auth_set->count <= 1)
-		group->balancer_listen_fd = -1;
-	else {
-		path = t_strconcat(auth_set->parent->defaults->base_dir, "/",
-				   auth_set->name, "-balancer", NULL);
-		group->balancer_listen_fd =
-			unix_socket_create(path, 0600, (uid_t)-1, (gid_t)-1,
-					   group->set->count);
-		group->need_balancer = TRUE;
-	}
+	net_set_nonblock(group->listen_fd, TRUE);
+	fd_close_on_exec(group->listen_fd, TRUE);
 }
 
 static void auth_process_group_destroy(struct auth_process_group *group)
@@ -597,12 +656,6 @@
 			   group->set->name, NULL);
 	(void)unlink(path);
 
-	if (group->balancer_listen_fd != -1) {
-		path = t_strconcat(group->set->parent->defaults->base_dir, "/",
-				   group->set->name, "-balancer", NULL);
-		(void)unlink(path);
-	}
-
 	if (close(group->listen_fd) < 0)
 		i_error("close(%s) failed: %m", path);
 	i_free(group);
@@ -637,7 +690,6 @@
 {
 	struct auth_process_group *group;
 	unsigned int count;
-	int balancer_worker;
 
 	if (process_groups == NULL) {
 		/* first time here, create the groups */
@@ -645,13 +697,9 @@
 	}
 
 	for (group = process_groups; group != NULL; group = group->next) {
-		if (group->need_balancer)
-			(void)create_auth_process(group, FALSE);
-
-		balancer_worker = group->balancer_listen_fd != -1;
 		count = group->process_count;
 		for (; count < group->set->count; count++)
-			(void)create_auth_process(group, balancer_worker);
+			(void)create_auth_process(group);
 	}
 }
 
--- a/src/master/common.h	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/master/common.h	Tue Mar 01 00:19:21 2005 +0200
@@ -10,6 +10,7 @@
 enum process_type {
 	PROCESS_TYPE_UNKNOWN,
 	PROCESS_TYPE_AUTH,
+	PROCESS_TYPE_AUTH_WORKER,
 	PROCESS_TYPE_LOGIN,
 	PROCESS_TYPE_IMAP,
 	PROCESS_TYPE_POP3,
--- a/src/master/main.c	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/master/main.c	Tue Mar 01 00:19:21 2005 +0200
@@ -25,6 +25,7 @@
 const char *process_names[PROCESS_TYPE_MAX] = {
 	"unknown",
 	"auth",
+	"auth-worker",
 	"login",
 	"imap",
 	"pop3",
--- a/src/master/master-settings.c	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/master/master-settings.c	Tue Mar 01 00:19:21 2005 +0200
@@ -155,6 +155,7 @@
 	DEF(SET_BOOL, ssl_require_client_cert),
 
 	DEF(SET_INT, count),
+	DEF(SET_INT, worker_max_count),
 	DEF(SET_INT, process_size),
 
 	{ 0, NULL, 0 }
@@ -330,6 +331,7 @@
 	MEMBER(ssl_require_client_cert) FALSE,
 
 	MEMBER(count) 1,
+	MEMBER(worker_max_count) 30,
 	MEMBER(process_size) 256,
 
 	/* .. */
--- a/src/master/master-settings.h	Tue Mar 01 00:15:25 2005 +0200
+++ b/src/master/master-settings.h	Tue Mar 01 00:19:21 2005 +0200
@@ -144,6 +144,7 @@
 	int ssl_require_client_cert;
 
 	unsigned int count;
+	unsigned int worker_max_count;
 	unsigned int process_size;
 
 	/* .. */