]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
director: Added a new DIRECTOR-LOOKUP command that auth connections can use.
authorTimo Sirainen <tss@iki.fi>
Wed, 13 May 2015 02:25:31 +0000 (05:25 +0300)
committerTimo Sirainen <tss@iki.fi>
Wed, 13 May 2015 02:25:31 +0000 (05:25 +0300)
The parameters are the same as what auth lookup would receive from auth
process. So the idea is that a proxy could do an auth lookup, then forward
the reply to director, which would return back the updated reply with the
host field added.

src/director/auth-connection.c
src/director/auth-connection.h
src/director/login-connection.c

index ec2acc9fef10c33d5f74b35dd0782044d76b423e..cf679ddd01431efe47d10f113d9123a420697ec9 100644 (file)
@@ -121,12 +121,10 @@ static void auth_connection_disconnected(struct auth_connection **_conn)
        conn->callback(NULL, conn->context);
 }
 
-void auth_connection_send(struct auth_connection *conn,
-                         const void *data, size_t size)
+struct ostream *auth_connection_send(struct auth_connection *conn)
 {
-       i_assert(conn->fd != -1);
-
-       o_stream_nsend(conn->output, data, size);
+       i_assert(conn->output != NULL);
+       return conn->output;
 }
 
 void auth_connections_deinit(void)
index c5d5063aa1c122c7d161d52f194b364f47eb1f57..27e01467bf85d8287721d12c1d946ab8cf806cac 100644 (file)
@@ -13,9 +13,8 @@ void auth_connection_set_callback(struct auth_connection *conn,
 
 /* Start connecting. Returns 0 if ok, -1 if connect failed. */
 int auth_connection_connect(struct auth_connection *conn);
-/* Send data to auth connection. */
-void auth_connection_send(struct auth_connection *conn,
-                         const void *data, size_t size);
+/* Get auth connection's output stream. */
+struct ostream *auth_connection_send(struct auth_connection *conn);
 
 void auth_connections_deinit(void);
 
index 2f31ea98c4c69ab7898253e58b8e2e460dfafa7b..0cd9e2862d038049c0d1c4ad33d1ab2c7f1e19f4 100644 (file)
@@ -3,8 +3,10 @@
 #include "lib.h"
 #include "ioloop.h"
 #include "net.h"
+#include "istream.h"
 #include "ostream.h"
 #include "llist.h"
+#include "str.h"
 #include "master-service.h"
 #include "director.h"
 #include "director-request.h"
@@ -20,12 +22,14 @@ struct login_connection {
 
        int fd;
        struct io *io;
+       struct istream *input;
        struct ostream *output;
        struct auth_connection *auth;
        struct director *dir;
 
        unsigned int destroyed:1;
        unsigned int userdb:1;
+       unsigned int input_newline:1;
 };
 
 struct login_host_request {
@@ -40,25 +44,66 @@ struct login_host_request {
 
 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_director_lookup(struct login_connection *conn,
+                                const unsigned char *data, size_t size)
+{
+       T_BEGIN {
+               string_t *line = t_str_new(128);
+               str_append(line, "OK\t");
+               str_append_n(line, data, size);
+               auth_input_line(str_c(line), conn);
+       } T_END;
+}
+
 static void login_connection_input(struct login_connection *conn)
 {
-       unsigned char buf[4096];
-       ssize_t ret;
-
-       ret = read(conn->fd, buf, sizeof(buf));
-       if (ret <= 0) {
-               if (ret < 0) {
-                       if (errno == EAGAIN)
-                               return;
-                       if (errno != ECONNRESET)
-                               i_error("read(login connection) failed: %m");
+       const unsigned char *data, *p;
+       size_t size;
+       struct ostream *auth_output;
+
+       auth_output = auth_connection_send(conn->auth);
+       switch (i_stream_read(conn->input)) {
+       case -2:
+               data = i_stream_get_data(conn->input, &size);
+               o_stream_nsend(auth_output, data, size);
+               i_stream_skip(conn->input, size);
+               conn->input_newline = FALSE;
+               return;
+       case -1:
+               if (conn->input->stream_errno != 0 &&
+                   conn->input->stream_errno != ECONNRESET) {
+                       i_error("read(login connection) failed: %s",
+                               i_stream_get_error(conn->input));
                }
                login_connection_deinit(&conn);
                return;
+       case 0:
+               return;
+       default:
+               break;
+       }
+
+       o_stream_cork(auth_output);
+       data = i_stream_get_data(conn->input, &size);
+       while ((p = memchr(data, '\n', size)) != NULL) {
+               size_t linelen = p-data;
+
+               if (!conn->input_newline || linelen <= 16 ||
+                   memcmp(data, "DIRECTOR-LOOKUP\t", 16) != 0) {
+                       /* forward data to auth process */
+                       o_stream_nsend(auth_output, data, linelen+1);
+                       conn->input_newline = TRUE;
+               } else {
+                       login_connection_director_lookup(conn, data+16, linelen-16);
+               }
+               i_stream_skip(conn->input, linelen+1);
+               data = i_stream_get_data(conn->input, &size);
        }
-       auth_connection_send(conn->auth, buf, ret);
+       o_stream_uncork(auth_output);
 }
 
 static void
@@ -215,10 +260,12 @@ login_connection_init(struct director *dir, int fd,
        conn->fd = fd;
        conn->auth = auth;
        conn->dir = dir;
+       conn->input = i_stream_create_fd(conn->fd, IO_BLOCK_SIZE, FALSE);
        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;
+       conn->input_newline = TRUE;
 
        auth_connection_set_callback(conn->auth, auth_input_line, conn);
        DLLIST_PREPEND(&login_connections, conn);
@@ -237,6 +284,7 @@ void login_connection_deinit(struct login_connection **_conn)
 
        DLLIST_REMOVE(&login_connections, conn);
        io_remove(&conn->io);
+       i_stream_destroy(&conn->input);
        o_stream_destroy(&conn->output);
        if (close(conn->fd) < 0)
                i_error("close(login connection) failed: %m");