summaryrefslogtreecommitdiffstats
path: root/src/util/tcpwrap.c
blob: 0d492bb8ebd8e16bd28fc4014a17d554418792ec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "ioloop.h"
#include "fdpass.h"
#include "write-full.h"
#include "restrict-access.h"
#include "master-service.h"

#include <unistd.h>
#include <syslog.h>
#include <tcpd.h>

struct tcpwrap_client {
	int fd;
	struct io *io;
	struct timeout *to;
};

#define INPUT_TIMEOUT_MSECS (1000*10)

/* for tcpwrap library */
int allow_severity = LOG_INFO;
int deny_severity = LOG_WARNING;

static struct tcpwrap_client *tcpwrap_client = NULL;

static void tcpwrap_client_destroy(struct tcpwrap_client **client);

static void tcpwrap_client_handle(struct tcpwrap_client *client, int check_fd,
				  const char *daemon_name)
{
	struct request_info request;

	request_init(&request, RQ_DAEMON, daemon_name,
		     RQ_FILE, check_fd, 0);
	fromhost(&request);

	if (!hosts_access(&request))
		(void)write_full(client->fd, "0\n", 2);
	else
		(void)write_full(client->fd, "1\n", 2);
	lib_exit(0);
}

static void tcpwrap_client_input(struct tcpwrap_client *client)
{
	unsigned char buf[1024];
	ssize_t ret;
	int check_fd = -1;

	ret = fd_read(client->fd, buf, sizeof(buf), &check_fd);
	if (ret <= 0) {
		i_error("fd_read() failed: %m");
	} else if (ret > 1 && (size_t)ret < sizeof(buf) && buf[ret-1] == '\n') {
		tcpwrap_client_handle(client, check_fd, t_strndup(buf, ret-1));
	} else {
		i_error("Invalid input from client");
	}

	i_close_fd(&check_fd);
	tcpwrap_client_destroy(&client);
}

static void tcpwrap_client_timeout(struct tcpwrap_client *client)
{
	tcpwrap_client_destroy(&client);
}

static struct tcpwrap_client *tcpwrap_client_create(int fd)
{
	struct tcpwrap_client *client;

	client = i_new(struct tcpwrap_client, 1);
	client->fd = fd;
	client->io = io_add(fd, IO_READ, tcpwrap_client_input, client);
	client->to = timeout_add(INPUT_TIMEOUT_MSECS, tcpwrap_client_timeout,
				 client);
	return client;
}

static void tcpwrap_client_destroy(struct tcpwrap_client **_client)
{
	struct tcpwrap_client *client = *_client;

	*_client = NULL;

	timeout_remove(&client->to);
	io_remove(&client->io);
	if (close(client->fd) < 0)
		i_error("close() failed: %m");
	i_free(client);

	tcpwrap_client = NULL;
	master_service_client_connection_destroyed(master_service);
}

static void client_connected(struct master_service_connection *conn)
{
	if (tcpwrap_client != NULL) {
		i_error("tcpwrap must be configured with client_limit=1");
		return;
	}

	master_service_client_connection_accept(conn);
	tcpwrap_client = tcpwrap_client_create(conn->fd);
}

int main(int argc, char *argv[])
{
	const enum master_service_flags service_flags =
		MASTER_SERVICE_FLAG_DONT_SEND_STATS;

	master_service = master_service_init("tcpwrap", service_flags,
					     &argc, &argv, "");
	if (master_getopt(master_service) > 0)
		return FATAL_DEFAULT;

	master_service_init_log(master_service);
	restrict_access_by_env(RESTRICT_ACCESS_FLAG_ALLOW_ROOT, NULL);
	restrict_access_allow_coredumps(TRUE);

	master_service_init_finish(master_service);

	master_service_run(master_service, client_connected);
	if (tcpwrap_client != NULL)
		tcpwrap_client_destroy(&tcpwrap_client);

	master_service_deinit(&master_service);
        return 0;
}