xref: /openbsd/usr.sbin/relayd/control.c (revision 7b36286a)
1 /*	$OpenBSD: control.c,v 1.26 2008/07/19 10:52:32 reyk 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 
179 	msgbuf_clear(&c->ibuf.w);
180 	TAILQ_REMOVE(&ctl_conns, c, entry);
181 
182 	event_del(&c->ibuf.ev);
183 	close(c->ibuf.fd);
184 	free(c);
185 }
186 
187 /* ARGSUSED */
188 void
189 control_dispatch_imsg(int fd, short event, void *arg)
190 {
191 	struct ctl_conn		*c;
192 	struct imsg		 imsg;
193 	struct ctl_id		 id;
194 	int			 n;
195 	struct relayd		*env = arg;
196 
197 	if ((c = control_connbyfd(fd)) == NULL) {
198 		log_warn("control_dispatch_imsg: fd %d: not found", fd);
199 		return;
200 	}
201 
202 	switch (event) {
203 	case EV_READ:
204 		if ((n = imsg_read(&c->ibuf)) == -1 || n == 0) {
205 			control_close(fd);
206 			return;
207 		}
208 		break;
209 	case EV_WRITE:
210 		if (msgbuf_write(&c->ibuf.w) < 0) {
211 			control_close(fd);
212 			return;
213 		}
214 		imsg_event_add(&c->ibuf);
215 		return;
216 	default:
217 		fatalx("unknown event");
218 	}
219 
220 	for (;;) {
221 		if ((n = imsg_get(&c->ibuf, &imsg)) == -1) {
222 			control_close(fd);
223 			return;
224 		}
225 
226 		if (n == 0)
227 			break;
228 
229 		switch (imsg.hdr.type) {
230 		case IMSG_CTL_SHOW_SUM:
231 			show(c);
232 			break;
233 		case IMSG_CTL_SESSION:
234 			show_sessions(c);
235 			break;
236 		case IMSG_CTL_RDR_DISABLE:
237 			if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(id))
238 				fatalx("invalid imsg header len");
239 			memcpy(&id, imsg.data, sizeof(id));
240 			if (disable_rdr(c, &id))
241 				imsg_compose(&c->ibuf, IMSG_CTL_FAIL, 0, 0, -1,
242 				    NULL, 0);
243 			else {
244 				memcpy(imsg.data, &id, sizeof(id));
245 				control_imsg_forward(&imsg);
246 				imsg_compose(&c->ibuf, IMSG_CTL_OK, 0, 0, -1,
247 				    NULL, 0);
248 			}
249 			break;
250 		case IMSG_CTL_RDR_ENABLE:
251 			if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(id))
252 				fatalx("invalid imsg header len");
253 			memcpy(&id, imsg.data, sizeof(id));
254 			if (enable_rdr(c, &id))
255 				imsg_compose(&c->ibuf, IMSG_CTL_FAIL, 0, 0, -1,
256 				    NULL, 0);
257 			else {
258 				memcpy(imsg.data, &id, sizeof(id));
259 				control_imsg_forward(&imsg);
260 				imsg_compose(&c->ibuf, IMSG_CTL_OK, 0, 0, -1,
261 				    NULL, 0);
262 			}
263 			break;
264 		case IMSG_CTL_TABLE_DISABLE:
265 			if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(id))
266 				fatalx("invalid imsg header len");
267 			memcpy(&id, imsg.data, sizeof(id));
268 			if (disable_table(c, &id))
269 				imsg_compose(&c->ibuf, IMSG_CTL_FAIL, 0, 0, -1,
270 				    NULL, 0);
271 			else {
272 				memcpy(imsg.data, &id, sizeof(id));
273 				control_imsg_forward(&imsg);
274 				imsg_compose(&c->ibuf, IMSG_CTL_OK, 0, 0, -1,
275 				    NULL, 0);
276 			}
277 			break;
278 		case IMSG_CTL_TABLE_ENABLE:
279 			if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(id))
280 				fatalx("invalid imsg header len");
281 			memcpy(&id, imsg.data, sizeof(id));
282 			if (enable_table(c, &id))
283 				imsg_compose(&c->ibuf, IMSG_CTL_FAIL, 0, 0, -1,
284 				    NULL, 0);
285 			else {
286 				memcpy(imsg.data, &id, sizeof(id));
287 				control_imsg_forward(&imsg);
288 				imsg_compose(&c->ibuf, IMSG_CTL_OK, 0, 0, -1,
289 				    NULL, 0);
290 			}
291 			break;
292 		case IMSG_CTL_HOST_DISABLE:
293 			if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(id))
294 				fatalx("invalid imsg header len");
295 			memcpy(&id, imsg.data, sizeof(id));
296 			if (disable_host(c, &id, NULL))
297 				imsg_compose(&c->ibuf, IMSG_CTL_FAIL, 0, 0, -1,
298 				    NULL, 0);
299 			else {
300 				memcpy(imsg.data, &id, sizeof(id));
301 				control_imsg_forward(&imsg);
302 				imsg_compose(&c->ibuf, IMSG_CTL_OK, 0, 0, -1,
303 				    NULL, 0);
304 			}
305 			break;
306 		case IMSG_CTL_HOST_ENABLE:
307 			if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(id))
308 				fatalx("invalid imsg header len");
309 			memcpy(&id, imsg.data, sizeof(id));
310 			if (enable_host(c, &id, NULL))
311 				imsg_compose(&c->ibuf, IMSG_CTL_FAIL, 0, 0, -1,
312 				    NULL, 0);
313 			else {
314 				memcpy(imsg.data, &id, sizeof(id));
315 				control_imsg_forward(&imsg);
316 				imsg_compose(&c->ibuf, IMSG_CTL_OK, 0, 0, -1,
317 				    NULL, 0);
318 			}
319 			break;
320 		case IMSG_CTL_SHUTDOWN:
321 			imsg_compose(&c->ibuf, IMSG_CTL_FAIL, 0, 0, -1, NULL,
322 			    0);
323 			break;
324 		case IMSG_CTL_POLL:
325 			imsg_compose(ibuf_hce, IMSG_CTL_POLL, 0, 0,-1, NULL, 0);
326 			imsg_compose(&c->ibuf, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
327 			break;
328 		case IMSG_CTL_RELOAD:
329 			if (env->sc_prefork_relay > 0) {
330 				imsg_compose(&c->ibuf, IMSG_CTL_FAIL, 0, 0, -1,
331 				    NULL, 0);
332 				break;
333 			}
334 			imsg_compose(ibuf_main, IMSG_CTL_RELOAD, 0, 0, -1, NULL,
335 			    0);
336 			/*
337 			 * we unconditionnaly return a CTL_OK imsg because
338 			 * we have no choice.
339 			 *
340 			 * so in this case, the reply relayctl gets means
341 			 * that the reload command has been set,
342 			 * it doesn't say wether the command succeeded or not.
343 			 */
344 			imsg_compose(&c->ibuf, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
345 			break;
346 		case IMSG_CTL_NOTIFY:
347 			if (c->flags & CTL_CONN_NOTIFY) {
348 				log_debug("control_dispatch_imsg: "
349 				    "client requested notify more than once");
350 				imsg_compose(&c->ibuf, IMSG_CTL_FAIL, 0, 0, -1,
351 				    NULL, 0);
352 				break;
353 			}
354 			c->flags |= CTL_CONN_NOTIFY;
355 			break;
356 		default:
357 			log_debug("control_dispatch_imsg: "
358 			    "error handling imsg %d", imsg.hdr.type);
359 			break;
360 		}
361 		imsg_free(&imsg);
362 	}
363 
364 	imsg_event_add(&c->ibuf);
365 }
366 
367 void
368 control_imsg_forward(struct imsg *imsg)
369 {
370 	struct ctl_conn *c;
371 
372 	TAILQ_FOREACH(c, &ctl_conns, entry)
373 		if (c->flags & CTL_CONN_NOTIFY)
374 			imsg_compose(&c->ibuf, imsg->hdr.type, 0, imsg->hdr.pid,
375 			    -1, imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE);
376 }
377 
378 void
379 session_socket_blockmode(int fd, enum blockmodes bm)
380 {
381 	int	flags;
382 
383 	if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
384 		fatal("fcntl F_GETFL");
385 
386 	if (bm == BM_NONBLOCK)
387 		flags |= O_NONBLOCK;
388 	else
389 		flags &= ~O_NONBLOCK;
390 
391 	if ((flags = fcntl(fd, F_SETFL, flags)) == -1)
392 		fatal("fcntl F_SETFL");
393 }
394