xref: /openbsd-src/usr.sbin/unwindctl/unwindctl.c (revision 9f11ffb7133c203312a01e4b986886bc88c7d74b)
1 /*	$OpenBSD: unwindctl.c,v 1.4 2019/02/03 12:02:30 florian Exp $	*/
2 
3 /*
4  * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
5  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
6  * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 
21 #include <sys/types.h>
22 #include <sys/queue.h>
23 #include <sys/socket.h>
24 #include <sys/un.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 #include <net/if.h>
28 #include <net/if_media.h>
29 #include <net/if_types.h>
30 
31 #include <err.h>
32 #include <errno.h>
33 #include <event.h>
34 #include <imsg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 
40 #include "unwind.h"
41 #include "captiveportal.h"
42 #include "frontend.h"
43 #include "resolver.h"
44 #include "parser.h"
45 
46 __dead void	 usage(void);
47 int		 show_status_msg(struct imsg *);
48 void		 print_indented_str(char *);
49 void		 print_histogram(void*, size_t len);
50 
51 struct imsgbuf	*ibuf;
52 
53 __dead void
54 usage(void)
55 {
56 	extern char *__progname;
57 
58 	fprintf(stderr, "usage: %s [-s socket] command [argument ...]\n",
59 	    __progname);
60 	exit(1);
61 }
62 
63 int
64 main(int argc, char *argv[])
65 {
66 	struct sockaddr_un	 sun;
67 	struct parse_result	*res;
68 	struct imsg		 imsg;
69 	int			 ctl_sock;
70 	int			 done = 0;
71 	int			 n, verbose = 0;
72 	int			 ch;
73 	int			 type;
74 	char			*sockname;
75 
76 	sockname = UNWIND_SOCKET;
77 	while ((ch = getopt(argc, argv, "s:")) != -1) {
78 		switch (ch) {
79 		case 's':
80 			sockname = optarg;
81 			break;
82 		default:
83 			usage();
84 		}
85 	}
86 	argc -= optind;
87 	argv += optind;
88 
89 	/* Parse command line. */
90 	if ((res = parse(argc, argv)) == NULL)
91 		exit(1);
92 
93 	/* Connect to control socket. */
94 	if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
95 		err(1, "socket");
96 
97 	memset(&sun, 0, sizeof(sun));
98 	sun.sun_family = AF_UNIX;
99 
100 	strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path));
101 	if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
102 		err(1, "connect: %s", sockname);
103 
104 	if (pledge("stdio", NULL) == -1)
105 		err(1, "pledge");
106 
107 	if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
108 		err(1, NULL);
109 	imsg_init(ibuf, ctl_sock);
110 	done = 0;
111 
112 	/* Check for root-only actions */
113 	switch (res->action) {
114 	case LOG_DEBUG:
115 	case LOG_VERBOSE:
116 	case LOG_BRIEF:
117 	case RELOAD:
118 		if (geteuid() != 0)
119 			errx(1, "need root privileges");
120 		break;
121 	default:
122 		break;
123 	}
124 
125 	/* Process user request. */
126 	switch (res->action) {
127 	case LOG_DEBUG:
128 		verbose |= OPT_VERBOSE2;
129 		/* FALLTHROUGH */
130 	case LOG_VERBOSE:
131 		verbose |= OPT_VERBOSE;
132 		/* FALLTHROUGH */
133 	case LOG_BRIEF:
134 		imsg_compose(ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1,
135 		    &verbose, sizeof(verbose));
136 		printf("logging request sent.\n");
137 		done = 1;
138 		break;
139 	case RELOAD:
140 		imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0);
141 		printf("reload request sent.\n");
142 		done = 1;
143 		break;
144 	case PORTAL:
145 		imsg_compose(ibuf, IMSG_CTL_RECHECK_CAPTIVEPORTAL, 0, 0, -1,
146 		    NULL, 0);
147 		printf("recheck request sent.\n");
148 		done = 1;
149 		break;
150 	case STATUS_RECURSOR:
151 		type = RECURSOR;
152 		imsg_compose(ibuf, IMSG_CTL_STATUS, 0, 0, -1, &type,
153 		    sizeof(type));
154 		break;
155 	case STATUS_DHCP:
156 		type = FORWARDER;
157 		imsg_compose(ibuf, IMSG_CTL_STATUS, 0, 0, -1, &type,
158 		    sizeof(type));
159 		break;
160 	case STATUS_STATIC:
161 		type = STATIC_FORWARDER;
162 		imsg_compose(ibuf, IMSG_CTL_STATUS, 0, 0, -1, &type,
163 		    sizeof(type));
164 		break;
165 	case STATUS_DOT:
166 		type = STATIC_DOT_FORWARDER;
167 		imsg_compose(ibuf, IMSG_CTL_STATUS, 0, 0, -1, &type,
168 		    sizeof(type));
169 		break;
170 	case STATUS:
171 		type = RESOLVER_NONE;
172 		imsg_compose(ibuf, IMSG_CTL_STATUS, 0, 0, -1, &type,
173 		    sizeof(type));
174 		break;
175 	default:
176 		usage();
177 	}
178 
179 	while (ibuf->w.queued)
180 		if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
181 			err(1, "write error");
182 
183 	while (!done) {
184 		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
185 			errx(1, "imsg_read error");
186 		if (n == 0)
187 			errx(1, "pipe closed");
188 
189 		while (!done) {
190 			if ((n = imsg_get(ibuf, &imsg)) == -1)
191 				errx(1, "imsg_get error");
192 			if (n == 0)
193 				break;
194 
195 			switch (res->action) {
196 			case STATUS:
197 			case STATUS_RECURSOR:
198 			case STATUS_DHCP:
199 			case STATUS_STATIC:
200 			case STATUS_DOT:
201 				done = show_status_msg(&imsg);
202 				break;
203 			default:
204 				break;
205 			}
206 			imsg_free(&imsg);
207 		}
208 	}
209 	close(ctl_sock);
210 	free(ibuf);
211 
212 	return (0);
213 }
214 
215 int
216 show_status_msg(struct imsg *imsg)
217 {
218 	static int			 header;
219 	struct ctl_resolver_info	*cri;
220 	enum captive_portal_state	 captive_portal_state;
221 
222 	if (imsg->hdr.type != IMSG_CTL_CAPTIVEPORTAL_INFO && !header++)
223 		printf("%8s %16s %s\n", "selected", "type", "status");
224 
225 	switch (imsg->hdr.type) {
226 	case IMSG_CTL_CAPTIVEPORTAL_INFO:
227 		memcpy(&captive_portal_state, imsg->data,
228 		    sizeof(captive_portal_state));
229 		switch (captive_portal_state) {
230 		case PORTAL_UNCHECKED:
231 		case PORTAL_UNKNOWN:
232 			printf("captive portal is %s\n\n",
233 			    captive_portal_state_str[captive_portal_state]);
234 			break;
235 		case BEHIND:
236 		case NOT_BEHIND:
237 			printf("%s captive portal\n\n",
238 			    captive_portal_state_str[captive_portal_state]);
239 			break;
240 		}
241 		break;
242 	case IMSG_CTL_RESOLVER_INFO:
243 		cri = imsg->data;
244 		printf("%8s %16s %s\n", cri->selected ? "*" : " ",
245 		    unwind_resolver_type_str[cri->type],
246 		    unwind_resolver_state_str[cri->state]);
247 		break;
248 	case IMSG_CTL_RESOLVER_WHY_BOGUS:
249 		/* make sure this is a string */
250 		((char *)imsg->data)[imsg->hdr.len - IMSG_HEADER_SIZE -1] =
251 		    '\0';
252 		printf("\nReason for not validating:\n");
253 		print_indented_str(imsg->data);
254 		break;
255 	case IMSG_CTL_RESOLVER_HISTOGRAM:
256 		print_histogram(imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE);
257 		break;
258 	case IMSG_CTL_END:
259 		return (1);
260 	default:
261 		break;
262 	}
263 
264 	return (0);
265 }
266 
267 void
268 print_indented_str(char * str)
269 {
270 	int	 i;
271 	char	*cur;
272 
273 	if (strlen(str) <= 72) {
274 		printf("\t%s\n", str);
275 		return;
276 	}
277 
278 	for (i = 71; i >= 0; i--)
279 		if (str[i] == ' ')
280 			break;
281 
282 	if (i < 0)
283 		cur = strchr(str, ' ');
284 	else
285 		cur = &str[i];
286 
287 
288 	if (cur == NULL)
289 		printf("\t%s\n", str);
290 	else {
291 		*cur = '\0';
292 		printf("\t%s\n", str);
293 		print_indented_str(cur + 1);
294 	}
295 }
296 
297 void
298 print_histogram(void* data, size_t len)
299 {
300 	int64_t	 histogram[nitems(histogram_limits)];
301 	size_t	 i;
302 	char	 buf[10];
303 
304 	if (len != sizeof(histogram))
305 		errx(1, "invalid histogram size");
306 
307 	printf("\n%40s\n", "histogram[ms]");
308 
309 	memcpy(histogram, data, len);
310 
311 	for(i = 1; i < nitems(histogram_limits) - 1; i++) {
312 		snprintf(buf, sizeof(buf), "<%lld", histogram_limits[i]);
313 		printf("%6s", buf);
314 	}
315 	printf("%6s\n", ">");
316 	for(i = 1; i < nitems(histogram); i++)
317 		printf("%6lld", histogram[i]);
318 	printf("\n");
319 }
320