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