xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.c (revision 4ab75253616c6d68e967c10221bb663c0bfa99df)
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