print-snmp.c revision 26180
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
44static 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 */
62char *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 */
85char *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 */
100char *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 */
117char *Private[] = {
118	"P-0"
119};
120
121/*
122 * error-status values for any SNMP PDU
123 */
124char *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 */
139char *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 */
159struct {
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 */
177char *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 */
188struct 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 */
210struct 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 */
253struct 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 */
305static 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 */
316static int
317asn1_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 */
530static void
531asn1_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 */
655static void
656asn1_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 */
714static void
715varbind_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 */
793static void
794snmppdu_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 */
866static void
867trap_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 */
953void
954snmp_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