summaryrefslogtreecommitdiffstats
path: root/src/login-common/login-proxy-state.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/login-common/login-proxy-state.c')
-rw-r--r--src/login-common/login-proxy-state.c165
1 files changed, 165 insertions, 0 deletions
diff --git a/src/login-common/login-proxy-state.c b/src/login-common/login-proxy-state.c
new file mode 100644
index 0000000..e97d809
--- /dev/null
+++ b/src/login-common/login-proxy-state.c
@@ -0,0 +1,165 @@
+/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "net.h"
+#include "ioloop.h"
+#include "hash.h"
+#include "strescape.h"
+#include "login-proxy-state.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#define NOTIFY_RETRY_REOPEN_MSECS (60*1000)
+
+struct login_proxy_state {
+ HASH_TABLE(struct login_proxy_record *,
+ struct login_proxy_record *) hash;
+ pool_t pool;
+
+ const char *notify_path;
+ int notify_fd;
+
+ struct timeout *to_reopen;
+};
+
+static int login_proxy_state_notify_open(struct login_proxy_state *state);
+
+static unsigned int
+login_proxy_record_hash(const struct login_proxy_record *rec)
+{
+ return net_ip_hash(&rec->ip) ^ rec->port;
+}
+
+static int login_proxy_record_cmp(struct login_proxy_record *rec1,
+ struct login_proxy_record *rec2)
+{
+ if (!net_ip_compare(&rec1->ip, &rec2->ip))
+ return 1;
+
+ return (int)rec1->port - (int)rec2->port;
+}
+
+struct login_proxy_state *login_proxy_state_init(const char *notify_path)
+{
+ struct login_proxy_state *state;
+
+ state = i_new(struct login_proxy_state, 1);
+ state->pool = pool_alloconly_create("login proxy state", 1024);
+ hash_table_create(&state->hash, state->pool, 0,
+ login_proxy_record_hash, login_proxy_record_cmp);
+ state->notify_path = p_strdup(state->pool, notify_path);
+ state->notify_fd = -1;
+ return state;
+}
+
+static void login_proxy_state_close(struct login_proxy_state *state)
+{
+ i_close_fd_path(&state->notify_fd, state->notify_path);
+}
+
+void login_proxy_state_deinit(struct login_proxy_state **_state)
+{
+ struct login_proxy_state *state = *_state;
+ struct hash_iterate_context *iter;
+ struct login_proxy_record *rec;
+
+ *_state = NULL;
+
+ /* sanity check: */
+ iter = hash_table_iterate_init(state->hash);
+ while (hash_table_iterate(iter, state->hash, &rec, &rec))
+ i_assert(rec->num_waiting_connections == 0);
+ hash_table_iterate_deinit(&iter);
+
+ timeout_remove(&state->to_reopen);
+ login_proxy_state_close(state);
+ 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, in_port_t port)
+{
+ struct login_proxy_record *rec, key;
+
+ i_zero(&key);
+ key.ip = *ip;
+ key.port = port;
+
+ rec = hash_table_lookup(state->hash, &key);
+ if (rec == NULL) {
+ rec = p_new(state->pool, struct login_proxy_record, 1);
+ rec->ip = *ip;
+ rec->port = port;
+ hash_table_insert(state->hash, rec, rec);
+ }
+ return rec;
+}
+
+static void login_proxy_state_reopen(struct login_proxy_state *state)
+{
+ timeout_remove(&state->to_reopen);
+ (void)login_proxy_state_notify_open(state);
+}
+
+static int login_proxy_state_notify_open(struct login_proxy_state *state)
+{
+ if (state->to_reopen != NULL) {
+ /* reopen later */
+ return -1;
+ }
+
+ state->notify_fd = open(state->notify_path, O_WRONLY);
+ if (state->notify_fd == -1) {
+ i_error("open(%s) failed: %m", state->notify_path);
+ state->to_reopen = timeout_add(NOTIFY_RETRY_REOPEN_MSECS,
+ login_proxy_state_reopen, state);
+ return -1;
+ }
+ fd_set_nonblock(state->notify_fd, TRUE);
+ return 0;
+}
+
+static bool login_proxy_state_try_notify(struct login_proxy_state *state,
+ const char *user)
+{
+ size_t len;
+ ssize_t ret;
+
+ if (state->notify_fd == -1) {
+ if (login_proxy_state_notify_open(state) < 0)
+ return TRUE;
+ i_assert(state->notify_fd != -1);
+ }
+
+ T_BEGIN {
+ const char *cmd;
+
+ cmd = t_strconcat(str_tabescape(user), "\n", NULL);
+ len = strlen(cmd);
+ ret = write(state->notify_fd, cmd, len);
+ } T_END;
+
+ if (ret != (ssize_t)len) {
+ if (ret < 0)
+ i_error("write(%s) failed: %m", state->notify_path);
+ else {
+ i_error("write(%s) wrote partial update",
+ state->notify_path);
+ }
+ login_proxy_state_close(state);
+ /* retry sending */
+ return FALSE;
+ }
+ return TRUE;
+}
+
+void login_proxy_state_notify(struct login_proxy_state *state,
+ const char *user)
+{
+ if (!login_proxy_state_try_notify(state, user))
+ (void)login_proxy_state_try_notify(state, user);
+}