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