login-proxy-state.c revision 0d1b8b6bec79746c5d89d57dd8c1688946bd9237
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen#include "lib.h"
bdd36cfdba3ff66d25570a9ff568d69e1eb543cfTimo Sirainen#include "net.h"
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen#include "ioloop.h"
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen#include "hash.h"
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen#include "strescape.h"
e303353998b1959d1fa914a6be2e85cb9a2615c4Timo Sirainen#include "fd-set-nonblock.h"
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen#include "login-proxy-state.h"
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen#include <unistd.h>
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen#include <fcntl.h>
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen#define NOTIFY_RETRY_REOPEN_MSECS (60*1000)
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainenstruct login_proxy_state {
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen HASH_TABLE(struct login_proxy_record *,
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen struct login_proxy_record *) hash;
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen pool_t pool;
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen const char *notify_path;
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen int notify_fd;
2129efcd74a72d051d182171b7e07916308e386dTimo Sirainen
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen struct timeout *to_reopen;
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen};
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainenstatic int login_proxy_state_notify_open(struct login_proxy_state *state);
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainenstatic unsigned int
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainenlogin_proxy_record_hash(const struct login_proxy_record *rec)
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen{
7dc4bf28c0c4c673a198070edd1ed54e14ae39aeTimo Sirainen return net_ip_hash(&rec->ip) ^ rec->port;
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen}
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainenstatic int login_proxy_record_cmp(struct login_proxy_record *rec1,
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen struct login_proxy_record *rec2)
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen{
7dc4bf28c0c4c673a198070edd1ed54e14ae39aeTimo Sirainen if (!net_ip_compare(&rec1->ip, &rec2->ip))
7dc4bf28c0c4c673a198070edd1ed54e14ae39aeTimo Sirainen return 1;
7dc4bf28c0c4c673a198070edd1ed54e14ae39aeTimo Sirainen
7dc4bf28c0c4c673a198070edd1ed54e14ae39aeTimo Sirainen return (int)rec1->port - (int)rec2->port;
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen}
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainenstruct login_proxy_state *login_proxy_state_init(const char *notify_path)
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen{
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen struct login_proxy_state *state;
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen state = i_new(struct login_proxy_state, 1);
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen state->pool = pool_alloconly_create("login proxy state", 1024);
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen hash_table_create(&state->hash, state->pool, 0,
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen login_proxy_record_hash, login_proxy_record_cmp);
2129efcd74a72d051d182171b7e07916308e386dTimo Sirainen state->notify_path = p_strdup(state->pool, notify_path);
2129efcd74a72d051d182171b7e07916308e386dTimo Sirainen state->notify_fd = -1;
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen return state;
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen}
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainenstatic void login_proxy_state_close(struct login_proxy_state *state)
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen{
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen if (state->notify_fd != -1) {
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen if (close(state->notify_fd) < 0)
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen i_error("close(%s) failed: %m", state->notify_path);
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen state->notify_fd = -1;
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen }
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen}
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainenvoid login_proxy_state_deinit(struct login_proxy_state **_state)
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen{
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen struct login_proxy_state *state = *_state;
623eaaf530b2bd9cddbaeb88a211873ec9dee436Timo Sirainen struct hash_iterate_context *iter;
623eaaf530b2bd9cddbaeb88a211873ec9dee436Timo Sirainen struct login_proxy_record *rec;
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen *_state = NULL;
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen
623eaaf530b2bd9cddbaeb88a211873ec9dee436Timo Sirainen /* sanity check: */
623eaaf530b2bd9cddbaeb88a211873ec9dee436Timo Sirainen iter = hash_table_iterate_init(state->hash);
623eaaf530b2bd9cddbaeb88a211873ec9dee436Timo Sirainen while (hash_table_iterate(iter, state->hash, &rec, &rec))
623eaaf530b2bd9cddbaeb88a211873ec9dee436Timo Sirainen i_assert(rec->num_waiting_connections == 0);
623eaaf530b2bd9cddbaeb88a211873ec9dee436Timo Sirainen hash_table_iterate_deinit(&iter);
623eaaf530b2bd9cddbaeb88a211873ec9dee436Timo Sirainen
0d1b8b6bec79746c5d89d57dd8c1688946bd9237Josef 'Jeff' Sipek timeout_remove(&state->to_reopen);
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen login_proxy_state_close(state);
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen hash_table_destroy(&state->hash);
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen pool_unref(&state->pool);
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen i_free(state);
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen}
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainenstruct login_proxy_record *
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainenlogin_proxy_state_get(struct login_proxy_state *state,
009217abb57a24a4076092e8e4e165545747839eStephan Bosch const struct ip_addr *ip, in_port_t port)
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen{
7dc4bf28c0c4c673a198070edd1ed54e14ae39aeTimo Sirainen struct login_proxy_record *rec, key;
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch i_zero(&key);
7dc4bf28c0c4c673a198070edd1ed54e14ae39aeTimo Sirainen key.ip = *ip;
7dc4bf28c0c4c673a198070edd1ed54e14ae39aeTimo Sirainen key.port = port;
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen
7dc4bf28c0c4c673a198070edd1ed54e14ae39aeTimo Sirainen rec = hash_table_lookup(state->hash, &key);
7dc4bf28c0c4c673a198070edd1ed54e14ae39aeTimo Sirainen if (rec == NULL) {
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen rec = p_new(state->pool, struct login_proxy_record, 1);
7dc4bf28c0c4c673a198070edd1ed54e14ae39aeTimo Sirainen rec->ip = *ip;
7dc4bf28c0c4c673a198070edd1ed54e14ae39aeTimo Sirainen rec->port = port;
7dc4bf28c0c4c673a198070edd1ed54e14ae39aeTimo Sirainen hash_table_insert(state->hash, rec, rec);
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen }
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen return rec;
088cb24027234024aff2c1ce5b6870c5a308a44bTimo Sirainen}
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainenstatic void login_proxy_state_reopen(struct login_proxy_state *state)
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen{
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen timeout_remove(&state->to_reopen);
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen (void)login_proxy_state_notify_open(state);
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen}
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen
2129efcd74a72d051d182171b7e07916308e386dTimo Sirainenstatic int login_proxy_state_notify_open(struct login_proxy_state *state)
2129efcd74a72d051d182171b7e07916308e386dTimo Sirainen{
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen if (state->to_reopen != NULL) {
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen /* reopen later */
2129efcd74a72d051d182171b7e07916308e386dTimo Sirainen return -1;
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen }
2129efcd74a72d051d182171b7e07916308e386dTimo Sirainen
2129efcd74a72d051d182171b7e07916308e386dTimo Sirainen state->notify_fd = open(state->notify_path, O_WRONLY);
2129efcd74a72d051d182171b7e07916308e386dTimo Sirainen if (state->notify_fd == -1) {
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen i_error("open(%s) failed: %m", state->notify_path);
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen state->to_reopen = timeout_add(NOTIFY_RETRY_REOPEN_MSECS,
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen login_proxy_state_reopen, state);
2129efcd74a72d051d182171b7e07916308e386dTimo Sirainen return -1;
2129efcd74a72d051d182171b7e07916308e386dTimo Sirainen }
e303353998b1959d1fa914a6be2e85cb9a2615c4Timo Sirainen fd_set_nonblock(state->notify_fd, TRUE);
2129efcd74a72d051d182171b7e07916308e386dTimo Sirainen return 0;
2129efcd74a72d051d182171b7e07916308e386dTimo Sirainen}
2129efcd74a72d051d182171b7e07916308e386dTimo Sirainen
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainenstatic bool login_proxy_state_try_notify(struct login_proxy_state *state,
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen const char *user)
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen{
2ac5f36aa7c2e7a07ba8815d43a6d7483f62e74cTimo Sirainen size_t len;
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen ssize_t ret;
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen
2129efcd74a72d051d182171b7e07916308e386dTimo Sirainen if (state->notify_fd == -1) {
2129efcd74a72d051d182171b7e07916308e386dTimo Sirainen if (login_proxy_state_notify_open(state) < 0)
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen return TRUE;
abb404575a238f27ea03b6049880f30f1656ddc4Timo Sirainen i_assert(state->notify_fd != -1);
2129efcd74a72d051d182171b7e07916308e386dTimo Sirainen }
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen T_BEGIN {
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen const char *cmd;
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen cmd = t_strconcat(str_tabescape(user), "\n", NULL);
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen len = strlen(cmd);
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen ret = write(state->notify_fd, cmd, len);
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen } T_END;
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen
dc1bc1685e4a0d58ae7bacaecc282d0ebde2d7daTimo Sirainen if (ret != (ssize_t)len) {
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen if (ret < 0)
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen i_error("write(%s) failed: %m", state->notify_path);
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen else {
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen i_error("write(%s) wrote partial update",
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen state->notify_path);
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen }
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen login_proxy_state_close(state);
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen /* retry sending */
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen return FALSE;
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen }
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen return TRUE;
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen}
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainenvoid login_proxy_state_notify(struct login_proxy_state *state,
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen const char *user)
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen{
c53af2efae7a3484a23412e9be2c8e81523ab65eTimo Sirainen if (!login_proxy_state_try_notify(state, user))
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen (void)login_proxy_state_try_notify(state, user);
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen}