1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" /* SunOS */ 28 29 #include <stdio.h> 30 #include <unistd.h> 31 #include <stropts.h> 32 #include <string.h> 33 #include <stdlib.h> 34 #include <fcntl.h> 35 #include <stdarg.h> 36 #include <setjmp.h> 37 #include <string.h> 38 #include <errno.h> 39 #include <sys/types.h> 40 #include <sys/time.h> 41 #include <signal.h> 42 #include <sys/mman.h> 43 #include <assert.h> 44 #include <sys/sysmacros.h> 45 46 #include <sys/socket.h> 47 #include <sys/pfmod.h> 48 #include <net/if.h> 49 #include <netinet/in_systm.h> 50 #include <netinet/in.h> 51 #include <netinet/if_ether.h> 52 #include <netdb.h> 53 54 #include "snoop.h" 55 56 static int snaplen; 57 char *device = NULL; 58 59 /* Global error recovery variables */ 60 sigjmp_buf jmp_env, ojmp_env; /* error recovery jmp buf */ 61 int snoop_nrecover; /* number of recoveries on curr pkt */ 62 int quitting; /* user termination flag */ 63 64 static struct snoop_handler *snoop_hp; /* global alarm handler head */ 65 static struct snoop_handler *snoop_tp; /* global alarm handler tail */ 66 static time_t snoop_nalarm; /* time of next alarm */ 67 68 /* protected interpreter output areas */ 69 #define MAXSUM 8 70 #define REDZONE 64 71 static char *sumline[MAXSUM]; 72 static char *detail_line; 73 static char *line; 74 static char *encap; 75 76 static int audio; 77 int maxcount; /* maximum no of packets to capture */ 78 int count; /* count of packets captured */ 79 static int sumcount; 80 int x_offset = -1; 81 int x_length = 0x7fffffff; 82 FILE *namefile; 83 int Pflg; 84 boolean_t qflg = B_FALSE; 85 boolean_t rflg = B_FALSE; 86 #ifdef DEBUG 87 boolean_t zflg = B_FALSE; /* debugging packet corrupt flag */ 88 #endif 89 struct Pf_ext_packetfilt pf; 90 91 static void usage(void); 92 void show_count(); 93 static void snoop_sigrecover(int sig, siginfo_t *info, void *p); 94 static char *protmalloc(size_t); 95 static void resetperm(void); 96 97 int 98 main(int argc, char **argv) 99 { 100 int c; 101 int filter = 0; 102 int flags = F_SUM; 103 struct Pf_ext_packetfilt *fp = NULL; 104 char *icapfile = NULL; 105 char *ocapfile = NULL; 106 int nflg = 0; 107 int Nflg = 0; 108 int Cflg = 0; 109 int first = 1; 110 int last = 0x7fffffff; 111 int ppa; 112 int use_kern_pf; 113 char *p, *p2; 114 char names[MAXPATHLEN + 1]; 115 char self[MAXHOSTNAMELEN + 1]; 116 char *argstr = NULL; 117 void (*proc)(); 118 char *audiodev; 119 int ret; 120 struct sigaction sigact; 121 stack_t sigstk; 122 char *output_area; 123 int nbytes; 124 125 names[0] = '\0'; 126 /* 127 * Global error recovery: Prepare for interpreter failures 128 * with corrupted packets or confused interpreters. 129 * Allocate protected output and stack areas, with generous 130 * red-zones. 131 */ 132 nbytes = (MAXSUM + 3) * (MAXLINE + REDZONE); 133 output_area = protmalloc(nbytes); 134 if (output_area == NULL) { 135 perror("Warning: mmap"); 136 exit(1); 137 } 138 139 /* Allocate protected output areas */ 140 for (ret = 0; ret < MAXSUM; ret++) { 141 sumline[ret] = (char *)output_area; 142 output_area += (MAXLINE + REDZONE); 143 } 144 detail_line = output_area; 145 output_area += MAXLINE + REDZONE; 146 line = output_area; 147 output_area += MAXLINE + REDZONE; 148 encap = output_area; 149 output_area += MAXLINE + REDZONE; 150 151 /* Initialize an alternate signal stack to increase robustness */ 152 if ((sigstk.ss_sp = (char *)malloc(SIGSTKSZ+REDZONE)) == NULL) { 153 perror("Warning: malloc"); 154 exit(1); 155 } 156 sigstk.ss_size = SIGSTKSZ; 157 sigstk.ss_flags = 0; 158 if (sigaltstack(&sigstk, (stack_t *)NULL) < 0) { 159 perror("Warning: sigaltstack"); 160 exit(1); 161 } 162 163 /* Initialize a master signal handler */ 164 sigact.sa_handler = NULL; 165 sigact.sa_sigaction = snoop_sigrecover; 166 (void) sigemptyset(&sigact.sa_mask); 167 sigact.sa_flags = SA_ONSTACK|SA_SIGINFO; 168 169 /* Register master signal handler */ 170 if (sigaction(SIGHUP, &sigact, (struct sigaction *)NULL) < 0) { 171 perror("Warning: sigaction"); 172 exit(1); 173 } 174 if (sigaction(SIGINT, &sigact, (struct sigaction *)NULL) < 0) { 175 perror("Warning: sigaction"); 176 exit(1); 177 } 178 if (sigaction(SIGQUIT, &sigact, (struct sigaction *)NULL) < 0) { 179 perror("Warning: sigaction"); 180 exit(1); 181 } 182 if (sigaction(SIGILL, &sigact, (struct sigaction *)NULL) < 0) { 183 perror("Warning: sigaction"); 184 exit(1); 185 } 186 if (sigaction(SIGTRAP, &sigact, (struct sigaction *)NULL) < 0) { 187 perror("Warning: sigaction"); 188 exit(1); 189 } 190 if (sigaction(SIGIOT, &sigact, (struct sigaction *)NULL) < 0) { 191 perror("Warning: sigaction"); 192 exit(1); 193 } 194 if (sigaction(SIGEMT, &sigact, (struct sigaction *)NULL) < 0) { 195 perror("Warning: sigaction"); 196 exit(1); 197 } 198 if (sigaction(SIGFPE, &sigact, (struct sigaction *)NULL) < 0) { 199 perror("Warning: sigaction"); 200 exit(1); 201 } 202 if (sigaction(SIGBUS, &sigact, (struct sigaction *)NULL) < 0) { 203 perror("Warning: sigaction"); 204 exit(1); 205 } 206 if (sigaction(SIGSEGV, &sigact, (struct sigaction *)NULL) < 0) { 207 perror("Warning: sigaction"); 208 exit(1); 209 } 210 if (sigaction(SIGSYS, &sigact, (struct sigaction *)NULL) < 0) { 211 perror("Warning: sigaction"); 212 exit(1); 213 } 214 if (sigaction(SIGALRM, &sigact, (struct sigaction *)NULL) < 0) { 215 perror("Warning: sigaction"); 216 exit(1); 217 } 218 if (sigaction(SIGTERM, &sigact, (struct sigaction *)NULL) < 0) { 219 perror("Warning: sigaction"); 220 exit(1); 221 } 222 223 /* Prepare for failure during program initialization/exit */ 224 if (sigsetjmp(jmp_env, 1)) { 225 exit(1); 226 } 227 (void) setvbuf(stdout, NULL, _IOLBF, BUFSIZ); 228 229 while ((c = getopt(argc, argv, "at:CPDSi:o:Nn:s:d:vVp:f:c:x:?rqz")) 230 != EOF) { 231 switch (c) { 232 case 'a': 233 audiodev = getenv("AUDIODEV"); 234 if (audiodev == NULL) 235 audiodev = "/dev/audio"; 236 audio = open(audiodev, O_WRONLY); 237 if (audio < 0) { 238 pr_err("Audio device %s: %m", 239 audiodev); 240 exit(1); 241 } 242 break; 243 case 't': 244 flags |= F_TIME; 245 switch (*optarg) { 246 case 'r': flags |= F_RTIME; break; 247 case 'a': flags |= F_ATIME; break; 248 case 'd': break; 249 default: usage(); 250 } 251 break; 252 case 'P': 253 Pflg++; 254 break; 255 case 'D': 256 flags |= F_DROPS; 257 break; 258 case 'S': 259 flags |= F_LEN; 260 break; 261 case 'i': 262 icapfile = optarg; 263 break; 264 case 'o': 265 ocapfile = optarg; 266 break; 267 case 'N': 268 Nflg++; 269 break; 270 case 'n': 271 nflg++; 272 (void) strlcpy(names, optarg, MAXPATHLEN); 273 break; 274 case 's': 275 snaplen = atoi(optarg); 276 break; 277 case 'd': 278 device = optarg; 279 break; 280 case 'v': 281 flags &= ~(F_SUM); 282 flags |= F_DTAIL; 283 break; 284 case 'V': 285 flags |= F_ALLSUM; 286 break; 287 case 'p': 288 p = optarg; 289 p2 = strpbrk(p, ",:-"); 290 if (p2 == NULL) { 291 first = last = atoi(p); 292 } else { 293 *p2++ = '\0'; 294 first = atoi(p); 295 last = atoi(p2); 296 } 297 break; 298 case 'f': 299 (void) gethostname(self, MAXHOSTNAMELEN); 300 p = strchr(optarg, ':'); 301 if (p) { 302 *p = '\0'; 303 if (strcmp(optarg, self) == 0 || 304 strcmp(p+1, self) == 0) 305 (void) fprintf(stderr, 306 "Warning: cannot capture packets from %s\n", 307 self); 308 *p = ' '; 309 } else if (strcmp(optarg, self) == 0) 310 (void) fprintf(stderr, 311 "Warning: cannot capture packets from %s\n", 312 self); 313 argstr = optarg; 314 break; 315 case 'x': 316 p = optarg; 317 p2 = strpbrk(p, ",:-"); 318 if (p2 == NULL) { 319 x_offset = atoi(p); 320 x_length = -1; 321 } else { 322 *p2++ = '\0'; 323 x_offset = atoi(p); 324 x_length = atoi(p2); 325 } 326 break; 327 case 'c': 328 maxcount = atoi(optarg); 329 break; 330 case 'C': 331 Cflg++; 332 break; 333 case 'q': 334 qflg = B_TRUE; 335 break; 336 case 'r': 337 rflg = B_TRUE; 338 break; 339 #ifdef DEBUG 340 case 'z': 341 zflg = B_TRUE; 342 break; 343 #endif /* DEBUG */ 344 case '?': 345 default: 346 usage(); 347 } 348 } 349 350 if (argc > optind) 351 argstr = (char *)concat_args(&argv[optind], argc - optind); 352 353 /* 354 * Need to know before we decide on filtering method some things 355 * about the interface. So, go ahead and do part of the initialization 356 * now so we have that data. Note that if no device is specified, 357 * check_device selects one and returns it. In an ideal world, 358 * it might be nice if the "correct" interface for the filter 359 * requested was chosen, but that's too hard. 360 */ 361 if (!icapfile) { 362 use_kern_pf = check_device(&device, &ppa); 363 } else { 364 cap_open_read(icapfile); 365 366 if (!nflg) { 367 names[0] = '\0'; 368 (void) strlcpy(names, icapfile, MAXPATHLEN); 369 (void) strlcat(names, ".names", MAXPATHLEN); 370 } 371 } 372 373 /* attempt to read .names file if it exists before filtering */ 374 if ((!Nflg) && names[0] != '\0') { 375 if (access(names, F_OK) == 0) { 376 load_names(names); 377 } else if (nflg) { 378 (void) fprintf(stderr, "%s not found\n", names); 379 exit(1); 380 } 381 } 382 383 if (argstr) { 384 if (!icapfile && use_kern_pf) { 385 ret = pf_compile(argstr, Cflg); 386 switch (ret) { 387 case 0: 388 filter++; 389 compile(argstr, Cflg); 390 break; 391 case 1: 392 fp = &pf; 393 break; 394 case 2: 395 fp = &pf; 396 filter++; 397 break; 398 } 399 } else { 400 filter++; 401 compile(argstr, Cflg); 402 } 403 404 if (Cflg) 405 exit(0); 406 } 407 408 if (flags & F_SUM) 409 flags |= F_WHO; 410 411 /* 412 * If the -o flag is set then capture packets 413 * directly to a file. Don't attempt to 414 * interpret them on the fly (F_NOW). 415 * Note: capture to file is much less likely 416 * to drop packets since we don't spend cpu 417 * cycles running through the interpreters 418 * and possibly hanging in address-to-name 419 * mappings through the name service. 420 */ 421 if (ocapfile) { 422 cap_open_write(ocapfile); 423 proc = cap_write; 424 } else { 425 flags |= F_NOW; 426 proc = process_pkt; 427 } 428 429 430 /* 431 * If the -i flag is set then get packets from 432 * the log file which has been previously captured 433 * with the -o option. 434 */ 435 if (icapfile) { 436 names[0] = '\0'; 437 (void) strlcpy(names, icapfile, MAXPATHLEN); 438 (void) strlcat(names, ".names", MAXPATHLEN); 439 440 if (Nflg) { 441 namefile = fopen(names, "w"); 442 if (namefile == NULL) { 443 perror(names); 444 exit(1); 445 } 446 flags = 0; 447 (void) fprintf(stderr, 448 "Creating name file %s\n", names); 449 } 450 451 if (flags & F_DTAIL) 452 flags = F_DTAIL; 453 else 454 flags |= F_NUM | F_TIME; 455 456 resetperm(); 457 cap_read(first, last, filter, proc, flags); 458 459 if (Nflg) 460 (void) fclose(namefile); 461 462 } else { 463 const int chunksize = 8 * 8192; 464 struct timeval timeout; 465 466 /* 467 * If listening to packets on audio 468 * then set the buffer timeout down 469 * to 1/10 sec. A higher value 470 * makes the audio "bursty". 471 */ 472 if (audio) { 473 timeout.tv_sec = 0; 474 timeout.tv_usec = 100000; 475 } else { 476 timeout.tv_sec = 1; 477 timeout.tv_usec = 0; 478 } 479 480 initdevice(device, snaplen, chunksize, &timeout, fp, ppa); 481 if (! qflg && ocapfile) 482 show_count(); 483 resetperm(); 484 net_read(chunksize, filter, proc, flags); 485 486 if (!(flags & F_NOW)) 487 (void) printf("\n"); 488 } 489 490 if (ocapfile) 491 cap_close(); 492 493 return (0); 494 } 495 496 static int tone[] = { 497 0x076113, 0x153333, 0x147317, 0x144311, 0x147315, 0x050353, 0x037103, 0x051106, 498 0x157155, 0x142723, 0x133273, 0x134664, 0x051712, 0x024465, 0x026447, 0x072473, 499 0x136715, 0x126257, 0x135256, 0x047344, 0x034476, 0x027464, 0x036062, 0x133334, 500 0x127256, 0x130660, 0x136262, 0x040724, 0x016446, 0x025437, 0x137171, 0x127672, 501 0x124655, 0x134654, 0x032741, 0x021447, 0x037450, 0x125675, 0x127650, 0x077277, 502 0x046514, 0x036077, 0x035471, 0x147131, 0x136272, 0x162720, 0x166151, 0x037527, 503 }; 504 505 /* 506 * Make a sound on /dev/audio according to the length of the packet. The 507 * tone data was ripped from /usr/share/audio/samples/au/bark.au. The 508 * amount of waveform used is a function of packet length e.g. a series 509 * of small packets is heard as clicks, whereas a series of NFS packets in 510 * an 8k read sounds like a "WHAAAARP". 511 */ 512 void 513 click(len) 514 int len; 515 { 516 len /= 8; 517 len = len ? len : 4; 518 519 if (audio) { 520 (void) write(audio, tone, len); 521 } 522 } 523 524 /* Display a count of packets */ 525 void 526 show_count() 527 { 528 static int prev = -1; 529 530 if (count == prev) 531 return; 532 533 prev = count; 534 (void) fprintf(stderr, "\r%d ", count); 535 } 536 537 #define ENCAP_LEN 16 /* Hold "(NN encap)" */ 538 539 /* 540 * Display data that's external to the packet. 541 * This constitutes the first half of the summary 542 * line display. 543 */ 544 void 545 show_pktinfo(flags, num, src, dst, ptvp, tvp, drops, len) 546 int flags, num, drops, len; 547 char *src, *dst; 548 struct timeval *ptvp, *tvp; 549 { 550 struct tm *tm; 551 static struct timeval tvp0; 552 int sec, usec; 553 char *lp = line; 554 int i, start; 555 556 if (flags & F_NUM) { 557 (void) sprintf(lp, "%3d ", num); 558 lp += strlen(lp); 559 } 560 tm = localtime(&tvp->tv_sec); 561 562 if (flags & F_TIME) { 563 if (flags & F_ATIME) { 564 (void) sprintf(lp, "%d:%02d:%d.%05d ", 565 tm->tm_hour, tm->tm_min, tm->tm_sec, 566 (int)tvp->tv_usec / 10); 567 lp += strlen(lp); 568 } else { 569 if (flags & F_RTIME) { 570 if (tvp0.tv_sec == 0) { 571 tvp0.tv_sec = tvp->tv_sec; 572 tvp0.tv_usec = tvp->tv_usec; 573 } 574 ptvp = &tvp0; 575 } 576 sec = tvp->tv_sec - ptvp->tv_sec; 577 usec = tvp->tv_usec - ptvp->tv_usec; 578 if (usec < 0) { 579 usec += 1000000; 580 sec -= 1; 581 } 582 (void) sprintf(lp, "%3d.%05d ", sec, usec / 10); 583 lp += strlen(lp); 584 } 585 } 586 587 if (flags & F_WHO) { 588 (void) sprintf(lp, "%12s -> %-12s ", src, dst); 589 lp += strlen(lp); 590 } 591 592 if (flags & F_DROPS) { 593 (void) sprintf(lp, "drops: %d ", drops); 594 lp += strlen(lp); 595 } 596 597 if (flags & F_LEN) { 598 (void) sprintf(lp, "length: %4d ", len); 599 lp += strlen(lp); 600 } 601 602 if (flags & F_SUM) { 603 if (flags & F_ALLSUM) 604 (void) printf("________________________________\n"); 605 606 start = flags & F_ALLSUM ? 0 : sumcount - 1; 607 (void) sprintf(encap, " (%d encap)", total_encap_levels - 1); 608 (void) printf("%s%s%s\n", line, sumline[start], 609 ((flags & F_ALLSUM) || (total_encap_levels == 1)) ? "" : 610 encap); 611 612 for (i = start + 1; i < sumcount; i++) 613 (void) printf("%s%s\n", line, sumline[i]); 614 615 sumcount = 0; 616 } 617 618 if (flags & F_DTAIL) { 619 (void) printf("%s\n\n", detail_line); 620 detail_line[0] = '\0'; 621 } 622 } 623 624 /* 625 * The following two routines are called back 626 * from the interpreters to display their stuff. 627 * The theory is that when snoop becomes a window 628 * based tool we can just supply a new version of 629 * get_sum_line and get_detail_line and not have 630 * to touch the interpreters at all. 631 */ 632 char * 633 get_sum_line() 634 { 635 int tsumcount = sumcount; 636 637 if (sumcount >= MAXSUM) { 638 sumcount = 0; /* error recovery */ 639 pr_err( 640 "get_sum_line: sumline overflow (sumcount=%d, MAXSUM=%d)\n", 641 tsumcount, MAXSUM); 642 } 643 644 sumline[sumcount][0] = '\0'; 645 return (sumline[sumcount++]); 646 } 647 648 /*ARGSUSED*/ 649 char * 650 get_detail_line(off, len) 651 int off, len; 652 { 653 if (detail_line[0]) { 654 (void) printf("%s\n", detail_line); 655 detail_line[0] = '\0'; 656 } 657 return (detail_line); 658 } 659 660 /* 661 * Print an error. 662 * Works like printf (fmt string and variable args) 663 * except that it will substitute an error message 664 * for a "%m" string (like syslog) and it calls 665 * long_jump - it doesn't return to where it was 666 * called from - it goes to the last setjmp(). 667 */ 668 /* VARARGS1 */ 669 void 670 pr_err(const char *fmt, ...) 671 { 672 va_list ap; 673 char buf[1024], *p2; 674 const char *p1; 675 676 (void) strcpy(buf, "snoop: "); 677 p2 = buf + strlen(buf); 678 679 /* 680 * Note that we terminate the buffer with '\n' and '\0'. 681 */ 682 for (p1 = fmt; *p1 != '\0' && p2 < buf + sizeof (buf) - 2; p1++) { 683 if (*p1 == '%' && *(p1+1) == 'm') { 684 const char *errstr; 685 686 if ((errstr = strerror(errno)) != NULL) { 687 *p2 = '\0'; 688 (void) strlcat(buf, errstr, sizeof (buf)); 689 p2 += strlen(p2); 690 } 691 p1++; 692 } else { 693 *p2++ = *p1; 694 } 695 } 696 if (p2 > buf && *(p2-1) != '\n') 697 *p2++ = '\n'; 698 *p2 = '\0'; 699 700 va_start(ap, fmt); 701 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 702 (void) vfprintf(stderr, buf, ap); 703 va_end(ap); 704 snoop_sigrecover(-1, NULL, NULL); /* global error recovery */ 705 } 706 707 /* 708 * Ye olde usage proc 709 * PLEASE keep this up to date! 710 * Naive users *love* this stuff. 711 */ 712 static void 713 usage(void) 714 { 715 (void) fprintf(stderr, "\nUsage: snoop\n"); 716 (void) fprintf(stderr, 717 "\t[ -a ] # Listen to packets on audio\n"); 718 (void) fprintf(stderr, 719 "\t[ -d device ] # Listen on interface named device\n"); 720 (void) fprintf(stderr, 721 "\t[ -s snaplen ] # Truncate packets\n"); 722 (void) fprintf(stderr, 723 "\t[ -c count ] # Quit after count packets\n"); 724 (void) fprintf(stderr, 725 "\t[ -P ] # Turn OFF promiscuous mode\n"); 726 (void) fprintf(stderr, 727 "\t[ -D ] # Report dropped packets\n"); 728 (void) fprintf(stderr, 729 "\t[ -S ] # Report packet size\n"); 730 (void) fprintf(stderr, 731 "\t[ -i file ] # Read previously captured packets\n"); 732 (void) fprintf(stderr, 733 "\t[ -o file ] # Capture packets in file\n"); 734 (void) fprintf(stderr, 735 "\t[ -n file ] # Load addr-to-name table from file\n"); 736 (void) fprintf(stderr, 737 "\t[ -N ] # Create addr-to-name table\n"); 738 (void) fprintf(stderr, 739 "\t[ -t r|a|d ] # Time: Relative, Absolute or Delta\n"); 740 (void) fprintf(stderr, 741 "\t[ -v ] # Verbose packet display\n"); 742 (void) fprintf(stderr, 743 "\t[ -V ] # Show all summary lines\n"); 744 (void) fprintf(stderr, 745 "\t[ -p first[,last] ] # Select packet(s) to display\n"); 746 (void) fprintf(stderr, 747 "\t[ -x offset[,length] ] # Hex dump from offset for length\n"); 748 (void) fprintf(stderr, 749 "\t[ -C ] # Print packet filter code\n"); 750 (void) fprintf(stderr, 751 "\t[ -q ] # Suppress printing packet count\n"); 752 (void) fprintf(stderr, 753 "\t[ -r ] # Do not resolve address to name\n"); 754 (void) fprintf(stderr, 755 "\n\t[ filter expression ]\n"); 756 (void) fprintf(stderr, "\nExample:\n"); 757 (void) fprintf(stderr, "\tsnoop -o saved host fred\n\n"); 758 (void) fprintf(stderr, "\tsnoop -i saved -tr -v -p19\n"); 759 exit(1); 760 } 761 762 /* 763 * sdefault: default global alarm handler. Causes the current packet 764 * to be skipped. 765 */ 766 static void 767 sdefault(void) 768 { 769 snoop_nrecover = SNOOP_MAXRECOVER; 770 } 771 772 /* 773 * snoop_alarm: register or unregister an alarm handler to be called after 774 * s_sec seconds. Because snoop wasn't written to tolerate random signal 775 * delivery, periodic SIGALRM delivery (or SA_RESTART) cannot be used. 776 * 777 * s_sec argument of 0 seconds unregisters the handler. 778 * s_handler argument of NULL registers default handler sdefault(), or 779 * unregisters all signal handlers (for error recovery). 780 * 781 * Variables must be volatile to force the compiler to not optimize 782 * out the signal blocking. 783 */ 784 /*ARGSUSED*/ 785 int 786 snoop_alarm(int s_sec, void (*s_handler)()) 787 { 788 volatile time_t now; 789 volatile time_t nalarm = 0; 790 volatile struct snoop_handler *sh = NULL; 791 volatile struct snoop_handler *hp, *tp, *next; 792 volatile sigset_t s_mask; 793 volatile int ret = -1; 794 795 (void) sigemptyset((sigset_t *)&s_mask); 796 (void) sigaddset((sigset_t *)&s_mask, SIGALRM); 797 if (s_sec < 0) 798 return (-1); 799 800 /* register an alarm handler */ 801 now = time(NULL); 802 if (s_sec) { 803 sh = malloc(sizeof (struct snoop_handler)); 804 sh->s_time = now + s_sec; 805 if (s_handler == NULL) 806 s_handler = sdefault; 807 sh->s_handler = s_handler; 808 sh->s_next = NULL; 809 (void) sigprocmask(SIG_BLOCK, (sigset_t *)&s_mask, NULL); 810 if (snoop_hp == NULL) { 811 snoop_hp = snoop_tp = (struct snoop_handler *)sh; 812 813 snoop_nalarm = sh->s_time; 814 (void) alarm(sh->s_time - now); 815 } else { 816 snoop_tp->s_next = (struct snoop_handler *)sh; 817 snoop_tp = (struct snoop_handler *)sh; 818 819 if (sh->s_time < snoop_nalarm) { 820 snoop_nalarm = sh->s_time; 821 (void) alarm(sh->s_time - now); 822 } 823 } 824 (void) sigprocmask(SIG_UNBLOCK, (sigset_t *)&s_mask, NULL); 825 826 return (0); 827 } 828 829 /* unregister an alarm handler */ 830 (void) sigprocmask(SIG_BLOCK, (sigset_t *)&s_mask, NULL); 831 tp = (struct snoop_handler *)&snoop_hp; 832 for (hp = snoop_hp; hp; hp = next) { 833 next = hp->s_next; 834 if (s_handler == NULL || hp->s_handler == s_handler) { 835 ret = 0; 836 tp->s_next = hp->s_next; 837 if (snoop_tp == hp) { 838 if (tp == (struct snoop_handler *)&snoop_hp) 839 snoop_tp = NULL; 840 else 841 snoop_tp = (struct snoop_handler *)tp; 842 } 843 free((void *)hp); 844 } else { 845 if (nalarm == 0 || nalarm > hp->s_time) 846 nalarm = now < hp->s_time ? hp->s_time : 847 now + 1; 848 tp = hp; 849 } 850 } 851 /* 852 * Stop or adjust timer 853 */ 854 if (snoop_hp == NULL) { 855 snoop_nalarm = 0; 856 (void) alarm(0); 857 } else if (nalarm > 0 && nalarm < snoop_nalarm) { 858 snoop_nalarm = nalarm; 859 (void) alarm(nalarm - now); 860 } 861 862 (void) sigprocmask(SIG_UNBLOCK, (sigset_t *)&s_mask, NULL); 863 return (ret); 864 } 865 866 /* 867 * snoop_recover: reset snoop's output area, and any internal variables, 868 * to allow continuation. 869 * XXX: make this an interface such that each interpreter can 870 * register a reset routine. 871 */ 872 void 873 snoop_recover(void) 874 { 875 int i; 876 877 /* Error recovery: reset output_area and associated variables */ 878 for (i = 0; i < MAXSUM; i++) 879 sumline[i][0] = '\0'; 880 detail_line[0] = '\0'; 881 line[0] = '\0'; 882 encap[0] = '\0'; 883 sumcount = 0; 884 885 /* stacking/unstacking cannot be relied upon */ 886 encap_levels = 0; 887 total_encap_levels = 1; 888 889 /* remove any pending timeouts */ 890 (void) snoop_alarm(0, NULL); 891 } 892 893 /* 894 * snoop_sigrecover: global sigaction routine to manage recovery 895 * from catastrophic interpreter failures while interpreting 896 * corrupt trace files/packets. SIGALRM timeouts, program errors, 897 * and user termination are all handled. In the case of a corrupt 898 * packet or confused interpreter, the packet will be skipped, and 899 * execution will continue in scan(). 900 * 901 * Global alarm handling (see snoop_alarm()) is managed here. 902 * 903 * Variables must be volatile to force the compiler to not optimize 904 * out the signal blocking. 905 */ 906 /*ARGSUSED*/ 907 static void 908 snoop_sigrecover(int sig, siginfo_t *info, void *p) 909 { 910 volatile time_t now; 911 volatile time_t nalarm = 0; 912 volatile struct snoop_handler *hp; 913 914 /* 915 * Invoke any registered alarms. This involves first calculating 916 * the time for the next alarm, setting it up, then progressing 917 * through handler invocations. Note that since handlers may 918 * use siglongjmp(), in the worst case handlers may be serviced 919 * at a later time. 920 */ 921 if (sig == SIGALRM) { 922 now = time(NULL); 923 /* Calculate next alarm time */ 924 for (hp = snoop_hp; hp; hp = hp->s_next) { 925 if (hp->s_time) { 926 if ((hp->s_time - now) > 0) { 927 if (nalarm == 0 || nalarm > hp->s_time) 928 nalarm = now < hp->s_time ? 929 hp->s_time : now + 1; 930 } 931 } 932 } 933 /* Setup next alarm */ 934 if (nalarm) { 935 snoop_nalarm = nalarm; 936 (void) alarm(nalarm - now); 937 } else { 938 snoop_nalarm = 0; 939 } 940 941 /* Invoke alarm handlers (may not return) */ 942 for (hp = snoop_hp; hp; hp = hp->s_next) { 943 if (hp->s_time) { 944 if ((now - hp->s_time) >= 0) { 945 hp->s_time = 0; /* only invoke once */ 946 if (hp->s_handler) 947 hp->s_handler(); 948 } 949 } 950 } 951 } else { 952 snoop_nrecover++; 953 } 954 955 /* 956 * Exit if a signal has occurred after snoop has begun the process 957 * of quitting. 958 */ 959 if (quitting) 960 exit(1); 961 962 /* 963 * If an alarm handler has timed out, and snoop_nrecover has 964 * reached SNOOP_MAXRECOVER, skip to the next packet. 965 * 966 * If any other signal has occurred, and snoop_nrecover has 967 * reached SNOOP_MAXRECOVER, give up. 968 */ 969 if (sig == SIGALRM) { 970 if (ioctl(STDOUT_FILENO, I_CANPUT, 0) == 0) { 971 /* 972 * We've stalled on output, which is not a critical 973 * failure. Reset the recovery counter so we do not 974 * consider this a persistent failure, and return so 975 * we do not skip this packet. 976 */ 977 snoop_nrecover = 0; 978 return; 979 } 980 if (snoop_nrecover >= SNOOP_MAXRECOVER) { 981 (void) fprintf(stderr, 982 "snoop: WARNING: skipping from packet %d\n", 983 count); 984 snoop_nrecover = 0; 985 } else { 986 /* continue trying */ 987 return; 988 } 989 } else if (snoop_nrecover >= SNOOP_MAXRECOVER) { 990 (void) fprintf(stderr, 991 "snoop: ERROR: cannot recover from packet %d\n", count); 992 exit(1); 993 } 994 995 #ifdef DEBUG 996 (void) fprintf(stderr, "snoop_sigrecover(%d, %p, %p)\n", sig, info, p); 997 #endif /* DEBUG */ 998 999 /* 1000 * Prepare to quit. This allows final processing to occur 1001 * after first terminal interruption. 1002 */ 1003 if (sig == SIGTERM || sig == SIGHUP || sig == SIGINT) { 1004 quitting = 1; 1005 return; 1006 } else if (sig != -1 && sig != SIGALRM) { 1007 /* Inform user that snoop has taken a fault */ 1008 (void) fprintf(stderr, 1009 "WARNING: received signal %d from packet %d\n", 1010 sig, count); 1011 } 1012 1013 /* Reset interpreter variables */ 1014 snoop_recover(); 1015 1016 /* Continue in scan() with the next packet */ 1017 siglongjmp(jmp_env, 1); 1018 /*NOTREACHED*/ 1019 } 1020 1021 /* 1022 * Protected malloc for global error recovery: prepare for interpreter 1023 * failures with corrupted packets or confused interpreters. Dynamically 1024 * allocate `nbytes' bytes, and sandwich it between two PROT_NONE pages to 1025 * catch writes outside of the allocated region. 1026 */ 1027 static char * 1028 protmalloc(size_t nbytes) 1029 { 1030 caddr_t start; 1031 int psz = sysconf(_SC_PAGESIZE); 1032 1033 nbytes = P2ROUNDUP(nbytes, psz); 1034 start = mmap(NULL, nbytes + psz * 2, PROT_READ|PROT_WRITE, 1035 MAP_PRIVATE|MAP_ANON, -1, 0); 1036 if (start == MAP_FAILED) { 1037 perror("Error: protmalloc: mmap"); 1038 return (NULL); 1039 } 1040 assert(IS_P2ALIGNED(start, psz)); 1041 if (mprotect(start, 1, PROT_NONE) == -1) 1042 perror("Warning: mprotect"); 1043 1044 start += psz; 1045 if (mprotect(start + nbytes, 1, PROT_NONE) == -1) 1046 perror("Warning: mprotect"); 1047 1048 return (start); 1049 } 1050 1051 /* 1052 * resetperm - reduce security vulnerabilities by resetting 1053 * owner/group/permissions. Always attempt setuid() - if we have 1054 * permission to drop our privilege level, do so. 1055 */ 1056 void 1057 resetperm(void) 1058 { 1059 if (geteuid() == 0) { 1060 (void) setgid(GID_NOBODY); 1061 (void) setuid(UID_NOBODY); 1062 } 1063 } 1064