xref: /openbsd-src/usr.sbin/relayd/control.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /*	$OpenBSD: control.c,v 1.27 2009/02/25 17:09:55 claudio Exp $	*/
2 
3 /*
4  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/queue.h>
20 #include <sys/param.h>
21 #include <sys/stat.h>
22 #include <sys/socket.h>
23 #include <sys/un.h>
24 
25 #include <net/if.h>
26 
27 #include <errno.h>
28 #include <event.h>
29 #include <fcntl.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <signal.h>
34 
35 #include <openssl/ssl.h>
36 
37 #include "relayd.h"
38 
39 #define	CONTROL_BACKLOG	5
40 
41 struct ctl_connlist ctl_conns;
42 
43 struct ctl_conn	*control_connbyfd(int);
44 void		 control_close(int);
45 
46 struct imsgbuf	*ibuf_main = NULL;
47 struct imsgbuf	*ibuf_hce = NULL;
48 
49 int
50 control_init(void)
51 {
52 	struct sockaddr_un	 sun;
53 	int			 fd;
54 	mode_t			 old_umask;
55 
56 	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
57 		log_warn("control_init: socket");
58 		return (-1);
59 	}
60 
61 	sun.sun_family = AF_UNIX;
62 	if (strlcpy(sun.sun_path, RELAYD_SOCKET,
63 	    sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) {
64 		log_warn("control_init: %s name too long", RELAYD_SOCKET);
65 		close(fd);
66 		return (-1);
67 	}
68 
69 	if (unlink(RELAYD_SOCKET) == -1)
70 		if (errno != ENOENT) {
71 			log_warn("control_init: unlink %s", RELAYD_SOCKET);
72 			close(fd);
73 			return (-1);
74 		}
75 
76 	old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
77 	if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
78 		log_warn("control_init: bind: %s", RELAYD_SOCKET);
79 		close(fd);
80 		(void)umask(old_umask);
81 		return (-1);
82 	}
83 	(void)umask(old_umask);
84 
85 	if (chmod(RELAYD_SOCKET, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) {
86 		log_warn("control_init: chmod");
87 		close(fd);
88 		(void)unlink(RELAYD_SOCKET);
89 		return (-1);
90 	}
91 
92 	session_socket_blockmode(fd, BM_NONBLOCK);
93 	control_state.fd = fd;
94 
95 	return (0);
96 }
97 
98 int
99 control_listen(struct relayd *env, struct imsgbuf *i_main,
100     struct imsgbuf *i_hce)
101 {
102 
103 	ibuf_main = i_main;
104 	ibuf_hce = i_hce;
105 
106 	if (listen(control_state.fd, CONTROL_BACKLOG) == -1) {
107 		log_warn("control_listen: listen");
108 		return (-1);
109 	}
110 
111 	event_set(&control_state.ev, control_state.fd, EV_READ | EV_PERSIST,
112 	    control_accept, env);
113 	event_add(&control_state.ev, NULL);
114 
115 	return (0);
116 }
117 
118 void
119 control_cleanup(void)
120 {
121 	(void)unlink(RELAYD_SOCKET);
122 }
123 
124 /* ARGSUSED */
125 void
126 control_accept(int listenfd, short event, void *arg)
127 {
128 	int			 connfd;
129 	socklen_t		 len;
130 	struct sockaddr_un	 sun;
131 	struct ctl_conn		*c;
132 	struct relayd		*env = arg;
133 
134 	len = sizeof(sun);
135 	if ((connfd = accept(listenfd,
136 	    (struct sockaddr *)&sun, &len)) == -1) {
137 		if (errno != EWOULDBLOCK && errno != EINTR)
138 			log_warn("control_accept");
139 		return;
140 	}
141 
142 	session_socket_blockmode(connfd, BM_NONBLOCK);
143 
144 	if ((c = malloc(sizeof(struct ctl_conn))) == NULL) {
145 		close(connfd);
146 		log_warn("control_accept");
147 		return;
148 	}
149 
150 	imsg_init(&c->ibuf, connfd, control_dispatch_imsg);
151 	c->ibuf.events = EV_READ;
152 	event_set(&c->ibuf.ev, c->ibuf.fd, c->ibuf.events,
153 	    c->ibuf.handler, env);
154 	event_add(&c->ibuf.ev, NULL);
155 
156 	TAILQ_INSERT_TAIL(&ctl_conns, c, entry);
157 }
158 
159 struct ctl_conn *
160 control_connbyfd(int fd)
161 {
162 	struct ctl_conn	*c;
163 
164 	for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->ibuf.fd != fd;
165 	    c = TAILQ_NEXT(c, entry))
166 		;	/* nothing */
167 
168 	return (c);
169 }
170 
171 void
172 control_close(int fd)
173 {
174 	struct ctl_conn	*c;
175 
176 	if ((c = control_connbyfd(fd)) == NULL) {
177 		log_warn("control_close: fd %d: not found", fd);
178 		return;
179 	}
180 
181 	msgbuf_clear(&c->ibuf.w);
182 	TAILQ_REMOVE(&ctl_conns, c, entry);
183 
184 	event_del(&c->ibuf.ev);
185 	close(c->ibuf.fd);
186 	free(c);
187 }
188 
189 /* ARGSUSED */
190 void
191 control_dispatch_imsg(int fd, short event, void *arg)
192 {
193 	struct ctl_conn		*c;
194 	struct imsg		 imsg;
195 	struct ctl_id		 id;
196 	int			 n;
197 	struct relayd		*env = arg;
198 
199 	if ((c = control_connbyfd(fd)) == NULL) {
200 		log_warn("control_dispatch_imsg: fd %d: not found", fd);
201 		return;
202 	}
203 
204 	switch (event) {
205 	case EV_READ:
206 		if ((n = imsg_read(&c->ibuf)) == -1 || n == 0) {
207 			control_close(fd);
208 			return;
209 		}
210 		break;
211 	case EV_WRITE:
212 		if (msgbuf_write(&c->ibuf.w) < 0) {
213 			control_close(fd);
214 			return;
215 		}
216 		imsg_event_add(&c->ibuf);
217 		return;
218 	default:
219 		fatalx("unknown event");
220 	}
221 
222 	for (;;) {
223 		if ((n = imsg_get(&c->ibuf, &imsg)) == -1) {
224 			control_close(fd);
225 			return;
226 		}
227 
228 		if (n == 0)
229 			break;
230 
231 		switch (imsg.hdr.type) {
232 		case IMSG_CTL_SHOW_SUM:
233 			show(c);
234 			break;
235 		case IMSG_CTL_SESSION:
236 			show_sessions(c);
237 			break;
238 		case IMSG_CTL_RDR_DISABLE:
239 			if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(id))
240 				fatalx("invalid imsg header len");
241 			memcpy(&id, imsg.data, sizeof(id));
242 			if (disable_rdr(c, &id))
243 				imsg_compose(&c->ibuf, IMSG_CTL_FAIL, 0, 0, -1,
244 				    NULL, 0);
245 			else {
246 				memcpy(imsg.data, &id, sizeof(id));
247 				control_imsg_forward(&imsg);
248 				imsg_compose(&c->ibuf, IMSG_CTL_OK, 0, 0, -1,
249 				    NULL, 0);
250 			}
251 			break;
252 		case IMSG_CTL_RDR_ENABLE:
253 			if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(id))
254 				fatalx("invalid imsg header len");
255 			memcpy(&id, imsg.data, sizeof(id));
256 			if (enable_rdr(c, &id))
257 				imsg_compose(&c->ibuf, IMSG_CTL_FAIL, 0, 0, -1,
258 				    NULL, 0);
259 			else {
260 				memcpy(imsg.data, &id, sizeof(id));
261 				control_imsg_forward(&imsg);
262 				imsg_compose(&c->ibuf, IMSG_CTL_OK, 0, 0, -1,
263 				    NULL, 0);
264 			}
265 			break;
266 		case IMSG_CTL_TABLE_DISABLE:
267 			if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(id))
268 				fatalx("invalid imsg header len");
269 			memcpy(&id, imsg.data, sizeof(id));
270 			if (disable_table(c, &id))
271 				imsg_compose(&c->ibuf, IMSG_CTL_FAIL, 0, 0, -1,
272 				    NULL, 0);
273 			else {
274 				memcpy(imsg.data, &id, sizeof(id));
275 				control_imsg_forward(&imsg);
276 				imsg_compose(&c->ibuf, IMSG_CTL_OK, 0, 0, -1,
277 				    NULL, 0);
278 			}
279 			break;
280 		case IMSG_CTL_TABLE_ENABLE:
281 			if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(id))
282 				fatalx("invalid imsg header len");
283 			memcpy(&id, imsg.data, sizeof(id));
284 			if (enable_table(c, &id))
285 				imsg_compose(&c->ibuf, IMSG_CTL_FAIL, 0, 0, -1,
286 				    NULL, 0);
287 			else {
288 				memcpy(imsg.data, &id, sizeof(id));
289 				control_imsg_forward(&imsg);
290 				imsg_compose(&c->ibuf, IMSG_CTL_OK, 0, 0, -1,
291 				    NULL, 0);
292 			}
293 			break;
294 		case IMSG_CTL_HOST_DISABLE:
295 			if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(id))
296 				fatalx("invalid imsg header len");
297 			memcpy(&id, imsg.data, sizeof(id));
298 			if (disable_host(c, &id, NULL))
299 				imsg_compose(&c->ibuf, IMSG_CTL_FAIL, 0, 0, -1,
300 				    NULL, 0);
301 			else {
302 				memcpy(imsg.data, &id, sizeof(id));
303 				control_imsg_forward(&imsg);
304 				imsg_compose(&c->ibuf, IMSG_CTL_OK, 0, 0, -1,
305 				    NULL, 0);
306 			}
307 			break;
308 		case IMSG_CTL_HOST_ENABLE:
309 			if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(id))
310 				fatalx("invalid imsg header len");
311 			memcpy(&id, imsg.data, sizeof(id));
312 			if (enable_host(c, &id, NULL))
313 				imsg_compose(&c->ibuf, IMSG_CTL_FAIL, 0, 0, -1,
314 				    NULL, 0);
315 			else {
316 				memcpy(imsg.data, &id, sizeof(id));
317 				control_imsg_forward(&imsg);
318 				imsg_compose(&c->ibuf, IMSG_CTL_OK, 0, 0, -1,
319 				    NULL, 0);
320 			}
321 			break;
322 		case IMSG_CTL_SHUTDOWN:
323 			imsg_compose(&c->ibuf, IMSG_CTL_FAIL, 0, 0, -1, NULL,
324 			    0);
325 			break;
326 		case IMSG_CTL_POLL:
327 			imsg_compose(ibuf_hce, IMSG_CTL_POLL, 0, 0,-1, NULL, 0);
328 			imsg_compose(&c->ibuf, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
329 			break;
330 		case IMSG_CTL_RELOAD:
331 			if (env->sc_prefork_relay > 0) {
332 				imsg_compose(&c->ibuf, IMSG_CTL_FAIL, 0, 0, -1,
333 				    NULL, 0);
334 				break;
335 			}
336 			imsg_compose(ibuf_main, IMSG_CTL_RELOAD, 0, 0, -1, NULL,
337 			    0);
338 			/*
339 			 * we unconditionnaly return a CTL_OK imsg because
340 			 * we have no choice.
341 			 *
342 			 * so in this case, the reply relayctl gets means
343 			 * that the reload command has been set,
344 			 * it doesn't say wether the command succeeded or not.
345 			 */
346 			imsg_compose(&c->ibuf, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
347 			break;
348 		case IMSG_CTL_NOTIFY:
349 			if (c->flags & CTL_CONN_NOTIFY) {
350 				log_debug("control_dispatch_imsg: "
351 				    "client requested notify more than once");
352 				imsg_compose(&c->ibuf, IMSG_CTL_FAIL, 0, 0, -1,
353 				    NULL, 0);
354 				break;
355 			}
356 			c->flags |= CTL_CONN_NOTIFY;
357 			break;
358 		default:
359 			log_debug("control_dispatch_imsg: "
360 			    "error handling imsg %d", imsg.hdr.type);
361 			break;
362 		}
363 		imsg_free(&imsg);
364 	}
365 
366 	imsg_event_add(&c->ibuf);
367 }
368 
369 void
370 control_imsg_forward(struct imsg *imsg)
371 {
372 	struct ctl_conn *c;
373 
374 	TAILQ_FOREACH(c, &ctl_conns, entry)
375 		if (c->flags & CTL_CONN_NOTIFY)
376 			imsg_compose(&c->ibuf, imsg->hdr.type, 0, imsg->hdr.pid,
377 			    -1, imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE);
378 }
379 
380 void
381 session_socket_blockmode(int fd, enum blockmodes bm)
382 {
383 	int	flags;
384 
385 	if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
386 		fatal("fcntl F_GETFL");
387 
388 	if (bm == BM_NONBLOCK)
389 		flags |= O_NONBLOCK;
390 	else
391 		flags &= ~O_NONBLOCK;
392 
393 	if ((flags = fcntl(fd, F_SETFL, flags)) == -1)
394 		fatal("fcntl F_SETFL");
395 }
396