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