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 2005 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 0x034057, 0x026074, 0x136710, 0x126660, 0x147551, 0x034460, 505 0x026775, 0x141727, 0x127670, 0x156532, 0x036064, 0x030721, 506 0x134703, 0x126705, 0x046071, 0x030073, 0x036667, 0x140666, 507 0x126137, 0x064463, 0x031064, 0x072677, 0x135652, 0x141734, 508 0x036463, 0x027472, 0x137333, 0x127257, 0x152534, 0x033065, 509 0x027723, 0x136313, 0x127735, 0x053473, 0x035470, 0x052666, 510 0x167260, 0x140535, 0x045471, 0x034474, 0x132711, 0x132266, 511 0x047127, 0x027077, 0x043705, 0x141676, 0x134110, 0x063400, 512 }; 513 514 /* 515 * Make a sound on /dev/audio according 516 * to the length of the packet. The tone 517 * data above is a piece of waveform from 518 * a Pink Floyd track. The amount of waveform 519 * used is a function of packet length e.g. 520 * a series of small packets is heard as 521 * clicks, whereas a series of NFS packets 522 * in an 8k read sounds like a "WHAAAARP". 523 */ 524 void 525 click(len) 526 int len; 527 { 528 len /= 8; 529 len = len ? len : 4; 530 531 if (audio) { 532 write(audio, tone, len); 533 } 534 } 535 536 /* Display a count of packets */ 537 void 538 show_count() 539 { 540 static int prev = -1; 541 542 if (count == prev) 543 return; 544 545 prev = count; 546 (void) fprintf(stderr, "\r%d ", count); 547 } 548 549 #define ENCAP_LEN 16 /* Hold "(NN encap)" */ 550 551 /* 552 * Display data that's external to the packet. 553 * This constitutes the first half of the summary 554 * line display. 555 */ 556 void 557 show_pktinfo(flags, num, src, dst, ptvp, tvp, drops, len) 558 int flags, num, drops, len; 559 char *src, *dst; 560 struct timeval *ptvp, *tvp; 561 { 562 struct tm *tm; 563 static struct timeval tvp0; 564 int sec, usec; 565 char *lp = line; 566 int i, start; 567 568 if (flags & F_NUM) { 569 sprintf(lp, "%3d ", num); 570 lp += strlen(lp); 571 } 572 tm = localtime(&tvp->tv_sec); 573 574 if (flags & F_TIME) { 575 if (flags & F_ATIME) { 576 sprintf(lp, "%d:%02d:%d.%05d ", 577 tm->tm_hour, tm->tm_min, tm->tm_sec, 578 tvp->tv_usec / 10); 579 lp += strlen(lp); 580 } else { 581 if (flags & F_RTIME) { 582 if (tvp0.tv_sec == 0) { 583 tvp0.tv_sec = tvp->tv_sec; 584 tvp0.tv_usec = tvp->tv_usec; 585 } 586 ptvp = &tvp0; 587 } 588 sec = tvp->tv_sec - ptvp->tv_sec; 589 usec = tvp->tv_usec - ptvp->tv_usec; 590 if (usec < 0) { 591 usec += 1000000; 592 sec -= 1; 593 } 594 sprintf(lp, "%3d.%05d ", sec, usec / 10); 595 lp += strlen(lp); 596 } 597 } 598 599 if (flags & F_WHO) { 600 sprintf(lp, "%12s -> %-12s ", src, dst); 601 lp += strlen(lp); 602 } 603 604 if (flags & F_DROPS) { 605 sprintf(lp, "drops: %d ", drops); 606 lp += strlen(lp); 607 } 608 609 if (flags & F_LEN) { 610 sprintf(lp, "length: %4d ", len); 611 lp += strlen(lp); 612 } 613 614 if (flags & F_SUM) { 615 if (flags & F_ALLSUM) 616 printf("________________________________\n"); 617 618 start = flags & F_ALLSUM ? 0 : sumcount - 1; 619 sprintf(encap, " (%d encap)", total_encap_levels - 1); 620 printf("%s%s%s\n", line, sumline[start], 621 ((flags & F_ALLSUM) || (total_encap_levels == 1)) ? "" : 622 encap); 623 624 for (i = start + 1; i < sumcount; i++) 625 printf("%s%s\n", line, sumline[i]); 626 627 sumcount = 0; 628 } 629 630 if (flags & F_DTAIL) { 631 printf("%s\n\n", detail_line); 632 detail_line[0] = '\0'; 633 } 634 } 635 636 /* 637 * The following two routines are called back 638 * from the interpreters to display their stuff. 639 * The theory is that when snoop becomes a window 640 * based tool we can just supply a new version of 641 * get_sum_line and get_detail_line and not have 642 * to touch the interpreters at all. 643 */ 644 char * 645 get_sum_line() 646 { 647 int tsumcount = sumcount; 648 649 if (sumcount >= MAXSUM) { 650 sumcount = 0; /* error recovery */ 651 pr_err( 652 "get_sum_line: sumline overflow (sumcount=%d, MAXSUM=%d)\n", 653 tsumcount, MAXSUM); 654 } 655 656 sumline[sumcount][0] = '\0'; 657 return (sumline[sumcount++]); 658 } 659 660 /*ARGSUSED*/ 661 char * 662 get_detail_line(off, len) 663 int off, len; 664 { 665 if (detail_line[0]) { 666 printf("%s\n", detail_line); 667 detail_line[0] = '\0'; 668 } 669 return (detail_line); 670 } 671 672 /* 673 * Print an error. 674 * Works like printf (fmt string and variable args) 675 * except that it will subsititute an error message 676 * for a "%m" string (like syslog) and it calls 677 * long_jump - it doesn't return to where it was 678 * called from - it goes to the last setjmp(). 679 */ 680 void 681 pr_err(char *fmt, ...) 682 { 683 va_list ap; 684 char buf[BUFSIZ], *p2; 685 char *p1; 686 687 strcpy(buf, "snoop: "); 688 p2 = buf + strlen(buf); 689 690 for (p1 = fmt; *p1; p1++) { 691 if (*p1 == '%' && *(p1+1) == 'm') { 692 char *errstr; 693 694 if ((errstr = strerror(errno)) != (char *)NULL) { 695 (void) strcpy(p2, errstr); 696 p2 += strlen(p2); 697 } 698 p1++; 699 } else { 700 *p2++ = *p1; 701 } 702 } 703 if (p2 > buf && *(p2-1) != '\n') 704 *p2++ = '\n'; 705 *p2 = '\0'; 706 707 va_start(ap, fmt); 708 (void) vfprintf(stderr, buf, ap); 709 va_end(ap); 710 snoop_sigrecover(-1, NULL, NULL); /* global error recovery */ 711 } 712 713 /* 714 * Ye olde usage proc 715 * PLEASE keep this up to date! 716 * Naive users *love* this stuff. 717 */ 718 void 719 usage() 720 { 721 (void) fprintf(stderr, "\nUsage: snoop\n"); 722 (void) fprintf(stderr, 723 "\t[ -a ] # Listen to packets on audio\n"); 724 (void) fprintf(stderr, 725 "\t[ -d device ] # Listen on interface named device\n"); 726 (void) fprintf(stderr, 727 "\t[ -s snaplen ] # Truncate packets\n"); 728 (void) fprintf(stderr, 729 "\t[ -c count ] # Quit after count packets\n"); 730 (void) fprintf(stderr, 731 "\t[ -P ] # Turn OFF promiscuous mode\n"); 732 (void) fprintf(stderr, 733 "\t[ -D ] # Report dropped packets\n"); 734 (void) fprintf(stderr, 735 "\t[ -S ] # Report packet size\n"); 736 (void) fprintf(stderr, 737 "\t[ -i file ] # Read previously captured packets\n"); 738 (void) fprintf(stderr, 739 "\t[ -o file ] # Capture packets in file\n"); 740 (void) fprintf(stderr, 741 "\t[ -n file ] # Load addr-to-name table from file\n"); 742 (void) fprintf(stderr, 743 "\t[ -N ] # Create addr-to-name table\n"); 744 (void) fprintf(stderr, 745 "\t[ -t r|a|d ] # Time: Relative, Absolute or Delta\n"); 746 (void) fprintf(stderr, 747 "\t[ -v ] # Verbose packet display\n"); 748 (void) fprintf(stderr, 749 "\t[ -V ] # Show all summary lines\n"); 750 (void) fprintf(stderr, 751 "\t[ -p first[,last] ] # Select packet(s) to display\n"); 752 (void) fprintf(stderr, 753 "\t[ -x offset[,length] ] # Hex dump from offset for length\n"); 754 (void) fprintf(stderr, 755 "\t[ -C ] # Print packet filter code\n"); 756 (void) fprintf(stderr, 757 "\t[ -q ] # Suppress printing packet count\n"); 758 (void) fprintf(stderr, 759 "\t[ -r ] # Do not resolve address to name\n"); 760 (void) fprintf(stderr, 761 "\n\t[ filter expression ]\n"); 762 (void) fprintf(stderr, "\nExample:\n"); 763 (void) fprintf(stderr, "\tsnoop -o saved host fred\n\n"); 764 (void) fprintf(stderr, "\tsnoop -i saved -tr -v -p19\n"); 765 exit(1); 766 } 767 768 /* 769 * sdefault: default global alarm handler. Causes the current packet 770 * to be skipped. 771 */ 772 static void 773 sdefault(void) 774 { 775 snoop_nrecover = SNOOP_MAXRECOVER; 776 } 777 778 /* 779 * snoop_alarm: register or unregister an alarm handler to be called after 780 * s_sec seconds. Because snoop wasn't written to tolerate random signal 781 * delivery, periodic SIGALRM delivery (or SA_RESTART) cannot be used. 782 * 783 * s_sec argument of 0 seconds unregisters the handler. 784 * s_handler argument of NULL registers default handler sdefault(), or 785 * unregisters all signal handlers (for error recovery). 786 * 787 * Variables must be volatile to force the compiler to not optimize 788 * out the signal blocking. 789 */ 790 /*ARGSUSED*/ 791 int 792 snoop_alarm(int s_sec, void (*s_handler)()) 793 { 794 volatile time_t now; 795 volatile time_t nalarm = 0; 796 volatile struct snoop_handler *sh = NULL; 797 volatile struct snoop_handler *hp, *tp, *next; 798 volatile sigset_t s_mask; 799 volatile int ret = -1; 800 801 sigemptyset((sigset_t *)&s_mask); 802 sigaddset((sigset_t *)&s_mask, SIGALRM); 803 if (s_sec < 0) 804 return (-1); 805 806 /* register an alarm handler */ 807 now = time(NULL); 808 if (s_sec) { 809 sh = malloc(sizeof (struct snoop_handler)); 810 sh->s_time = now + s_sec; 811 if (s_handler == NULL) 812 s_handler = sdefault; 813 sh->s_handler = s_handler; 814 sh->s_next = NULL; 815 (void) sigprocmask(SIG_BLOCK, (sigset_t *)&s_mask, NULL); 816 if (snoop_hp == NULL) { 817 snoop_hp = snoop_tp = (struct snoop_handler *)sh; 818 819 snoop_nalarm = sh->s_time; 820 alarm(sh->s_time - now); 821 } else { 822 snoop_tp->s_next = (struct snoop_handler *)sh; 823 snoop_tp = (struct snoop_handler *)sh; 824 825 if (sh->s_time < snoop_nalarm) { 826 snoop_nalarm = sh->s_time; 827 (void) alarm(sh->s_time - now); 828 } 829 } 830 (void) sigprocmask(SIG_UNBLOCK, (sigset_t *)&s_mask, NULL); 831 832 return (0); 833 } 834 835 /* unregister an alarm handler */ 836 (void) sigprocmask(SIG_BLOCK, (sigset_t *)&s_mask, NULL); 837 tp = (struct snoop_handler *)&snoop_hp; 838 for (hp = snoop_hp; hp; hp = next) { 839 next = hp->s_next; 840 if (s_handler == NULL || hp->s_handler == s_handler) { 841 ret = 0; 842 tp->s_next = hp->s_next; 843 if (snoop_tp == hp) { 844 if (tp == (struct snoop_handler *)&snoop_hp) 845 snoop_tp = NULL; 846 else 847 snoop_tp = (struct snoop_handler *)tp; 848 } 849 free((void *)hp); 850 } else { 851 if (nalarm == 0 || nalarm > hp->s_time) 852 nalarm = now < hp->s_time ? hp->s_time : 853 now + 1; 854 tp = hp; 855 } 856 } 857 /* 858 * Stop or adjust timer 859 */ 860 if (snoop_hp == NULL) { 861 snoop_nalarm = 0; 862 (void) alarm(0); 863 } else if (nalarm > 0 && nalarm < snoop_nalarm) { 864 snoop_nalarm = nalarm; 865 (void) alarm(nalarm - now); 866 } 867 868 (void) sigprocmask(SIG_UNBLOCK, (sigset_t *)&s_mask, NULL); 869 return (ret); 870 } 871 872 /* 873 * snoop_recover: reset snoop's output area, and any internal variables, 874 * to allow continuation. 875 * XXX: make this an interface such that each interpreter can 876 * register a reset routine. 877 */ 878 void 879 snoop_recover(void) 880 { 881 int i; 882 883 /* Error recovery: reset output_area and associated variables */ 884 for (i = 0; i < MAXSUM; i++) 885 sumline[i][0] = '\0'; 886 detail_line[0] = '\0'; 887 line[0] = '\0'; 888 encap[0] = '\0'; 889 sumcount = 0; 890 891 /* stacking/unstacking cannot be relied upon */ 892 encap_levels = 0; 893 total_encap_levels = 1; 894 895 /* remove any pending timeouts */ 896 (void) snoop_alarm(0, NULL); 897 } 898 899 /* 900 * snoop_sigrecover: global sigaction routine to manage recovery 901 * from catastrophic interpreter failures while interpreting 902 * corrupt trace files/packets. SIGALRM timeouts, program errors, 903 * and user termination are all handled. In the case of a corrupt 904 * packet or confused interpreter, the packet will be skipped, and 905 * execution will continue in scan(). 906 * 907 * Global alarm handling (see snoop_alarm()) is managed here. 908 * 909 * Variables must be volatile to force the compiler to not optimize 910 * out the signal blocking. 911 */ 912 /*ARGSUSED*/ 913 void 914 snoop_sigrecover(int sig, siginfo_t *info, void *p) 915 { 916 volatile time_t now; 917 volatile time_t nalarm = 0; 918 volatile struct snoop_handler *hp; 919 920 /* 921 * Invoke any registered alarms. This involves first calculating 922 * the time for the next alarm, setting it up, then progressing 923 * through handler invocations. Note that since handlers may 924 * use siglongjmp(), in the worst case handlers may be serviced 925 * at a later time. 926 */ 927 if (sig == SIGALRM) { 928 now = time(NULL); 929 /* Calculate next alarm time */ 930 for (hp = snoop_hp; hp; hp = hp->s_next) { 931 if (hp->s_time) { 932 if ((hp->s_time - now) > 0) { 933 if (nalarm == 0 || nalarm > hp->s_time) 934 nalarm = now < hp->s_time ? 935 hp->s_time : now + 1; 936 } 937 } 938 } 939 /* Setup next alarm */ 940 if (nalarm) { 941 snoop_nalarm = nalarm; 942 alarm(nalarm - now); 943 } else { 944 snoop_nalarm = 0; 945 } 946 947 /* Invoke alarm handlers (may not return) */ 948 for (hp = snoop_hp; hp; hp = hp->s_next) { 949 if (hp->s_time) { 950 if ((now - hp->s_time) >= 0) { 951 hp->s_time = 0; /* only invoke once */ 952 if (hp->s_handler) 953 hp->s_handler(); 954 } 955 } 956 } 957 } else { 958 snoop_nrecover++; 959 } 960 961 /* 962 * Exit if a signal has occurred after snoop has begun the process 963 * of quitting. 964 */ 965 if (quitting) 966 exit(1); 967 968 /* 969 * If an alarm handler has timed out, and snoop_nrecover has 970 * reached SNOOP_MAXRECOVER, skip to the next packet. 971 * 972 * If any other signal has occurred, and snoop_nrecover has 973 * reached SNOOP_MAXRECOVER, give up. 974 */ 975 if (sig == SIGALRM) { 976 if (ioctl(STDOUT_FILENO, I_CANPUT, 0) == 0) { 977 /* 978 * We've stalled on output, which is not a critical 979 * failure. Reset the recovery counter so we do not 980 * consider this a persistent failure, and return so 981 * we do not skip this packet. 982 */ 983 snoop_nrecover = 0; 984 return; 985 } 986 if (snoop_nrecover >= SNOOP_MAXRECOVER) { 987 fprintf(stderr, 988 "snoop: WARNING: skipping from packet %d\n", 989 count); 990 snoop_nrecover = 0; 991 } else { 992 /* continue trying */ 993 return; 994 } 995 } else if (snoop_nrecover >= SNOOP_MAXRECOVER) { 996 fprintf(stderr, 997 "snoop: ERROR: cannot recover from packet %d\n", count); 998 exit(1); 999 } 1000 1001 #ifdef DEBUG 1002 fprintf(stderr, "snoop_sigrecover(%d, %p, %p)\n", sig, info, p); 1003 #endif /* DEBUG */ 1004 1005 /* 1006 * Prepare to quit. This allows final processing to occur 1007 * after first terminal interruption. 1008 */ 1009 if (sig == SIGTERM || sig == SIGHUP || sig == SIGINT) { 1010 quitting = 1; 1011 return; 1012 } else if (sig != -1 && sig != SIGALRM) { 1013 /* Inform user that snoop has taken a fault */ 1014 fprintf(stderr, "WARNING: received signal %d from packet %d\n", 1015 sig, count); 1016 } 1017 1018 /* Reset interpreter variables */ 1019 snoop_recover(); 1020 1021 /* Continue in scan() with the next packet */ 1022 siglongjmp(jmp_env, 1); 1023 /*NOTREACHED*/ 1024 } 1025 1026 /* 1027 * Protected malloc for global error recovery: prepare for interpreter 1028 * failures with corrupted packets or confused interpreters. Dynamically 1029 * allocate `nbytes' bytes, and sandwich it between two PROT_NONE pages to 1030 * catch writes outside of the allocated region. 1031 */ 1032 static char * 1033 protmalloc(size_t nbytes) 1034 { 1035 caddr_t start; 1036 int psz = sysconf(_SC_PAGESIZE); 1037 1038 nbytes = P2ROUNDUP(nbytes, psz); 1039 start = mmap(NULL, nbytes + psz * 2, PROT_READ|PROT_WRITE, 1040 MAP_PRIVATE|MAP_ANON, -1, 0); 1041 if (start == MAP_FAILED) { 1042 perror("Error: protmalloc: mmap"); 1043 return (NULL); 1044 } 1045 assert(IS_P2ALIGNED(start, psz)); 1046 if (mprotect(start, 1, PROT_NONE) == -1) 1047 perror("Warning: mprotect"); 1048 1049 start += psz; 1050 if (mprotect(start + nbytes, 1, PROT_NONE) == -1) 1051 perror("Warning: mprotect"); 1052 1053 return (start); 1054 } 1055 1056 /* 1057 * resetperm - reduce security vulnerabilities by resetting 1058 * owner/group/permissions. Always attempt setuid() - if we have 1059 * permission to drop our privilege level, do so. 1060 */ 1061 void 1062 resetperm(void) 1063 { 1064 if (geteuid() == 0) { 1065 (void) setgid(GID_NOBODY); 1066 (void) setuid(UID_NOBODY); 1067 } 1068 } 1069