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