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