summaryrefslogtreecommitdiffstats
path: root/src/login-common/access-lookup.c
blob: 692b43ca1ba8d16fd396c784071c7b5bc300e535 (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
/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "ioloop.h"
#include "net.h"
#include "fdpass.h"
#include "access-lookup.h"

#include <unistd.h>

#define ACCESS_LOOKUP_TIMEOUT_MSECS (1000*60)

struct access_lookup {
	int refcount;

	int fd;
	char *path;

	struct io *io;
	struct timeout *to;

	access_lookup_callback_t *callback;
	void *context;
};

static void access_lookup_input(struct access_lookup *lookup)
{
	unsigned char buf[3];
	ssize_t ret;
	bool success = FALSE;

	ret = read(lookup->fd, buf, sizeof(buf));
	if (ret < 0) {
		i_error("read(%s) failed: %m", lookup->path);
	} else if (ret == 0) {
		/* connection close -> no success */
	} else if (ret == 2 && buf[0] == '0' && buf[1] == '\n') {
		/* no success */
	} else if (ret == 2 && buf[0] == '1' && buf[1] == '\n') {
		success = TRUE;
	} else {
		i_error("access(%s): Invalid input", lookup->path);
	}

	lookup->refcount++;
	lookup->callback(success, lookup->context);
	if (lookup->refcount > 1)
		access_lookup_destroy(&lookup);
	access_lookup_destroy(&lookup);
}

static void access_lookup_timeout(struct access_lookup *lookup)
{
	i_error("access(%s): Timed out while waiting for reply", lookup->path);

	lookup->refcount++;
	lookup->callback(FALSE, lookup->context);
	if (lookup->refcount > 1)
		access_lookup_destroy(&lookup);
	access_lookup_destroy(&lookup);
}

struct access_lookup *
access_lookup(const char *path, int client_fd, const char *daemon_name,
	      access_lookup_callback_t *callback, void *context)
{
	struct access_lookup *lookup;
	const char *cmd;
	ssize_t ret;
	int fd;

	fd = net_connect_unix(path);
	if (fd == -1) {
		i_error("connect(%s) failed: %m", path);
		return NULL;
	}

	cmd = t_strconcat(daemon_name, "\n", NULL);
	ret = fd_send(fd, client_fd, cmd, strlen(cmd));
	if (ret != (ssize_t)strlen(cmd)) {
		if (ret < 0)
			i_error("fd_send(%s) failed: %m", path);
		else
			i_error("fd_send(%s) didn't write enough bytes", path);
		i_close_fd(&fd);
		return NULL;
	}

	lookup = i_new(struct access_lookup, 1);
	lookup->refcount = 1;
	lookup->fd = fd;
	lookup->path = i_strdup(path);
	lookup->io = io_add(fd, IO_READ, access_lookup_input, lookup);
	lookup->to = timeout_add(ACCESS_LOOKUP_TIMEOUT_MSECS,
				 access_lookup_timeout, lookup);
	lookup->callback = callback;
	lookup->context = context;
	return lookup;
}

void access_lookup_destroy(struct access_lookup **_lookup)
{
	struct access_lookup *lookup = *_lookup;

	i_assert(lookup->refcount > 0);
	if (--lookup->refcount > 0)
		return;

	*_lookup = NULL;

	timeout_remove(&lookup->to);
	io_remove(&lookup->io);
	if (close(lookup->fd) < 0)
		i_error("close(%s) failed: %m", lookup->path);

	i_free(lookup->path);
	i_free(lookup);
}