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