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