changeset 9308:1072d2b53f72 HEAD

login-proxy: If proxy destination is known to be down, fail immediately. We'll use a simple rule: If connection failed or timed out more recently than it succeeded AND there are currently no clients trying to connect to it, fail it. Since the connect isn't failed unless there is at least one client already trying to connect to it, the proxy notices immediately when the server comes back up and then starts serving it again.
author Timo Sirainen <tss@iki.fi>
date Wed, 12 Aug 2009 14:51:35 -0400
parents dfbcb8ead5ef
children bac1371c18e4
files src/login-common/Makefile.am src/login-common/login-proxy-state.c src/login-common/login-proxy.c src/login-common/login-proxy.h src/login-common/main.c
diffstat 5 files changed, 98 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/login-common/Makefile.am	Wed Aug 12 14:36:05 2009 -0400
+++ b/src/login-common/Makefile.am	Wed Aug 12 14:51:35 2009 -0400
@@ -10,6 +10,7 @@
 liblogin_common_a_SOURCES = \
 	client-common.c \
 	login-proxy.c \
+	login-proxy-state.c \
 	main.c \
 	master.c \
 	sasl-server.c \
@@ -20,6 +21,7 @@
 noinst_HEADERS = \
 	client-common.h \
 	login-proxy.h \
+	login-proxy-state.h \
 	common.h \
 	master.h \
 	sasl-server.h \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/login-common/login-proxy-state.c	Wed Aug 12 14:51:35 2009 -0400
@@ -0,0 +1,64 @@
+/* Copyright (c) 2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "network.h"
+#include "hash.h"
+#include "login-proxy-state.h"
+
+struct login_proxy_state {
+	struct hash_table *hash;
+	pool_t pool;
+};
+
+static unsigned int ip_addr_hash(const void *p)
+{
+	const struct ip_addr *ip = p;
+
+	return net_ip_hash(ip);
+}
+
+static int ip_addr_cmp(const void *p1, const void *p2)
+{
+	const struct ip_addr *ip1 = p1, *ip2 = p2;
+
+	return net_ip_compare(ip1, ip2) ? 0 : 1;
+}
+
+struct login_proxy_state *login_proxy_state_init(void)
+{
+	struct login_proxy_state *state;
+
+	state = i_new(struct login_proxy_state, 1);
+	state->pool = pool_alloconly_create("login proxy state", 1024);
+	state->hash = hash_table_create(default_pool, state->pool, 0,
+					ip_addr_hash, ip_addr_cmp);
+	return state;
+}
+
+void login_proxy_state_deinit(struct login_proxy_state **_state)
+{
+	struct login_proxy_state *state = *_state;
+
+	*_state = NULL;
+	hash_table_destroy(&state->hash);
+	pool_unref(&state->pool);
+	i_free(state);
+}
+
+struct login_proxy_record *
+login_proxy_state_get(struct login_proxy_state *state,
+		      const struct ip_addr *ip)
+{
+	struct login_proxy_record *rec;
+	struct ip_addr *new_ip;
+
+	rec = hash_table_lookup(state->hash, ip);
+	if (rec == NULL) {
+		new_ip = p_new(state->pool, struct ip_addr, 1);
+		*new_ip = *ip;
+
+		rec = p_new(state->pool, struct login_proxy_record, 1);
+		hash_table_insert(state->hash, new_ip, rec);
+	}
+	return rec;
+}
--- a/src/login-common/login-proxy.c	Wed Aug 12 14:36:05 2009 -0400
+++ b/src/login-common/login-proxy.c	Wed Aug 12 14:51:35 2009 -0400
@@ -8,6 +8,7 @@
 #include "str-sanitize.h"
 #include "client-common.h"
 #include "ssl-proxy.h"
+#include "login-proxy-state.h"
 #include "login-proxy.h"
 
 #define MAX_PROXY_INPUT_SIZE 4096
@@ -25,6 +26,7 @@
 	struct ssl_proxy *ssl_proxy;
 
 	struct timeout *to;
+	struct login_proxy_record *state_rec;
 
 	char *host, *user;
 	unsigned int port;
@@ -37,6 +39,7 @@
 	unsigned int disconnecting:1;
 };
 
+static struct login_proxy_state *proxy_state;
 static struct login_proxy *login_proxies = NULL;
 static unsigned int login_proxy_count = 0;
 
@@ -137,9 +140,13 @@
 	if (err != 0) {
 		i_error("proxy: connect(%s, %u) failed: %s",
 			proxy->host, proxy->port, strerror(err));
+		proxy->state_rec->last_failure = ioloop_time;
                 login_proxy_free(&proxy);
 		return;
 	}
+	proxy->state_rec->last_success = ioloop_time;
+	proxy->state_rec->num_waiting_connections--;
+	proxy->state_rec = NULL;
 
 	if (proxy->to != NULL)
 		timeout_remove(&proxy->to);
@@ -159,6 +166,7 @@
 static void proxy_connect_timeout(struct login_proxy *proxy)
 {
 	i_error("proxy: connect(%s, %u) timed out", proxy->host, proxy->port);
+	proxy->state_rec->last_failure = ioloop_time;
 	login_proxy_free(&proxy);
 }
 
@@ -170,6 +178,7 @@
 		proxy_callback_t *callback, void *context)
 {
 	struct login_proxy *proxy;
+	struct login_proxy_record *rec;
 	struct ip_addr ip;
 	int fd;
 
@@ -184,6 +193,13 @@
 		return NULL;
 	}
 
+	rec = login_proxy_state_get(proxy_state, &ip);
+	if (rec->last_failure > rec->last_success &&
+	    rec->num_waiting_connections != 0) {
+		/* the server is down. fail immediately */
+		return NULL;
+	}
+
 	fd = net_connect_ip(&ip, port, NULL);
 	if (fd < 0) {
 		i_error("proxy(%s): connect(%s, %u) failed: %m",
@@ -210,6 +226,9 @@
 
 	proxy->ip = client->ip;
 	proxy->client_fd = -1;
+
+	proxy->state_rec = rec;
+	rec->num_waiting_connections++;
 	return proxy;
 }
 
@@ -227,6 +246,11 @@
 	if (proxy->to != NULL)
 		timeout_remove(&proxy->to);
 
+	if (proxy->state_rec != NULL)
+		proxy->state_rec->num_waiting_connections--;
+	if (proxy->to != NULL)
+		timeout_remove(&proxy->to);
+
 	if (proxy->server_io != NULL)
 		io_remove(&proxy->server_io);
 	if (proxy->server_input != NULL)
@@ -400,6 +424,11 @@
 	return 0;
 }
 
+void login_proxy_init(void)
+{
+	proxy_state = login_proxy_state_init();
+}
+
 void login_proxy_deinit(void)
 {
 	struct login_proxy *proxy;
@@ -408,4 +437,5 @@
 		proxy = login_proxies;
 		login_proxy_free(&proxy);
 	}
+	login_proxy_state_deinit(&proxy_state);
 }
--- a/src/login-common/login-proxy.h	Wed Aug 12 14:36:05 2009 -0400
+++ b/src/login-common/login-proxy.h	Wed Aug 12 14:51:35 2009 -0400
@@ -63,6 +63,7 @@
 /* Return number of active detached login proxies */
 unsigned int login_proxy_get_count(void) ATTR_PURE;
 
+void login_proxy_init(void);
 void login_proxy_deinit(void);
 
 #endif
--- a/src/login-common/main.c	Wed Aug 12 14:36:05 2009 -0400
+++ b/src/login-common/main.c	Wed Aug 12 14:51:35 2009 -0400
@@ -363,6 +363,7 @@
 	auth_client = auth_client_new(login_process_uid);
         auth_client_set_connect_notify(auth_client, auth_connect_notify, NULL);
 	clients_init();
+	login_proxy_init();
 
 	if (!ssl_initialized && ssl_listen_count > 0) {
 		/* this shouldn't happen, master should have