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