changeset 9984:097588a7903c HEAD

lib-auth: Changed API to connect to only a single specified auth socket. Login processes now always connect to socket called "auth".
author Timo Sirainen <tss@iki.fi>
date Wed, 07 Oct 2009 17:46:14 -0400
parents 9716b5a4b14a
children ea36bad4d9da
files doc/example-config/conf.d/master.conf src/lib-auth/Makefile.am src/lib-auth/auth-client-private.h src/lib-auth/auth-client-request.c src/lib-auth/auth-client-request.h src/lib-auth/auth-client.c src/lib-auth/auth-client.h src/lib-auth/auth-server-connection.c src/lib-auth/auth-server-connection.h src/lib-auth/auth-server-request.c src/lib-auth/auth-server-request.h src/login-common/client-common.h src/login-common/main.c src/login-common/sasl-server.c src/pop3-login/client-authenticate.c src/pop3-login/client.c src/pop3-login/client.h src/util/authtest.c
diffstat 18 files changed, 590 insertions(+), 933 deletions(-) [+]
line wrap: on
line diff
--- a/doc/example-config/conf.d/master.conf	Wed Oct 07 17:44:38 2009 -0400
+++ b/doc/example-config/conf.d/master.conf	Wed Oct 07 17:46:14 2009 -0400
@@ -36,8 +36,7 @@
 
   # default
   unix_listener {
-    # The path must match the auth section name
-    path = login/default
+    path = login/auth
     mode = 0666
   }
 
--- a/src/lib-auth/Makefile.am	Wed Oct 07 17:44:38 2009 -0400
+++ b/src/lib-auth/Makefile.am	Wed Oct 07 17:46:14 2009 -0400
@@ -5,16 +5,17 @@
 
 libauth_la_SOURCES = \
 	auth-client.c \
+	auth-client-request.c \
 	auth-master.c \
-	auth-server-connection.c \
-	auth-server-request.c
+	auth-server-connection.c
 
 headers = \
 	auth-client.h \
 	auth-client-interface.h \
+	auth-client-private.h \
+	auth-client-request.h \
 	auth-master.h \
-	auth-server-connection.h \
-	auth-server-request.h
+	auth-server-connection.h
 
 if INSTALL_HEADERS
   pkginc_libdir=$(pkgincludedir)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-auth/auth-client-private.h	Wed Oct 07 17:46:14 2009 -0400
@@ -0,0 +1,20 @@
+#ifndef AUTH_CLIENT_PRIVATE_H
+#define AUTH_CLIENT_PRIVATE_H
+
+#include "auth-client.h"
+
+struct auth_client {
+	char *auth_socket_path;
+	unsigned int client_pid;
+
+	struct auth_server_connection *conn;
+
+	auth_connect_notify_callback_t *connect_notify_callback;
+	void *connect_notify_context;
+
+	unsigned int request_id_counter;
+
+	unsigned int debug:1;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-auth/auth-client-request.c	Wed Oct 07 17:46:14 2009 -0400
@@ -0,0 +1,173 @@
+/* Copyright (c) 2003-2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str.h"
+#include "strescape.h"
+#include "ostream.h"
+#include "auth-client-private.h"
+#include "auth-server-connection.h"
+#include "auth-client-request.h"
+
+#include <stdlib.h>
+
+struct auth_client_request {
+	pool_t pool;
+
+	struct auth_server_connection *conn;
+	unsigned int id;
+
+	struct auth_request_info request_info;
+
+	auth_request_callback_t *callback;
+	void *context;
+};
+
+static void auth_server_send_new_request(struct auth_server_connection *conn,
+					 struct auth_client_request *request)
+{
+	struct auth_request_info *info = &request->request_info;
+	string_t *str;
+
+	str = t_str_new(512);
+	str_printfa(str, "AUTH\t%u\t", request->id);
+	str_tabescape_write(str, info->mech);
+	str_append(str, "\tservice=");
+	str_tabescape_write(str, info->service);
+
+	if ((info->flags & AUTH_REQUEST_FLAG_SECURED) != 0)
+		str_append(str, "\tsecured");
+	if ((info->flags & AUTH_REQUEST_FLAG_VALID_CLIENT_CERT) != 0)
+		str_append(str, "\tvalid-client-cert");
+
+	if (info->cert_username != NULL) {
+		str_append(str, "\tcert_username=");
+		str_tabescape_write(str, info->cert_username);
+	}
+	if (info->local_ip.family != 0)
+		str_printfa(str, "\tlip=%s", net_ip2addr(&info->local_ip));
+	if (info->remote_ip.family != 0)
+		str_printfa(str, "\trip=%s", net_ip2addr(&info->remote_ip));
+	if (info->local_port != 0)
+		str_printfa(str, "\tlport=%u", info->local_port);
+	if (info->remote_port != 0)
+		str_printfa(str, "\trport=%u", info->remote_port);
+	if (info->initial_resp_base64 != NULL) {
+		str_append(str, "\tresp=");
+		str_tabescape_write(str, info->initial_resp_base64);
+	}
+	str_append_c(str, '\n');
+
+	if (o_stream_send(conn->output, str_data(str), str_len(str)) < 0)
+		i_error("Error sending request to auth server: %m");
+}
+
+struct auth_client_request *
+auth_client_request_new(struct auth_client *client,
+			const struct auth_request_info *request_info,
+			auth_request_callback_t *callback, void *context)
+{
+	struct auth_client_request *request;
+	pool_t pool;
+
+	pool = pool_alloconly_create("auth client request", 512);
+	request = p_new(pool, struct auth_client_request, 1);
+	request->pool = pool;
+	request->conn = client->conn;
+
+	request->request_info = *request_info;
+	request->request_info.mech = p_strdup(pool, request_info->mech);
+	request->request_info.service = p_strdup(pool, request_info->service);
+	request->request_info.cert_username =
+		p_strdup(pool, request_info->cert_username);
+	request->request_info.initial_resp_base64 =
+		p_strdup(pool, request_info->initial_resp_base64);
+	
+	request->callback = callback;
+	request->context = context;
+
+	request->id =
+		auth_server_connection_add_request(request->conn, request);
+	T_BEGIN {
+		auth_server_send_new_request(request->conn, request);
+	} T_END;
+	return request;
+}
+
+void auth_client_request_continue(struct auth_client_request *request,
+                                  const char *data_base64)
+{
+	struct const_iovec iov[3];
+	const char *prefix;
+
+	prefix = t_strdup_printf("CONT\t%u\t", request->id);
+
+	iov[0].iov_base = prefix;
+	iov[0].iov_len = strlen(prefix);
+	iov[1].iov_base = data_base64;
+	iov[1].iov_len = strlen(data_base64);
+	iov[2].iov_base = "\n";
+	iov[2].iov_len = 1;
+
+	if (o_stream_sendv(request->conn->output, iov, 3) < 0)
+		i_error("Error sending continue request to auth server: %m");
+}
+
+void auth_client_request_abort(struct auth_client_request **_request)
+{
+	struct auth_client_request *request = *_request;
+
+	*_request = NULL;
+
+	request->callback(request, AUTH_REQUEST_STATUS_FAIL, NULL, NULL,
+			  request->context);
+	request->callback = NULL;
+}
+
+unsigned int auth_client_request_get_id(struct auth_client_request *request)
+{
+	return request->id;
+}
+
+unsigned int
+auth_client_request_get_server_pid(struct auth_client_request *request)
+{
+	return request->conn->server_pid;
+}
+
+bool auth_client_request_is_aborted(struct auth_client_request *request)
+{
+	return request->callback == NULL;
+}
+
+void auth_client_request_server_input(struct auth_client_request *request,
+				      enum auth_request_status status,
+				      const char *const *args)
+{
+	const char *const *tmp, *base64_data = NULL;
+
+	if (request->callback == NULL) {
+		/* aborted already */
+		return;
+	}
+
+	switch (status) {
+	case AUTH_REQUEST_STATUS_OK:
+		for (tmp = args; *tmp != NULL; tmp++) {
+			if (strncmp(*tmp, "resp=", 5) == 0) {
+				base64_data = *tmp + 5;
+				break;
+			}
+		}
+		break;
+	case AUTH_REQUEST_STATUS_CONTINUE:
+		base64_data = args[0];
+		args = NULL;
+		break;
+	case AUTH_REQUEST_STATUS_FAIL:
+		break;
+	}
+
+	request->callback(request, status, base64_data, args, request->context);
+	if (status != AUTH_REQUEST_STATUS_CONTINUE)
+		pool_unref(&request->pool);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-auth/auth-client-request.h	Wed Oct 07 17:46:14 2009 -0400
@@ -0,0 +1,12 @@
+#ifndef AUTH_CLIENT_REQUEST_H
+#define AUTH_CLIENT_REQUEST_H
+
+struct auth_server_connection;
+
+bool auth_client_request_is_aborted(struct auth_client_request *request);
+
+void auth_client_request_server_input(struct auth_client_request *request,
+				      enum auth_request_status status,
+				      const char *const *args);
+
+#endif
--- a/src/lib-auth/auth-client.c	Wed Oct 07 17:44:38 2009 -0400
+++ b/src/lib-auth/auth-client.c	Wed Oct 07 17:46:14 2009 -0400
@@ -1,118 +1,39 @@
-/* Copyright (c) 2003-2009 Dovecot authors, see the included COPYING file */
+/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
-#include "buffer.h"
-#include "ioloop.h"
-#include "hash.h"
-#include "auth-client.h"
+#include "array.h"
+#include "auth-client-private.h"
 #include "auth-server-connection.h"
 
-#include <dirent.h>
-#include <sys/stat.h>
-
-#define AUTH_CLIENT_SOCKET_MAX_WAIT_TIME 10
-
-struct auth_client *auth_client_new(unsigned int client_pid)
+struct auth_client *
+auth_client_init(const char *auth_socket_path, unsigned int client_pid,
+		 bool debug)
 {
 	struct auth_client *client;
 
 	client = i_new(struct auth_client, 1);
-	client->pid = client_pid;
-	client->available_auth_mechs = buffer_create_dynamic(default_pool, 128);
-
-	auth_client_connect_missing_servers(client);
+	client->client_pid = client_pid;
+	client->auth_socket_path = i_strdup(auth_socket_path);
+	client->debug = debug;
+	client->conn = auth_server_connection_init(client);
+	(void)auth_server_connection_connect(client->conn);
 	return client;
 }
 
-void auth_client_free(struct auth_client **_client)
+void auth_client_deinit(struct auth_client **_client)
 {
 	struct auth_client *client = *_client;
-	struct auth_server_connection *next;
-	struct auth_mech_desc *mech;
-	size_t i, size;
 
 	*_client = NULL;
 
-	mech = buffer_get_modifiable_data(client->available_auth_mechs, &size);
-	size /= sizeof(*mech);
-	for (i = 0; i < size; i++)
-		i_free(mech[i].name);
-	buffer_free(&client->available_auth_mechs);
-
-	while (client->connections != NULL) {
-		next = client->connections->next;
-		auth_server_connection_destroy(&client->connections, FALSE);
-		client->connections = next;
-	}
-
-	if (client->to_reconnect != NULL)
-		timeout_remove(&client->to_reconnect);
+	auth_server_connection_deinit(&client->conn);
+	i_free(client->auth_socket_path);
 	i_free(client);
 }
 
-void auth_client_reconnect(struct auth_client *client)
-{
-	struct auth_server_connection *next;
-
-	while (client->connections != NULL) {
-		next = client->connections->next;
-		auth_server_connection_destroy(&client->connections, FALSE);
-		client->connections = next;
-	}
-
-	auth_client_connect_missing_servers(client);
-}
-
-const struct auth_mech_desc *
-auth_client_get_available_mechs(struct auth_client *client,
-				unsigned int *mech_count)
-{
-	const struct auth_mech_desc *mechs;
-	size_t size;
-
-	mechs = buffer_get_data(client->available_auth_mechs, &size);
-	*mech_count = size / sizeof(*mechs);
-	return mechs;
-}
-
-const struct auth_mech_desc *
-auth_client_find_mech(struct auth_client *client, const char *name)
-{
-	const struct auth_mech_desc *mech;
-	size_t i, size;
-
-	mech = buffer_get_data(client->available_auth_mechs, &size);
-	size /= sizeof(*mech);
-	for (i = 0; i < size; i++) {
-		if (strcasecmp(mech[i].name, name) == 0)
-			return &mech[i];
-	}
-
-	return NULL;
-}
-
-bool auth_client_reserve_connection(struct auth_client *client,
-				    const char *mech,
-				    struct auth_connect_id *id_r)
-{
-	struct auth_server_connection *conn;
-	const char *error;
-
-	conn = auth_server_connection_find_mech(client, mech, &error);
-	if (conn == NULL)
-		return FALSE;
-
-	id_r->server_pid = conn->server_pid;
-	id_r->connect_uid = conn->connect_uid;
-
-	return TRUE;
-}
-
 bool auth_client_is_connected(struct auth_client *client)
 {
-	return !client->reconnect &&
-		client->conn_waiting_handshake_count == 0 &&
-		client->connections != NULL;
+	return client->conn->handshake_received;
 }
 
 void auth_client_set_connect_notify(struct auth_client *client,
@@ -123,67 +44,31 @@
 	client->connect_notify_context = context;
 }
 
-static void reconnect_timeout(struct auth_client *client)
+const struct auth_mech_desc *
+auth_client_get_available_mechs(struct auth_client *client,
+				unsigned int *mech_count)
 {
-	auth_client_connect_missing_servers(client);
+	return array_get(&client->conn->available_auth_mechs, mech_count);
 }
 
-void auth_client_connect_missing_servers(struct auth_client *client)
+const struct auth_mech_desc *
+auth_client_find_mech(struct auth_client *client, const char *name)
 {
-	DIR *dirp;
-	struct dirent *dp;
-	struct stat st;
-
-	/* we're chrooted */
-	dirp = opendir(".");
-	if (dirp == NULL) {
-		i_fatal("opendir(.) failed when trying to get list of "
-			"authentication servers: %m");
-	}
-
-	client->reconnect = FALSE;
-	while ((dp = readdir(dirp)) != NULL) {
-		const char *name = dp->d_name;
-
-		if (name[0] == '.')
-			continue;
-
-		if (auth_server_connection_find_path(client, name) != NULL) {
-			/* already connected */
-			continue;
-		}
+	const struct auth_mech_desc *mechs;
+	unsigned int i, count;
 
-		/* Normally they're sockets, but in UnixWare they're
-		   created as fifos. */
-		if (stat(name, &st) == 0 &&
-		    (S_ISSOCK(st.st_mode) || S_ISFIFO(st.st_mode))) {
-			if (auth_server_connection_new(client, name) == NULL)
-				client->reconnect = TRUE;
-		}
+	mechs = array_get(&client->conn->available_auth_mechs, &count);
+	for (i = 0; i < count; i++) {
+		if (strcasecmp(mechs[i].name, name) == 0)
+			return &mechs[i];
 	}
-
-	if (client->connections == NULL && !client->reconnect) {
-		if (client->missing_sockets_start_time == 0)
-			client->missing_sockets_start_time = ioloop_time;
-		else if (ioloop_time - client->missing_sockets_start_time >
-			 AUTH_CLIENT_SOCKET_MAX_WAIT_TIME)
-			i_fatal("No authentication sockets found");
-	}
+	return NULL;
+}
 
-	if (closedir(dirp) < 0)
-		i_error("closedir() failed: %m");
-
-	if (client->reconnect || client->connections == NULL) {
-		if (client->to_reconnect == NULL) {
-			client->to_reconnect =
-				timeout_add(1000, reconnect_timeout, client);
-		}
-	} else if (client->to_reconnect != NULL)
-		timeout_remove(&client->to_reconnect);
-
-	if (client->connect_notify_callback != NULL) {
-		client->connect_notify_callback(client,
-				auth_client_is_connected(client),
-				client->connect_notify_context);
-	}
+void auth_client_get_connect_id(struct auth_client *client,
+				unsigned int *server_pid_r,
+				unsigned int *connect_uid_r)
+{
+	*server_pid_r = client->conn->server_pid;
+	*connect_uid_r = client->conn->connect_uid;
 }
--- a/src/lib-auth/auth-client.h	Wed Oct 07 17:44:38 2009 -0400
+++ b/src/lib-auth/auth-client.h	Wed Oct 07 17:46:14 2009 -0400
@@ -5,13 +5,19 @@
 #include "auth-client-interface.h"
 
 struct auth_client;
-struct auth_request;
+struct auth_client_request;
 
 enum auth_request_flags {
 	AUTH_REQUEST_FLAG_SECURED		= 0x01,
 	AUTH_REQUEST_FLAG_VALID_CLIENT_CERT	= 0x02
 };
 
+enum auth_request_status {
+	AUTH_REQUEST_STATUS_FAIL = -1,
+	AUTH_REQUEST_STATUS_CONTINUE,
+	AUTH_REQUEST_STATUS_OK
+};
+
 struct auth_mech_desc {
 	char *name;
         enum mech_security_flags flags;
@@ -34,7 +40,8 @@
 	const char *initial_resp_base64;
 };
 
-typedef void auth_request_callback_t(struct auth_request *request, int status,
+typedef void auth_request_callback_t(struct auth_client_request *request,
+				     enum auth_request_status status,
 				     const char *data_base64,
 				     const char *const *args, void *context);
 
@@ -42,11 +49,10 @@
 					    bool connected, void *context);
 
 /* Create new authentication client. */
-struct auth_client *auth_client_new(unsigned int client_pid);
-void auth_client_free(struct auth_client **client);
-
-/* Destroy all connections and reconnect. */
-void auth_client_reconnect(struct auth_client *client);
+struct auth_client *
+auth_client_init(const char *auth_socket_path, unsigned int client_pid,
+		 bool debug);
+void auth_client_deinit(struct auth_client **client);
 
 bool auth_client_is_connected(struct auth_client *client);
 void auth_client_set_connect_notify(struct auth_client *client,
@@ -58,35 +64,27 @@
 const struct auth_mech_desc *
 auth_client_find_mech(struct auth_client *client, const char *name);
 
-/* Reserve connection for specific mechanism. The id can be given to
-   auth_client_request_new() to force it to use the same connection, or fail.
-   This is currently useful only for APOP authentication. Returns TRUE if
-   successfull. */
-bool auth_client_reserve_connection(struct auth_client *client,
-				    const char *mech,
-				    struct auth_connect_id *id_r);
+/* Return current connection's identifiers. */
+void auth_client_get_connect_id(struct auth_client *client,
+				unsigned int *server_pid_r,
+				unsigned int *connect_uid_r);
 
 /* Create a new authentication request. callback is called whenever something
-   happens for the request. id can be NULL. */
-struct auth_request *
-auth_client_request_new(struct auth_client *client, struct auth_connect_id *id,
+   happens for the request. */
+struct auth_client_request *
+auth_client_request_new(struct auth_client *client,
 			const struct auth_request_info *request_info,
-			auth_request_callback_t *callback, void *context,
-			const char **error_r);
+			auth_request_callback_t *callback, void *context);
 /* Continue authentication. Call when
    reply->result == AUTH_CLIENT_REQUEST_CONTINUE */
-void auth_client_request_continue(struct auth_request *request,
+void auth_client_request_continue(struct auth_client_request *request,
 				  const char *data_base64);
-
 /* Abort ongoing authentication request. */
-void auth_client_request_abort(struct auth_request *request);
-
+void auth_client_request_abort(struct auth_client_request **request);
 /* Return ID of this request. */
-unsigned int auth_client_request_get_id(struct auth_request *request);
-
+unsigned int auth_client_request_get_id(struct auth_client_request *request);
 /* Return the PID of the server that handled this request. */
-unsigned int auth_client_request_get_server_pid(struct auth_request *request);
-
-void auth_client_connect_missing_servers(struct auth_client *client);
+unsigned int
+auth_client_request_get_server_pid(struct auth_client_request *request);
 
 #endif
--- a/src/lib-auth/auth-server-connection.c	Wed Oct 07 17:44:38 2009 -0400
+++ b/src/lib-auth/auth-server-connection.c	Wed Oct 07 17:46:14 2009 -0400
@@ -1,158 +1,211 @@
 /* Copyright (c) 2003-2009 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
-#include "buffer.h"
+#include "array.h"
 #include "hash.h"
 #include "ioloop.h"
 #include "istream.h"
 #include "ostream.h"
 #include "network.h"
-#include "auth-client.h"
+#include "eacces-error.h"
+#include "auth-client-private.h"
+#include "auth-client-request.h"
 #include "auth-server-connection.h"
-#include "auth-server-request.h"
 
 #include <unistd.h>
 #include <stdlib.h>
 
+#define AUTH_SERVER_CONN_MAX_LINE_LENGTH AUTH_CLIENT_MAX_LINE_LENGTH
 #define AUTH_HANDSHAKE_TIMEOUT (30*1000)
-
-static void auth_server_connection_unref(struct auth_server_connection *conn);
-
-static void update_available_auth_mechs(struct auth_server_connection *conn)
-{
-	struct auth_client *client = conn->client;
-	const struct auth_mech_desc *mech;
-	struct auth_mech_desc *new_mech;
-	unsigned int i;
+#define AUTH_SERVER_RECONNECT_TIMEOUT_SECS 5
 
-	mech = conn->available_auth_mechs;
-	for (i = 0; i < conn->available_auth_mechs_count; i++) {
-		if (auth_client_find_mech(client, mech[i].name) == NULL) {
-			new_mech = buffer_append_space_unsafe(
-				client->available_auth_mechs, sizeof(*mech));
-			*new_mech = mech[i];
-			new_mech->name = i_strdup(mech[i].name);
-		}
-	}
-}
+static void
+auth_server_connection_disconnect(struct auth_server_connection *conn);
 
-static bool
-auth_client_input_mech(struct auth_server_connection *conn, const char *args)
+static int
+auth_server_input_mech(struct auth_server_connection *conn,
+		       const char *const *args)
 {
-	const char *const *list;
 	struct auth_mech_desc mech_desc;
 
 	if (conn->handshake_received) {
 		i_error("BUG: Authentication server already sent handshake");
-		return FALSE;
+		return -1;
 	}
-
-	list = t_strsplit(args, "\t");
-	if (list[0] == NULL) {
+	if (args[0] == NULL) {
 		i_error("BUG: Authentication server sent broken MECH line");
-		return FALSE;
+		return -1;
 	}
 
 	memset(&mech_desc, 0, sizeof(mech_desc));
-	mech_desc.name = p_strdup(conn->pool, list[0]);
+	mech_desc.name = p_strdup(conn->pool, args[0]);
 
 	if (strcmp(mech_desc.name, "PLAIN") == 0)
 		conn->has_plain_mech = TRUE;
 
-	for (list++; *list != NULL; list++) {
-		if (strcmp(*list, "private") == 0)
+	for (args++; *args != NULL; args++) {
+		if (strcmp(*args, "private") == 0)
 			mech_desc.flags |= MECH_SEC_PRIVATE;
-		else if (strcmp(*list, "anonymous") == 0)
+		else if (strcmp(*args, "anonymous") == 0)
 			mech_desc.flags |= MECH_SEC_ANONYMOUS;
-		else if (strcmp(*list, "plaintext") == 0)
+		else if (strcmp(*args, "plaintext") == 0)
 			mech_desc.flags |= MECH_SEC_PLAINTEXT;
-		else if (strcmp(*list, "dictionary") == 0)
+		else if (strcmp(*args, "dictionary") == 0)
 			mech_desc.flags |= MECH_SEC_DICTIONARY;
-		else if (strcmp(*list, "active") == 0)
+		else if (strcmp(*args, "active") == 0)
 			mech_desc.flags |= MECH_SEC_ACTIVE;
-		else if (strcmp(*list, "forward-secrecy") == 0)
+		else if (strcmp(*args, "forward-secrecy") == 0)
 			mech_desc.flags |= MECH_SEC_FORWARD_SECRECY;
-		else if (strcmp(*list, "mutual-auth") == 0)
+		else if (strcmp(*args, "mutual-auth") == 0)
 			mech_desc.flags |= MECH_SEC_MUTUAL_AUTH;
 	}
-	buffer_append(conn->auth_mechs_buf, &mech_desc, sizeof(mech_desc));
-	return TRUE;
+	array_append(&conn->available_auth_mechs, &mech_desc, 1);
+	return 0;
 }
 
-static bool
-auth_client_input_spid(struct auth_server_connection *conn, const char *args)
+static int
+auth_server_input_spid(struct auth_server_connection *conn,
+		       const char *const *args)
 {
 	if (conn->handshake_received) {
 		i_error("BUG: Authentication server already sent handshake");
-		return FALSE;
+		return -1;
 	}
 
-	conn->server_pid = (unsigned int)strtoul(args, NULL, 10);
-	return TRUE;
+	conn->server_pid = (unsigned int)strtoul(args[0], NULL, 10);
+	return 0;
 }
 
-static bool
-auth_client_input_cuid(struct auth_server_connection *conn, const char *args)
+static int
+auth_server_input_cuid(struct auth_server_connection *conn,
+		       const char *const *args)
 {
 	if (conn->handshake_received) {
 		i_error("BUG: Authentication server already sent handshake");
-		return FALSE;
+		return -1;
 	}
 
-	conn->connect_uid = (unsigned int)strtoul(args, NULL, 10);
-	return TRUE;
+	conn->connect_uid = (unsigned int)strtoul(args[0], NULL, 10);
+	return 0;
 }
 
-static bool auth_client_input_done(struct auth_server_connection *conn)
+static int auth_server_input_done(struct auth_server_connection *conn)
 {
-	conn->available_auth_mechs = conn->auth_mechs_buf->data;
-	conn->available_auth_mechs_count =
-		conn->auth_mechs_buf->used / sizeof(struct auth_mech_desc);
-
-	if (conn->available_auth_mechs_count == 0) {
+	if (array_count(&conn->available_auth_mechs) == 0) {
 		i_error("BUG: Authentication server returned no mechanisms");
-		return FALSE;
+		return -1;
 	}
 
 	if (conn->to != NULL)
 		timeout_remove(&conn->to);
 
 	conn->handshake_received = TRUE;
-	conn->client->conn_waiting_handshake_count--;
-	update_available_auth_mechs(conn);
-
-	if (conn->client->connect_notify_callback != NULL &&
-	    auth_client_is_connected(conn->client)) {
+	if (conn->client->connect_notify_callback != NULL) {
 		conn->client->connect_notify_callback(conn->client, TRUE,
 				conn->client->connect_notify_context);
 	}
-	return TRUE;
+	return 0;
+}
+
+static int
+auth_server_lookup_request(struct auth_server_connection *conn,
+			   const char *id_arg, bool remove,
+			   struct auth_client_request **request_r)
+{
+	struct auth_client_request *request;
+	unsigned int id;
+
+	if (id_arg == NULL) {
+		i_error("BUG: Authentication server input missing ID");
+		return -1;
+	}
+	id = (unsigned int)strtoul(id_arg, NULL, 10);
+
+	request = hash_table_lookup(conn->requests, POINTER_CAST(id));
+	if (request == NULL) {
+		i_error("BUG: Authentication server sent unknown id %u", id);
+		return -1;
+	}
+	if (remove || auth_client_request_is_aborted(request))
+		hash_table_remove(conn->requests, POINTER_CAST(id));
+
+	*request_r = request;
+	return 0;
+}
+
+static int
+auth_server_input_ok(struct auth_server_connection *conn,
+		     const char *const *args)
+{
+	struct auth_client_request *request;
+
+	if (auth_server_lookup_request(conn, args[0], TRUE, &request) < 0)
+		return -1;
+	auth_client_request_server_input(request, AUTH_REQUEST_STATUS_OK,
+					 args + 1);
+	return 0;
 }
 
-static bool
-auth_client_input_line(struct auth_server_connection *conn, const char *line)
+static int auth_server_input_cont(struct auth_server_connection *conn,
+				  const char *const *args)
+{
+	struct auth_client_request *request;
+
+	if (str_array_length(args) < 2) {
+		i_error("BUG: Authentication server sent broken CONT line");
+		return -1;
+	}
+
+	if (auth_server_lookup_request(conn, args[0], FALSE, &request) < 0)
+		return -1;
+	auth_client_request_server_input(request, AUTH_REQUEST_STATUS_CONTINUE,
+					 args + 1);
+	return 0;
+}
+
+static int auth_server_input_fail(struct auth_server_connection *conn,
+				  const char *const *args)
 {
-	if (strncmp(line, "OK\t", 3) == 0)
-		return auth_client_input_ok(conn, line + 3);
-	else if (strncmp(line, "CONT\t", 5) == 0)
-		return auth_client_input_cont(conn, line + 5);
-	else if (strncmp(line, "FAIL\t", 5) == 0)
-		return auth_client_input_fail(conn, line + 5);
-	else if (strncmp(line, "MECH\t", 5) == 0)
-		return auth_client_input_mech(conn, line + 5);
-	else if (strncmp(line, "SPID\t", 5) == 0)
-		return auth_client_input_spid(conn, line + 5);
-	else if (strncmp(line, "CUID\t", 5) == 0)
-		return auth_client_input_cuid(conn, line + 5);
-	else if (strcmp(line, "DONE") == 0)
-		return auth_client_input_done(conn);
+	struct auth_client_request *request;
+
+	if (auth_server_lookup_request(conn, args[0], TRUE, &request) < 0)
+		return -1;
+	auth_client_request_server_input(request, AUTH_REQUEST_STATUS_FAIL,
+					 args + 1);
+	return 0;
+}
+
+static int
+auth_server_connection_input_line(struct auth_server_connection *conn,
+				  const char *line)
+{
+	const char *const *args;
+
+	if (conn->client->debug)
+		i_info("auth input: %s", line);
+
+	args = t_strsplit(line, "\t");
+	if (strcmp(args[0], "OK") == 0)
+		return auth_server_input_ok(conn, args + 1);
+	else if (strcmp(args[0], "CONT") == 0)
+		return auth_server_input_cont(conn, args + 1);
+	else if (strcmp(args[0], "FAIL") == 0)
+		return auth_server_input_fail(conn, args + 1);
+	else if (strcmp(args[0], "MECH") == 0)
+		return auth_server_input_mech(conn, args + 1);
+	else if (strcmp(args[0], "SPID") == 0)
+		return auth_server_input_spid(conn, args + 1);
+	else if (strcmp(args[0], "CUID") == 0)
+		return auth_server_input_cuid(conn, args + 1);
+	else if (strcmp(args[0], "DONE") == 0)
+		return auth_server_input_done(conn);
 	else {
-		/* ignore unknown command */
-		return TRUE;
+		i_error("Auth server sent unknown command: %s", args[0]);
+		return -1;
 	}
 }
 
-static void auth_client_input(struct auth_server_connection *conn)
+static void auth_server_connection_input(struct auth_server_connection *conn)
 {
 	const char *line;
 	int ret;
@@ -162,17 +215,17 @@
 		return;
 	case -1:
 		/* disconnected */
-		auth_server_connection_destroy(&conn, TRUE);
+		auth_server_connection_disconnect(conn);
 		return;
 	case -2:
 		/* buffer full - can't happen unless auth is buggy */
 		i_error("BUG: Auth server sent us more than %d bytes of data",
-			AUTH_CLIENT_MAX_LINE_LENGTH);
-		auth_server_connection_destroy(&conn, FALSE);
+			AUTH_SERVER_CONN_MAX_LINE_LENGTH);
+		auth_server_connection_disconnect(conn);
 		return;
 	}
 
-	if (conn->version_received) {
+	if (!conn->version_received) {
 		line = i_stream_next_line(conn->input);
 		if (line == NULL)
 			return;
@@ -183,45 +236,132 @@
 		    AUTH_CLIENT_PROTOCOL_MAJOR_VERSION) {
 			i_error("Authentication server not compatible with "
 				"this client (mixed old and new binaries?)");
-			auth_server_connection_destroy(&conn, FALSE);
+			auth_server_connection_disconnect(conn);
 			return;
 		}
 		conn->version_received = TRUE;
 	}
 
-	conn->refcount++;
 	while ((line = i_stream_next_line(conn->input)) != NULL) {
 		T_BEGIN {
-			ret = auth_client_input_line(conn, line);
+			ret = auth_server_connection_input_line(conn, line);
 		} T_END;
 
-		if (!ret) {
-			auth_server_connection_destroy(&conn, FALSE);
+		if (ret < 0) {
+			auth_server_connection_disconnect(conn);
 			break;
 		}
 	}
-	auth_server_connection_unref(conn);
+}
+
+struct auth_server_connection *
+auth_server_connection_init(struct auth_client *client)
+{
+	struct auth_server_connection *conn;
+	pool_t pool;
+
+	pool = pool_alloconly_create("auth server connection", 1024);
+	conn = p_new(pool, struct auth_server_connection, 1);
+	conn->pool = pool;
+
+	conn->client = client;
+	conn->fd = -1;
+	conn->requests = hash_table_create(default_pool, pool, 100, NULL, NULL);
+	i_array_init(&conn->available_auth_mechs, 8);
+	return conn;
+}
+
+static void
+auth_server_connection_remove_requests(struct auth_server_connection *conn)
+{
+	static const char *const temp_failure_args[] = { "temp", NULL };
+	struct hash_iterate_context *iter;
+	void *key, *value;
+
+	iter = hash_table_iterate_init(conn->requests);
+	while (hash_table_iterate(iter, &key, &value)) {
+		struct auth_client_request *request = value;
+
+		auth_client_request_server_input(request,
+						 AUTH_REQUEST_STATUS_FAIL,
+						 temp_failure_args);
+	}
+	hash_table_iterate_deinit(&iter);
+}
+
+static void auth_server_connection_close(struct auth_server_connection *conn)
+{
+	if (conn->to != NULL)
+		timeout_remove(&conn->to);
+	if (conn->io != NULL)
+		io_remove(&conn->io);
+	i_stream_destroy(&conn->input);
+	o_stream_destroy(&conn->output);
+
+	if (close(conn->fd) < 0)
+		i_error("close(auth server connection) failed: %m");
+	conn->fd = -1;
+
+	auth_server_connection_remove_requests(conn);
+
+	if (conn->client->connect_notify_callback != NULL) {
+		conn->client->connect_notify_callback(conn->client, FALSE,
+				conn->client->connect_notify_context);
+	}
+}
+
+static void auth_server_reconnect_timeout(struct auth_server_connection *conn)
+{
+	timeout_remove(&conn->to);
+	(void)auth_server_connection_connect(conn);
+}
+
+static void
+auth_server_connection_disconnect(struct auth_server_connection *conn)
+{
+	time_t next_connect;
+
+	auth_server_connection_close(conn);
+
+	next_connect = conn->last_connect + AUTH_SERVER_RECONNECT_TIMEOUT_SECS;
+	conn->to = timeout_add(ioloop_time >= next_connect ? 0 :
+			       (next_connect - ioloop_time) * 1000,
+			       auth_server_reconnect_timeout, conn);
+}
+
+void auth_server_connection_deinit(struct auth_server_connection **_conn)
+{
+        struct auth_server_connection *conn = *_conn;
+
+	*_conn = NULL;
+
+	auth_server_connection_close(conn);
+
+	hash_table_destroy(&conn->requests);
+	array_free(&conn->available_auth_mechs);
+	pool_unref(&conn->pool);
 }
 
 static void auth_client_handshake_timeout(struct auth_server_connection *conn)
 {
 	i_error("Timeout waiting for handshake from auth server. "
 		"my pid=%u, input bytes=%"PRIuUOFF_T,
-		conn->client->pid, conn->input->v_offset);
-	auth_server_connection_destroy(&conn, TRUE);
+		conn->client->client_pid, conn->input->v_offset);
+	auth_server_connection_disconnect(conn);
 }
 
-struct auth_server_connection *
-auth_server_connection_new(struct auth_client *client, const char *path)
+int auth_server_connection_connect(struct auth_server_connection *conn)
 {
-	struct auth_server_connection *conn;
 	const char *handshake;
-	pool_t pool;
 	int fd, try;
 
+	i_assert(conn->fd == -1);
+
+	conn->last_connect = ioloop_time;
+
 	/* max. 1 second wait here. */
 	for (try = 0; try < 10; try++) {
-		fd = net_connect_unix(path);
+		fd = net_connect_unix(conn->client->auth_socket_path);
 		if (fd != -1 || (errno != EAGAIN && errno != ECONNREFUSED))
 			break;
 
@@ -229,161 +369,48 @@
 		usleep(((rand() % 10) + 1) * 10000);
 	}
 	if (fd == -1) {
-		i_error("Can't connect to auth server at %s: %m", path);
-		return NULL;
+		if (errno == EACCES) {
+			i_error("auth: %s",
+				eacces_error_get("connect",
+					conn->client->auth_socket_path));
+		} else {
+			i_error("auth: connect(%s) failed: %m",
+				conn->client->auth_socket_path);
+		}
+		return -1;
 	}
-
-	/* use blocking connection since we depend on auth server -
-	   if it's slow, just wait */
-
-	pool = pool_alloconly_create("Auth connection", 1024);
-	conn = p_new(pool, struct auth_server_connection, 1);
-	conn->refcount = 1;
-	conn->pool = pool;
-
-	conn->client = client;
-	conn->path = p_strdup(pool, path);
 	conn->fd = fd;
-	conn->io = io_add(fd, IO_READ, auth_client_input, conn);
-	conn->input = i_stream_create_fd(fd, AUTH_CLIENT_MAX_LINE_LENGTH,
+	conn->io = io_add(fd, IO_READ, auth_server_connection_input, conn);
+	conn->input = i_stream_create_fd(fd, AUTH_SERVER_CONN_MAX_LINE_LENGTH,
 					 FALSE);
 	conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE);
-	conn->requests = hash_table_create(default_pool, pool, 100, NULL, NULL);
-	conn->auth_mechs_buf = buffer_create_dynamic(default_pool, 256);
-
-	conn->to = timeout_add(AUTH_HANDSHAKE_TIMEOUT,
-			       auth_client_handshake_timeout, conn);
-	conn->next = client->connections;
-	client->connections = conn;
 
 	handshake = t_strdup_printf("VERSION\t%u\t%u\nCPID\t%u\n",
 				    AUTH_CLIENT_PROTOCOL_MAJOR_VERSION,
                                     AUTH_CLIENT_PROTOCOL_MINOR_VERSION,
-				    client->pid);
-
-        client->conn_waiting_handshake_count++;
+				    conn->client->client_pid);
 	if (o_stream_send_str(conn->output, handshake) < 0) {
-		errno = conn->output->stream_errno;
 		i_warning("Error sending handshake to auth server: %m");
-		auth_server_connection_destroy(&conn, TRUE);
-		return NULL;
-	}
-	return conn;
-}
-
-void auth_server_connection_destroy(struct auth_server_connection **_conn,
-				    bool reconnect)
-{
-        struct auth_server_connection *conn = *_conn;
-	struct auth_client *client = conn->client;
-	struct auth_server_connection **pos;
-
-	*_conn = NULL;
-
-	if (conn->fd == -1)
-		return;
-
-        pos = &conn->client->connections;
-	for (; *pos != NULL; pos = &(*pos)->next) {
-		if (*pos == conn) {
-			*pos = conn->next;
-			break;
-		}
+		auth_server_connection_disconnect(conn);
+		return -1;
 	}
 
-	if (!conn->handshake_received)
-		client->conn_waiting_handshake_count--;
-
-	if (conn->to != NULL)
-		timeout_remove(&conn->to);
-	if (conn->io != NULL)
-		io_remove(&conn->io);
-
-	i_stream_close(conn->input);
-	o_stream_close(conn->output);
-
-	if (close(conn->fd) < 0)
-		i_error("close(auth) failed: %m");
-	conn->fd = -1;
-
-	auth_server_requests_remove_all(conn);
-	auth_server_connection_unref(conn);
-
-	if (reconnect)
-		auth_client_connect_missing_servers(client);
-	else if (client->connect_notify_callback != NULL) {
-		client->connect_notify_callback(client,
-				auth_client_is_connected(client),
-				client->connect_notify_context);
-	}
+	conn->to = timeout_add(AUTH_HANDSHAKE_TIMEOUT,
+			       auth_client_handshake_timeout, conn);
+	return 0;
 }
 
-static void auth_server_connection_unref(struct auth_server_connection *conn)
-{
-	if (--conn->refcount > 0)
-		return;
-	i_assert(conn->refcount == 0);
-
-	hash_table_destroy(&conn->requests);
-	buffer_free(&conn->auth_mechs_buf);
-
-	i_stream_unref(&conn->input);
-	o_stream_unref(&conn->output);
-	pool_unref(&conn->pool);
-}
-
-struct auth_server_connection *
-auth_server_connection_find_path(struct auth_client *client, const char *path)
+unsigned int
+auth_server_connection_add_request(struct auth_server_connection *conn,
+				   struct auth_client_request *request)
 {
-	struct auth_server_connection *conn;
-
-	for (conn = client->connections; conn != NULL; conn = conn->next) {
-		if (strcmp(conn->path, path) == 0)
-			return conn;
-	}
-
-	return NULL;
-}
-
-struct auth_server_connection *
-auth_server_connection_find_mech(struct auth_client *client,
-				 const char *name, const char **error_r)
-{
-	struct auth_server_connection *conn, *match;
-	const struct auth_mech_desc *mech;
-	unsigned int i, n, match_n;
+	unsigned int id;
 
-	/* find a connection which has this mechanism. if there are multiple
-	   available connections to use, do round robin load balancing */
-	match = NULL; match_n = n = 0;
-	for (conn = client->connections; conn != NULL; conn = conn->next, n++) {
-		mech = conn->available_auth_mechs;
-		for (i = 0; i < conn->available_auth_mechs_count; i++) {
-			if (strcasecmp(mech[i].name, name) == 0) {
-				if (n > client->last_used_auth_process) {
-					client->last_used_auth_process = n;
-					return conn;
-				}
-				if (match == NULL) {
-					match = conn;
-					match_n = n;
-				}
-				break;
-			}
-		}
+	id = ++conn->client->request_id_counter;
+	if (id == 0) {
+		/* wrapped - ID 0 not allowed */
+		id = ++conn->client->request_id_counter;
 	}
-
-	if (match != NULL) {
-		client->last_used_auth_process = match_n;
-		return match;
-	}
-
-	if (auth_client_find_mech(client, name) == NULL)
-		*error_r = "Unsupported authentication mechanism";
-	else {
-		*error_r = "Authentication server isn't connected, "
-			"try again later..";
-	}
-
-	return NULL;
+	hash_table_insert(conn->requests, POINTER_CAST(id), request);
+	return id;
 }
--- a/src/lib-auth/auth-server-connection.h	Wed Oct 07 17:44:38 2009 -0400
+++ b/src/lib-auth/auth-server-connection.h	Wed Oct 07 17:46:14 2009 -0400
@@ -1,34 +1,12 @@
 #ifndef AUTH_SERVER_CONNECTION_H
 #define AUTH_SERVER_CONNECTION_H
 
-struct auth_client {
-	unsigned int pid;
-
-	struct auth_server_connection *connections;
-	struct timeout *to_reconnect;
-
-	time_t missing_sockets_start_time;
-	unsigned int conn_waiting_handshake_count;
-
-	buffer_t *available_auth_mechs;
-	unsigned int request_id_counter;
-	unsigned int last_used_auth_process;
-
-	auth_connect_notify_callback_t *connect_notify_callback;
-	void *connect_notify_context;
-
-	unsigned int reconnect:1;
-};
-
 struct auth_server_connection {
-	struct auth_server_connection *next;
-
 	pool_t pool;
-	int refcount;
 
 	struct auth_client *client;
-	const char *path;
 	int fd;
+	time_t last_connect;
 
 	struct io *io;
 	struct timeout *to;
@@ -38,9 +16,7 @@
 	unsigned int server_pid;
 	unsigned int connect_uid;
 
-	buffer_t *auth_mechs_buf;
-	const struct auth_mech_desc *available_auth_mechs;
-	unsigned int available_auth_mechs_count;
+	ARRAY_DEFINE(available_auth_mechs, struct auth_mech_desc);
 
         struct hash_table *requests;
 
@@ -50,15 +26,12 @@
 };
 
 struct auth_server_connection *
-auth_server_connection_new(struct auth_client *client, const char *path);
-void auth_server_connection_destroy(struct auth_server_connection **conn,
-				    bool reconnect);
+auth_server_connection_init(struct auth_client *client);
+void auth_server_connection_deinit(struct auth_server_connection **conn);
+
+int auth_server_connection_connect(struct auth_server_connection *conn);
 
-struct auth_server_connection *
-auth_server_connection_find_path(struct auth_client *client, const char *path);
-
-struct auth_server_connection *
-auth_server_connection_find_mech(struct auth_client *client,
-				 const char *name, const char **error_r);
-
+unsigned int
+auth_server_connection_add_request(struct auth_server_connection *conn,
+				   struct auth_client_request *request);
 #endif
--- a/src/lib-auth/auth-server-request.c	Wed Oct 07 17:44:38 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,419 +0,0 @@
-/* Copyright (c) 2003-2009 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "str.h"
-#include "hash.h"
-#include "ostream.h"
-#include "auth-client.h"
-#include "auth-server-connection.h"
-#include "auth-server-request.h"
-
-#include <stdlib.h>
-
-struct auth_request {
-        struct auth_server_connection *conn;
-
-	unsigned int id;
-
-	char *mech, *service, *cert_username;
-        enum auth_request_flags flags;
-	struct ip_addr local_ip, remote_ip;
-	unsigned int local_port, remote_port;
-
-	char *initial_resp_base64;
-
-	auth_request_callback_t *callback;
-	void *context;
-
-        struct auth_server_connection *next_conn;
-	char *plaintext_data; /* for resending to other servers */
-
-	unsigned int init_sent:1;
-	unsigned int retrying:1;
-};
-
-static int auth_server_send_new_request(struct auth_server_connection *conn,
-					struct auth_request *request,
-					const char **error_r);
-static void auth_client_request_free(struct auth_request *request);
-
-static struct auth_server_connection *
-get_next_plain_server(struct auth_server_connection *conn)
-{
-	conn = conn->next;
-	while (conn != NULL) {
-		if (conn->has_plain_mech)
-			return conn;
-		conn = conn->next;
-	}
-	return NULL;
-}
-
-static void
-auth_server_request_check_retry(struct auth_request *request, const char *data)
-{
-	const char *error;
-
-	if (strcmp(request->mech, "PLAIN") == 0 && data != NULL &&
-	    request->plaintext_data == NULL && request->conn != NULL) {
-		request->next_conn = get_next_plain_server(request->conn);
-		if (request->next_conn != NULL) {
-			/* plaintext authentication - save the data so we can
-			   try it for the next */
-			request->plaintext_data = i_strdup(data);
-
-			hash_table_insert(request->next_conn->requests,
-					  POINTER_CAST(request->id), request);
-			if (auth_server_send_new_request(request->next_conn,
-							 request, &error) == 0)
-				request->retrying = TRUE;
-		}
-	}
-}
-
-static bool is_valid_string(const char *str)
-{
-	const char *p;
-
-	/* make sure we're not sending any characters that have a special
-	   meaning. */
-	for (p = str; *p != '\0'; p++) {
-		if (*p == '\t' || *p == '\n' || *p == '\r')
-			return FALSE;
-	}
-	return TRUE;
-}
-
-static int auth_server_send_new_request(struct auth_server_connection *conn,
-					struct auth_request *request,
-					const char **error_r)
-{
-	string_t *str;
-
-	str = t_str_new(512);
-
-	str_printfa(str, "AUTH\t%u\t%s\tservice=%s",
-		    request->id, request->mech, request->service);
-	if ((request->flags & AUTH_REQUEST_FLAG_SECURED) != 0)
-		str_append(str, "\tsecured");
-	if ((request->flags & AUTH_REQUEST_FLAG_VALID_CLIENT_CERT) != 0)
-		str_append(str, "\tvalid-client-cert");
-
-	if (request->cert_username != NULL) {
-		if (!is_valid_string(request->cert_username)) {
-			*error_r = "Invalid username in SSL certificate";
-			return -1;
-		}
-		str_printfa(str, "\tcert_username=%s", request->cert_username);
-	}
-	if (request->local_ip.family != 0)
-		str_printfa(str, "\tlip=%s", net_ip2addr(&request->local_ip));
-	if (request->remote_ip.family != 0)
-		str_printfa(str, "\trip=%s", net_ip2addr(&request->remote_ip));
-	if (request->local_port != 0)
-		str_printfa(str, "\tlport=%u", request->local_port);
-	if (request->remote_port != 0)
-		str_printfa(str, "\trport=%u", request->remote_port);
-	if (request->initial_resp_base64 != NULL) {
-		/*if (!is_valid_string(request->initial_resp_base64)) {
-			*error_r = "Invalid base64 data in initial response";
-			return -1;
-		}*/
-		str_printfa(str, "\tresp=%s", request->initial_resp_base64);
-	}
-	str_append_c(str, '\n');
-
-	if (o_stream_send(conn->output, str_data(str), str_len(str)) < 0) {
-		errno = conn->output->stream_errno;
-		i_warning("Error sending request to auth server: %m");
-		auth_server_connection_destroy(&conn, TRUE);
-		return -1;
-	}
-
-	auth_server_request_check_retry(request, request->initial_resp_base64);
-	return 0;
-}
-
-static void auth_server_send_continue(struct auth_server_connection *conn,
-				      struct auth_request *request,
-				      const char *data_base64)
-{
-	struct const_iovec iov[3];
-	const char *prefix;
-
-	prefix = t_strdup_printf("CONT\t%u\t", request->id);
-
-	iov[0].iov_base = prefix;
-	iov[0].iov_len = strlen(prefix);
-	iov[1].iov_base = data_base64;
-	iov[1].iov_len = strlen(data_base64);
-	iov[2].iov_base = "\n";
-	iov[2].iov_len = 1;
-
-	if (o_stream_sendv(conn->output, iov, 3) < 0) {
-		errno = conn->output->stream_errno;
-		i_warning("Error sending continue request to auth server: %m");
-		auth_server_connection_destroy(&conn, TRUE);
-	}
-}
-
-bool auth_client_input_ok(struct auth_server_connection *conn, const char *args)
-{
-	const char *const *list, *const *args_list, *data_base64;
-	struct auth_request *request;
-	unsigned int id;
-
-	list = t_strsplit(args, "\t");
-	if (list[0] == NULL) {
-		i_error("BUG: Authentication server sent broken OK line");
-		return FALSE;
-	}
-
-	id = (unsigned int)strtoul(list[0], NULL, 10);
-
-	request = hash_table_lookup(conn->requests, POINTER_CAST(id));
-	if (request == NULL) {
-		/* We've already destroyed the request */
-		return TRUE;
-	}
-
-	hash_table_remove(request->conn->requests, POINTER_CAST(id));
-	if (request->next_conn != NULL) {
-		hash_table_remove(request->next_conn->requests,
-				  POINTER_CAST(id));
-	}
-	request->conn = conn;
-	request->next_conn = NULL;
-
-	data_base64 = NULL;
-	for (args_list = ++list; *list != NULL; list++) {
-		if (strncmp(*list, "resp=", 5) == 0) {
-			data_base64 = *list + 5;
-			break;
-		}
-	}
-
-	request->callback(request, 1, data_base64, args_list, request->context);
-	auth_client_request_free(request);
-	return TRUE;
-}
-
-bool auth_client_input_cont(struct auth_server_connection *conn,
-			    const char *args)
-{
-	struct auth_request *request;
-	const char *data;
-	unsigned int id;
-
-	data = strchr(args, '\t');
-	if (data == NULL) {
-		i_error("BUG: Authentication server sent broken CONT line");
-		return FALSE;
-	}
-	data++;
-
-	id = (unsigned int)strtoul(args, NULL, 10);
-
-	request = hash_table_lookup(conn->requests, POINTER_CAST(id));
-	if (request == NULL) {
-		/* We've already destroyed the request */
-		return TRUE;
-	}
-
-	if (request->retrying) {
-		auth_server_send_continue(conn, request,
-					  request->plaintext_data);
-	} else {
-		request->callback(request, 0, data, NULL, request->context);
-	}
-	return TRUE;
-}
-
-bool auth_client_input_fail(struct auth_server_connection *conn,
-			    const char *args)
-{
-	struct auth_request *request;
-        struct auth_server_connection *next;
-	const char *const *list, *error;
-	unsigned int id;
-
-	list = t_strsplit(args, "\t");
-	if (list[0] == NULL) {
-		i_error("BUG: Authentication server sent broken FAIL line");
-		return FALSE;
-	}
-
-	id = (unsigned int)strtoul(list[0], NULL, 10);
-
-	request = hash_table_lookup(conn->requests, POINTER_CAST(id));
-	if (request == NULL) {
-		/* We've already destroyed the request */
-		return TRUE;
-	}
-
-	hash_table_remove(conn->requests, POINTER_CAST(request->id));
-	if (request->retrying) {
-		next = request->next_conn == NULL ? NULL :
-			get_next_plain_server(request->next_conn);
-
-		if (conn == request->conn)
-			request->conn = request->next_conn;
-		request->next_conn = NULL;
-
-		if (next == NULL) {
-			if (request->conn != NULL) {
-				/* the other one hasn't replied yet */
-				return TRUE;
-			}
-			request->conn = conn;
-		} else {
-			hash_table_insert(next->requests,
-					  POINTER_CAST(request->id), request);
-			request->next_conn = next;
-
-			T_BEGIN {
-				(void)auth_server_send_new_request(next,
-								   request,
-								   &error);
-			} T_END;
-			return TRUE;
-		}
-	}
-
-	request->callback(request, -1, NULL, list+1, request->context);
-	auth_client_request_free(request);
-	return TRUE;
-}
-
-static void request_hash_remove(struct auth_server_connection *conn,
-                                struct auth_request *request)
-{
-	static const char *const temp_failure_args[] = { "temp", NULL };
-
-	if (request->conn == conn) {
-		if (request->next_conn == NULL) {
-			request->callback(request, -1, NULL, temp_failure_args,
-					  request->context);
-			request->conn = NULL;
-		} else {
-			request->conn = request->next_conn;
-			request->next_conn = NULL;
-		}
-	} else {
-		request->next_conn = NULL;
-	}
-}
-
-void auth_server_requests_remove_all(struct auth_server_connection *conn)
-{
-	struct hash_iterate_context *iter;
-	void *key, *value;
-
-	iter = hash_table_iterate_init(conn->requests);
-	while (hash_table_iterate(iter, &key, &value))
-		request_hash_remove(conn, value);
-	hash_table_iterate_deinit(&iter);
-}
-
-struct auth_request *
-auth_client_request_new(struct auth_client *client, struct auth_connect_id *id,
-			const struct auth_request_info *request_info,
-			auth_request_callback_t *callback, void *context,
-			const char **error_r)
-{
-	struct auth_server_connection *conn;
-	struct auth_request *request;
-
-	*error_r = "Temporary authentication failure.";
-
-	if (id == NULL) {
-		conn = auth_server_connection_find_mech(client,
-							request_info->mech,
-							error_r);
-	} else {
-		*error_r = NULL;
-		conn = client->connections;
-		for (; conn != NULL; conn = conn->next) {
-			if (conn->connect_uid == id->connect_uid &&
-			    conn->server_pid == id->server_pid)
-				break;
-		}
-	}
-
-	if (conn == NULL)
-		return NULL;
-
-	request = i_new(struct auth_request, 1);
-	request->conn = conn;
-	request->mech = i_strdup(request_info->mech);
-	request->service = i_strdup(request_info->service);
-	request->cert_username = i_strdup(request_info->cert_username);
-	request->flags = request_info->flags;
-	request->local_ip = request_info->local_ip;
-	request->remote_ip = request_info->remote_ip;
-	request->local_port = request_info->local_port;
-	request->remote_port = request_info->remote_port;
-	request->id = ++client->request_id_counter;
-
-	if (request_info->initial_resp_base64 != NULL) {
-		request->initial_resp_base64 =
-			i_strdup(request_info->initial_resp_base64);
-	}
-	
-	if (request->id == 0) {
-		/* wrapped - ID 0 not allowed */
-		request->id = ++client->request_id_counter;
-	}
-	request->callback = callback;
-	request->context = context;
-
-	T_BEGIN {
-		if (auth_server_send_new_request(conn, request, error_r) == 0) {
-			hash_table_insert(conn->requests,
-					  POINTER_CAST(request->id), request);
-		} else {
-			auth_client_request_free(request);
-			request = NULL;
-		}
-	} T_END;
-	return request;
-}
-
-void auth_client_request_continue(struct auth_request *request,
-                                  const char *data_base64)
-{
-	auth_server_send_continue(request->conn, request, data_base64);
-	auth_server_request_check_retry(request, data_base64);
-}
-
-static void auth_client_request_free(struct auth_request *request)
-{
-	i_free(request->initial_resp_base64);
-	i_free(request->plaintext_data);
-	i_free(request->mech);
-	i_free(request->service);
-	i_free(request->cert_username);
-	i_free(request);
-}
-
-void auth_client_request_abort(struct auth_request *request)
-{
-	void *id = POINTER_CAST(request->id);
-
-	hash_table_remove(request->conn->requests, id);
-	if (request->next_conn != NULL)
-		hash_table_remove(request->next_conn->requests, id);
-
-	request->callback(request, -1, NULL, NULL, request->context);
-	auth_client_request_free(request);
-}
-
-unsigned int auth_client_request_get_id(struct auth_request *request)
-{
-	return request->id;
-}
-
-unsigned int auth_client_request_get_server_pid(struct auth_request *request)
-{
-	return request->conn->server_pid;
-}
--- a/src/lib-auth/auth-server-request.h	Wed Oct 07 17:44:38 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-#ifndef AUTH_SERVER_REQUEST_H
-#define AUTH_SERVER_REQUEST_H
-
-bool auth_client_input_ok(struct auth_server_connection *conn,
-			  const char *args);
-bool auth_client_input_cont(struct auth_server_connection *conn,
-			   const char *args);
-bool auth_client_input_fail(struct auth_server_connection *conn,
-			    const char *args);
-
-void auth_server_requests_remove_all(struct auth_server_connection *conn);
-
-#endif
--- a/src/login-common/client-common.h	Wed Oct 07 17:44:38 2009 -0400
+++ b/src/login-common/client-common.h	Wed Oct 07 17:46:14 2009 -0400
@@ -96,7 +96,7 @@
 	char *proxy_user, *proxy_master_user, *proxy_password;
 
 	char *auth_mech_name;
-	struct auth_request *auth_request;
+	struct auth_client_request *auth_request;
 	string_t *auth_response;
 
 	unsigned int master_tag;
--- a/src/login-common/main.c	Wed Oct 07 17:44:38 2009 -0400
+++ b/src/login-common/main.c	Wed Oct 07 17:46:14 2009 -0400
@@ -137,7 +137,7 @@
 	master_service_set_avail_overflow_callback(master_service,
 						   client_destroy_oldest);
 
-	auth_client = auth_client_new((unsigned int)getpid());
+	auth_client = auth_client_init("auth", (unsigned int)getpid(), FALSE);
         auth_client_set_connect_notify(auth_client, auth_connect_notify, NULL);
 
 	clients_init();
@@ -151,7 +151,7 @@
 	login_proxy_deinit();
 
 	if (auth_client != NULL)
-		auth_client_free(&auth_client);
+		auth_client_deinit(&auth_client);
 	clients_deinit();
 
 	if (anvil_fd != -1) {
--- a/src/login-common/sasl-server.c	Wed Oct 07 17:44:38 2009 -0400
+++ b/src/login-common/sasl-server.c	Wed Oct 07 17:46:14 2009 -0400
@@ -98,7 +98,7 @@
 }
 
 static void
-master_send_request(struct client *client, struct auth_request *request)
+master_send_request(struct client *client, struct auth_client_request *request)
 {
 	struct master_auth_request req;
 	const unsigned char *data;
@@ -159,9 +159,10 @@
 		client->set->mail_max_userip_connections;
 }
 
-static void authenticate_callback(struct auth_request *request, int status,
-				  const char *data_base64,
-				  const char *const *args, void *context)
+static void
+authenticate_callback(struct auth_client_request *request,
+		      enum auth_request_status status, const char *data_base64,
+		      const char *const *args, void *context)
 {
 	struct client *client = context;
 	unsigned int i;
@@ -175,12 +176,12 @@
 
 	i_assert(client->auth_request == request);
 	switch (status) {
-	case 0:
+	case AUTH_REQUEST_STATUS_CONTINUE:
 		/* continue */
 		client->sasl_callback(client, SASL_SERVER_REPLY_CONTINUE,
 				      data_base64, NULL);
 		break;
-	case 1:
+	case AUTH_REQUEST_STATUS_OK:
 		client->auth_request = NULL;
 
 		nologin = FALSE;
@@ -209,7 +210,7 @@
 			master_send_request(client, request);
 		}
 		break;
-	case -1:
+	case AUTH_REQUEST_STATUS_FAIL:
 		client->auth_request = NULL;
 
 		if (args != NULL) {
@@ -237,7 +238,6 @@
 {
 	struct auth_request_info info;
 	const struct auth_mech_desc *mech;
-	const char *error;
 
 	client->auth_attempts++;
 	client->authenticating = TRUE;
@@ -274,12 +274,8 @@
 	info.initial_resp_base64 = initial_resp_base64;
 
 	client->auth_request =
-		auth_client_request_new(auth_client, NULL, &info,
-					authenticate_callback, client, &error);
-	if (client->auth_request == NULL) {
-		sasl_server_auth_failed(client,
-			 t_strconcat("Authentication failed: ", error, NULL));
-	}
+		auth_client_request_new(auth_client, &info,
+					authenticate_callback, client);
 }
 
 static void sasl_server_auth_cancel(struct client *client, const char *reason,
@@ -295,10 +291,8 @@
 	}
 
 	client->authenticating = FALSE;
-	if (client->auth_request != NULL) {
-		auth_client_request_abort(client->auth_request);
-		client->auth_request = NULL;
-	}
+	if (client->auth_request != NULL)
+		auth_client_request_abort(&client->auth_request);
 
 	call_client_callback(client, reply, reason, NULL);
 }
--- a/src/pop3-login/client-authenticate.c	Wed Oct 07 17:44:38 2009 -0400
+++ b/src/pop3-login/client-authenticate.c	Wed Oct 07 17:46:14 2009 -0400
@@ -155,6 +155,7 @@
 	struct client *client = &pop3_client->common;
 	buffer_t *apop_data, *base64;
 	const char *p;
+	unsigned int server_pid, connect_uid;
 
 	if (pop3_client->apop_challenge == NULL) {
 		if (client->set->verbose_auth)
@@ -195,6 +196,17 @@
         			MAX_BASE64_ENCODED_SIZE(apop_data->used));
 	base64_encode(apop_data->data, apop_data->used, base64);
 
+	auth_client_get_connect_id(auth_client, &server_pid, &connect_uid);
+	if (pop3_client->apop_server_pid != server_pid ||
+	    pop3_client->apop_connect_uid != connect_uid) {
+		/* we reconnected to auth server and can't authenticate
+		   with APOP in this session anymore. disconnecting the user
+		   is probably the best solution now. */
+		client_destroy(client,
+			"Reconnected to auth server, can't do APOP");
+		return TRUE;
+	}
+
 	(void)client_auth_begin(client, "APOP", str_c(base64));
 	return TRUE;
 }
--- a/src/pop3-login/client.c	Wed Oct 07 17:44:38 2009 -0400
+++ b/src/pop3-login/client.c	Wed Oct 07 17:46:14 2009 -0400
@@ -119,12 +119,11 @@
 
 static char *get_apop_challenge(struct pop3_client *client)
 {
-	struct auth_connect_id *id = &client->auth_id;
 	unsigned char buffer[16];
-        buffer_t *buf;
+	buffer_t *buf;
 
-	if (!auth_client_reserve_connection(auth_client, "APOP", id))
-		return NULL;
+	auth_client_get_connect_id(auth_client, &client->apop_server_pid,
+				   &client->apop_connect_uid);
 
 	random_fill(buffer, sizeof(buffer));
 	buf = buffer_create_static_hard(pool_datastack_create(),
@@ -133,7 +132,8 @@
 	buffer_append_c(buf, '\0');
 
 	return i_strdup_printf("<%x.%x.%lx.%s@%s>",
-			       id->server_pid, id->connect_uid,
+			       client->apop_server_pid,
+			       client->apop_connect_uid,
 			       (unsigned long)ioloop_time,
 			       (const char *)buf->data, my_hostname);
 }
--- a/src/pop3-login/client.h	Wed Oct 07 17:44:38 2009 -0400
+++ b/src/pop3-login/client.h	Wed Oct 07 17:46:14 2009 -0400
@@ -19,7 +19,7 @@
 
 	char *last_user;
 	char *apop_challenge;
-	struct auth_connect_id auth_id;
+	unsigned int apop_server_pid, apop_connect_uid;
 };
 
 #endif
--- a/src/util/authtest.c	Wed Oct 07 17:44:38 2009 -0400
+++ b/src/util/authtest.c	Wed Oct 07 17:46:14 2009 -0400
@@ -68,9 +68,11 @@
 	return ret == 0 ? 1 : 0;
 }
 
-static void auth_callback(struct auth_request *request ATTR_UNUSED, int status,
-			  const char *data_base64 ATTR_UNUSED,
-			  const char *const *args, void *context)
+static void
+auth_callback(struct auth_client_request *request ATTR_UNUSED,
+	      enum auth_request_status status,
+	      const char *data_base64 ATTR_UNUSED,
+	      const char *const *args, void *context)
 {
 	const struct authtest_input *input = context;
 
@@ -97,10 +99,8 @@
 			   bool connected, void *context)
 {
 	struct authtest_input *input = context;
-	struct auth_request *request;
 	struct auth_request_info info;
 	string_t *init_resp, *base64_resp;
-	const char *error;
 
 	if (!connected)
 		i_fatal("Couldn't connect to auth socket");
@@ -123,30 +123,25 @@
 	info.remote_port = input->info.remote_port;
 	info.initial_resp_base64 = str_c(base64_resp);
 
-	request = auth_client_request_new(client, NULL, &info,
-					  auth_callback, input, &error);
-	if (request == NULL)
-		i_fatal("passdb lookup failed: %s", error);
+	(void)auth_client_request_new(client, &info,
+				      auth_callback, input);
 }
 
 static int
 authtest_passdb(struct authtest_input *input)
 {
 	struct auth_client *client;
-	struct auth_server_connection *conn;
 
 	if (auth_socket_path == NULL)
 		auth_socket_path = PKG_RUNDIR"/auth-client";
 
-	client = auth_client_new(getpid());
+	client = auth_client_init(auth_socket_path, getpid(), FALSE);
 	auth_client_set_connect_notify(client, auth_connected, input);
-	conn = auth_server_connection_new(client, auth_socket_path);
 
 	io_loop_run(current_ioloop);
 
 	auth_client_set_connect_notify(client, NULL, NULL);
-	auth_server_connection_destroy(&conn, FALSE);
-	auth_client_free(&client);
+	auth_client_deinit(&client);
 	return 0;
 }