Mercurial > dovecot > core-2.2
view src/lib-master/master-auth.c @ 22652:09523ad05bef
director: Log whenever HOST-RESET-USERS is used
author | Timo Sirainen <timo.sirainen@dovecot.fi> |
---|---|
date | Sun, 05 Nov 2017 22:53:23 +0200 |
parents | 2e2563132d5f |
children | cb108f786fb4 |
line wrap: on
line source
/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "fdpass.h" #include "buffer.h" #include "hash.h" #include "master-service-private.h" #include "master-auth.h" #include <unistd.h> #include <sys/stat.h> #define SOCKET_CONNECT_RETRY_MSECS 500 #define MASTER_AUTH_REQUEST_TIMEOUT_MSECS (MASTER_LOGIN_TIMEOUT_SECS/2*1000) struct master_auth_connection { struct master_auth *auth; unsigned int tag; char *path; int fd; struct io *io; struct timeout *to; char buf[sizeof(struct master_auth_reply)]; unsigned int buf_pos; master_auth_callback_t *callback; void *context; }; struct master_auth { struct master_service *service; pool_t pool; const char *default_path; unsigned int tag_counter; HASH_TABLE(void *, struct master_auth_connection *) connections; }; struct master_auth * master_auth_init(struct master_service *service, const char *path) { struct master_auth *auth; pool_t pool; pool = pool_alloconly_create("master auth", 1024); auth = p_new(pool, struct master_auth, 1); auth->pool = pool; auth->service = service; auth->default_path = p_strdup(pool, path); hash_table_create_direct(&auth->connections, pool, 0); return auth; } static void master_auth_connection_deinit(struct master_auth_connection **_conn) { struct master_auth_connection *conn = *_conn; *_conn = NULL; if (conn->tag != 0) hash_table_remove(conn->auth->connections, POINTER_CAST(conn->tag)); if (conn->callback != NULL) conn->callback(NULL, conn->context); if (conn->to != NULL) timeout_remove(&conn->to); if (conn->io != NULL) io_remove(&conn->io); if (conn->fd != -1) { if (close(conn->fd) < 0) i_fatal("close(%s) failed: %m", conn->path); conn->fd = -1; } i_free(conn->path); i_free(conn); } void master_auth_deinit(struct master_auth **_auth) { struct master_auth *auth = *_auth; struct hash_iterate_context *iter; void *key; struct master_auth_connection *conn; *_auth = NULL; iter = hash_table_iterate_init(auth->connections); while (hash_table_iterate(iter, auth->connections, &key, &conn)) { conn->tag = 0; master_auth_connection_deinit(&conn); } hash_table_iterate_deinit(&iter); hash_table_destroy(&auth->connections); pool_unref(&auth->pool); } static void master_auth_connection_input(struct master_auth_connection *conn) { const struct master_auth_reply *reply; int ret; ret = read(conn->fd, conn->buf + conn->buf_pos, sizeof(conn->buf) - conn->buf_pos); if (ret <= 0) { if (ret == 0 || errno == ECONNRESET) { i_error("read(%s) failed: Remote closed connection " "(destination service { process_limit } reached?)", conn->path); } else { if (errno == EAGAIN) return; i_error("read(%s) failed: %m", conn->path); } master_auth_connection_deinit(&conn); return; } conn->buf_pos += ret; if (conn->buf_pos < sizeof(conn->buf)) return; /* reply is now read */ reply = (const void *)conn->buf; conn->buf_pos = 0; if (conn->tag != reply->tag) i_error("master(%s): Received reply with unknown tag %u", conn->path, reply->tag); else if (conn->callback == NULL) { /* request aborted */ } else { conn->callback(reply, conn->context); conn->callback = NULL; } master_auth_connection_deinit(&conn); } static void master_auth_connection_timeout(struct master_auth_connection *conn) { i_error("master(%s): Auth request timed out (received %u/%u bytes)", conn->path, conn->buf_pos, (unsigned int)sizeof(conn->buf)); master_auth_connection_deinit(&conn); } void master_auth_request_full(struct master_auth *auth, const struct master_auth_request_params *params, master_auth_callback_t *callback, void *context, unsigned int *tag_r) { struct master_auth_connection *conn; struct master_auth_request req; buffer_t *buf; struct stat st; ssize_t ret; i_assert(params->request.client_pid != 0); i_assert(params->request.auth_pid != 0); conn = i_new(struct master_auth_connection, 1); conn->auth = auth; conn->callback = callback; conn->context = context; conn->path = params->socket_path != NULL ? i_strdup(params->socket_path) : i_strdup(auth->default_path); req = params->request; req.tag = ++auth->tag_counter; if (req.tag == 0) req.tag = ++auth->tag_counter; if (fstat(params->client_fd, &st) < 0) i_fatal("fstat(auth dest fd) failed: %m"); req.ino = st.st_ino; buf = buffer_create_dynamic(pool_datastack_create(), sizeof(req) + req.data_size); buffer_append(buf, &req, sizeof(req)); buffer_append(buf, params->data, req.data_size); conn->fd = net_connect_unix_with_retries(conn->path, SOCKET_CONNECT_RETRY_MSECS); if (conn->fd == -1) { i_error("net_connect_unix(%s) failed: %m%s", conn->path, errno != EAGAIN ? "" : " - http://wiki2.dovecot.org/SocketUnavailable"); master_auth_connection_deinit(&conn); return; } ret = fd_send(conn->fd, params->client_fd, buf->data, buf->used); if (ret < 0) { i_error("fd_send(%s, %d) failed: %m", conn->path, params->client_fd); } else if ((size_t)ret != buf->used) { i_error("fd_send(%s) sent only %d of %d bytes", conn->path, (int)ret, (int)buf->used); ret = -1; } if (ret < 0) { master_auth_connection_deinit(&conn); return; } conn->tag = req.tag; conn->to = timeout_add(MASTER_AUTH_REQUEST_TIMEOUT_MSECS, master_auth_connection_timeout, conn); conn->io = io_add(conn->fd, IO_READ, master_auth_connection_input, conn); i_assert(hash_table_lookup(auth->connections, POINTER_CAST(req.tag)) == NULL); hash_table_insert(auth->connections, POINTER_CAST(req.tag), conn); *tag_r = req.tag; } void master_auth_request(struct master_auth *auth, int fd, const struct master_auth_request *request, const unsigned char *data, master_auth_callback_t *callback, void *context, unsigned int *tag_r) { struct master_auth_request_params params; i_zero(¶ms); params.client_fd = fd; params.request = *request; params.data = data; master_auth_request_full(auth, ¶ms, callback, context, tag_r); } void master_auth_request_abort(struct master_auth *auth, unsigned int tag) { struct master_auth_connection *conn; conn = hash_table_lookup(auth->connections, POINTER_CAST(tag)); if (conn == NULL) i_panic("master_auth_request_abort(): tag %u not found", tag); conn->callback = NULL; }