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