<commit_before>/* Copyright (c) 2009-2010 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "ioloop.h"
#include "ostream.h"
#include "fdpass.h"
#include "fd-close-on-exec.h"
#include "llist.h"
#include "str.h"
#include "strescape.h"
#include "master-service-private.h"
#include "master-login.h"
#include "master-login-auth.h"

#include <sys/stat.h>
#include <unistd.h>

#define MASTER_LOGIN_POSTLOGIN_TIMEOUT_MSECS (60*1000)

struct master_login_connection {
	struct master_login_connection *prev, *next;

	struct master_login *login;
	int refcount;
	int fd;
	struct io *io;
	struct ostream *output;
};

struct master_login_postlogin {
	struct master_login_client *client;

	int fd;
	struct io *io;
	struct timeout *to;
	string_t *input;
};

struct master_login {
	struct master_service *service;
	master_login_callback_t *callback;
	struct master_login_connection *conns;
	struct master_login_auth *auth;
	char *postlogin_socket_path;

	unsigned int stopping:1;
};

static void master_login_conn_deinit(struct master_login_connection **_conn);
static void master_login_conn_unref(struct master_login_connection **_conn);

struct master_login *
master_login_init(struct master_service *service, const char *auth_socket_path,
		  const char *postlogin_socket_path,
		  master_login_callback_t *callback)
{
	struct master_login *login;

	login = i_new(struct master_login, 1);
	login->service = service;
	login->callback = callback;
	login->auth = master_login_auth_init(auth_socket_path);
	login->postlogin_socket_path = i_strdup(postlogin_socket_path);

	i_assert(service->login == NULL);
	service->login = login;
	return login;
}

void master_login_deinit(struct master_login **_login)
{
	struct master_login *login = *_login;

	*_login = NULL;

	i_assert(login->service->login == login);
	login->service->login = NULL;

	master_login_auth_deinit(&login->auth);
	while (login->conns != NULL) {
		struct master_login_connection *conn = login->conns;

		master_login_conn_deinit(&conn);
	}
	i_free(login->postlogin_socket_path);
	i_free(login);
}

static int
master_login_conn_read_request(struct master_login_connection *conn,
			       struct master_auth_request *req_r,
			       unsigned char data[MASTER_AUTH_MAX_DATA_SIZE],
			       int *client_fd_r)
{
	struct stat st;
	ssize_t ret;

	*client_fd_r = -1;

	ret = fd_read(conn->fd, req_r, sizeof(*req_r), client_fd_r);
	if (ret != sizeof(*req_r)) {
		if (ret == 0) {
			/* disconnected */
		} else if (ret > 0) {
			/* request wasn't fully read */
			i_error("fd_read() partial input (%d/%d)",
				(int)ret, (int)sizeof(*req_r));
		} else {
			if (errno == EAGAIN)
				return 0;

			i_error("fd_read() failed: %m");
		}
		return -1;
	}

	if (req_r->data_size != 0) {
		if (req_r->data_size > MASTER_AUTH_MAX_DATA_SIZE) {
			i_error("Too large auth data_size sent");
			return -1;
		}
		/* @UNSAFE */
		ret = read(conn->fd, data, req_r->data_size);
		if (ret != (ssize_t)req_r->data_size) {
			if (ret == 0) {
				/* disconnected */
			} else if (ret > 0) {
				/* request wasn't fully read */
				i_error("Data read partially %d/%u",
					(int)ret, req_r->data_size);
			} else {
				i_error("read(data) failed: %m");
			}
			return -1;
		}
	}

	if (*client_fd_r == -1) {
		i_error("Auth request missing a file descriptor");
		return -1;
	}

	if (fstat(*client_fd_r, &st) < 0) {
		i_error("fstat(fd_read client) failed: %m");
		return -1;
	}
	if (st.st_ino != req_r->ino) {
		i_error("Auth request inode mismatch: %s != %s",
			dec2str(st.st_ino), dec2str(req_r->ino));
		return -1;
	}
	return 1;
}

static void master_login_client_free(struct master_login_client **_client)
{
	struct master_login_client *client = *_client;

	*_client = NULL;
	if (client->fd != -1) {
		if (close(client->fd) < 0)
			i_error("close(fd_read client) failed: %m");
	}

	master_login_conn_unref(&client->conn);
	i_free(client);
}

static void master_login_auth_finish(struct master_login_client *client,
				     const char *const *auth_args)
{
	struct master_login_connection *conn = client->conn;
	struct master_login *login = conn->login;
	struct master_service *service = login->service;
	bool close_sockets;

	close_sockets = service->master_status.available_count == 0 &&
		service->service_count_left == 1;

	login->callback(client, auth_args[0], auth_args+1);

	if (close_sockets) {
		/* we're dying as soon as this connection closes. */
		i_assert(master_login_auth_request_count(login->auth) == 0);
		master_login_auth_disconnect(login->auth);

		master_service_close_config_fd(service);
		master_login_conn_deinit(&conn);
	} else if (login->stopping) {
		/* try stopping again */
		master_login_stop(login);
	}

	client->fd = -1;
	master_login_client_free(&client);
}

static void master_login_postlogin_free(struct master_login_postlogin *pl)
{
	timeout_remove(&pl->to);
	io_remove(&pl->io);
	if (close(pl->fd) < 0)
		i_error("close(postlogin) failed: %m");
	str_free(&pl->input);
	i_free(pl);
}

static void master_login_postlogin_input(struct master_login_postlogin *pl)
{
	struct master_login *login = pl->client->conn->login;
	char buf[1024];
	const char **auth_args, **p;
	unsigned int len;
	ssize_t ret;
	int fd = -1;

	while ((ret = fd_read(pl->fd, buf, sizeof(buf), &fd)) > 0) {
		if (fd != -1) {
			/* post-login script replaced fd */<commit_msg>lib-master: Disconnect from auth service immediately after receiving reply. With the current code we can't reuse the connection anyway.<commit_after>/* Copyright (c) 2009-2010 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "ioloop.h"
#include "ostream.h"
#include "fdpass.h"
#include "fd-close-on-exec.h"
#include "llist.h"
#include "str.h"
#include "strescape.h"
#include "master-service-private.h"
#include "master-login.h"
#include "master-login-auth.h"

#include <sys/stat.h>
#include <unistd.h>

#define MASTER_LOGIN_POSTLOGIN_TIMEOUT_MSECS (60*1000)

struct master_login_connection {
	struct master_login_connection *prev, *next;

	struct master_login *login;
	int refcount;
	int fd;
	struct io *io;
	struct ostream *output;
};

struct master_login_postlogin {
	struct master_login_client *client;

	int fd;
	struct io *io;
	struct timeout *to;
	string_t *input;
};

struct master_login {
	struct master_service *service;
	master_login_callback_t *callback;
	struct master_login_connection *conns;
	struct master_login_auth *auth;
	char *postlogin_socket_path;

	unsigned int stopping:1;
};

static void master_login_conn_deinit(struct master_login_connection **_conn);
static void master_login_conn_unref(struct master_login_connection **_conn);

struct master_login *
master_login_init(struct master_service *service, const char *auth_socket_path,
		  const char *postlogin_socket_path,
		  master_login_callback_t *callback)
{
	struct master_login *login;

	login = i_new(struct master_login, 1);
	login->service = service;
	login->callback = callback;
	login->auth = master_login_auth_init(auth_socket_path);
	login->postlogin_socket_path = i_strdup(postlogin_socket_path);

	i_assert(service->login == NULL);
	service->login = login;
	return login;
}

void master_login_deinit(struct master_login **_login)
{
	struct master_login *login = *_login;

	*_login = NULL;

	i_assert(login->service->login == login);
	login->service->login = NULL;

	master_login_auth_deinit(&login->auth);
	while (login->conns != NULL) {
		struct master_login_connection *conn = login->conns;

		master_login_conn_deinit(&conn);
	}
	i_free(login->postlogin_socket_path);
	i_free(login);
}

static int
master_login_conn_read_request(struct master_login_connection *conn,
			       struct master_auth_request *req_r,
			       unsigned char data[MASTER_AUTH_MAX_DATA_SIZE],
			       int *client_fd_r)
{
	struct stat st;
	ssize_t ret;

	*client_fd_r = -1;

	ret = fd_read(conn->fd, req_r, sizeof(*req_r), client_fd_r);
	if (ret != sizeof(*req_r)) {
		if (ret == 0) {
			/* disconnected */
		} else if (ret > 0) {
			/* request wasn't fully read */
			i_error("fd_read() partial input (%d/%d)",
				(int)ret, (int)sizeof(*req_r));
		} else {
			if (errno == EAGAIN)
				return 0;

			i_error("fd_read() failed: %m");
		}
		return -1;
	}

	if (req_r->data_size != 0) {
		if (req_r->data_size > MASTER_AUTH_MAX_DATA_SIZE) {
			i_error("Too large auth data_size sent");
			return -1;
		}
		/* @UNSAFE */
		ret = read(conn->fd, data, req_r->data_size);
		if (ret != (ssize_t)req_r->data_size) {
			if (ret == 0) {
				/* disconnected */
			} else if (ret > 0) {
				/* request wasn't fully read */
				i_error("Data read partially %d/%u",
					(int)ret, req_r->data_size);
			} else {
				i_error("read(data) failed: %m");
			}
			return -1;
		}
	}

	if (*client_fd_r == -1) {
		i_error("Auth request missing a file descriptor");
		return -1;
	}

	if (fstat(*client_fd_r, &st) < 0) {
		i_error("fstat(fd_read client) failed: %m");
		return -1;
	}
	if (st.st_ino != req_r->ino) {
		i_error("Auth request inode mismatch: %s != %s",
			dec2str(st.st_ino), dec2str(req_r->ino));
		return -1;
	}
	return 1;
}

static void master_login_client_free(struct master_login_client **_client)
{
	struct master_login_client *client = *_client;

	*_client = NULL;
	if (client->fd != -1) {
		if (close(client->fd) < 0)
			i_error("close(fd_read client) failed: %m");
	}

	master_login_conn_unref(&client->conn);
	i_free(client);
}

static void master_login_auth_finish(struct master_login_client *client,
				     const char *const *auth_args)
{
	struct master_login_connection *conn = client->conn;
	struct master_login *login = conn->login;
	struct master_service *service = login->service;
	bool close_sockets;

	close_sockets = service->master_status.available_count == 0 &&
		service->service_count_left == 1;

	login->callback(client, auth_args[0], auth_args+1);

	if (close_sockets) {
		/* we're dying as soon as this connection closes. */
		i_assert(master_login_auth_request_count(login->auth) == 0);
		master_login_auth_disconnect(login->auth);

		master_service_close_config_fd(service);
	} else if (login->stopping) {
		/* try stopping again */
		master_login_stop(login);
	}
	/* FIXME: currently we create a separate connection for each request */
	master_login_conn_deinit(&conn);

	client->fd = -1;
	master_login_client_free(&client);
}

static void master_login_postlogin_free(struct master_login_postlogin *pl)
{
	timeout_remove(&pl->to);
	io_remove(&pl->io);
	if (close(pl->fd) < 0)
		i_error("close(postlogin) failed: %m");
	str_free(&pl->input);
	i_free(pl);
}

static void master_login_postlogin_input(struct master_login_postlogin *pl)
{
	struct master_login *login = pl->client->conn->login;
	char buf[1024];
	const char **auth_args, **p;
	unsigned int len;
	ssize_t ret;
	int fd = -1;

	while ((ret = fd_read(pl->fd, buf, sizeof(buf), &fd)) > 0) {
		if (fd != -1) {
			/* post-login script replaced fd */
{'fraction_non_alphanumeric': 0.1374861776631036, 'fraction_numerical': 0.009214891264283081, 'mean_word_length': 4.600103199174407, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 103, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3284062', 'n_tokens_mistral': 4267, 'n_tokens_neox': 3946, 'n_words': 888}