xref: /openbsd/usr.sbin/relayd/control.c (revision 404b540a)
1 /*	$OpenBSD: control.c,v 1.32 2009/06/05 23:39:51 pyr 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 imsgev	*iev_main = NULL;
47 struct imsgev	*iev_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 	TAILQ_INIT(&ctl_conns);
95 
96 	return (0);
97 }
98 
99 int
100 control_listen(struct relayd *env, struct imsgev *i_main,
101     struct imsgev *i_hce)
102 {
103 
104 	iev_main = i_main;
105 	iev_hce = i_hce;
106 
107 	if (listen(control_state.fd, CONTROL_BACKLOG) == -1) {
108 		log_warn("control_listen: listen");
109 		return (-1);
110 	}
111 
112 	event_set(&control_state.ev, control_state.fd, EV_READ | EV_PERSIST,
113 	    control_accept, env);
114 	event_add(&control_state.ev, NULL);
115 
116 	return (0);
117 }
118 
119 void
120 control_cleanup(void)
121 {
122 	(void)unlink(RELAYD_SOCKET);
123 }
124 
125 /* ARGSUSED */
126 void
127 control_accept(int listenfd, short event, void *arg)
128 {
129 	int			 connfd;
130 	socklen_t		 len;
131 	struct sockaddr_un	 sun;
132 	struct ctl_conn		*c;
133 	struct relayd		*env = arg;
134 
135 	len = sizeof(sun);
136 	if ((connfd = accept(listenfd,
137 	    (struct sockaddr *)&sun, &len)) == -1) {
138 		if (errno != EWOULDBLOCK && errno != EINTR)
139 			log_warn("control_accept");
140 		return;
141 	}
142 
143 	session_socket_blockmode(connfd, BM_NONBLOCK);
144 
145 	if ((c = malloc(sizeof(struct ctl_conn))) == NULL) {
146 		close(connfd);
147 		log_warn("control_accept");
148 		return;
149 	}
150 
151 	imsg_init(&c->iev.ibuf, connfd);
152 	c->iev.handler = control_dispatch_imsg;
153 	c->iev.events = EV_READ;
154 	event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events,
155 	    c->iev.handler, env);
156 	event_add(&c->iev.ev, NULL);
157 
158 	TAILQ_INSERT_TAIL(&ctl_conns, c, entry);
159 }
160 
161 struct ctl_conn *
162 control_connbyfd(int fd)
163 {
164 	struct ctl_conn	*c;
165 
166 	for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->iev.ibuf.fd != fd;
167 	    c = TAILQ_NEXT(c, entry))
168 		;	/* nothing */
169 
170 	return (c);
171 }
172 
173 void
174 control_close(int fd)
175 {
176 	struct ctl_conn	*c;
177 
178 	if ((c = control_connbyfd(fd)) == NULL) {
179 		log_warn("control_close: fd %d: not found", fd);
180 		return;
181 	}
182 
183 	msgbuf_clear(&c->iev.ibuf.w);
184 	TAILQ_REMOVE(&ctl_conns, c, entry);
185 
186 	event_del(&c->iev.ev);
187 	close(c->iev.ibuf.fd);
188 	free(c);
189 }
190 
191 /* ARGSUSED */
192 void
193 control_dispatch_imsg(int fd, short event, void *arg)
194 {
195 	struct ctl_conn		*c;
196 	struct imsg		 imsg;
197 	struct ctl_id		 id;
198 	int			 n;
199 	struct relayd		*env = arg;
200 
201 	if ((c = control_connbyfd(fd)) == NULL) {
202 		log_warn("control_dispatch_imsg: fd %d: not found", fd);
203 		return;
204 	}
205 
206 	if (event & EV_READ) {
207 		if ((n = imsg_read(&c->iev.ibuf)) == -1 || n == 0) {
208 			control_close(fd);
209 			return;
210 		}
211 	}
212 
213 	if (event & EV_WRITE) {
214 		if (msgbuf_write(&c->iev.ibuf.w) < 0) {
215 			control_close(fd);
216 			return;
217 		}
218 	}
219 
220 	for (;;) {
221 		if ((n = imsg_get(&c->iev.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_event(&c->iev, IMSG_CTL_FAIL,
242 				    0, 0, -1, NULL, 0);
243 			else {
244 				memcpy(imsg.data, &id, sizeof(id));
245 				control_imsg_forward(&imsg);
246 				imsg_compose_event(&c->iev, IMSG_CTL_OK,
247 				    0, 0, -1, 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_event(&c->iev, IMSG_CTL_FAIL,
256 				    0, 0, -1, NULL, 0);
257 			else {
258 				memcpy(imsg.data, &id, sizeof(id));
259 				control_imsg_forward(&imsg);
260 				imsg_compose_event(&c->iev, IMSG_CTL_OK,
261 				    0, 0, -1, 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_event(&c->iev, IMSG_CTL_FAIL,
270 				    0, 0, -1, NULL, 0);
271 			else {
272 				memcpy(imsg.data, &id, sizeof(id));
273 				control_imsg_forward(&imsg);
274 				imsg_compose_event(&c->iev, IMSG_CTL_OK,
275 				    0, 0, -1, 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_event(&c->iev, IMSG_CTL_FAIL,
284 				    0, 0, -1, NULL, 0);
285 			else {
286 				memcpy(imsg.data, &id, sizeof(id));
287 				control_imsg_forward(&imsg);
288 				imsg_compose_event(&c->iev, IMSG_CTL_OK,
289 				    0, 0, -1, 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_event(&c->iev, IMSG_CTL_FAIL,
298 				    0, 0, -1, NULL, 0);
299 			else {
300 				memcpy(imsg.data, &id, sizeof(id));
301 				control_imsg_forward(&imsg);
302 				imsg_compose_event(&c->iev, IMSG_CTL_OK,
303 				    0, 0, -1, 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_event(&c->iev, IMSG_CTL_FAIL,
312 				    0, 0, -1, NULL, 0);
313 			else {
314 				memcpy(imsg.data, &id, sizeof(id));
315 				control_imsg_forward(&imsg);
316 				imsg_compose_event(&c->iev, IMSG_CTL_OK,
317 				    0, 0, -1, NULL, 0);
318 			}
319 			break;
320 		case IMSG_CTL_SHUTDOWN:
321 			imsg_compose_event(&c->iev, IMSG_CTL_FAIL,
322 			    0, 0, -1, NULL, 0);
323 			break;
324 		case IMSG_CTL_POLL:
325 			imsg_compose_event(iev_hce, IMSG_CTL_POLL,
326 			    0, 0,-1, NULL, 0);
327 			imsg_compose_event(&c->iev, IMSG_CTL_OK,
328 			    0, 0, -1, NULL, 0);
329 			break;
330 		case IMSG_CTL_RELOAD:
331 			if (env->sc_prefork_relay > 0) {
332 				imsg_compose_event(&c->iev, IMSG_CTL_FAIL,
333 				    0, 0, -1, NULL, 0);
334 				break;
335 			}
336 			imsg_compose_event(iev_main, IMSG_CTL_RELOAD,
337 			    0, 0, -1, NULL, 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_event(&c->iev, IMSG_CTL_OK,
347 			    0, 0, -1, NULL, 0);
348 			break;
349 		case IMSG_CTL_NOTIFY:
350 			if (c->flags & CTL_CONN_NOTIFY) {
351 				log_debug("control_dispatch_imsg: "
352 				    "client requested notify more than once");
353 				imsg_compose_event(&c->iev, IMSG_CTL_FAIL,
354 				    0, 0, -1, NULL, 0);
355 				break;
356 			}
357 			c->flags |= CTL_CONN_NOTIFY;
358 			break;
359 		default:
360 			log_debug("control_dispatch_imsg: "
361 			    "error handling imsg %d", imsg.hdr.type);
362 			break;
363 		}
364 		imsg_free(&imsg);
365 	}
366 
367 	imsg_event_add(&c->iev);
368 }
369 
370 void
371 control_imsg_forward(struct imsg *imsg)
372 {
373 	struct ctl_conn *c;
374 
375 	TAILQ_FOREACH(c, &ctl_conns, entry)
376 		if (c->flags & CTL_CONN_NOTIFY)
377 			imsg_compose_event(&c->iev, imsg->hdr.type,
378 			    0, imsg->hdr.pid, -1, imsg->data,
379 			    imsg->hdr.len - IMSG_HEADER_SIZE);
380 }
381 
382 void
383 session_socket_blockmode(int fd, enum blockmodes bm)
384 {
385 	int	flags;
386 
387 	if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
388 		fatal("fcntl F_GETFL");
389 
390 	if (bm == BM_NONBLOCK)
391 		flags |= O_NONBLOCK;
392 	else
393 		flags &= ~O_NONBLOCK;
394 
395 	if ((flags = fcntl(fd, F_SETFL, flags)) == -1)
396 		fatal("fcntl F_SETFL");
397 }
398