xref: /openbsd/usr.sbin/unwindctl/unwindctl.c (revision 28ba4729)
1*28ba4729Sflorian /*	$OpenBSD: unwindctl.c,v 1.11 2019/11/11 05:51:06 florian Exp $	*/
25c077b0fSflorian 
35c077b0fSflorian /*
45c077b0fSflorian  * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
55c077b0fSflorian  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
65c077b0fSflorian  * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
75c077b0fSflorian  *
85c077b0fSflorian  * Permission to use, copy, modify, and distribute this software for any
95c077b0fSflorian  * purpose with or without fee is hereby granted, provided that the above
105c077b0fSflorian  * copyright notice and this permission notice appear in all copies.
115c077b0fSflorian  *
125c077b0fSflorian  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
135c077b0fSflorian  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
145c077b0fSflorian  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
155c077b0fSflorian  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
165c077b0fSflorian  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
175c077b0fSflorian  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
185c077b0fSflorian  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
195c077b0fSflorian  */
205c077b0fSflorian 
215c077b0fSflorian #include <sys/types.h>
225c077b0fSflorian #include <sys/queue.h>
235c077b0fSflorian #include <sys/socket.h>
245c077b0fSflorian #include <sys/un.h>
255c077b0fSflorian #include <netinet/in.h>
265c077b0fSflorian #include <arpa/inet.h>
275c077b0fSflorian #include <net/if.h>
285c077b0fSflorian #include <net/if_media.h>
295c077b0fSflorian #include <net/if_types.h>
30*28ba4729Sflorian #include <net/route.h>
315c077b0fSflorian 
325c077b0fSflorian #include <err.h>
335c077b0fSflorian #include <errno.h>
345c077b0fSflorian #include <event.h>
355c077b0fSflorian #include <imsg.h>
365c077b0fSflorian #include <stdio.h>
375c077b0fSflorian #include <stdlib.h>
385c077b0fSflorian #include <string.h>
395c077b0fSflorian #include <unistd.h>
405c077b0fSflorian 
415c077b0fSflorian #include "unwind.h"
42b2501eadSflorian #include "captiveportal.h"
435c077b0fSflorian #include "frontend.h"
445c077b0fSflorian #include "resolver.h"
455c077b0fSflorian #include "parser.h"
465c077b0fSflorian 
475c077b0fSflorian __dead void	 usage(void);
485c077b0fSflorian int		 show_status_msg(struct imsg *);
495c077b0fSflorian void		 print_indented_str(char *);
505c077b0fSflorian void		 print_histogram(void*, size_t len);
515c077b0fSflorian 
525c077b0fSflorian struct imsgbuf	*ibuf;
535c077b0fSflorian 
545c077b0fSflorian __dead void
555c077b0fSflorian usage(void)
565c077b0fSflorian {
575c077b0fSflorian 	extern char *__progname;
585c077b0fSflorian 
595c077b0fSflorian 	fprintf(stderr, "usage: %s [-s socket] command [argument ...]\n",
605c077b0fSflorian 	    __progname);
615c077b0fSflorian 	exit(1);
625c077b0fSflorian }
635c077b0fSflorian 
645c077b0fSflorian int
655c077b0fSflorian main(int argc, char *argv[])
665c077b0fSflorian {
675c077b0fSflorian 	struct sockaddr_un	 sun;
685c077b0fSflorian 	struct parse_result	*res;
695c077b0fSflorian 	struct imsg		 imsg;
705c077b0fSflorian 	int			 ctl_sock;
715c077b0fSflorian 	int			 done = 0;
725c077b0fSflorian 	int			 n, verbose = 0;
735c077b0fSflorian 	int			 ch;
745c077b0fSflorian 	int			 type;
755c077b0fSflorian 	char			*sockname;
765c077b0fSflorian 
775c077b0fSflorian 	sockname = UNWIND_SOCKET;
785c077b0fSflorian 	while ((ch = getopt(argc, argv, "s:")) != -1) {
795c077b0fSflorian 		switch (ch) {
805c077b0fSflorian 		case 's':
815c077b0fSflorian 			sockname = optarg;
825c077b0fSflorian 			break;
835c077b0fSflorian 		default:
845c077b0fSflorian 			usage();
855c077b0fSflorian 		}
865c077b0fSflorian 	}
875c077b0fSflorian 	argc -= optind;
885c077b0fSflorian 	argv += optind;
895c077b0fSflorian 
905c077b0fSflorian 	/* Parse command line. */
915c077b0fSflorian 	if ((res = parse(argc, argv)) == NULL)
925c077b0fSflorian 		exit(1);
935c077b0fSflorian 
945c077b0fSflorian 	/* Connect to control socket. */
955c077b0fSflorian 	if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
965c077b0fSflorian 		err(1, "socket");
975c077b0fSflorian 
985c077b0fSflorian 	memset(&sun, 0, sizeof(sun));
995c077b0fSflorian 	sun.sun_family = AF_UNIX;
1005c077b0fSflorian 	strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path));
101cee51daaSflorian 
1025c077b0fSflorian 	if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
1035c077b0fSflorian 		err(1, "connect: %s", sockname);
1045c077b0fSflorian 
1055c077b0fSflorian 	if (pledge("stdio", NULL) == -1)
1065c077b0fSflorian 		err(1, "pledge");
1075c077b0fSflorian 
1085c077b0fSflorian 	if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
1095c077b0fSflorian 		err(1, NULL);
1105c077b0fSflorian 	imsg_init(ibuf, ctl_sock);
1115c077b0fSflorian 	done = 0;
1125c077b0fSflorian 
1133a1cc939Ssolene 	/* Check for root-only actions */
1143a1cc939Ssolene 	switch (res->action) {
1153a1cc939Ssolene 	case LOG_DEBUG:
1163a1cc939Ssolene 	case LOG_VERBOSE:
1173a1cc939Ssolene 	case LOG_BRIEF:
1183a1cc939Ssolene 	case RELOAD:
1193a1cc939Ssolene 		if (geteuid() != 0)
1203a1cc939Ssolene 			errx(1, "need root privileges");
1213a1cc939Ssolene 		break;
1223a1cc939Ssolene 	default:
1233a1cc939Ssolene 		break;
1243a1cc939Ssolene 	}
1253a1cc939Ssolene 
1265c077b0fSflorian 	/* Process user request. */
1275c077b0fSflorian 	switch (res->action) {
1285c077b0fSflorian 	case LOG_DEBUG:
1295c077b0fSflorian 		verbose |= OPT_VERBOSE2;
1305c077b0fSflorian 		/* FALLTHROUGH */
1315c077b0fSflorian 	case LOG_VERBOSE:
1325c077b0fSflorian 		verbose |= OPT_VERBOSE;
1335c077b0fSflorian 		/* FALLTHROUGH */
1345c077b0fSflorian 	case LOG_BRIEF:
1355c077b0fSflorian 		imsg_compose(ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1,
1365c077b0fSflorian 		    &verbose, sizeof(verbose));
1375c077b0fSflorian 		printf("logging request sent.\n");
1385c077b0fSflorian 		done = 1;
1395c077b0fSflorian 		break;
1405c077b0fSflorian 	case RELOAD:
1415c077b0fSflorian 		imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0);
1425c077b0fSflorian 		printf("reload request sent.\n");
1435c077b0fSflorian 		done = 1;
1445c077b0fSflorian 		break;
145b2501eadSflorian 	case PORTAL:
146b2501eadSflorian 		imsg_compose(ibuf, IMSG_CTL_RECHECK_CAPTIVEPORTAL, 0, 0, -1,
147b2501eadSflorian 		    NULL, 0);
148b2501eadSflorian 		printf("recheck request sent.\n");
149b2501eadSflorian 		done = 1;
150b2501eadSflorian 		break;
1515c077b0fSflorian 	case STATUS_RECURSOR:
152fd873f7fSflorian 		type = UW_RES_RECURSOR;
1535c077b0fSflorian 		imsg_compose(ibuf, IMSG_CTL_STATUS, 0, 0, -1, &type,
1545c077b0fSflorian 		    sizeof(type));
1555c077b0fSflorian 		break;
1565c077b0fSflorian 	case STATUS_DHCP:
157fd873f7fSflorian 		type = UW_RES_DHCP;
1585c077b0fSflorian 		imsg_compose(ibuf, IMSG_CTL_STATUS, 0, 0, -1, &type,
1595c077b0fSflorian 		    sizeof(type));
1605c077b0fSflorian 		break;
1615c077b0fSflorian 	case STATUS_STATIC:
162fd873f7fSflorian 		type = UW_RES_FORWARDER;
1635c077b0fSflorian 		imsg_compose(ibuf, IMSG_CTL_STATUS, 0, 0, -1, &type,
1645c077b0fSflorian 		    sizeof(type));
1655c077b0fSflorian 		break;
16646e4cbfcSflorian 	case STATUS_DOT:
167fd873f7fSflorian 		type = UW_RES_DOT;
16846e4cbfcSflorian 		imsg_compose(ibuf, IMSG_CTL_STATUS, 0, 0, -1, &type,
16946e4cbfcSflorian 		    sizeof(type));
17046e4cbfcSflorian 		break;
171217b4d33Sflorian 	case STATUS_STUB:
172530f5ecfSflorian 		type = UW_RES_ASR;
173530f5ecfSflorian 		imsg_compose(ibuf, IMSG_CTL_STATUS, 0, 0, -1, &type,
174530f5ecfSflorian 		    sizeof(type));
175530f5ecfSflorian 		break;
1765c077b0fSflorian 	case STATUS:
177fd873f7fSflorian 		type = UW_RES_NONE;
1785c077b0fSflorian 		imsg_compose(ibuf, IMSG_CTL_STATUS, 0, 0, -1, &type,
1795c077b0fSflorian 		    sizeof(type));
1805c077b0fSflorian 		break;
1815c077b0fSflorian 	default:
1825c077b0fSflorian 		usage();
1835c077b0fSflorian 	}
1845c077b0fSflorian 
1855c077b0fSflorian 	while (ibuf->w.queued)
1865c077b0fSflorian 		if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
1875c077b0fSflorian 			err(1, "write error");
1885c077b0fSflorian 
1895c077b0fSflorian 	while (!done) {
1905c077b0fSflorian 		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
1915c077b0fSflorian 			errx(1, "imsg_read error");
1925c077b0fSflorian 		if (n == 0)
1935c077b0fSflorian 			errx(1, "pipe closed");
1945c077b0fSflorian 
1955c077b0fSflorian 		while (!done) {
1965c077b0fSflorian 			if ((n = imsg_get(ibuf, &imsg)) == -1)
1975c077b0fSflorian 				errx(1, "imsg_get error");
1985c077b0fSflorian 			if (n == 0)
1995c077b0fSflorian 				break;
2005c077b0fSflorian 
2015c077b0fSflorian 			switch (res->action) {
2025c077b0fSflorian 			case STATUS:
2035c077b0fSflorian 			case STATUS_RECURSOR:
2045c077b0fSflorian 			case STATUS_DHCP:
2055c077b0fSflorian 			case STATUS_STATIC:
20646e4cbfcSflorian 			case STATUS_DOT:
207217b4d33Sflorian 			case STATUS_STUB:
2085c077b0fSflorian 				done = show_status_msg(&imsg);
2095c077b0fSflorian 				break;
2105c077b0fSflorian 			default:
2115c077b0fSflorian 				break;
2125c077b0fSflorian 			}
2135c077b0fSflorian 			imsg_free(&imsg);
2145c077b0fSflorian 		}
2155c077b0fSflorian 	}
2165c077b0fSflorian 	close(ctl_sock);
2175c077b0fSflorian 	free(ibuf);
2185c077b0fSflorian 
2195c077b0fSflorian 	return (0);
2205c077b0fSflorian }
2215c077b0fSflorian 
2225c077b0fSflorian int
2235c077b0fSflorian show_status_msg(struct imsg *imsg)
2245c077b0fSflorian {
2255c077b0fSflorian 	static int			 header;
2265c077b0fSflorian 	struct ctl_resolver_info	*cri;
227b2501eadSflorian 	enum captive_portal_state	 captive_portal_state;
2285c077b0fSflorian 
229b2501eadSflorian 	if (imsg->hdr.type != IMSG_CTL_CAPTIVEPORTAL_INFO && !header++)
2305c077b0fSflorian 		printf("%8s %16s %s\n", "selected", "type", "status");
2315c077b0fSflorian 
2325c077b0fSflorian 	switch (imsg->hdr.type) {
233b2501eadSflorian 	case IMSG_CTL_CAPTIVEPORTAL_INFO:
234b2501eadSflorian 		memcpy(&captive_portal_state, imsg->data,
235b2501eadSflorian 		    sizeof(captive_portal_state));
236b2501eadSflorian 		switch (captive_portal_state) {
237b2501eadSflorian 		case PORTAL_UNCHECKED:
238b2501eadSflorian 		case PORTAL_UNKNOWN:
239b2501eadSflorian 			printf("captive portal is %s\n\n",
240b2501eadSflorian 			    captive_portal_state_str[captive_portal_state]);
241b2501eadSflorian 			break;
242b2501eadSflorian 		case BEHIND:
243b2501eadSflorian 		case NOT_BEHIND:
244b2501eadSflorian 			printf("%s captive portal\n\n",
245b2501eadSflorian 			    captive_portal_state_str[captive_portal_state]);
246b2501eadSflorian 			break;
247b2501eadSflorian 		}
248b2501eadSflorian 		break;
2495c077b0fSflorian 	case IMSG_CTL_RESOLVER_INFO:
2505c077b0fSflorian 		cri = imsg->data;
2511d722b06Sotto 		printf("%8s %16s %s%s\n", cri->selected ? "*" : " ",
25215621e39Sflorian 		    uw_resolver_type_str[cri->type],
2531d722b06Sotto 		    uw_resolver_state_str[cri->state],
2541d722b06Sotto 		    cri->oppdot ? " (opportunistic DoT)" : "");
2555c077b0fSflorian 		break;
2565c077b0fSflorian 	case IMSG_CTL_RESOLVER_WHY_BOGUS:
2575c077b0fSflorian 		/* make sure this is a string */
2585c077b0fSflorian 		((char *)imsg->data)[imsg->hdr.len - IMSG_HEADER_SIZE -1] =
2595c077b0fSflorian 		    '\0';
2605c077b0fSflorian 		printf("\nReason for not validating:\n");
2615c077b0fSflorian 		print_indented_str(imsg->data);
2625c077b0fSflorian 		break;
2635c077b0fSflorian 	case IMSG_CTL_RESOLVER_HISTOGRAM:
2645c077b0fSflorian 		print_histogram(imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE);
2655c077b0fSflorian 		break;
2665c077b0fSflorian 	case IMSG_CTL_END:
2675c077b0fSflorian 		return (1);
2685c077b0fSflorian 	default:
2695c077b0fSflorian 		break;
2705c077b0fSflorian 	}
2715c077b0fSflorian 
2725c077b0fSflorian 	return (0);
2735c077b0fSflorian }
2745c077b0fSflorian 
2755c077b0fSflorian void
2765c077b0fSflorian print_indented_str(char * str)
2775c077b0fSflorian {
2785c077b0fSflorian 	int	 i;
2795c077b0fSflorian 	char	*cur;
2805c077b0fSflorian 
2815c077b0fSflorian 	if (strlen(str) <= 72) {
2825c077b0fSflorian 		printf("\t%s\n", str);
2835c077b0fSflorian 		return;
2845c077b0fSflorian 	}
2855c077b0fSflorian 
2865c077b0fSflorian 	for (i = 71; i >= 0; i--)
2875c077b0fSflorian 		if (str[i] == ' ')
2885c077b0fSflorian 			break;
2895c077b0fSflorian 
2905c077b0fSflorian 	if (i < 0)
2915c077b0fSflorian 		cur = strchr(str, ' ');
2925c077b0fSflorian 	else
2935c077b0fSflorian 		cur = &str[i];
2945c077b0fSflorian 
2955c077b0fSflorian 
2965c077b0fSflorian 	if (cur == NULL)
2975c077b0fSflorian 		printf("\t%s\n", str);
2985c077b0fSflorian 	else {
2995c077b0fSflorian 		*cur = '\0';
3005c077b0fSflorian 		printf("\t%s\n", str);
3015c077b0fSflorian 		print_indented_str(cur + 1);
3025c077b0fSflorian 	}
3035c077b0fSflorian }
3045c077b0fSflorian 
3055c077b0fSflorian void
3065c077b0fSflorian print_histogram(void* data, size_t len)
3075c077b0fSflorian {
3085c077b0fSflorian 	int64_t	 histogram[nitems(histogram_limits)];
3095c077b0fSflorian 	size_t	 i;
3105c077b0fSflorian 	char	 buf[10];
3115c077b0fSflorian 
3125c077b0fSflorian 	if (len != sizeof(histogram))
3135c077b0fSflorian 		errx(1, "invalid histogram size");
3145c077b0fSflorian 
3155c077b0fSflorian 	printf("\n%40s\n", "histogram[ms]");
3165c077b0fSflorian 
3175c077b0fSflorian 	memcpy(histogram, data, len);
3185c077b0fSflorian 
3195c077b0fSflorian 	for(i = 1; i < nitems(histogram_limits) - 1; i++) {
3205c077b0fSflorian 		snprintf(buf, sizeof(buf), "<%lld", histogram_limits[i]);
3215c077b0fSflorian 		printf("%6s", buf);
3225c077b0fSflorian 	}
3235c077b0fSflorian 	printf("%6s\n", ">");
3245c077b0fSflorian 	for(i = 1; i < nitems(histogram); i++)
3255c077b0fSflorian 		printf("%6lld", histogram[i]);
3265c077b0fSflorian 	printf("\n");
3275c077b0fSflorian }
328