#include "net.h"
#include "fdpass.h"
#include "istream.h"
+#include "istream-unix.h"
#include "ostream.h"
#include "str.h"
#include "str-sanitize.h"
#include "randgen.h"
#include "restrict-access.h"
#include "settings-parser.h"
+#include "connection.h"
#include "master-service.h"
#include "master-interface.h"
#include "mail-storage.h"
(getenv(MASTER_IS_PARENT_ENV) == NULL)
struct client {
+ struct connection conn;
+ struct connection conn_ctrl;
+
struct client *prev, *next;
struct event *event;
- int fd_in, fd_out, fd_ctrl;
-
- struct io *io, *ctrl_io;
- struct istream *input, *ctrl_input;
- struct ostream *output, *ctrl_output;
struct timeout *to_idle;
char *access_user, *access_service;
static bool verbose_proctitle = FALSE;
static struct mail_storage_service_ctx *storage_service;
-struct client *imap_urlauth_worker_clients;
-unsigned int imap_urlauth_worker_client_count;
+static struct connection_list *clist;
+static struct connection_list *clist_ctrl;
static void client_destroy(struct client *client);
static void client_abort(struct client *client, const char *reason);
static int client_run_url(struct client *client);
-static void client_input(struct client *client);
static bool client_handle_input(struct client *client);
static int client_output(struct client *client);
-static void client_ctrl_input(struct client *client);
-
static void imap_urlauth_worker_refresh_proctitle(void)
{
- struct client *client = imap_urlauth_worker_clients;
+ struct client *client;
string_t *title;
if (!verbose_proctitle)
title = t_str_new(128);
str_append_c(title, '[');
- switch (imap_urlauth_worker_client_count) {
+ switch (clist_ctrl->connections_count) {
case 0:
str_append(title, "idling");
break;
case 1:
+ client = container_of(clist->connections, struct client, conn);
if (client->mail_user == NULL)
str_append(title, client->access_user);
else {
break;
default:
str_printfa(title, "%u connections",
- imap_urlauth_worker_client_count);
+ clist_ctrl->connections_count);
break;
}
str_append_c(title, ']');
client = i_new(struct client, 1);
i_array_init(&client->access_apps, 16);
- client->fd_in = -1;
- client->fd_out = -1;
- client->fd_ctrl = fd;
client->access_anonymous = TRUE; /* default until overridden */
client->event = event_create(NULL);
- client->ctrl_io = io_add(fd, IO_READ, client_ctrl_input, client);
+ client->conn_ctrl.event_parent = client->event;
+ client->conn_ctrl.unix_socket = TRUE;
+ connection_init_server(clist_ctrl, &client->conn_ctrl, NULL, fd, fd);
+ i_stream_unix_set_read_fd(client->conn_ctrl.input);
+
+ client->conn.event_parent = client->event;
+ connection_init(clist, &client->conn, NULL);
+
client->to_idle = timeout_add(CLIENT_IDLE_TIMEOUT_MSECS,
client_idle_timeout, client);
- imap_urlauth_worker_client_count++;
- DLLIST_PREPEND(&imap_urlauth_worker_clients, client);
-
imap_urlauth_worker_refresh_proctitle();
return client;
}
client = i_new(struct client, 1);
i_array_init(&client->access_apps, 16);
- client->fd_in = fd_in;
- client->fd_out = fd_out;
- client->fd_ctrl = -1;
if (access_user != NULL && *access_user != '\0')
client->access_user = i_strdup(access_user);
client->event = event_create(NULL);
event_set_forced_debug(client->event, debug);
- client->input = i_stream_create_fd(fd_in, MAX_INBUF_SIZE);
- client->output = o_stream_create_fd(fd_out, SIZE_MAX);
- client->io = io_add(fd_in, IO_READ, client_input, client);
+ client->conn_ctrl.event_parent = client->event;
+ connection_init(clist_ctrl, &client->conn_ctrl, NULL);
+
+ client->conn.event_parent = client->event;
+ connection_init_server(clist, &client->conn, NULL, fd_in, fd_out);
+
client->to_idle = timeout_add(CLIENT_IDLE_TIMEOUT_MSECS,
client_idle_timeout, client);
- o_stream_set_no_error_handling(client->output, TRUE);
- o_stream_set_flush_callback(client->output, client_output, client);
-
- imap_urlauth_worker_client_count++;
- DLLIST_PREPEND(&imap_urlauth_worker_clients, client);
i_set_failure_prefix("imap-urlauth[%s](%s): ",
my_pid, client->access_user);
i_set_failure_prefix("imap-urlauth[%s](%s): ",
my_pid, client->access_user);
+ connection_disconnect(&client->conn);
if (client->url != NULL) {
/* deinitialize url */
- i_stream_close(client->input);
- o_stream_close(client->output);
(void)client_run_url(client);
i_assert(client->url == NULL);
}
- imap_urlauth_worker_client_count--;
- DLLIST_REMOVE(&imap_urlauth_worker_clients, client);
-
if (client->urlauth_ctx != NULL)
imap_urlauth_deinit(&client->urlauth_ctx);
if (client->mail_user != NULL)
mail_user_deinit(&client->mail_user);
- io_remove(&client->io);
- io_remove(&client->ctrl_io);
timeout_remove(&client->to_idle);
- i_stream_destroy(&client->input);
- o_stream_destroy(&client->output);
-
- i_stream_destroy(&client->ctrl_input);
- o_stream_destroy(&client->ctrl_output);
-
- fd_close_maybe_stdio(&client->fd_in, &client->fd_out);
- if (client->fd_ctrl >= 0)
- net_disconnect(client->fd_ctrl);
+ connection_disconnect(&client->conn_ctrl);
i_free(client->access_user);
i_free(client->access_service);
array_foreach_elem(&client->access_apps, app)
i_free(app);
array_free(&client->access_apps);
+ connection_deinit(&client->conn_ctrl);
+ connection_deinit(&client->conn);
event_unref(&client->event);
i_free(client);
ssize_t ret = 0;
while (i_stream_read_more(client->msg_part_input, &data, &size) > 0) {
- if ((ret = o_stream_send(client->output, data, size)) < 0)
+ if (client->conn.output == NULL ||
+ (ret = o_stream_send(client->conn.output, data, size)) < 0)
break;
i_stream_skip(client->msg_part_input, ret);
- if (o_stream_get_buffer_used_size(client->output) >= 4096) {
- if ((ret = o_stream_flush(client->output)) < 0)
+ if (o_stream_get_buffer_used_size(client->conn.output) >= 4096) {
+ if ((ret = o_stream_flush(client->conn.output)) < 0)
break;
if (ret == 0)
return 0;
}
}
- if (client->output->closed || ret < 0) {
+ if (client->conn.output == NULL || client->conn. output->closed ||
+ ret < 0) {
imap_msgpart_url_free(&client->url);
return -1;
}
if (client->msg_part_input->eof) {
- o_stream_nsend(client->output, "\n", 1);
+ o_stream_nsend(client->conn.output, "\n", 1);
imap_msgpart_url_free(&client->url);
return 1;
}
return 0;
}
-static void clients_destroy_all(void)
-{
- while (imap_urlauth_worker_clients != NULL)
- client_destroy(imap_urlauth_worker_clients);
-}
-
static void ATTR_FORMAT(2, 3)
client_send_line(struct client *client, const char *fmt, ...)
{
va_list va;
- if (client->output->closed)
+ if (client->conn.output == NULL || client->conn.output->closed)
return;
va_start(va, fmt);
str_vprintfa(str, fmt, va);
str_append(str, "\n");
- o_stream_nsend(client->output, str_data(str), str_len(str));
+ o_stream_nsend(client->conn.output,
+ str_data(str), str_len(str));
} T_END;
va_end(va);
}
/* return content */
- o_stream_cork(client->output);
+ o_stream_cork(client->conn.output);
if (client->msg_part_size == 0 || client->msg_part_input == NULL) {
/* empty */
str_append(response, "\t0");
if (client->url != NULL) {
/* URL not finished */
- o_stream_set_flush_pending(client->output, TRUE);
+ o_stream_set_flush_pending(client->conn.output, TRUE);
client->waiting_input = TRUE;
}
- o_stream_uncork(client->output);
+ o_stream_uncork(client->conn.output);
return client->url != NULL ? 0 : 1;
}
}
client->finished = TRUE;
- if (client->ctrl_output != NULL)
- o_stream_nsend_str(client->ctrl_output, "FINISHED\n");
+ if (client->conn_ctrl.output != NULL) {
+ o_stream_nsend_str(client->conn_ctrl.output,
+ "FINISHED\n");
+ }
client_destroy(client);
return 0;
}
if (client->url != NULL) {
/* we're still processing a URL. wait until it's
finished. */
- io_remove(&client->io);
- client->io = NULL;
+ connection_input_halt(&client->conn);
client->waiting_input = TRUE;
return TRUE;
}
- if (client->io == NULL) {
- client->io = io_add(client->fd_in, IO_READ,
- client_input, client);
- }
+ connection_input_resume(&client->conn);
client->waiting_input = FALSE;
timeout_reset(client->to_idle);
- switch (i_stream_read(client->input)) {
- case -1:
- /* disconnected */
- if (client->ctrl_output != NULL)
- o_stream_nsend_str(client->ctrl_output, "DISCONNECTED\n");
- client_destroy(client);
- return FALSE;
- case -2:
- /* line too long, kill it */
- client_abort(client, "Session aborted: Input line too long");
+ if (connection_input_read(&client->conn) < 0)
return FALSE;
- }
- while ((line = i_stream_next_line(client->input)) != NULL) {
+ while ((line = i_stream_next_line(client->conn.input)) != NULL) {
const char *const *args = t_strsplit_tabescaped(line);
if (args[0] == NULL)
return TRUE;
}
-static void client_input(struct client *client)
+static void client_input(struct connection *_conn)
{
+ struct client *client = container_of(_conn, struct client, conn);
+
(void)client_handle_input(client);
}
static int client_output(struct client *client)
{
- if (o_stream_flush(client->output) < 0) {
- if (client->ctrl_output != NULL)
- o_stream_nsend_str(client->ctrl_output, "DISCONNECTED\n");
+ if (o_stream_flush(client->conn.output) < 0) {
+ if (client->conn_ctrl.output != NULL) {
+ o_stream_nsend_str(client->conn_ctrl.output,
+ "DISCONNECTED\n");
+ }
client_destroy(client);
return 1;
}
if (client->url != NULL) {
/* url not finished yet */
return 0;
- } else if (client->io == NULL) {
+ } else if (client->conn.io == NULL) {
/* data still in output buffer, get back here to add IO */
return 0;
} else {
}
static int
-client_ctrl_read_fds(struct client *client)
+client_ctrl_read_fd(struct client *client, unsigned char *data_r, int *fd_r)
+{
+ const unsigned char *data;
+ size_t size;
+ int ret;
+
+ ret = i_stream_read_more(client->conn_ctrl.input, &data, &size);
+ if (ret <= 0)
+ return ret;
+ i_stream_skip(client->conn_ctrl.input, 1);
+
+ *data_r = data[0];
+ *fd_r = i_stream_unix_get_read_fd(client->conn_ctrl.input);
+ return 1;
+}
+
+static int client_ctrl_read_fds(struct client *client)
{
unsigned char data = 0;
- ssize_t ret = 1;
+ int ret = 1;
- if (client->fd_in == -1) {
- ret = fd_read(client->fd_ctrl, &data,
- sizeof(data), &client->fd_in);
+ if (client->conn.fd_in == -1) {
+ ret = client_ctrl_read_fd(client, &data, &client->conn.fd_in);
if (ret > 0 && data == '0')
- client->fd_out = client->fd_in;
+ client->conn.fd_out = client->conn.fd_in;
+ else
+ i_stream_unix_set_read_fd(client->conn_ctrl.input);
}
- if (ret > 0 && client->fd_out == -1) {
- ret = fd_read(client->fd_ctrl, &data,
- sizeof(data), &client->fd_out);
+ if (ret > 0 && client->conn.fd_out == -1) {
+ ret = client_ctrl_read_fd(client, &data, &client->conn.fd_out);
}
if (ret == 0) {
- /* unexpectedly disconnected */
- client_destroy(client);
return 0;
} else if (ret < 0) {
if (errno == EAGAIN)
return -1;
}
- if (client->fd_in == -1 || client->fd_out == -1) {
+ if (client->conn.fd_in == -1 || client->conn.fd_out == -1) {
e_error(client->event,
"Handshake is missing a file descriptor");
return -1;
}
- client->ctrl_input =
- i_stream_create_fd(client->fd_ctrl, MAX_INBUF_SIZE);
- client->ctrl_output = o_stream_create_fd(client->fd_ctrl, SIZE_MAX);
- o_stream_set_no_error_handling(client->ctrl_output, TRUE);
+ connection_init_server(clist, &client->conn, NULL,
+ client->conn.fd_in, client->conn.fd_out);
+ connection_input_halt(&client->conn);
+
return 1;
}
-static void client_ctrl_input(struct client *client)
+static void client_ctrl_input(struct connection *_conn)
{
+ struct client *client = container_of(_conn, struct client, conn_ctrl);
const char *const *args;
const char *line, *value;
int ret;
timeout_reset(client->to_idle);
- if (client->fd_in == -1 || client->fd_out == -1) {
+ if (client->conn.fd_in == -1 || client->conn.fd_out == -1) {
if ((ret = client_ctrl_read_fds(client)) <= 0) {
if (ret < 0)
client_abort(client, "FD Transfer failed");
}
}
- switch (i_stream_read(client->ctrl_input)) {
- case -1:
- /* disconnected */
- client_destroy(client);
- return;
- case -2:
- /* line too long, kill it */
- client_abort(client,
- "Control session aborted: Input line too long");
+ if (connection_input_read(&client->conn_ctrl) < 0)
return;
- }
if (!client->version_received) {
- if ((line = i_stream_next_line(client->ctrl_input)) == NULL)
+ line = i_stream_next_line(client->conn_ctrl.input);
+ if (line == NULL)
return;
if (!version_string_verify(line, "imap-urlauth-worker",
}
client->version_received = TRUE;
- if (o_stream_send_str(client->ctrl_output, "OK\n") < 0) {
+ if (o_stream_send_str(client->conn_ctrl.output, "OK\n") < 0) {
client_destroy(client);
return;
}
return;
}
- if ((line = i_stream_next_line(client->ctrl_input)) == NULL)
+ if ((line = i_stream_next_line(client->conn_ctrl.input)) == NULL)
return;
args = t_strsplit_tabescaped(line);
client->access_received = TRUE;
- if (o_stream_send_str(client->ctrl_output, "OK\n") < 0) {
+ if (o_stream_send_str(client->conn_ctrl.output, "OK\n") < 0) {
client_destroy(client);
return;
}
- client->input = i_stream_create_fd(client->fd_in, MAX_INBUF_SIZE);
- client->output = o_stream_create_fd(client->fd_out, SIZE_MAX);
- client->io = io_add(client->fd_in, IO_READ, client_input, client);
- o_stream_set_no_error_handling(client->output, TRUE);
- o_stream_set_flush_callback(client->output, client_output, client);
+ connection_input_resume(&client->conn);
+ o_stream_set_flush_callback(client->conn.output, client_output, client);
e_debug(client->event,
"Worker activated for access by user `%s' using service `%s'",
client->access_user, client->access_service);
}
+static void client_ctrl_connection_destroy(struct connection *conn)
+{
+ struct client *client = container_of(conn, struct client, conn_ctrl);
+
+ switch (conn->disconnect_reason) {
+ case CONNECTION_DISCONNECT_NOT:
+ case CONNECTION_DISCONNECT_HANDSHAKE_FAILED:
+ case CONNECTION_DISCONNECT_BUFFER_FULL:
+ i_unreached();
+ default:
+ break;
+ }
+
+ client_destroy(client);
+}
+
+static void client_connection_destroy(struct connection *conn)
+{
+ struct client *client = container_of(conn, struct client, conn);
+
+ switch (conn->disconnect_reason) {
+ case CONNECTION_DISCONNECT_NOT:
+ case CONNECTION_DISCONNECT_HANDSHAKE_FAILED:
+ case CONNECTION_DISCONNECT_BUFFER_FULL:
+ i_unreached();
+ default:
+ break;
+ }
+
+ client_destroy(client);
+}
+
+static const struct connection_vfuncs client_ctrl_connection_vfuncs = {
+ .input = client_ctrl_input,
+ .destroy = client_ctrl_connection_destroy,
+};
+
+static const struct connection_settings client_ctrl_connection_set = {
+ .service_name_in = IMAP_URLAUTH_WORKER_SOCKET,
+ .service_name_out = IMAP_URLAUTH_WORKER_SOCKET,
+ .unix_client_connect_msecs = 1000,
+ .input_max_size = SIZE_MAX,
+ .output_max_size = SIZE_MAX,
+};
+
+static const struct connection_vfuncs client_connection_vfuncs = {
+ .input = client_input,
+ .destroy = client_connection_destroy,
+};
+
+static const struct connection_settings client_connection_set = {
+ .unix_client_connect_msecs = 1000,
+ .input_max_size = SIZE_MAX,
+ .output_max_size = SIZE_MAX,
+};
+
static void imap_urlauth_worker_die(void)
{
/* do nothing */
while handling its initial input */
io_loop_set_running(current_ioloop);
+ clist = connection_list_init(&client_connection_set,
+ &client_connection_vfuncs);
+ clist_ctrl = connection_list_init(&client_ctrl_connection_set,
+ &client_ctrl_connection_vfuncs);
+
if (IS_STANDALONE()) {
T_BEGIN {
if (array_count(&access_apps) > 0) {
if (io_loop_is_running(current_ioloop))
master_service_run(master_service, client_connected);
- clients_destroy_all();
+
+ connection_list_deinit(&clist);
+ connection_list_deinit(&clist_ctrl);
mail_storage_service_deinit(&storage_service);
master_service_deinit(&master_service);