changeset 7099:3f5b7bebfd82 HEAD

Use separate per-client timeouts instead of going through all clients in one timeout.
author Timo Sirainen <tss@iki.fi>
date Thu, 03 Jan 2008 23:46:04 +0200
parents becdf2eacdce
children 4c6364f99ff0
files src/imap-login/client-authenticate.c src/imap-login/client.c src/imap-login/client.h src/pop3-login/client.c src/pop3-login/client.h
diffstat 5 files changed, 96 insertions(+), 116 deletions(-) [+]
line wrap: on
line diff
--- a/src/imap-login/client-authenticate.c	Thu Jan 03 23:19:33 2008 +0200
+++ b/src/imap-login/client-authenticate.c	Thu Jan 03 23:46:04 2008 +0200
@@ -19,6 +19,8 @@
 
 #define IMAP_SERVICE_NAME "imap"
 
+static void client_auth_failed(struct imap_client *client);
+
 const char *client_authenticate_get_capabilities(bool secured)
 {
 	const struct auth_mech_desc *mech;
@@ -211,13 +213,8 @@
 				  NULL);
 		client_send_tagline(client, msg);
 
-		if (!client->destroyed) {
-			/* get back to normal client input. */
-			if (client->io != NULL)
-				io_remove(&client->io);
-			client->io = io_add(client->common.fd, IO_READ,
-					    client_input, client);
-		}
+		if (!client->destroyed)
+			client_auth_failed(client);
 		break;
 	case SASL_SERVER_REPLY_MASTER_FAILED:
 		if (data == NULL)
@@ -251,6 +248,33 @@
 	client_unref(client);
 }
 
+static int client_auth_begin(struct imap_client *client, const char *mech_name,
+			     const char *init_resp)
+{
+	client_ref(client);
+	sasl_server_auth_begin(&client->common, IMAP_SERVICE_NAME, mech_name,
+			       init_resp, sasl_callback);
+	if (!client->common.authenticating)
+		return 1;
+
+	/* don't handle input until we get the initial auth reply */
+	if (client->io != NULL)
+		io_remove(&client->io);
+	client_set_auth_waiting(client);
+	return 0;
+}
+
+static void client_auth_failed(struct imap_client *client)
+{
+	/* get back to normal client input. */
+	if (client->io != NULL)
+		io_remove(&client->io);
+	client->io = io_add(client->common.fd, IO_READ,
+			    client_input, client);
+
+	timeout_remove(&client->to_auth_waiting);
+}
+
 int cmd_authenticate(struct imap_client *client, const struct imap_arg *args)
 {
 	const char *mech_name, *init_resp = NULL;
@@ -269,17 +293,7 @@
 	mech_name = IMAP_ARG_STR(&args[0]);
 	if (*mech_name == '\0')
 		return -1;
-
-	client_ref(client);
-	sasl_server_auth_begin(&client->common, IMAP_SERVICE_NAME, mech_name,
-			       init_resp, sasl_callback);
-	if (!client->common.authenticating)
-		return 1;
-
-	/* don't handle input until we get the initial auth reply */
-	if (client->io != NULL)
-		io_remove(&client->io);
-	return 0;
+	return client_auth_begin(client, mech_name, init_resp);
 }
 
 int cmd_login(struct imap_client *client, const struct imap_arg *args)
@@ -322,16 +336,5 @@
 	base64 = buffer_create_dynamic(pool_datastack_create(),
         			MAX_BASE64_ENCODED_SIZE(plain_login->used));
 	base64_encode(plain_login->data, plain_login->used, base64);
-
-	client_ref(client);
-	sasl_server_auth_begin(&client->common, IMAP_SERVICE_NAME, "PLAIN",
-			       str_c(base64), sasl_callback);
-	if (!client->common.authenticating)
-		return 1;
-
-	/* don't read any input from client until login is finished */
-	if (client->io != NULL)
-		io_remove(&client->io);
-
-	return 0;
+	return client_auth_begin(client, "PLAIN", str_c(base64));
 }
--- a/src/imap-login/client.c	Thu Jan 03 23:19:33 2008 +0200
+++ b/src/imap-login/client.c	Thu Jan 03 23:46:04 2008 +0200
@@ -28,8 +28,8 @@
 /* maximum length for IMAP command line. */
 #define MAX_IMAP_LINE 8192
 
-/* Disconnect client after idling this many seconds */
-#define CLIENT_LOGIN_IDLE_TIMEOUT (3*60)
+/* Disconnect client after idling this many milliseconds */
+#define CLIENT_LOGIN_IDLE_TIMEOUT_MSECS (3*60*1000)
 
 /* Disconnect client when it sends too many bad commands */
 #define CLIENT_MAX_BAD_COMMANDS 10
@@ -39,21 +39,21 @@
    client hash, it's faster if we disconnect multiple clients. */
 #define CLIENT_DESTROY_OLDEST_COUNT 16
 
-/* If we've been waiting auth server to respond for over this many seconds,
+/* If we've been waiting auth server to respond for over this many milliseconds,
    send a "waiting" message. */
-#define AUTH_WAITING_TIMEOUT 30
+#define AUTH_WAITING_TIMEOUT_MSECS 30
 
-#if CLIENT_LOGIN_IDLE_TIMEOUT >= AUTH_REQUEST_TIMEOUT
+#if CLIENT_LOGIN_IDLE_TIMEOUT_MSECS >= AUTH_REQUEST_TIMEOUT*1000
 #  error client idle timeout must be smaller than authentication timeout
 #endif
 
+#define AUTH_WAITING_MSG \
+	"* OK Waiting for authentication process to respond.."
+
 const char *login_protocol = "IMAP";
 const char *capability_string = CAPABILITY_STRING;
 
 static struct hash_table *clients;
-static struct timeout *to_idle;
-
-static void idle_timeout(void *context);
 
 static void client_set_title(struct imap_client *client)
 {
@@ -345,7 +345,7 @@
 
 void client_input(struct imap_client *client)
 {
-	client->last_input = ioloop_time;
+	timeout_reset(client->to_idle_disconnect);
 
 	if (!client_read(client))
 		return;
@@ -355,12 +355,12 @@
 	if (!auth_client_is_connected(auth_client)) {
 		/* we're not yet connected to auth process -
 		   don't allow any commands */
-		client->waiting_sent = TRUE;
-		client_send_line(client,
-			"* OK Waiting for authentication process to respond..");
+		client_send_line(client, AUTH_WAITING_MSG);
+		if (client->to_auth_waiting != NULL)
+			timeout_remove(&client->to_auth_waiting);
+
 		client->input_blocked = TRUE;
 	} else {
-		client->waiting_sent = FALSE;
 		o_stream_cork(client->output);
 		while (client_handle_input(client)) ;
 		o_stream_uncork(client->output);
@@ -425,6 +425,26 @@
 	client->greeting_sent = TRUE;
 }
 
+static void client_idle_disconnect_timeout(struct imap_client *client)
+{
+	client_send_line(client, "* BYE Disconnected for inactivity.");
+	client_destroy(client, "Disconnected: Inactivity");
+}
+
+static void client_auth_waiting_timeout(struct imap_client *client)
+{
+	client_send_line(client, AUTH_WAITING_MSG);
+	timeout_remove(&client->to_auth_waiting);
+}
+
+void client_set_auth_waiting(struct imap_client *client)
+{
+	i_assert(client->to_auth_waiting == NULL);
+	client->to_auth_waiting =
+		timeout_add(AUTH_WAITING_TIMEOUT_MSECS,
+			    client_auth_waiting_timeout, client);
+}
+
 struct client *client_create(int fd, bool ssl, const struct ip_addr *local_ip,
 			     const struct ip_addr *ip)
 {
@@ -450,17 +470,19 @@
 	client_open_streams(client, fd);
 	client->io = io_add(fd, IO_READ, client_input, client);
 
-	client->last_input = ioloop_time;
 	hash_insert(clients, client, client);
 
 	main_ref();
 
 	if (!greeting_capability || auth_client_is_connected(auth_client))
-                client_send_greeting(client);
+		client_send_greeting(client);
+	else
+		client_set_auth_waiting(client);
 	client_set_title(client);
 
-	if (to_idle == NULL)
-		to_idle = timeout_add(1000, idle_timeout, NULL);
+	client->to_idle_disconnect =
+		timeout_add(CLIENT_LOGIN_IDLE_TIMEOUT_MSECS,
+			    client_idle_disconnect_timeout, client);
 	return &client->common;
 }
 
@@ -474,8 +496,6 @@
 		client_syslog(&client->common, reason);
 
 	hash_remove(clients, client);
-	if (hash_count(clients) == 0)
-		timeout_remove(&to_idle);
 
 	if (client->input != NULL)
 		i_stream_close(client->input);
@@ -494,6 +514,10 @@
 
 	if (client->io != NULL)
 		io_remove(&client->io);
+	if (client->to_idle_disconnect != NULL)
+		timeout_remove(&client->to_idle_disconnect);
+	if (client->to_auth_waiting != NULL)
+		timeout_remove(&client->to_auth_waiting);
 
 	if (client->common.fd != -1) {
 		net_disconnect(client->common.fd);
@@ -584,34 +608,6 @@
 	client_send_line(client, t_strconcat(client->cmd_tag, " ", line, NULL));
 }
 
-static void client_check_idle(struct imap_client *client)
-{
-	if (ioloop_time - client->last_input >= CLIENT_LOGIN_IDLE_TIMEOUT) {
-		client_send_line(client, "* BYE Disconnected for inactivity.");
-		client_destroy(client, "Disconnected: Inactivity");
-	} else if (!client->waiting_sent &&
-		   ioloop_time - client->last_input > AUTH_WAITING_TIMEOUT &&
-		   (client->common.authenticating || !client->greeting_sent)) {
-		client->waiting_sent = TRUE;
-		client_send_line(client,
-			"* OK Waiting for authentication process to respond..");
-	}
-}
-
-static void idle_timeout(void *context ATTR_UNUSED)
-{
-	struct hash_iterate_context *iter;
-	void *key, *value;
-
-	iter = hash_iterate_init(clients);
-	while (hash_iterate(iter, &key, &value)) {
-		struct imap_client *client = key;
-
-		client_check_idle(client);
-	}
-	hash_iterate_deinit(&iter);
-}
-
 unsigned int clients_get_count(void)
 {
 	return hash_count(clients);
@@ -626,6 +622,8 @@
 	while (hash_iterate(iter, &key, &value)) {
 		struct imap_client *client = key;
 
+		if (client->to_auth_waiting != NULL)
+			timeout_remove(&client->to_auth_waiting);
 		if (!client->greeting_sent)
 			client_send_greeting(client);
 		if (client->input_blocked) {
@@ -659,6 +657,4 @@
 {
 	clients_destroy_all();
 	hash_destroy(&clients);
-
-	i_assert(to_idle == NULL);
 }
--- a/src/imap-login/client.h	Thu Jan 03 23:19:33 2008 +0200
+++ b/src/imap-login/client.h	Thu Jan 03 23:46:04 2008 +0200
@@ -15,11 +15,11 @@
 	struct istream *input;
 	struct ostream *output;
 	struct imap_parser *parser;
+	struct timeout *to_idle_disconnect, *to_auth_waiting;
 
 	struct login_proxy *proxy;
 	char *proxy_user, *proxy_password;
 
-	time_t last_input;
 	unsigned int bad_counter;
 
 	const char *cmd_tag, *cmd_name;
@@ -30,7 +30,6 @@
 	unsigned int input_blocked:1;
 	unsigned int destroyed:1;
 	unsigned int greeting_sent:1;
-	unsigned int waiting_sent:1;
 };
 
 void client_destroy(struct imap_client *client, const char *reason);
@@ -46,4 +45,6 @@
 void client_ref(struct imap_client *client);
 bool client_unref(struct imap_client *client);
 
+void client_set_auth_waiting(struct imap_client *client);
+
 #endif
--- a/src/pop3-login/client.c	Thu Jan 03 23:19:33 2008 +0200
+++ b/src/pop3-login/client.c	Thu Jan 03 23:46:04 2008 +0200
@@ -26,8 +26,8 @@
    SASL authentication gives the largest output. */
 #define MAX_OUTBUF_SIZE 4096
 
-/* Disconnect client after idling this many seconds */
-#define CLIENT_LOGIN_IDLE_TIMEOUT (3*60)
+/* Disconnect client after idling this many milliseconds */
+#define CLIENT_LOGIN_IDLE_TIMEOUT_MSECS (3*60)
 
 /* Disconnect client when it sends too many bad commands */
 #define CLIENT_MAX_BAD_COMMANDS 10
@@ -37,16 +37,13 @@
    client hash, it's faster if we disconnect multiple clients. */
 #define CLIENT_DESTROY_OLDEST_COUNT 16
 
-#if CLIENT_LOGIN_IDLE_TIMEOUT >= AUTH_REQUEST_TIMEOUT
+#if CLIENT_LOGIN_IDLE_TIMEOUT_MSECS >= AUTH_REQUEST_TIMEOUT*1000
 #  error client idle timeout must be smaller than authentication timeout
 #endif
 
 const char *login_protocol = "POP3";
 
 static struct hash_table *clients;
-static struct timeout *to_idle;
-
-static void idle_timeout(void *context);
 
 static void client_set_title(struct pop3_client *client)
 {
@@ -208,7 +205,7 @@
 {
 	char *line, *args;
 
-	client->last_input = ioloop_time;
+	timeout_reset(client->to_idle_disconnect);
 
 	if (!client_read(client))
 		return;
@@ -308,6 +305,11 @@
 					     client->apop_challenge, NULL));
 }
 
+static void client_idle_disconnect_timeout(struct pop3_client *client)
+{
+	client_destroy(client, "Disconnected: Inactivity");
+}
+
 struct client *client_create(int fd, bool ssl, const struct ip_addr *local_ip,
 			     const struct ip_addr *ip)
 {
@@ -331,7 +333,6 @@
 	client->common.fd = fd;
 	client_open_streams(client, fd);
 
-	client->last_input = ioloop_time;
 	hash_insert(clients, client, client);
 
 	main_ref();
@@ -341,8 +342,9 @@
 		client_auth_ready(client);
 	client_set_title(client);
 
-	if (to_idle == NULL)
-		to_idle = timeout_add(1000, idle_timeout, NULL);
+	client->to_idle_disconnect =
+		timeout_add(CLIENT_LOGIN_IDLE_TIMEOUT_MSECS,
+			    client_idle_disconnect_timeout, client);
 	return &client->common;
 }
 
@@ -356,8 +358,6 @@
 		client_syslog(&client->common, reason);
 
 	hash_remove(clients, client);
-	if (hash_count(clients) == 0)
-		timeout_remove(&to_idle);
 
 	if (client->input != NULL)
 		i_stream_close(client->input);
@@ -376,6 +376,8 @@
 
 	if (client->io != NULL)
 		io_remove(&client->io);
+	if (client->to_idle_disconnect != NULL)
+		timeout_remove(&client->to_idle_disconnect);
 
 	if (client->common.fd != -1) {
 		net_disconnect(client->common.fd);
@@ -461,26 +463,6 @@
 	}
 }
 
-static void client_check_idle(struct pop3_client *client)
-{
-	if (ioloop_time - client->last_input >= CLIENT_LOGIN_IDLE_TIMEOUT)
-		client_destroy(client, "Disconnected: Inactivity");
-}
-
-static void idle_timeout(void *context ATTR_UNUSED)
-{
-	struct hash_iterate_context *iter;
-	void *key, *value;
-
-	iter = hash_iterate_init(clients);
-	while (hash_iterate(iter, &key, &value)) {
-		struct pop3_client *client = key;
-
-		client_check_idle(client);
-	}
-	hash_iterate_deinit(&iter);
-}
-
 unsigned int clients_get_count(void)
 {
 	return hash_count(clients);
@@ -526,6 +508,4 @@
 {
 	clients_destroy_all();
 	hash_destroy(&clients);
-
-	i_assert(to_idle == NULL);
 }
--- a/src/pop3-login/client.h	Thu Jan 03 23:19:33 2008 +0200
+++ b/src/pop3-login/client.h	Thu Jan 03 23:46:04 2008 +0200
@@ -15,12 +15,12 @@
 	struct io *io;
 	struct istream *input;
 	struct ostream *output;
+	struct timeout *to_idle_disconnect;
 
 	struct login_proxy *proxy;
 	char *proxy_user, *proxy_password;
 	int proxy_state;
 
-	time_t last_input;
 	unsigned int bad_counter;
 
 	char *last_user;