print-snmp.c revision 39297
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
44static 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 */
65char *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 */
88char *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 */
103char *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 */
120char *Private[] = {
121	"P-0"
122};
123
124/*
125 * error-status values for any SNMP PDU
126 */
127char *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 */
142char *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 */
162struct {
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 */
180char *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 */
191struct 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 */
213struct 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 */
256struct 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 */
308static 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 */
319static int
320asn1_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 */
533static void
534asn1_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 */
658static void
659asn1_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 */
717static void
718varbind_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 */
796static void
797snmppdu_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 */
869static void
870trap_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 */
956void
957snmp_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