]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
auth: auth-worker-client - Use connection code
authorAki Tuomi <aki.tuomi@dovecot.fi>
Mon, 26 Nov 2018 17:03:11 +0000 (19:03 +0200)
committerAki Tuomi <aki.tuomi@dovecot.fi>
Wed, 28 Nov 2018 13:36:01 +0000 (15:36 +0200)
src/auth/auth-worker-client.c

index d2e23a7e59d223a397b87771317740c3d7f95a67..0fdd0de3c4cf99abaf77602f5078edd6c3d6e109 100644 (file)
@@ -29,12 +29,10 @@ struct auth_worker_client {
        int refcount;
 
         struct auth *auth;
-       struct timeout *to_idle;
        time_t cmd_start;
 
-       bool version_received:1;
-       bool dbhash_received:1;
        bool error_sent:1;
+       bool destroyed:1;
 };
 
 struct auth_worker_list_context {
@@ -44,12 +42,12 @@ struct auth_worker_list_context {
        bool sending, sent, done;
 };
 
+static struct connection_list *clients = NULL;
 static struct auth_worker_client *auth_worker_client = NULL;
 static bool auth_worker_client_error = FALSE;
 
-static void auth_worker_input(struct auth_worker_client *client);
 static int auth_worker_output(struct auth_worker_client *client);
-static void auth_worker_client_destroy(struct auth_worker_client **_client);
+static void auth_worker_client_destroy(struct connection *conn);
 static void auth_worker_client_unref(struct auth_worker_client **_client);
 
 void auth_worker_refresh_proctitle(const char *state)
@@ -71,7 +69,7 @@ auth_worker_client_check_throttle(struct auth_worker_client *client)
            OUTBUF_THROTTLE_SIZE) {
                /* stop reading new requests until client has read the pending
                   replies. */
-               io_remove(&client->conn.io);
+               connection_input_halt(&client->conn);
        }
 }
 
@@ -513,26 +511,11 @@ auth_worker_handle_user(struct auth_worker_client *client,
 }
 
 static void
-auth_worker_client_idle_kill(struct auth_worker_client *client ATTR_UNUSED)
+auth_worker_client_idle_kill(struct connection *conn ATTR_UNUSED)
 {
        auth_worker_client_send_shutdown();
 }
 
-static void
-auth_worker_client_set_idle_timeout(struct auth_worker_client *client)
-{
-       unsigned int idle_kill_secs;
-
-       i_assert(client->to_idle == NULL);
-
-       idle_kill_secs = master_service_get_idle_kill_secs(master_service);
-       if (idle_kill_secs > 0) {
-               client->to_idle = timeout_add(idle_kill_secs * 1000,
-                                             auth_worker_client_idle_kill,
-                                             client);
-       }
-}
-
 static void list_iter_deinit(struct auth_worker_list_context *ctx)
 {
        struct auth_worker_client *client = ctx->client;
@@ -548,10 +531,7 @@ static void list_iter_deinit(struct auth_worker_list_context *ctx)
                str_printfa(str, "%u\tOK\n", ctx->auth_request->id);
        auth_worker_send_reply(client, NULL, str);
 
-       client->conn.io = io_add(client->conn.fd_in, IO_READ,
-                                auth_worker_input, client);
-       auth_worker_client_set_idle_timeout(client);
-
+       connection_input_resume(&client->conn);
        o_stream_set_flush_callback(client->conn.output, auth_worker_output,
                                    client);
        auth_request_unref(&ctx->auth_request);
@@ -655,8 +635,7 @@ auth_worker_handle_list(struct auth_worker_client *client,
        }
        ctx->auth_request->userdb = userdb;
 
-       io_remove(&ctx->client->conn.io);
-       timeout_remove(&ctx->client->to_idle);
+       connection_input_halt(&ctx->client->conn);
 
        o_stream_set_flush_callback(ctx->client->conn.output,
                                    auth_worker_list_output, ctx);
@@ -666,41 +645,64 @@ auth_worker_handle_list(struct auth_worker_client *client,
        return TRUE;
 }
 
-static bool auth_worker_verify_db_hash(const char *line)
+static bool auth_worker_verify_db_hash(const char *passdb_hash, const char *userdb_hash)
 {
-       string_t *str;
+       string_t *str = t_str_new(MD5_RESULTLEN*2);
        unsigned char passdb_md5[MD5_RESULTLEN];
        unsigned char userdb_md5[MD5_RESULTLEN];
 
        passdbs_generate_md5(passdb_md5);
        userdbs_generate_md5(userdb_md5);
 
-       str = t_str_new(128);
-       str_append(str, "DBHASH\t");
        binary_to_hex_append(str, passdb_md5, sizeof(passdb_md5));
-       str_append_c(str, '\t');
+       if (strcmp(str_c(str), passdb_hash) != 0)
+               return FALSE;
+       str_truncate(str, 0);
        binary_to_hex_append(str, userdb_md5, sizeof(userdb_md5));
+       return strcmp(str_c(str), userdb_hash) == 0;
+};
 
-       return strcmp(line, str_c(str)) == 0;
+static int auth_worker_client_handshake_args(struct connection *conn, const char *const *args)
+{
+       if (!conn->version_received) {
+               if (connection_verify_version(conn, args) < 0)
+                       return -1;
+               return 0;
+       }
+
+       if (str_array_length(args) < 3 ||
+           strcmp(args[0], "DBHASH") != 0) {
+               i_error("BUG: Invalid input: %s", t_strarray_join(args, "\t"));
+               return -1;
+       }
+
+       if (!auth_worker_verify_db_hash(args[1], args[2])) {
+               i_error("Auth worker sees different passdbs/userdbs "
+                       "than auth server. Maybe config just changed "
+                       "and this goes away automatically?");
+               return -1;
+       }
+       return 1;
 }
 
-static bool
-auth_worker_handle_line(struct auth_worker_client *client, const char *line)
+static int
+auth_worker_client_input_args(struct connection *conn, const char *const *args)
 {
-       const char *const *args;
        unsigned int id;
        bool ret = FALSE;
        const char *error = NULL;
+       struct auth_worker_client *client =
+               container_of(conn, struct auth_worker_client, conn);
 
-       args = t_strsplit_tabescaped(line);
-       if (args[0] == NULL || args[1] == NULL || args[2] == NULL ||
+       if (str_array_length(args) < 3 ||
            str_to_uint(args[0], &id) < 0) {
-               i_error("BUG: Invalid input: %s", line);
-               return FALSE;
+               i_error("BUG: Invalid input: %s", t_strarray_join(args, "\t"));
+               return -1;
        }
 
        io_loop_time_refresh();
        client->cmd_start = ioloop_time;
+       client->refcount++;
 
        auth_worker_refresh_proctitle(args[1]);
        if (strcmp(args[1], "PASSV") == 0)
@@ -727,111 +729,90 @@ auth_worker_handle_line(struct auth_worker_client *client, const char *line)
 
        if (client->conn.io != NULL)
                auth_worker_refresh_proctitle(CLIENT_STATE_IDLE);
-        return ret;
-}
-
-static void auth_worker_input(struct auth_worker_client *client)
-{
-       char *line;
-       bool ret;
-
-       switch (i_stream_read(client->conn.input)) {
-       case 0:
-               return;
-       case -1:
-               /* disconnected */
-               auth_worker_client_destroy(&client);
-               return;
-       case -2:
-               /* buffer full */
-               i_error("BUG: Auth worker server sent us more than %d bytes",
-                       (int)AUTH_WORKER_MAX_LINE_LENGTH);
-               auth_worker_client_destroy(&client);
-               return;
-       }
-
-       if (!client->version_received) {
-               line = i_stream_next_line(client->conn.input);
-               if (line == NULL)
-                       return;
-
-               if (!version_string_verify(line, "auth-worker",
-                                    AUTH_WORKER_PROTOCOL_MAJOR_VERSION)) {
-                       i_error("Auth worker not compatible with this server "
-                               "(mixed old and new binaries?)");
-                       auth_worker_client_destroy(&client);
-                       return;
-               }
-               client->version_received = TRUE;
-       }
-       if (!client->dbhash_received) {
-               line = i_stream_next_line(client->conn.input);
-               if (line == NULL)
-                       return;
-
-               if (!auth_worker_verify_db_hash(line)) {
-                       i_error("Auth worker sees different passdbs/userdbs "
-                               "than auth server. Maybe config just changed "
-                               "and this goes away automatically?");
-                       auth_worker_client_destroy(&client);
-                       return;
-               }
-               client->dbhash_received = TRUE;
-               auth_worker_refresh_proctitle(CLIENT_STATE_IDLE);
-       }
-
-        client->refcount++;
-       while ((line = i_stream_next_line(client->conn.input)) != NULL) {
-               T_BEGIN {
-                       ret = auth_worker_handle_line(client, line);
-               } T_END;
-
-               if (!ret) {
-                       struct auth_worker_client *client2 = client;
-                       auth_worker_client_destroy(&client2);
-                       break;
-               }
-       }
-       if (client->to_idle != NULL)
-               timeout_reset(client->to_idle);
        auth_worker_client_unref(&client);
+       return ret ? 1 : -1;
 }
 
 static int auth_worker_output(struct auth_worker_client *client)
 {
        if (o_stream_flush(client->conn.output) < 0) {
-               auth_worker_client_destroy(&client);
+               auth_worker_client_destroy(&client->conn);
                return 1;
        }
 
        if (o_stream_get_buffer_used_size(client->conn.output) <=
            OUTBUF_THROTTLE_SIZE/3 && client->conn.io == NULL) {
                /* allow input again */
-               client->conn.io = io_add(client->conn.fd_in, IO_READ,
-                                   auth_worker_input, client);
+               connection_input_resume(&client->conn);
        }
        return 1;
 }
 
+static void auth_worker_client_unref(struct auth_worker_client **_client)
+{
+       struct auth_worker_client *client = *_client;
+       if (client == NULL)
+               return;
+       if (--client->refcount > 0)
+               return;
+
+       /* the connection should've been destroyed before getting here */
+       i_assert(client->destroyed);
+       connection_deinit(&client->conn);
+       i_free(client);
+}
+
+static void auth_worker_client_destroy(struct connection *conn)
+{
+       struct auth_worker_client *client =
+               container_of(conn, struct auth_worker_client, conn);
+
+       i_assert(!client->destroyed);
+       client->destroyed = TRUE;
+       connection_input_halt(conn);
+       i_stream_close(conn->input);
+       o_stream_close(conn->output);
+       net_disconnect(conn->fd_in);
+       conn->fd_out = conn->fd_in = -1;
+       auth_worker_client_unref(&client);
+       master_service_client_connection_destroyed(master_service);
+}
+
+static const struct connection_vfuncs auth_worker_client_v =
+{
+       .input_args = auth_worker_client_input_args,
+       .handshake_args = auth_worker_client_handshake_args,
+       .destroy = auth_worker_client_destroy,
+       .idle_timeout = auth_worker_client_idle_kill,
+};
+
+static const struct connection_settings auth_worker_client_set =
+{
+       .service_name_in = "auth-worker",
+       .service_name_out = "auth-worker",
+       .major_version = AUTH_WORKER_PROTOCOL_MAJOR_VERSION,
+       .minor_version = AUTH_WORKER_PROTOCOL_MINOR_VERSION,
+       .input_max_size = (size_t)-1,
+       .output_max_size = (size_t)-1, /* we use throttling */
+};
+
 struct auth_worker_client *
 auth_worker_client_create(struct auth *auth,
                          const struct master_service_connection *master_conn)
 {
        struct auth_worker_client *client;
-       int fd = master_conn->fd;
+
+       if (clients == NULL)
+               clients = connection_list_init(&auth_worker_client_set, &auth_worker_client_v);
 
        client = i_new(struct auth_worker_client, 1);
        client->refcount = 1;
-
        client->auth = auth;
-       client->conn.fd_in = fd;
-       client->conn.input = i_stream_create_fd(fd, AUTH_WORKER_MAX_LINE_LENGTH);
-       client->conn.output = o_stream_create_fd(fd, (size_t)-1);
-       o_stream_set_no_error_handling(client->conn.output, TRUE);
-       o_stream_set_flush_callback(client->conn.output, auth_worker_output,
-                                   client);
-       client->conn.io = io_add(fd, IO_READ, auth_worker_input, client);
-       auth_worker_client_set_idle_timeout(client);
+       client->conn.event_parent = auth_event;
+       client->conn.input_idle_timeout_secs = master_service_get_idle_kill_secs(master_service);
+       connection_init_server(clients, &client->conn, master_conn->name,
+                              master_conn->fd, master_conn->fd);
+
        auth_worker_refresh_proctitle(CLIENT_STATE_HANDSHAKE);
 
        auth_worker_client = client;
@@ -840,43 +821,6 @@ auth_worker_client_create(struct auth *auth,
        return client;
 }
 
-static void auth_worker_client_destroy(struct auth_worker_client **_client)
-{
-       struct auth_worker_client *client = *_client;
-
-       *_client = NULL;
-       if (client->conn.fd_in == -1)
-               return;
-
-       i_stream_close(client->conn.input);
-       o_stream_close(client->conn.output);
-
-       timeout_remove(&client->to_idle);
-       io_remove(&client->conn.io);
-
-       net_disconnect(client->conn.fd_in);
-       client->conn.fd_in = -1;
-       auth_worker_client_unref(&client);
-
-       auth_worker_client = NULL;
-       auth_worker_refresh_proctitle("");
-       master_service_client_connection_destroyed(master_service);
-}
-
-static void auth_worker_client_unref(struct auth_worker_client **_client)
-{
-       struct auth_worker_client *client = *_client;
-
-       *_client = NULL;
-
-       if (--client->refcount > 0)
-               return;
-
-       i_stream_unref(&client->conn.input);
-       o_stream_unref(&client->conn.output);
-       i_free(client);
-}
-
 void auth_worker_client_send_error(void)
 {
        auth_worker_client_error = TRUE;
@@ -912,12 +856,14 @@ void auth_worker_client_send_shutdown(void)
 
 void auth_worker_connections_destroy_all(void)
 {
-       if (auth_worker_client == NULL)
+       if (clients == NULL)
                return;
-       auth_worker_client_destroy(&auth_worker_client);
+       while (clients->connections != NULL)
+               connection_deinit(clients->connections);
+       connection_list_deinit(&clients);
 }
 
 bool auth_worker_has_client(void)
 {
-       return auth_worker_client != NULL;
+       return clients != NULL && clients->connections_count > 0;
 }