xref: /openbsd-src/usr.sbin/tcpdump/print-snmp.c (revision daf88648c0e349d5c02e1504293082072c981640)
1 /*	$OpenBSD: print-snmp.c,v 1.12 2005/04/11 07:40:10 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
5  *     John Robert LoVerso. All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  *
30  * This implementation has been influenced by the CMU SNMP release,
31  * by Steve Waldbusser.  However, this shares no code with that system.
32  * Additional ASN.1 insight gained from Marshall T. Rose's _The_Open_Book_.
33  * Earlier forms of this implementation were derived and/or inspired by an
34  * awk script originally written by C. Philip Wood of LANL (but later
35  * heavily modified by John Robert LoVerso).  The copyright notice for
36  * that work is preserved below, even though it may not rightly apply
37  * to this file.
38  *
39  * This started out as a very simple program, but the incremental decoding
40  * (into the BE structure) complicated things.
41  *
42  #			Los Alamos National Laboratory
43  #
44  #	Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
45  #	This software was produced under a U.S. Government contract
46  #	(W-7405-ENG-36) by Los Alamos National Laboratory, which is
47  #	operated by the	University of California for the U.S. Department
48  #	of Energy.  The U.S. Government is licensed to use, reproduce,
49  #	and distribute this software.  Permission is granted to the
50  #	public to copy and use this software without charge, provided
51  #	that this Notice and any statement of authorship are reproduced
52  #	on all copies.  Neither the Government nor the University makes
53  #	any warranty, express or implied, or assumes any liability or
54  #	responsibility for the use of this software.
55  #	@(#)snmp.awk.x	1.1 (LANL) 1/15/90
56  */
57 
58 #ifndef lint
59 static const char rcsid[] =
60     "@(#) $Header: /home/cvs/src/usr.sbin/tcpdump/print-snmp.c,v 1.12 2005/04/11 07:40:10 deraadt Exp $ (LBL)";
61 #endif
62 
63 #include <sys/param.h>
64 #include <sys/time.h>
65 
66 #include <ctype.h>
67 #ifdef HAVE_MEMORY_H
68 #include <memory.h>
69 #endif
70 #include <stdio.h>
71 #include <string.h>
72 
73 #include "interface.h"
74 #include "addrtoname.h"
75 
76 /*
77  * Universal ASN.1 types
78  * (we only care about the tag values for those allowed in the Internet SMI)
79  */
80 char *Universal[] = {
81 	"U-0",
82 	"Boolean",
83 	"Integer",
84 #define INTEGER 2
85 	"Bitstring",
86 	"String",
87 #define STRING 4
88 	"Null",
89 #define ASN_NULL 5
90 	"ObjID",
91 #define OBJECTID 6
92 	"ObjectDes",
93 	"U-8","U-9","U-10","U-11",	/* 8-11 */
94 	"U-12","U-13","U-14","U-15",	/* 12-15 */
95 	"Sequence",
96 #define SEQUENCE 16
97 	"Set"
98 };
99 
100 /*
101  * Application-wide ASN.1 types from the Internet SMI and their tags
102  */
103 char *Application[] = {
104 	"IpAddress",
105 #define IPADDR 0
106 	"Counter",
107 #define COUNTER 1
108 	"Gauge",
109 #define GAUGE 2
110 	"TimeTicks",
111 #define TIMETICKS 3
112 	"Opaque"
113 };
114 
115 /*
116  * Context-specific ASN.1 types for the SNMP PDUs and their tags
117  */
118 char *Context[] = {
119 	"GetRequest",
120 #define GETREQ 0
121 	"GetNextRequest",
122 #define GETNEXTREQ 1
123 	"GetResponse",
124 #define GETRESP 2
125 	"SetRequest",
126 #define SETREQ 3
127 	"Trap"
128 #define TRAP 4
129 };
130 
131 /*
132  * Private ASN.1 types
133  * The Internet SMI does not specify any
134  */
135 char *Private[] = {
136 	"P-0"
137 };
138 
139 /*
140  * error-status values for any SNMP PDU
141  */
142 char *ErrorStatus[] = {
143 	"noError",
144 	"tooBig",
145 	"noSuchName",
146 	"badValue",
147 	"readOnly",
148 	"genErr"
149 };
150 #define DECODE_ErrorStatus(e) \
151 	( e >= 0 && e <= sizeof(ErrorStatus)/sizeof(ErrorStatus[0]) \
152 	? ErrorStatus[e] : (snprintf(errbuf, sizeof(errbuf), "err=%u", e), errbuf))
153 
154 /*
155  * generic-trap values in the SNMP Trap-PDU
156  */
157 char *GenericTrap[] = {
158 	"coldStart",
159 	"warmStart",
160 	"linkDown",
161 	"linkUp",
162 	"authenticationFailure",
163 	"egpNeighborLoss",
164 	"enterpriseSpecific"
165 #define GT_ENTERPRISE 7
166 };
167 #define DECODE_GenericTrap(t) \
168 	( t >= 0 && t <= sizeof(GenericTrap)/sizeof(GenericTrap[0]) \
169 	? GenericTrap[t] : (snprintf(buf, sizeof(buf), "gt=%d", t), buf))
170 
171 /*
172  * ASN.1 type class table
173  * Ties together the preceding Universal, Application, Context, and Private
174  * type definitions.
175  */
176 #define defineCLASS(x) { "x", x, sizeof(x)/sizeof(x[0]) } /* not ANSI-C */
177 struct {
178 	char	*name;
179 	char	**Id;
180 	    int	numIDs;
181     } Class[] = {
182 	defineCLASS(Universal),
183 #define	UNIVERSAL	0
184 	defineCLASS(Application),
185 #define	APPLICATION	1
186 	defineCLASS(Context),
187 #define	CONTEXT		2
188 	defineCLASS(Private),
189 #define	PRIVATE		3
190 };
191 
192 /*
193  * defined forms for ASN.1 types
194  */
195 char *Form[] = {
196 	"Primitive",
197 #define PRIMITIVE	0
198 	"Constructed",
199 #define CONSTRUCTED	1
200 };
201 
202 /*
203  * A structure for the OID tree for the compiled-in MIB.
204  * This is stored as a general-order tree.
205  */
206 struct obj {
207 	char	*desc;			/* name of object */
208 	u_char	oid;			/* sub-id following parent */
209 	u_char	type;			/* object type (unused) */
210 	struct obj *child, *next;	/* child and next sibling pointers */
211 } *objp = NULL;
212 
213 /*
214  * Include the compiled in SNMP MIB.  "mib.h" is produced by feeding
215  * RFC-1156 format files into "makemib".  "mib.h" MUST define at least
216  * a value for `mibroot'.
217  *
218  * In particular, this is gross, as this is including initialized structures,
219  * and by right shouldn't be an "include" file.
220  */
221 #include "mib.h"
222 
223 /*
224  * This defines a list of OIDs which will be abbreviated on output.
225  * Currently, this includes the prefixes for the Internet MIB, the
226  * private enterprises tree, and the experimental tree.
227  */
228 struct obj_abrev {
229 	char *prefix;			/* prefix for this abrev */
230 	struct obj *node;		/* pointer into object table */
231 	char *oid;			/* ASN.1 encoded OID */
232 } obj_abrev_list[] = {
233 #ifndef NO_ABREV_MIB
234 	/* .iso.org.dod.internet.mgmt.mib */
235 	{ "",	&_mib_obj,		"\53\6\1\2\1" },
236 #endif
237 #ifndef NO_ABREV_ENTER
238 	/* .iso.org.dod.internet.private.enterprises */
239 	{ "E:",	&_enterprises_obj,	"\53\6\1\4\1" },
240 #endif
241 #ifndef NO_ABREV_EXPERI
242 	/* .iso.org.dod.internet.experimental */
243 	{ "X:",	&_experimental_obj,	"\53\6\1\3" },
244 #endif
245 	{ 0,0,0 }
246 };
247 
248 /*
249  * This is used in the OID print routine to walk down the object tree
250  * rooted at `mibroot'.
251  */
252 #define OBJ_PRINT(o, suppressdot) \
253 { \
254 	if (objp) { \
255 		do { \
256 			if ((o) == objp->oid) \
257 				break; \
258 		} while ((objp = objp->next) != NULL); \
259 	} \
260 	if (objp) { \
261 		printf(suppressdot?"%s":".%s", objp->desc); \
262 		objp = objp->child; \
263 	} else \
264 		printf(suppressdot?"%u":".%u", (o)); \
265 }
266 
267 /*
268  * This is the definition for the Any-Data-Type storage used purely for
269  * temporary internal representation while decoding an ASN.1 data stream.
270  */
271 struct be {
272 	u_int32_t asnlen;
273 	union {
274 		caddr_t raw;
275 		int32_t integer;
276 		u_int32_t uns;
277 		const u_char *str;
278 	} data;
279 	u_short id;
280 	u_char form, class;		/* tag info */
281 	u_char type;
282 #define BE_ANY		255
283 #define BE_NONE		0
284 #define BE_NULL		1
285 #define BE_OCTET	2
286 #define BE_OID		3
287 #define BE_INT		4
288 #define BE_UNS		5
289 #define BE_STR		6
290 #define BE_SEQ		7
291 #define BE_INETADDR	8
292 #define BE_PDU		9
293 };
294 
295 /*
296  * Defaults for SNMP PDU components
297  */
298 #define DEF_COMMUNITY "public"
299 #define DEF_VERSION 0
300 
301 /*
302  * constants for ASN.1 decoding
303  */
304 #define OIDMUX 40
305 #define ASNLEN_INETADDR 4
306 #define ASN_SHIFT7 7
307 #define ASN_SHIFT8 8
308 #define ASN_BIT8 0x80
309 #define ASN_LONGLEN 0x80
310 
311 #define ASN_ID_BITS 0x1f
312 #define ASN_FORM_BITS 0x20
313 #define ASN_FORM_SHIFT 5
314 #define ASN_CLASS_BITS 0xc0
315 #define ASN_CLASS_SHIFT 6
316 
317 #define ASN_ID_EXT 0x1f		/* extension ID in tag field */
318 
319 /*
320  * truncated==1 means the packet was complete, but we don't have all of
321  * it to decode.
322  */
323 static int truncated;
324 #define ifNotTruncated if (truncated) fputs("[|snmp]", stdout); else
325 
326 /*
327  * This decodes the next ASN.1 object in the stream pointed to by "p"
328  * (and of real-length "len") and stores the intermediate data in the
329  * provided BE object.
330  *
331  * This returns -l if it fails (i.e., the ASN.1 stream is not valid).
332  * O/w, this returns the number of bytes parsed from "p".
333  */
334 static int
335 asn1_parse(register const u_char *p, u_int len, struct be *elem)
336 {
337 	u_char form, class, id;
338 	int i, hdr;
339 
340 	elem->asnlen = 0;
341 	elem->type = BE_ANY;
342 	if (len < 1) {
343 		ifNotTruncated puts("[nothing to parse], stdout");
344 		return -1;
345 	}
346 
347 	/*
348 	 * it would be nice to use a bit field, but you can't depend on them.
349 	 *  +---+---+---+---+---+---+---+---+
350 	 *  + class |frm|        id         |
351 	 *  +---+---+---+---+---+---+---+---+
352 	 *    7   6   5   4   3   2   1   0
353 	 */
354 	id = *p & ASN_ID_BITS;		/* lower 5 bits, range 00-1f */
355 #ifdef notdef
356 	form = (*p & 0xe0) >> 5;	/* move upper 3 bits to lower 3 */
357 	class = form >> 1;		/* bits 7&6 -> bits 1&0, range 0-3 */
358 	form &= 0x1;			/* bit 5 -> bit 0, range 0-1 */
359 #else
360 	form = (u_char)(*p & ASN_FORM_BITS) >> ASN_FORM_SHIFT;
361 	class = (u_char)(*p & ASN_CLASS_BITS) >> ASN_CLASS_SHIFT;
362 #endif
363 	elem->form = form;
364 	elem->class = class;
365 	elem->id = id;
366 	if (vflag)
367 		printf("|%.2x", *p);
368 	p++; len--; hdr = 1;
369 	/* extended tag field */
370 	if (id == ASN_ID_EXT) {
371 		for (id = 0; *p & ASN_BIT8 && len > 0; len--, hdr++, p++) {
372 			if (vflag)
373 				printf("|%.2x", *p);
374 			id = (id << 7) | (*p & ~ASN_BIT8);
375 		}
376 		if (len == 0 && *p & ASN_BIT8) {
377 			ifNotTruncated fputs("[Xtagfield?]", stdout);
378 			return -1;
379 		}
380 		elem->id = id = (id << 7) | *p;
381 		--len;
382 		++hdr;
383 		++p;
384 	}
385 	if (len < 1) {
386 		ifNotTruncated fputs("[no asnlen]", stdout);
387 		return -1;
388 	}
389 	elem->asnlen = *p;
390 	if (vflag)
391 		printf("|%.2x", *p);
392 	p++; len--; hdr++;
393 	if (elem->asnlen & ASN_BIT8) {
394 		int noct = elem->asnlen % ASN_BIT8;
395 		elem->asnlen = 0;
396 		if (len < noct) {
397 			ifNotTruncated printf("[asnlen? %d<%d]", len, noct);
398 			return -1;
399 		}
400 		for (; noct-- > 0; len--, hdr++) {
401 			if (vflag)
402 				printf("|%.2x", *p);
403 			elem->asnlen = (elem->asnlen << ASN_SHIFT8) | *p++;
404 		}
405 	}
406 	if (len < elem->asnlen) {
407 		if (!truncated) {
408 			printf("[len%d<asnlen%u]", len, elem->asnlen);
409 			return -1;
410 		}
411 		/* maybe should check at least 4? */
412 		elem->asnlen = len;
413 	}
414 	if (form >= sizeof(Form)/sizeof(Form[0])) {
415 		ifNotTruncated printf("[form?%d]", form);
416 		return -1;
417 	}
418 	if (class >= sizeof(Class)/sizeof(Class[0])) {
419 		ifNotTruncated printf("[class?%c/%d]", *Form[form], class);
420 		return -1;
421 	}
422 	if ((int)id >= Class[class].numIDs) {
423 		ifNotTruncated printf("[id?%c/%s/%d]", *Form[form],
424 			Class[class].name, id);
425 		return -1;
426 	}
427 
428 	switch (form) {
429 	case PRIMITIVE:
430 		switch (class) {
431 		case UNIVERSAL:
432 			switch (id) {
433 			case STRING:
434 				elem->type = BE_STR;
435 				elem->data.str = p;
436 				break;
437 
438 			case INTEGER: {
439 				register int32_t data;
440 				elem->type = BE_INT;
441 				data = 0;
442 
443 				if (*p & ASN_BIT8)	/* negative */
444 					data = -1;
445 				for (i = elem->asnlen; i-- > 0; p++)
446 					data = (data << ASN_SHIFT8) | *p;
447 				elem->data.integer = data;
448 				break;
449 			}
450 
451 			case OBJECTID:
452 				elem->type = BE_OID;
453 				elem->data.raw = (caddr_t)p;
454 				break;
455 
456 			case ASN_NULL:
457 				elem->type = BE_NULL;
458 				elem->data.raw = NULL;
459 				break;
460 
461 			default:
462 				elem->type = BE_OCTET;
463 				elem->data.raw = (caddr_t)p;
464 				printf("[P/U/%s]",
465 					Class[class].Id[id]);
466 				break;
467 			}
468 			break;
469 
470 		case APPLICATION:
471 			switch (id) {
472 			case IPADDR:
473 				elem->type = BE_INETADDR;
474 				elem->data.raw = (caddr_t)p;
475 				break;
476 
477 			case COUNTER:
478 			case GAUGE:
479 			case TIMETICKS: {
480 				register u_int32_t data;
481 				elem->type = BE_UNS;
482 				data = 0;
483 				for (i = elem->asnlen; i-- > 0; p++)
484 					data = (data << 8) + *p;
485 				elem->data.uns = data;
486 				break;
487 			}
488 
489 			default:
490 				elem->type = BE_OCTET;
491 				elem->data.raw = (caddr_t)p;
492 				printf("[P/A/%s]",
493 					Class[class].Id[id]);
494 				break;
495 			}
496 			break;
497 
498 		default:
499 			elem->type = BE_OCTET;
500 			elem->data.raw = (caddr_t)p;
501 			printf("[P/%s/%s]",
502 				Class[class].name, Class[class].Id[id]);
503 			break;
504 		}
505 		break;
506 
507 	case CONSTRUCTED:
508 		switch (class) {
509 		case UNIVERSAL:
510 			switch (id) {
511 			case SEQUENCE:
512 				elem->type = BE_SEQ;
513 				elem->data.raw = (caddr_t)p;
514 				break;
515 
516 			default:
517 				elem->type = BE_OCTET;
518 				elem->data.raw = (caddr_t)p;
519 				printf("C/U/%s", Class[class].Id[id]);
520 				break;
521 			}
522 			break;
523 
524 		case CONTEXT:
525 			elem->type = BE_PDU;
526 			elem->data.raw = (caddr_t)p;
527 			break;
528 
529 		default:
530 			elem->type = BE_OCTET;
531 			elem->data.raw = (caddr_t)p;
532 			printf("C/%s/%s",
533 				Class[class].name, Class[class].Id[id]);
534 			break;
535 		}
536 		break;
537 	}
538 	p += elem->asnlen;
539 	len -= elem->asnlen;
540 	return elem->asnlen + hdr;
541 }
542 
543 /*
544  * Display the ASN.1 object represented by the BE object.
545  * This used to be an integral part of asn1_parse() before the intermediate
546  * BE form was added.
547  */
548 static void
549 asn1_print(struct be *elem)
550 {
551 	u_char *p = (u_char *)elem->data.raw;
552 	u_int32_t asnlen = elem->asnlen;
553 	int i;
554 
555 	switch (elem->type) {
556 
557 	case BE_OCTET:
558 		for (i = asnlen; i-- > 0; p++)
559 			printf("_%.2x", *p);
560 		break;
561 
562 	case BE_NULL:
563 		break;
564 
565 	case BE_OID: {
566 	int o = 0, first = -1, i = asnlen;
567 
568 		if (!nflag && asnlen > 2) {
569 			struct obj_abrev *a = &obj_abrev_list[0];
570 			for (; a->node; a++) {
571 				if (!memcmp(a->oid, (char *)p,
572 				    strlen(a->oid))) {
573 					objp = a->node->child;
574 					i -= strlen(a->oid);
575 					p += strlen(a->oid);
576 					fputs(a->prefix, stdout);
577 					first = 1;
578 					break;
579 				}
580 			}
581 		}
582 		for (; i-- > 0; p++) {
583 			o = (o << ASN_SHIFT7) + (*p & ~ASN_BIT8);
584 			if (*p & ASN_LONGLEN)
585 				continue;
586 
587 			/*
588 			 * first subitem encodes two items with 1st*OIDMUX+2nd
589 			 */
590 			if (first < 0) {
591 				if (!nflag)
592 					objp = mibroot;
593 				first = 0;
594 				OBJ_PRINT(o/OIDMUX, first);
595 				o %= OIDMUX;
596 			}
597 			OBJ_PRINT(o, first);
598 			if (--first < 0)
599 				first = 0;
600 			o = 0;
601 		}
602 		break;
603 	}
604 
605 	case BE_INT:
606 		printf("%d", elem->data.integer);
607 		break;
608 
609 	case BE_UNS:
610 		printf("%d", elem->data.uns);
611 		break;
612 
613 	case BE_STR: {
614 		register int printable = 1, first = 1;
615 		const u_char *p = elem->data.str;
616 		for (i = asnlen; printable && i-- > 0; p++)
617 			printable = isprint(*p) || isspace(*p);
618 		p = elem->data.str;
619 		if (printable) {
620 			putchar('"');
621 			(void)fn_print(p, p + asnlen);
622 			putchar('"');
623 		} else
624 			for (i = asnlen; i-- > 0; p++) {
625 				printf(first ? "%.2x" : "_%.2x", *p);
626 				first = 0;
627 			}
628 		break;
629 	}
630 
631 	case BE_SEQ:
632 		printf("Seq(%u)", elem->asnlen);
633 		break;
634 
635 	case BE_INETADDR: {
636 		char sep;
637 		if (asnlen != ASNLEN_INETADDR)
638 			printf("[inetaddr len!=%d]", ASNLEN_INETADDR);
639 		sep='[';
640 		for (i = asnlen; i-- > 0; p++) {
641 			printf("%c%u", sep, *p);
642 			sep='.';
643 		}
644 		putchar(']');
645 		break;
646 	}
647 
648 	case BE_PDU:
649 		printf("%s(%u)",
650 			Class[CONTEXT].Id[elem->id], elem->asnlen);
651 		break;
652 
653 	case BE_ANY:
654 		fputs("[BE_ANY!?]", stdout);
655 		break;
656 
657 	default:
658 		fputs("[be!?]", stdout);
659 		break;
660 	}
661 }
662 
663 #ifdef notdef
664 /*
665  * This is a brute force ASN.1 printer: recurses to dump an entire structure.
666  * This will work for any ASN.1 stream, not just an SNMP PDU.
667  *
668  * By adding newlines and spaces at the correct places, this would print in
669  * Rose-Normal-Form.
670  *
671  * This is not currently used.
672  */
673 static void
674 asn1_decode(u_char *p, u_int length)
675 {
676 	struct be elem;
677 	int i = 0;
678 
679 	while (i >= 0 && length > 0) {
680 		i = asn1_parse(p, length, &elem);
681 		if (i >= 0) {
682 			fputs(" ", stdout);
683 			asn1_print(&elem);
684 			if (elem.type == BE_SEQ || elem.type == BE_PDU) {
685 				fputs(" {", stdout);
686 				asn1_decode(elem.data.raw, elem.asnlen);
687 				fputs(" }", stdout);
688 			}
689 			length -= i;
690 			p += i;
691 		}
692 	}
693 }
694 #endif
695 
696 /*
697  * General SNMP header
698  *	SEQUENCE {
699  *		version INTEGER {version-1(0)},
700  *		community OCTET STRING,
701  *		data ANY	-- PDUs
702  *	}
703  * PDUs for all but Trap: (see rfc1157 from page 15 on)
704  *	SEQUENCE {
705  *		request-id INTEGER,
706  *		error-status INTEGER,
707  *		error-index INTEGER,
708  *		varbindlist SEQUENCE OF
709  *			SEQUENCE {
710  *				name ObjectName,
711  *				value ObjectValue
712  *			}
713  *	}
714  * PDU for Trap:
715  *	SEQUENCE {
716  *		enterprise OBJECT IDENTIFIER,
717  *		agent-addr NetworkAddress,
718  *		generic-trap INTEGER,
719  *		specific-trap INTEGER,
720  *		time-stamp TimeTicks,
721  *		varbindlist SEQUENCE OF
722  *			SEQUENCE {
723  *				name ObjectName,
724  *				value ObjectValue
725  *			}
726  *	}
727  */
728 
729 /*
730  * Decode SNMP varBind
731  */
732 static void
733 varbind_print(u_char pduid, const u_char *np, u_int length, int error)
734 {
735 	struct be elem;
736 	int count = 0, ind;
737 
738 	/* Sequence of varBind */
739 	if ((count = asn1_parse(np, length, &elem)) < 0)
740 		return;
741 	if (elem.type != BE_SEQ) {
742 		fputs("[!SEQ of varbind]", stdout);
743 		asn1_print(&elem);
744 		return;
745 	}
746 	if (count < length)
747 		printf("[%d extra after SEQ of varbind]", length - count);
748 	/* descend */
749 	length = elem.asnlen;
750 	np = (u_char *)elem.data.raw;
751 
752 	for (ind = 1; length > 0; ind++) {
753 		const u_char *vbend;
754 		u_int vblength;
755 
756 		if (!error || ind == error)
757 			fputs(" ", stdout);
758 
759 		/* Sequence */
760 		if ((count = asn1_parse(np, length, &elem)) < 0)
761 			return;
762 		if (elem.type != BE_SEQ) {
763 			fputs("[!varbind]", stdout);
764 			asn1_print(&elem);
765 			return;
766 		}
767 		vbend = np + count;
768 		vblength = length - count;
769 		/* descend */
770 		length = elem.asnlen;
771 		np = (u_char *)elem.data.raw;
772 
773 		/* objName (OID) */
774 		if ((count = asn1_parse(np, length, &elem)) < 0)
775 			return;
776 		if (elem.type != BE_OID) {
777 			fputs("[objName!=OID]", stdout);
778 			asn1_print(&elem);
779 			return;
780 		}
781 		if (!error || ind == error)
782 			asn1_print(&elem);
783 		length -= count;
784 		np += count;
785 
786 		if (pduid != GETREQ && pduid != GETNEXTREQ && !error)
787 				fputs("=", stdout);
788 
789 		/* objVal (ANY) */
790 		if ((count = asn1_parse(np, length, &elem)) < 0)
791 			return;
792 		if (pduid == GETREQ || pduid == GETNEXTREQ) {
793 			if (elem.type != BE_NULL) {
794 				fputs("[objVal!=NULL]", stdout);
795 				asn1_print(&elem);
796 			}
797 		} else
798 			if (error && ind == error && elem.type != BE_NULL)
799 				fputs("[err objVal!=NULL]", stdout);
800 			if (!error || ind == error)
801 				asn1_print(&elem);
802 
803 		length = vblength;
804 		np = vbend;
805 	}
806 }
807 
808 /*
809  * Decode SNMP PDUs: GetRequest, GetNextRequest, GetResponse, and SetRequest
810  */
811 static void
812 snmppdu_print(u_char pduid, const u_char *np, u_int length)
813 {
814 	struct be elem;
815 	int count = 0, error;
816 
817 	/* reqId (Integer) */
818 	if ((count = asn1_parse(np, length, &elem)) < 0)
819 		return;
820 	if (elem.type != BE_INT) {
821 		fputs("[reqId!=INT]", stdout);
822 		asn1_print(&elem);
823 		return;
824 	}
825 	/* ignore the reqId */
826 	length -= count;
827 	np += count;
828 
829 	/* errorStatus (Integer) */
830 	if ((count = asn1_parse(np, length, &elem)) < 0)
831 		return;
832 	if (elem.type != BE_INT) {
833 		fputs("[errorStatus!=INT]", stdout);
834 		asn1_print(&elem);
835 		return;
836 	}
837 	error = 0;
838 	if ((pduid == GETREQ || pduid == GETNEXTREQ)
839 	    && elem.data.integer != 0) {
840 		char errbuf[20];
841 		printf("[errorStatus(%s)!=0]",
842 			DECODE_ErrorStatus(elem.data.integer));
843 	} else if (elem.data.integer != 0) {
844 		char errbuf[20];
845 		printf(" %s", DECODE_ErrorStatus(elem.data.integer));
846 		error = elem.data.integer;
847 	}
848 	length -= count;
849 	np += count;
850 
851 	/* errorIndex (Integer) */
852 	if ((count = asn1_parse(np, length, &elem)) < 0)
853 		return;
854 	if (elem.type != BE_INT) {
855 		fputs("[errorIndex!=INT]", stdout);
856 		asn1_print(&elem);
857 		return;
858 	}
859 	if ((pduid == GETREQ || pduid == GETNEXTREQ)
860 	    && elem.data.integer != 0)
861 		printf("[errorIndex(%d)!=0]", elem.data.integer);
862 	else if (elem.data.integer != 0) {
863 		if (!error)
864 			printf("[errorIndex(%d) w/o errorStatus]",
865 				elem.data.integer);
866 		else {
867 			printf("@%d", elem.data.integer);
868 			error = elem.data.integer;
869 		}
870 	} else if (error) {
871 		fputs("[errorIndex==0]", stdout);
872 		error = 0;
873 	}
874 	length -= count;
875 	np += count;
876 
877 	varbind_print(pduid, np, length, error);
878 	return;
879 }
880 
881 /*
882  * Decode SNMP Trap PDU
883  */
884 static void
885 trap_print(const u_char *np, u_int length)
886 {
887 	struct be elem;
888 	int count = 0, generic;
889 
890 	putchar(' ');
891 
892 	/* enterprise (oid) */
893 	if ((count = asn1_parse(np, length, &elem)) < 0)
894 		return;
895 	if (elem.type != BE_OID) {
896 		fputs("[enterprise!=OID]", stdout);
897 		asn1_print(&elem);
898 		return;
899 	}
900 	asn1_print(&elem);
901 	length -= count;
902 	np += count;
903 
904 	putchar(' ');
905 
906 	/* agent-addr (inetaddr) */
907 	if ((count = asn1_parse(np, length, &elem)) < 0)
908 		return;
909 	if (elem.type != BE_INETADDR) {
910 		fputs("[agent-addr!=INETADDR]", stdout);
911 		asn1_print(&elem);
912 		return;
913 	}
914 	asn1_print(&elem);
915 	length -= count;
916 	np += count;
917 
918 	/* generic-trap (Integer) */
919 	if ((count = asn1_parse(np, length, &elem)) < 0)
920 		return;
921 	if (elem.type != BE_INT) {
922 		fputs("[generic-trap!=INT]", stdout);
923 		asn1_print(&elem);
924 		return;
925 	}
926 	generic = elem.data.integer;
927 	{
928 		char buf[20];
929 		printf(" %s", DECODE_GenericTrap(generic));
930 	}
931 	length -= count;
932 	np += count;
933 
934 	/* specific-trap (Integer) */
935 	if ((count = asn1_parse(np, length, &elem)) < 0)
936 		return;
937 	if (elem.type != BE_INT) {
938 		fputs("[specific-trap!=INT]", stdout);
939 		asn1_print(&elem);
940 		return;
941 	}
942 	if (generic != GT_ENTERPRISE) {
943 		if (elem.data.integer != 0)
944 			printf("[specific-trap(%d)!=0]", elem.data.integer);
945 	} else
946 		printf(" s=%d", elem.data.integer);
947 	length -= count;
948 	np += count;
949 
950 	putchar(' ');
951 
952 	/* time-stamp (TimeTicks) */
953 	if ((count = asn1_parse(np, length, &elem)) < 0)
954 		return;
955 	if (elem.type != BE_UNS) {			/* XXX */
956 		fputs("[time-stamp!=TIMETICKS]", stdout);
957 		asn1_print(&elem);
958 		return;
959 	}
960 	asn1_print(&elem);
961 	length -= count;
962 	np += count;
963 
964 	varbind_print (TRAP, np, length, 0);
965 	return;
966 }
967 
968 /*
969  * Decode SNMP header and pass on to PDU printing routines
970  */
971 void
972 snmp_print(const u_char *np, u_int length)
973 {
974 	struct be elem, pdu;
975 	int count = 0;
976 
977 	truncated = 0;
978 
979 	/* truncated packet? */
980 	if (np + length > snapend) {
981 		truncated = 1;
982 		length = snapend - np;
983 	}
984 
985 	putchar(' ');
986 
987 	/* initial Sequence */
988 	if ((count = asn1_parse(np, length, &elem)) < 0)
989 		return;
990 	if (elem.type != BE_SEQ) {
991 		fputs("[!init SEQ]", stdout);
992 		asn1_print(&elem);
993 		return;
994 	}
995 	if (count < length)
996 		printf("[%d extra after iSEQ]", length - count);
997 	/* descend */
998 	length = elem.asnlen;
999 	np = (u_char *)elem.data.raw;
1000 	/* Version (Integer) */
1001 	if ((count = asn1_parse(np, length, &elem)) < 0)
1002 		return;
1003 	if (elem.type != BE_INT) {
1004 		fputs("[version!=INT]", stdout);
1005 		asn1_print(&elem);
1006 		return;
1007 	}
1008 	/* only handle version==0 */
1009 	if (elem.data.integer != DEF_VERSION) {
1010 		printf("[version(%d)!=0]", elem.data.integer);
1011 		return;
1012 	}
1013 	length -= count;
1014 	np += count;
1015 
1016 	/* Community (String) */
1017 	if ((count = asn1_parse(np, length, &elem)) < 0)
1018 		return;
1019 	if (elem.type != BE_STR) {
1020 		fputs("[comm!=STR]", stdout);
1021 		asn1_print(&elem);
1022 		return;
1023 	}
1024 	/* default community */
1025 	if (strncmp((char *)elem.data.str, DEF_COMMUNITY,
1026 	    sizeof(DEF_COMMUNITY) - 1))
1027 		/* ! "public" */
1028 		printf("C=%.*s ", (int)elem.asnlen, elem.data.str);
1029 	length -= count;
1030 	np += count;
1031 
1032 	/* PDU (Context) */
1033 	if ((count = asn1_parse(np, length, &pdu)) < 0)
1034 		return;
1035 	if (pdu.type != BE_PDU) {
1036 		fputs("[no PDU]", stdout);
1037 		return;
1038 	}
1039 	if (count < length)
1040 		printf("[%d extra after PDU]", length - count);
1041 	asn1_print(&pdu);
1042 	/* descend into PDU */
1043 	length = pdu.asnlen;
1044 	np = (u_char *)pdu.data.raw;
1045 
1046 	switch (pdu.id) {
1047 	case TRAP:
1048 		trap_print(np, length);
1049 		break;
1050 	case GETREQ:
1051 	case GETNEXTREQ:
1052 	case GETRESP:
1053 	case SETREQ:
1054 		snmppdu_print(pdu.id, np, length);
1055 		break;
1056 	}
1057 	return;
1058 }
1059