1 /* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "ioloop.h"
5 #include "net.h"
6 #include "fdpass.h"
7 #include "access-lookup.h"
8 
9 #include <unistd.h>
10 
11 #define ACCESS_LOOKUP_TIMEOUT_MSECS (1000*60)
12 
13 struct access_lookup {
14 	int refcount;
15 
16 	int fd;
17 	char *path;
18 
19 	struct io *io;
20 	struct timeout *to;
21 
22 	access_lookup_callback_t *callback;
23 	void *context;
24 };
25 
access_lookup_input(struct access_lookup * lookup)26 static void access_lookup_input(struct access_lookup *lookup)
27 {
28 	unsigned char buf[3];
29 	ssize_t ret;
30 	bool success = FALSE;
31 
32 	ret = read(lookup->fd, buf, sizeof(buf));
33 	if (ret < 0) {
34 		i_error("read(%s) failed: %m", lookup->path);
35 	} else if (ret == 0) {
36 		/* connection close -> no success */
37 	} else if (ret == 2 && buf[0] == '0' && buf[1] == '\n') {
38 		/* no success */
39 	} else if (ret == 2 && buf[0] == '1' && buf[1] == '\n') {
40 		success = TRUE;
41 	} else {
42 		i_error("access(%s): Invalid input", lookup->path);
43 	}
44 
45 	lookup->refcount++;
46 	lookup->callback(success, lookup->context);
47 	if (lookup->refcount > 1)
48 		access_lookup_destroy(&lookup);
49 	access_lookup_destroy(&lookup);
50 }
51 
access_lookup_timeout(struct access_lookup * lookup)52 static void access_lookup_timeout(struct access_lookup *lookup)
53 {
54 	i_error("access(%s): Timed out while waiting for reply", lookup->path);
55 
56 	lookup->refcount++;
57 	lookup->callback(FALSE, lookup->context);
58 	if (lookup->refcount > 1)
59 		access_lookup_destroy(&lookup);
60 	access_lookup_destroy(&lookup);
61 }
62 
63 struct access_lookup *
access_lookup(const char * path,int client_fd,const char * daemon_name,access_lookup_callback_t * callback,void * context)64 access_lookup(const char *path, int client_fd, const char *daemon_name,
65 	      access_lookup_callback_t *callback, void *context)
66 {
67 	struct access_lookup *lookup;
68 	const char *cmd;
69 	ssize_t ret;
70 	int fd;
71 
72 	fd = net_connect_unix(path);
73 	if (fd == -1) {
74 		i_error("connect(%s) failed: %m", path);
75 		return NULL;
76 	}
77 
78 	cmd = t_strconcat(daemon_name, "\n", NULL);
79 	ret = fd_send(fd, client_fd, cmd, strlen(cmd));
80 	if (ret != (ssize_t)strlen(cmd)) {
81 		if (ret < 0)
82 			i_error("fd_send(%s) failed: %m", path);
83 		else
84 			i_error("fd_send(%s) didn't write enough bytes", path);
85 		i_close_fd(&fd);
86 		return NULL;
87 	}
88 
89 	lookup = i_new(struct access_lookup, 1);
90 	lookup->refcount = 1;
91 	lookup->fd = fd;
92 	lookup->path = i_strdup(path);
93 	lookup->io = io_add(fd, IO_READ, access_lookup_input, lookup);
94 	lookup->to = timeout_add(ACCESS_LOOKUP_TIMEOUT_MSECS,
95 				 access_lookup_timeout, lookup);
96 	lookup->callback = callback;
97 	lookup->context = context;
98 	return lookup;
99 }
100 
access_lookup_destroy(struct access_lookup ** _lookup)101 void access_lookup_destroy(struct access_lookup **_lookup)
102 {
103 	struct access_lookup *lookup = *_lookup;
104 
105 	i_assert(lookup->refcount > 0);
106 	if (--lookup->refcount > 0)
107 		return;
108 
109 	*_lookup = NULL;
110 
111 	timeout_remove(&lookup->to);
112 	io_remove(&lookup->io);
113 	if (close(lookup->fd) < 0)
114 		i_error("close(%s) failed: %m", lookup->path);
115 
116 	i_free(lookup->path);
117 	i_free(lookup);
118 }
119