diff src/director/login-connection.c @ 18675:52cdf321fa07

director: Added "authreply" socket type. This allows defining a socket, which receives auth replies. Director then adds the "host" field to it if it's missing and returns back the original string. The idea is that eventually a director ring could be running independently from Dovecot proxies.
author Timo Sirainen <tss@iki.fi>
date Wed, 13 May 2015 16:22:27 +0300
parents d54dc360cd3c
children aabfe48db1cf
line wrap: on
line diff
--- a/src/director/login-connection.c	Wed May 13 15:59:48 2015 +0300
+++ b/src/director/login-connection.c	Wed May 13 16:22:27 2015 +0300
@@ -3,6 +3,7 @@
 #include "lib.h"
 #include "ioloop.h"
 #include "net.h"
+#include "istream.h"
 #include "ostream.h"
 #include "llist.h"
 #include "master-service.h"
@@ -13,19 +14,24 @@
 
 #include <unistd.h>
 
+#define AUTHREPLY_PROTOCOL_MAJOR_VERSION 1
+#define AUTHREPLY_PROTOCOL_MINOR_VERSION 0
+
 struct login_connection {
 	struct login_connection *prev, *next;
 
 	int refcount;
+	enum login_connection_type type;
 
 	int fd;
 	struct io *io;
+	struct istream *input;
 	struct ostream *output;
 	struct auth_connection *auth;
 	struct director *dir;
 
+	unsigned int handshaked:1;
 	unsigned int destroyed:1;
-	unsigned int userdb:1;
 };
 
 struct login_host_request {
@@ -40,6 +46,7 @@
 
 static struct login_connection *login_connections;
 
+static void auth_input_line(const char *line, void *context);
 static void login_connection_unref(struct login_connection **_conn);
 
 static void login_connection_input(struct login_connection *conn)
@@ -63,6 +70,33 @@
 	o_stream_nsend(output, buf, ret);
 }
 
+static void login_connection_authreply_input(struct login_connection *conn)
+{
+	const char *line;
+
+	while ((line = i_stream_read_next_line(conn->input)) != NULL) T_BEGIN {
+		if (!conn->handshaked) {
+			if (!version_string_verify(line, "director-authreply-client",
+						   AUTHREPLY_PROTOCOL_MAJOR_VERSION)) {
+				i_error("authreply client sent invalid handshake: %s", line);
+				login_connection_deinit(&conn);
+				return;
+			}
+			conn->handshaked = TRUE;
+		} else {
+			auth_input_line(line, conn);
+		}
+	} T_END;
+	if (conn->input->eof) {
+		if (conn->input->stream_errno != 0 &&
+		    conn->input->stream_errno != ECONNRESET) {
+			i_error("read(authreply connection) failed: %s",
+				i_stream_get_error(conn->input));
+		}
+		login_connection_deinit(&conn);
+	}
+}
+
 static void
 login_connection_send_line(struct login_connection *conn, const char *line)
 {
@@ -138,9 +172,11 @@
 		login_connection_deinit(&conn);
 		return;
 	}
-	if (!conn->userdb && strncmp(line, "OK\t", 3) == 0)
+	if (conn->type != LOGIN_CONNECTION_TYPE_USERDB &&
+	    strncmp(line, "OK\t", 3) == 0)
 		line_params = line + 3;
-	else if (conn->userdb && strncmp(line, "PASS\t", 5) == 0)
+	else if (conn->type == LOGIN_CONNECTION_TYPE_USERDB &&
+		 strncmp(line, "PASS\t", 5) == 0)
 		line_params = line + 5;
 	else {
 		login_connection_send_line(conn, line);
@@ -208,21 +244,35 @@
 
 struct login_connection *
 login_connection_init(struct director *dir, int fd,
-		      struct auth_connection *auth, bool userdb)
+		      struct auth_connection *auth,
+		      enum login_connection_type type)
 {
 	struct login_connection *conn;
 
 	conn = i_new(struct login_connection, 1);
 	conn->refcount = 1;
 	conn->fd = fd;
-	conn->auth = auth;
 	conn->dir = dir;
 	conn->output = o_stream_create_fd(conn->fd, (size_t)-1, FALSE);
 	o_stream_set_no_error_handling(conn->output, TRUE);
-	conn->io = io_add(conn->fd, IO_READ, login_connection_input, conn);
-	conn->userdb = userdb;
+	if (type != LOGIN_CONNECTION_TYPE_AUTHREPLY) {
+		i_assert(auth != NULL);
+		conn->auth = auth;
+		conn->io = io_add(conn->fd, IO_READ,
+				  login_connection_input, conn);
+		auth_connection_set_callback(conn->auth, auth_input_line, conn);
+	} else {
+		i_assert(auth == NULL);
+		conn->input = i_stream_create_fd(conn->fd, IO_BLOCK_SIZE, FALSE);
+		conn->io = io_add(conn->fd, IO_READ,
+				  login_connection_authreply_input, conn);
+		o_stream_nsend_str(conn->output, t_strdup_printf(
+			"VERSION\tdirector-authreply-server\t%d\t%d\n",
+			AUTHREPLY_PROTOCOL_MAJOR_VERSION,
+			AUTHREPLY_PROTOCOL_MINOR_VERSION));
+	}
+	conn->type = type;
 
-	auth_connection_set_callback(conn->auth, auth_input_line, conn);
 	DLLIST_PREPEND(&login_connections, conn);
 	return conn;
 }
@@ -239,12 +289,15 @@
 
 	DLLIST_REMOVE(&login_connections, conn);
 	io_remove(&conn->io);
+	if (conn->input != NULL)
+		i_stream_destroy(&conn->input);
 	o_stream_destroy(&conn->output);
 	if (close(conn->fd) < 0)
 		i_error("close(login connection) failed: %m");
 	conn->fd = -1;
 
-	auth_connection_deinit(&conn->auth);
+	if (conn->auth != NULL)
+		auth_connection_deinit(&conn->auth);
 	login_connection_unref(&conn);
 
 	master_service_client_connection_destroyed(master_service);