print-snmp.c revision 75115
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 * Support for SNMPv2c/SNMPv3 and the ability to link the module against
25 * the libsmi was added by J. Schoenwaelder, Copyright (c) 1999.
26 *
27 * This started out as a very simple program, but the incremental decoding
28 * (into the BE structure) complicated things.
29 *
30 #			Los Alamos National Laboratory
31 #
32 #	Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
33 #	This software was produced under a U.S. Government contract
34 #	(W-7405-ENG-36) by Los Alamos National Laboratory, which is
35 #	operated by the	University of California for the U.S. Department
36 #	of Energy.  The U.S. Government is licensed to use, reproduce,
37 #	and distribute this software.  Permission is granted to the
38 #	public to copy and use this software without charge, provided
39 #	that this Notice and any statement of authorship are reproduced
40 #	on all copies.  Neither the Government nor the University makes
41 #	any warranty, express or implied, or assumes any liability or
42 #	responsibility for the use of this software.
43 #	@(#)snmp.awk.x	1.1 (LANL) 1/15/90
44 */
45
46#ifndef lint
47static const char rcsid[] =
48    "@(#) $Header: /tcpdump/master/tcpdump/print-snmp.c,v 1.44 2000/11/10 17:34:10 fenner Exp $ (LBL)";
49#endif
50
51#ifdef HAVE_CONFIG_H
52#include "config.h"
53#endif
54
55#include <sys/param.h>
56#include <sys/time.h>
57
58#include <ctype.h>
59#include <stdio.h>
60#include <string.h>
61
62#ifdef HAVE_SMI_H
63#include <smi.h>
64#endif
65
66#include "interface.h"
67#include "addrtoname.h"
68
69/*
70 * Universal ASN.1 types
71 * (we only care about the tag values for those allowed in the Internet SMI)
72 */
73char *Universal[] = {
74	"U-0",
75	"Boolean",
76	"Integer",
77#define INTEGER 2
78	"Bitstring",
79	"String",
80#define STRING 4
81	"Null",
82#define ASN_NULL 5
83	"ObjID",
84#define OBJECTID 6
85	"ObjectDes",
86	"U-8","U-9","U-10","U-11",	/* 8-11 */
87	"U-12","U-13","U-14","U-15",	/* 12-15 */
88	"Sequence",
89#define SEQUENCE 16
90	"Set"
91};
92
93/*
94 * Application-wide ASN.1 types from the Internet SMI and their tags
95 */
96char *Application[] = {
97	"IpAddress",
98#define IPADDR 0
99	"Counter",
100#define COUNTER 1
101	"Gauge",
102#define GAUGE 2
103	"TimeTicks",
104#define TIMETICKS 3
105	"Opaque",
106#define OPAQUE 4
107	"C-5",
108	"Counter64"
109#define COUNTER64 6
110};
111
112/*
113 * Context-specific ASN.1 types for the SNMP PDUs and their tags
114 */
115char *Context[] = {
116	"GetRequest",
117#define GETREQ 0
118	"GetNextRequest",
119#define GETNEXTREQ 1
120	"GetResponse",
121#define GETRESP 2
122	"SetRequest",
123#define SETREQ 3
124	"Trap",
125#define TRAP 4
126	"GetBulk",
127#define GETBULKREQ 5
128	"Inform",
129#define INFORMREQ 6
130	"V2Trap",
131#define V2TRAP 7
132	"Report"
133#define REPORT 8
134};
135
136#define NOTIFY_CLASS(x)	    (x == TRAP || x == V2TRAP || x == INFORMREQ)
137#define READ_CLASS(x)       (x == GETREQ || x == GETNEXTREQ || x == GETBULKREQ)
138#define WRITE_CLASS(x)	    (x == SETREQ)
139#define RESPONSE_CLASS(x)   (x == GETRESP)
140#define INTERNAL_CLASS(x)   (x == REPORT)
141
142/*
143 * Context-specific ASN.1 types for the SNMP Exceptions and their tags
144 */
145char *Exceptions[] = {
146	"noSuchObject",
147#define NOSUCHOBJECT 0
148	"noSuchInstance",
149#define NOSUCHINSTANCE 1
150	"endOfMibView",
151#define ENDOFMIBVIEW 2
152};
153
154/*
155 * Private ASN.1 types
156 * The Internet SMI does not specify any
157 */
158char *Private[] = {
159	"P-0"
160};
161
162/*
163 * error-status values for any SNMP PDU
164 */
165char *ErrorStatus[] = {
166	"noError",
167	"tooBig",
168	"noSuchName",
169	"badValue",
170	"readOnly",
171	"genErr",
172	"noAccess",
173	"wrongType",
174	"wrongLength",
175	"wrongEncoding",
176	"wrongValue",
177	"noCreation",
178	"inconsistentValue",
179	"resourceUnavailable",
180	"commitFailed",
181	"undoFailed",
182	"authorizationError",
183	"notWritable",
184	"inconsistentName"
185};
186#define DECODE_ErrorStatus(e) \
187	( e >= 0 && e < sizeof(ErrorStatus)/sizeof(ErrorStatus[0]) \
188		? ErrorStatus[e] \
189		: (snprintf(errbuf, sizeof(errbuf), "err=%u", e), errbuf))
190
191/*
192 * generic-trap values in the SNMP Trap-PDU
193 */
194char *GenericTrap[] = {
195	"coldStart",
196	"warmStart",
197	"linkDown",
198	"linkUp",
199	"authenticationFailure",
200	"egpNeighborLoss",
201	"enterpriseSpecific"
202#define GT_ENTERPRISE 7
203};
204#define DECODE_GenericTrap(t) \
205	( t >= 0 && t < sizeof(GenericTrap)/sizeof(GenericTrap[0]) \
206		? GenericTrap[t] \
207		: (snprintf(buf, sizeof(buf), "gt=%d", t), buf))
208
209/*
210 * ASN.1 type class table
211 * Ties together the preceding Universal, Application, Context, and Private
212 * type definitions.
213 */
214#define defineCLASS(x) { "x", x, sizeof(x)/sizeof(x[0]) } /* not ANSI-C */
215struct {
216	char	*name;
217	char	**Id;
218	    int	numIDs;
219    } Class[] = {
220	defineCLASS(Universal),
221#define	UNIVERSAL	0
222	defineCLASS(Application),
223#define	APPLICATION	1
224	defineCLASS(Context),
225#define	CONTEXT		2
226	defineCLASS(Private),
227#define	PRIVATE		3
228	defineCLASS(Exceptions),
229#define EXCEPTIONS	4
230};
231
232/*
233 * defined forms for ASN.1 types
234 */
235char *Form[] = {
236	"Primitive",
237#define PRIMITIVE	0
238	"Constructed",
239#define CONSTRUCTED	1
240};
241
242/*
243 * A structure for the OID tree for the compiled-in MIB.
244 * This is stored as a general-order tree.
245 */
246struct obj {
247	char	*desc;			/* name of object */
248	u_char	oid;			/* sub-id following parent */
249	u_char	type;			/* object type (unused) */
250	struct obj *child, *next;	/* child and next sibling pointers */
251} *objp = NULL;
252
253/*
254 * Include the compiled in SNMP MIB.  "mib.h" is produced by feeding
255 * RFC-1156 format files into "makemib".  "mib.h" MUST define at least
256 * a value for `mibroot'.
257 *
258 * In particular, this is gross, as this is including initialized structures,
259 * and by right shouldn't be an "include" file.
260 */
261#include "mib.h"
262
263/*
264 * This defines a list of OIDs which will be abbreviated on output.
265 * Currently, this includes the prefixes for the Internet MIB, the
266 * private enterprises tree, and the experimental tree.
267 */
268struct obj_abrev {
269	char *prefix;			/* prefix for this abrev */
270	struct obj *node;		/* pointer into object table */
271	char *oid;			/* ASN.1 encoded OID */
272} obj_abrev_list[] = {
273#ifndef NO_ABREV_MIB
274	/* .iso.org.dod.internet.mgmt.mib */
275	{ "",	&_mib_obj,		"\53\6\1\2\1" },
276#endif
277#ifndef NO_ABREV_ENTER
278	/* .iso.org.dod.internet.private.enterprises */
279	{ "E:",	&_enterprises_obj,	"\53\6\1\4\1" },
280#endif
281#ifndef NO_ABREV_EXPERI
282	/* .iso.org.dod.internet.experimental */
283	{ "X:",	&_experimental_obj,	"\53\6\1\3" },
284#endif
285#ifndef NO_ABBREV_SNMPMODS
286	/* .iso.org.dod.internet.snmpV2.snmpModules */
287        { "S:", &_snmpModules_obj,      "\53\6\1\6\3" },
288#endif
289	{ 0,0,0 }
290};
291
292/*
293 * This is used in the OID print routine to walk down the object tree
294 * rooted at `mibroot'.
295 */
296#define OBJ_PRINT(o, suppressdot) \
297{ \
298	if (objp) { \
299		do { \
300			if ((o) == objp->oid) \
301				break; \
302		} while ((objp = objp->next) != NULL); \
303	} \
304	if (objp) { \
305		printf(suppressdot?"%s":".%s", objp->desc); \
306		objp = objp->child; \
307	} else \
308		printf(suppressdot?"%u":".%u", (o)); \
309}
310
311/*
312 * This is the definition for the Any-Data-Type storage used purely for
313 * temporary internal representation while decoding an ASN.1 data stream.
314 */
315struct be {
316	u_int32_t asnlen;
317	union {
318		caddr_t raw;
319		int32_t integer;
320		u_int32_t uns;
321		const u_char *str;
322	        struct {
323		        u_int32_t high;
324		        u_int32_t low;
325		} uns64;
326	} data;
327	u_short id;
328	u_char form, class;		/* tag info */
329	u_char type;
330#define BE_ANY		255
331#define BE_NONE		0
332#define BE_NULL		1
333#define BE_OCTET	2
334#define BE_OID		3
335#define BE_INT		4
336#define BE_UNS		5
337#define BE_STR		6
338#define BE_SEQ		7
339#define BE_INETADDR	8
340#define BE_PDU		9
341#define BE_UNS64	10
342#define BE_NOSUCHOBJECT	128
343#define BE_NOSUCHINST	129
344#define BE_ENDOFMIBVIEW	130
345};
346
347/*
348 * SNMP versions recognized by this module
349 */
350char *SnmpVersion[] = {
351	"SNMPv1",
352#define SNMP_VERSION_1	0
353	"SNMPv2c",
354#define SNMP_VERSION_2	1
355	"SNMPv2u",
356#define SNMP_VERSION_2U	2
357	"SNMPv3"
358#define SNMP_VERSION_3	3
359};
360
361/*
362 * Defaults for SNMP PDU components
363 */
364#define DEF_COMMUNITY "public"
365
366/*
367 * constants for ASN.1 decoding
368 */
369#define OIDMUX 40
370#define ASNLEN_INETADDR 4
371#define ASN_SHIFT7 7
372#define ASN_SHIFT8 8
373#define ASN_BIT8 0x80
374#define ASN_LONGLEN 0x80
375
376#define ASN_ID_BITS 0x1f
377#define ASN_FORM_BITS 0x20
378#define ASN_FORM_SHIFT 5
379#define ASN_CLASS_BITS 0xc0
380#define ASN_CLASS_SHIFT 6
381
382#define ASN_ID_EXT 0x1f		/* extension ID in tag field */
383
384/*
385 * truncated==1 means the packet was complete, but we don't have all of
386 * it to decode.
387 */
388static int truncated;
389#define ifNotTruncated if (truncated) fputs("[|snmp]", stdout); else
390
391/*
392 * This decodes the next ASN.1 object in the stream pointed to by "p"
393 * (and of real-length "len") and stores the intermediate data in the
394 * provided BE object.
395 *
396 * This returns -l if it fails (i.e., the ASN.1 stream is not valid).
397 * O/w, this returns the number of bytes parsed from "p".
398 */
399static int
400asn1_parse(register const u_char *p, u_int len, struct be *elem)
401{
402	u_char form, class, id;
403	int i, hdr;
404
405	elem->asnlen = 0;
406	elem->type = BE_ANY;
407	if (len < 1) {
408		ifNotTruncated fputs("[nothing to parse]", stdout);
409		return -1;
410	}
411
412	/*
413	 * it would be nice to use a bit field, but you can't depend on them.
414	 *  +---+---+---+---+---+---+---+---+
415	 *  + class |frm|        id         |
416	 *  +---+---+---+---+---+---+---+---+
417	 *    7   6   5   4   3   2   1   0
418	 */
419	id = *p & ASN_ID_BITS;		/* lower 5 bits, range 00-1f */
420#ifdef notdef
421	form = (*p & 0xe0) >> 5;	/* move upper 3 bits to lower 3 */
422	class = form >> 1;		/* bits 7&6 -> bits 1&0, range 0-3 */
423	form &= 0x1;			/* bit 5 -> bit 0, range 0-1 */
424#else
425	form = (u_char)(*p & ASN_FORM_BITS) >> ASN_FORM_SHIFT;
426	class = (u_char)(*p & ASN_CLASS_BITS) >> ASN_CLASS_SHIFT;
427#endif
428	elem->form = form;
429	elem->class = class;
430	elem->id = id;
431	if (vflag > 1)
432		printf("|%.2x", *p);
433	p++; len--; hdr = 1;
434	/* extended tag field */
435	if (id == ASN_ID_EXT) {
436		for (id = 0; *p & ASN_BIT8 && len > 0; len--, hdr++, p++) {
437			if (vflag > 1)
438				printf("|%.2x", *p);
439			id = (id << 7) | (*p & ~ASN_BIT8);
440		}
441		if (len == 0 && *p & ASN_BIT8) {
442			ifNotTruncated fputs("[Xtagfield?]", stdout);
443			return -1;
444		}
445		elem->id = id = (id << 7) | *p;
446		--len;
447		++hdr;
448		++p;
449	}
450	if (len < 1) {
451		ifNotTruncated fputs("[no asnlen]", stdout);
452		return -1;
453	}
454	elem->asnlen = *p;
455	if (vflag > 1)
456		printf("|%.2x", *p);
457	p++; len--; hdr++;
458	if (elem->asnlen & ASN_BIT8) {
459		int noct = elem->asnlen % ASN_BIT8;
460		elem->asnlen = 0;
461		if (len < noct) {
462			ifNotTruncated printf("[asnlen? %d<%d]", len, noct);
463			return -1;
464		}
465		for (; noct-- > 0; len--, hdr++) {
466			if (vflag > 1)
467				printf("|%.2x", *p);
468			elem->asnlen = (elem->asnlen << ASN_SHIFT8) | *p++;
469		}
470	}
471	if (len < elem->asnlen) {
472		if (!truncated) {
473			printf("[len%d<asnlen%u]", len, elem->asnlen);
474			return -1;
475		}
476		/* maybe should check at least 4? */
477		elem->asnlen = len;
478	}
479	if (form >= sizeof(Form)/sizeof(Form[0])) {
480		ifNotTruncated printf("[form?%d]", form);
481		return -1;
482	}
483	if (class >= sizeof(Class)/sizeof(Class[0])) {
484		ifNotTruncated printf("[class?%c/%d]", *Form[form], class);
485		return -1;
486	}
487	if ((int)id >= Class[class].numIDs) {
488		ifNotTruncated printf("[id?%c/%s/%d]", *Form[form],
489			Class[class].name, id);
490		return -1;
491	}
492
493	switch (form) {
494	case PRIMITIVE:
495		switch (class) {
496		case UNIVERSAL:
497			switch (id) {
498			case STRING:
499				elem->type = BE_STR;
500				elem->data.str = p;
501				break;
502
503			case INTEGER: {
504				register int32_t data;
505				elem->type = BE_INT;
506				data = 0;
507
508				if (*p & ASN_BIT8)	/* negative */
509					data = -1;
510				for (i = elem->asnlen; i-- > 0; p++)
511					data = (data << ASN_SHIFT8) | *p;
512				elem->data.integer = data;
513				break;
514			}
515
516			case OBJECTID:
517				elem->type = BE_OID;
518				elem->data.raw = (caddr_t)p;
519				break;
520
521			case ASN_NULL:
522				elem->type = BE_NULL;
523				elem->data.raw = NULL;
524				break;
525
526			default:
527				elem->type = BE_OCTET;
528				elem->data.raw = (caddr_t)p;
529				printf("[P/U/%s]",
530					Class[class].Id[id]);
531				break;
532			}
533			break;
534
535		case APPLICATION:
536			switch (id) {
537			case IPADDR:
538				elem->type = BE_INETADDR;
539				elem->data.raw = (caddr_t)p;
540				break;
541
542			case COUNTER:
543			case GAUGE:
544			case TIMETICKS: {
545				register u_int32_t data;
546				elem->type = BE_UNS;
547				data = 0;
548				for (i = elem->asnlen; i-- > 0; p++)
549					data = (data << 8) + *p;
550				elem->data.uns = data;
551				break;
552			}
553
554			case COUNTER64: {
555				register u_int32_t high, low;
556			        elem->type = BE_UNS64;
557				high = 0, low = 0;
558				for (i = elem->asnlen; i-- > 0; p++) {
559				        high = (high << 8) |
560					    ((low & 0xFF000000) >> 24);
561					low = (low << 8) | *p;
562				}
563				elem->data.uns64.high = high;
564				elem->data.uns64.low = low;
565				break;
566			}
567
568			default:
569				elem->type = BE_OCTET;
570				elem->data.raw = (caddr_t)p;
571				printf("[P/A/%s]",
572					Class[class].Id[id]);
573				break;
574			}
575			break;
576
577		case CONTEXT:
578			switch (id) {
579			case NOSUCHOBJECT:
580				elem->type = BE_NOSUCHOBJECT;
581				elem->data.raw = NULL;
582				break;
583
584			case NOSUCHINSTANCE:
585				elem->type = BE_NOSUCHINST;
586				elem->data.raw = NULL;
587				break;
588
589			case ENDOFMIBVIEW:
590				elem->type = BE_ENDOFMIBVIEW;
591				elem->data.raw = NULL;
592				break;
593			}
594			break;
595
596		default:
597			elem->type = BE_OCTET;
598			elem->data.raw = (caddr_t)p;
599			printf("[P/%s/%s]",
600				Class[class].name, Class[class].Id[id]);
601			break;
602		}
603		break;
604
605	case CONSTRUCTED:
606		switch (class) {
607		case UNIVERSAL:
608			switch (id) {
609			case SEQUENCE:
610				elem->type = BE_SEQ;
611				elem->data.raw = (caddr_t)p;
612				break;
613
614			default:
615				elem->type = BE_OCTET;
616				elem->data.raw = (caddr_t)p;
617				printf("C/U/%s", Class[class].Id[id]);
618				break;
619			}
620			break;
621
622		case CONTEXT:
623			elem->type = BE_PDU;
624			elem->data.raw = (caddr_t)p;
625			break;
626
627		default:
628			elem->type = BE_OCTET;
629			elem->data.raw = (caddr_t)p;
630			printf("C/%s/%s",
631				Class[class].name, Class[class].Id[id]);
632			break;
633		}
634		break;
635	}
636	p += elem->asnlen;
637	len -= elem->asnlen;
638	return elem->asnlen + hdr;
639}
640
641/*
642 * Display the ASN.1 object represented by the BE object.
643 * This used to be an integral part of asn1_parse() before the intermediate
644 * BE form was added.
645 */
646static void
647asn1_print(struct be *elem)
648{
649	u_char *p = (u_char *)elem->data.raw;
650	u_int32_t asnlen = elem->asnlen;
651	int i;
652
653	switch (elem->type) {
654
655	case BE_OCTET:
656		for (i = asnlen; i-- > 0; p++)
657			printf("_%.2x", *p);
658		break;
659
660	case BE_NULL:
661		break;
662
663	case BE_OID: {
664	int o = 0, first = -1, i = asnlen;
665
666		if (!sflag && !nflag && asnlen > 2) {
667			struct obj_abrev *a = &obj_abrev_list[0];
668			for (; a->node; a++) {
669				if (!memcmp(a->oid, (char *)p,
670				    strlen(a->oid))) {
671					objp = a->node->child;
672					i -= strlen(a->oid);
673					p += strlen(a->oid);
674					fputs(a->prefix, stdout);
675					first = 1;
676					break;
677				}
678			}
679		}
680
681		for (; !sflag && i-- > 0; p++) {
682			o = (o << ASN_SHIFT7) + (*p & ~ASN_BIT8);
683			if (*p & ASN_LONGLEN)
684			        continue;
685
686			/*
687			 * first subitem encodes two items with 1st*OIDMUX+2nd
688			 */
689			if (first < 0) {
690				if (!nflag)
691					objp = mibroot;
692				first = 0;
693				OBJ_PRINT(o/OIDMUX, first);
694				o %= OIDMUX;
695			}
696			OBJ_PRINT(o, first);
697			if (--first < 0)
698				first = 0;
699			o = 0;
700		}
701		break;
702	}
703
704	case BE_INT:
705		printf("%d", elem->data.integer);
706		break;
707
708	case BE_UNS:
709		printf("%u", elem->data.uns);
710		break;
711
712	case BE_UNS64: {	/* idea borrowed from by Marshall Rose */
713	        double d;
714		int j, carry;
715		char *cpf, *cpl, last[6], first[30];
716		if (elem->data.uns64.high == 0) {
717		        printf("%u", elem->data.uns64.low);
718		        break;
719		}
720		d = elem->data.uns64.high * 4294967296.0;	/* 2^32 */
721		if (elem->data.uns64.high <= 0x1fffff) {
722		        d += elem->data.uns64.low;
723#if 0 /*is looks illegal, but what is the intention???*/
724			printf("%.f", d);
725#else
726			printf("%f", d);
727#endif
728			break;
729		}
730		d += (elem->data.uns64.low & 0xfffff000);
731#if 0 /*is looks illegal, but what is the intention???*/
732		snprintf(first, sizeof(first), "%.f", d);
733#else
734		snprintf(first, sizeof(first), "%f", d);
735#endif
736		snprintf(last, sizeof(last), "%5.5d",
737		    elem->data.uns64.low & 0xfff);
738		for (carry = 0, cpf = first+strlen(first)-1, cpl = last+4;
739		     cpl >= last;
740		     cpf--, cpl--) {
741		        j = carry + (*cpf - '0') + (*cpl - '0');
742			if (j > 9) {
743			        j -= 10;
744				carry = 1;
745			} else {
746			        carry = 0;
747		        }
748			*cpf = j + '0';
749		}
750		fputs(first, stdout);
751		break;
752	}
753
754	case BE_STR: {
755		register int printable = 1, first = 1;
756		const u_char *p = elem->data.str;
757		for (i = asnlen; printable && i-- > 0; p++)
758			printable = isprint(*p) || isspace(*p);
759		p = elem->data.str;
760		if (printable) {
761			putchar('"');
762			(void)fn_print(p, p + asnlen);
763			putchar('"');
764		} else
765			for (i = asnlen; i-- > 0; p++) {
766				printf(first ? "%.2x" : "_%.2x", *p);
767				first = 0;
768			}
769		break;
770	}
771
772	case BE_SEQ:
773		printf("Seq(%u)", elem->asnlen);
774		break;
775
776	case BE_INETADDR:
777		if (asnlen != ASNLEN_INETADDR)
778			printf("[inetaddr len!=%d]", ASNLEN_INETADDR);
779		for (i = asnlen; i-- > 0; p++) {
780			printf((i == asnlen-1) ? "%u" : ".%u", *p);
781		}
782		break;
783
784	case BE_NOSUCHOBJECT:
785	case BE_NOSUCHINST:
786	case BE_ENDOFMIBVIEW:
787	        printf("[%s]", Class[EXCEPTIONS].Id[elem->id]);
788		break;
789
790	case BE_PDU:
791		printf("%s(%u)",
792			Class[CONTEXT].Id[elem->id], elem->asnlen);
793		break;
794
795	case BE_ANY:
796		fputs("[BE_ANY!?]", stdout);
797		break;
798
799	default:
800		fputs("[be!?]", stdout);
801		break;
802	}
803}
804
805#ifdef notdef
806/*
807 * This is a brute force ASN.1 printer: recurses to dump an entire structure.
808 * This will work for any ASN.1 stream, not just an SNMP PDU.
809 *
810 * By adding newlines and spaces at the correct places, this would print in
811 * Rose-Normal-Form.
812 *
813 * This is not currently used.
814 */
815static void
816asn1_decode(u_char *p, u_int length)
817{
818	struct be elem;
819	int i = 0;
820
821	while (i >= 0 && length > 0) {
822		i = asn1_parse(p, length, &elem);
823		if (i >= 0) {
824			fputs(" ", stdout);
825			asn1_print(&elem);
826			if (elem.type == BE_SEQ || elem.type == BE_PDU) {
827				fputs(" {", stdout);
828				asn1_decode(elem.data.raw, elem.asnlen);
829				fputs(" }", stdout);
830			}
831			length -= i;
832			p += i;
833		}
834	}
835}
836#endif
837
838#ifdef LIBSMI
839
840#if (SMI_VERSION_MAJOR == 0 && SMI_VERSION_MINOR >= 2) || (SMI_VERSION_MAJOR > 0)
841#define LIBSMI_API_V2
842#else
843#define LIBSMI_API_V1
844#endif
845
846#ifdef LIBSMI_API_V1
847/* Some of the API revisions introduced new calls that can be
848 * represented by macros.
849 */
850#define	smiGetNodeType(n)	smiGetType((n)->typemodule, (n)->typename)
851
852#else
853/* These calls in the V1 API were removed in V2. */
854#define	smiFreeRange(r)
855#define	smiFreeType(r)
856#define	smiFreeNode(r)
857#endif
858
859struct smi2be {
860    SmiBasetype basetype;
861    int be;
862};
863
864static struct smi2be smi2betab[] = {
865    { SMI_BASETYPE_INTEGER32,		BE_INT },
866    { SMI_BASETYPE_OCTETSTRING,		BE_STR },
867    { SMI_BASETYPE_OCTETSTRING,		BE_INETADDR },
868    { SMI_BASETYPE_OBJECTIDENTIFIER,	BE_OID },
869    { SMI_BASETYPE_UNSIGNED32,		BE_UNS },
870    { SMI_BASETYPE_INTEGER64,		BE_NONE },
871    { SMI_BASETYPE_UNSIGNED64,		BE_UNS64 },
872    { SMI_BASETYPE_FLOAT32,		BE_NONE },
873    { SMI_BASETYPE_FLOAT64,		BE_NONE },
874    { SMI_BASETYPE_FLOAT128,		BE_NONE },
875    { SMI_BASETYPE_ENUM,		BE_INT },
876    { SMI_BASETYPE_BITS,		BE_STR },
877    { SMI_BASETYPE_UNKNOWN,		BE_NONE }
878};
879
880static void smi_decode_oid(struct be *elem, unsigned int *oid,
881			   unsigned int *oidlen)
882{
883	u_char *p = (u_char *)elem->data.raw;
884	u_int32_t asnlen = elem->asnlen;
885	int o = 0, first = -1, i = asnlen;
886
887	for (*oidlen = 0; sflag && i-- > 0; p++) {
888	        o = (o << ASN_SHIFT7) + (*p & ~ASN_BIT8);
889		if (*p & ASN_LONGLEN)
890		    continue;
891
892		/*
893		 * first subitem encodes two items with 1st*OIDMUX+2nd
894		 */
895		if (first < 0) {
896		        first = 0;
897			oid[(*oidlen)++] = o/OIDMUX;
898			o %= OIDMUX;
899		}
900		oid[(*oidlen)++] = o;
901		o = 0;
902	}
903}
904
905static int smi_check_type(SmiBasetype basetype, int be)
906{
907    int i;
908
909    for (i = 0; smi2betab[i].basetype != SMI_BASETYPE_UNKNOWN; i++) {
910	if (smi2betab[i].basetype == basetype && smi2betab[i].be == be) {
911	    return 1;
912	}
913    }
914
915    return 0;
916}
917
918static int smi_check_a_range(SmiType *smiType, SmiRange *smiRange,
919			     struct be *elem)
920{
921    int ok = 1;
922
923    switch (smiType->basetype) {
924    case SMI_BASETYPE_OBJECTIDENTIFIER:
925    case SMI_BASETYPE_OCTETSTRING:
926	if (smiRange->minValue.value.unsigned32
927	    == smiRange->maxValue.value.unsigned32) {
928	    ok = (elem->asnlen == smiRange->minValue.value.unsigned32);
929	} else {
930	    ok = (elem->asnlen >= smiRange->minValue.value.unsigned32
931		  && elem->asnlen <= smiRange->maxValue.value.unsigned32);
932	}
933	break;
934
935    case SMI_BASETYPE_INTEGER32:
936	ok = (elem->data.integer >= smiRange->minValue.value.integer32
937	      && elem->data.integer <= smiRange->maxValue.value.integer32);
938	break;
939
940    case SMI_BASETYPE_UNSIGNED32:
941	ok = (elem->data.uns >= smiRange->minValue.value.unsigned32
942	      && elem->data.uns <= smiRange->maxValue.value.unsigned32);
943	break;
944
945    case SMI_BASETYPE_UNSIGNED64:
946	/* XXX */
947	break;
948
949	/* case SMI_BASETYPE_INTEGER64: SMIng */
950	/* case SMI_BASETYPE_FLOAT32: SMIng */
951	/* case SMI_BASETYPE_FLOAT64: SMIng */
952	/* case SMI_BASETYPE_FLOAT128: SMIng */
953
954    case SMI_BASETYPE_ENUM:
955    case SMI_BASETYPE_BITS:
956    case SMI_BASETYPE_UNKNOWN:
957	ok = 1;
958	break;
959    }
960
961    return ok;
962}
963
964static int smi_check_range(SmiType *smiType, struct be *elem)
965{
966        SmiRange *smiRange;
967	int ok = 1;
968
969#ifdef LIBSMI_API_V1
970	for (smiRange = smiGetFirstRange(smiType->module, smiType->name);
971#else
972	for (smiRange = smiGetFirstRange(smiType);
973#endif
974	     smiRange;
975	     smiRange = smiGetNextRange(smiRange)) {
976
977	    ok = smi_check_a_range(smiType, smiRange, elem);
978
979	    if (ok) {
980		smiFreeRange(smiRange);
981		break;
982	    }
983	}
984
985	if (ok
986#ifdef LIBSMI_API_V1
987		&& smiType->parentmodule && smiType->parentname
988#endif
989		) {
990	    SmiType *parentType;
991#ifdef LIBSMI_API_V1
992	    parentType = smiGetType(smiType->parentmodule,
993				    smiType->parentname);
994#else
995	    parentType = smiGetParentType(smiType);
996#endif
997	    if (parentType) {
998		ok = smi_check_range(parentType, elem);
999		smiFreeType(parentType);
1000	    }
1001	}
1002
1003	return ok;
1004}
1005
1006static SmiNode *smi_print_variable(struct be *elem)
1007{
1008	unsigned int oid[128], oidlen;
1009	SmiNode *smiNode = NULL;
1010	int i;
1011
1012	smi_decode_oid(elem, oid, &oidlen);
1013	smiNode = smiGetNodeByOID(oidlen, oid);
1014	if (! smiNode) {
1015	        asn1_print(elem);
1016		return NULL;
1017	}
1018	if (vflag) {
1019#ifdef LIBSMI_API_V1
1020		fputs(smiNode->module, stdout);
1021#else
1022		fputs(smiGetNodeModule(smiNode)->name, stdout);
1023#endif
1024		fputs("::", stdout);
1025	}
1026	fputs(smiNode->name, stdout);
1027	if (smiNode->oidlen < oidlen) {
1028	        for (i = smiNode->oidlen; i < oidlen; i++) {
1029		        printf(".%u", oid[i]);
1030		}
1031	}
1032	return smiNode;
1033}
1034
1035static void smi_print_value(SmiNode *smiNode, u_char pduid, struct be *elem)
1036{
1037	unsigned int oid[128], oidlen;
1038	SmiType *smiType;
1039	SmiNamedNumber *nn;
1040	int i, done = 0;
1041
1042	if (! smiNode || ! (smiNode->nodekind
1043			    & (SMI_NODEKIND_SCALAR | SMI_NODEKIND_COLUMN))) {
1044	    asn1_print(elem);
1045	    return;
1046	}
1047
1048	if (elem->type == BE_NOSUCHOBJECT
1049	    || elem->type == BE_NOSUCHINST
1050	    || elem->type == BE_ENDOFMIBVIEW) {
1051	    asn1_print(elem);
1052	    return;
1053	}
1054
1055	if (NOTIFY_CLASS(pduid) && smiNode->access < SMI_ACCESS_NOTIFY) {
1056	    fputs("[notNotifyable]", stdout);
1057	}
1058
1059	if (READ_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_ONLY) {
1060	    fputs("[notReadable]", stdout);
1061	}
1062
1063	if (WRITE_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_WRITE) {
1064	    fputs("[notWritable]", stdout);
1065	}
1066
1067	if (RESPONSE_CLASS(pduid)
1068	    && smiNode->access == SMI_ACCESS_NOT_ACCESSIBLE) {
1069	    fputs("[noAccess]", stdout);
1070	}
1071
1072#ifdef LIBSMI_API_V1
1073	smiType = smiGetType(smiNode->typemodule, smiNode->typename);
1074#else
1075	smiType = smiGetNodeType(smiNode);
1076#endif
1077	if (! smiType) {
1078	    asn1_print(elem);
1079	    return;
1080	}
1081
1082#ifdef LIBSMI_API_V1
1083	if (! smi_check_type(smiNode->basetype, elem->type)) {
1084#else
1085	if (! smi_check_type(smiType->basetype, elem->type)) {
1086#endif
1087	    fputs("[wrongType]", stdout);
1088	}
1089
1090	if (! smi_check_range(smiType, elem)) {
1091	    fputs("[wrongLength]", stdout);
1092	}
1093
1094	/* resolve bits to named bits */
1095
1096	/* check whether instance identifier is valid */
1097
1098	/* apply display hints (integer, octetstring) */
1099
1100	/* convert instance identifier to index type values */
1101
1102	switch (elem->type) {
1103	case BE_OID:
1104	        if (smiType->basetype == SMI_BASETYPE_BITS) {
1105		        /* print bit labels */
1106		} else {
1107		        smi_decode_oid(elem, oid, &oidlen);
1108			smiNode = smiGetNodeByOID(oidlen, oid);
1109			if (smiNode) {
1110			        if (vflag) {
1111#ifdef LIBSMI_API_V1
1112					fputs(smiNode->module, stdout);
1113#else
1114					fputs(smiGetNodeModule(smiNode)->name, stdout);
1115#endif
1116					fputs("::", stdout);
1117				}
1118				fputs(smiNode->name, stdout);
1119				if (smiNode->oidlen < oidlen) {
1120				        for (i = smiNode->oidlen;
1121					     i < oidlen; i++) {
1122					        printf(".%u", oid[i]);
1123					}
1124				}
1125				done++;
1126			}
1127		}
1128		break;
1129
1130	case BE_INT:
1131#ifdef LIBSMI_API_V1
1132	        if (smiNode->basetype == SMI_BASETYPE_ENUM
1133		    && smiNode->typemodule && smiNode->typename) {
1134		        for (nn = smiGetFirstNamedNumber(smiNode->typemodule,
1135							 smiNode->typename);
1136#else
1137	        if (smiType->basetype == SMI_BASETYPE_ENUM) {
1138		        for (nn = smiGetFirstNamedNumber(smiType);
1139#endif
1140			     nn;
1141			     nn = smiGetNextNamedNumber(nn)) {
1142			         if (nn->value.value.integer32
1143				     == elem->data.integer) {
1144				         fputs(nn->name, stdout);
1145					 printf("(%d)", elem->data.integer);
1146					 done++;
1147					 break;
1148				}
1149			}
1150		}
1151		break;
1152	}
1153
1154	if (! done) {
1155	        asn1_print(elem);
1156	}
1157
1158	if (smiType) {
1159		smiFreeType(smiType);
1160	}
1161}
1162#endif
1163
1164/*
1165 * General SNMP header
1166 *	SEQUENCE {
1167 *		version INTEGER {version-1(0)},
1168 *		community OCTET STRING,
1169 *		data ANY	-- PDUs
1170 *	}
1171 * PDUs for all but Trap: (see rfc1157 from page 15 on)
1172 *	SEQUENCE {
1173 *		request-id INTEGER,
1174 *		error-status INTEGER,
1175 *		error-index INTEGER,
1176 *		varbindlist SEQUENCE OF
1177 *			SEQUENCE {
1178 *				name ObjectName,
1179 *				value ObjectValue
1180 *			}
1181 *	}
1182 * PDU for Trap:
1183 *	SEQUENCE {
1184 *		enterprise OBJECT IDENTIFIER,
1185 *		agent-addr NetworkAddress,
1186 *		generic-trap INTEGER,
1187 *		specific-trap INTEGER,
1188 *		time-stamp TimeTicks,
1189 *		varbindlist SEQUENCE OF
1190 *			SEQUENCE {
1191 *				name ObjectName,
1192 *				value ObjectValue
1193 *			}
1194 *	}
1195 */
1196
1197/*
1198 * Decode SNMP varBind
1199 */
1200static void
1201varbind_print(u_char pduid, const u_char *np, u_int length)
1202{
1203	struct be elem;
1204	int count = 0, ind;
1205#ifdef LIBSMI
1206	SmiNode *smiNode = NULL;
1207#endif
1208
1209	/* Sequence of varBind */
1210	if ((count = asn1_parse(np, length, &elem)) < 0)
1211		return;
1212	if (elem.type != BE_SEQ) {
1213		fputs("[!SEQ of varbind]", stdout);
1214		asn1_print(&elem);
1215		return;
1216	}
1217	if (count < length)
1218		printf("[%d extra after SEQ of varbind]", length - count);
1219	/* descend */
1220	length = elem.asnlen;
1221	np = (u_char *)elem.data.raw;
1222
1223	for (ind = 1; length > 0; ind++) {
1224		const u_char *vbend;
1225		u_int vblength;
1226
1227		fputs(" ", stdout);
1228
1229		/* Sequence */
1230		if ((count = asn1_parse(np, length, &elem)) < 0)
1231			return;
1232		if (elem.type != BE_SEQ) {
1233			fputs("[!varbind]", stdout);
1234			asn1_print(&elem);
1235			return;
1236		}
1237		vbend = np + count;
1238		vblength = length - count;
1239		/* descend */
1240		length = elem.asnlen;
1241		np = (u_char *)elem.data.raw;
1242
1243		/* objName (OID) */
1244		if ((count = asn1_parse(np, length, &elem)) < 0)
1245			return;
1246		if (elem.type != BE_OID) {
1247			fputs("[objName!=OID]", stdout);
1248			asn1_print(&elem);
1249			return;
1250		}
1251#ifdef LIBSMI
1252		smiNode = smi_print_variable(&elem);
1253#else
1254		asn1_print(&elem);
1255#endif
1256		length -= count;
1257		np += count;
1258
1259		if (pduid != GETREQ && pduid != GETNEXTREQ
1260		    && pduid != GETBULKREQ)
1261				fputs("=", stdout);
1262
1263		/* objVal (ANY) */
1264		if ((count = asn1_parse(np, length, &elem)) < 0)
1265			return;
1266		if (pduid == GETREQ || pduid == GETNEXTREQ
1267		    || pduid == GETBULKREQ) {
1268			if (elem.type != BE_NULL) {
1269				fputs("[objVal!=NULL]", stdout);
1270				asn1_print(&elem);
1271			}
1272		} else {
1273		        if (elem.type != BE_NULL) {
1274#ifdef LIBSMI
1275			        smi_print_value(smiNode, pduid, &elem);
1276				smiFreeNode(smiNode);
1277#else
1278				asn1_print(&elem);
1279#endif
1280			}
1281		}
1282		length = vblength;
1283		np = vbend;
1284	}
1285}
1286
1287/*
1288 * Decode SNMP PDUs: GetRequest, GetNextRequest, GetResponse, SetRequest,
1289 * GetBulk, Inform, V2Trap, and Report
1290 */
1291static void
1292snmppdu_print(u_char pduid, const u_char *np, u_int length)
1293{
1294	struct be elem;
1295	int count = 0, error;
1296
1297	/* reqId (Integer) */
1298	if ((count = asn1_parse(np, length, &elem)) < 0)
1299		return;
1300	if (elem.type != BE_INT) {
1301		fputs("[reqId!=INT]", stdout);
1302		asn1_print(&elem);
1303		return;
1304	}
1305	if (vflag)
1306		printf("R=%d ", elem.data.integer);
1307	length -= count;
1308	np += count;
1309
1310	/* errorStatus (Integer) */
1311	if ((count = asn1_parse(np, length, &elem)) < 0)
1312		return;
1313	if (elem.type != BE_INT) {
1314		fputs("[errorStatus!=INT]", stdout);
1315		asn1_print(&elem);
1316		return;
1317	}
1318	error = 0;
1319	if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
1320	    || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
1321	    && elem.data.integer != 0) {
1322		char errbuf[10];
1323		printf("[errorStatus(%s)!=0]",
1324			DECODE_ErrorStatus(elem.data.integer));
1325	} else if (pduid == GETBULKREQ) {
1326	        printf(" N=%d", elem.data.integer);
1327	} else if (elem.data.integer != 0) {
1328		char errbuf[10];
1329		printf(" %s", DECODE_ErrorStatus(elem.data.integer));
1330		error = elem.data.integer;
1331	}
1332	length -= count;
1333	np += count;
1334
1335	/* errorIndex (Integer) */
1336	if ((count = asn1_parse(np, length, &elem)) < 0)
1337		return;
1338	if (elem.type != BE_INT) {
1339		fputs("[errorIndex!=INT]", stdout);
1340		asn1_print(&elem);
1341		return;
1342	}
1343	if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
1344	    || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
1345	    && elem.data.integer != 0)
1346		printf("[errorIndex(%d)!=0]", elem.data.integer);
1347	else if (pduid == GETBULKREQ)
1348	        printf(" M=%d", elem.data.integer);
1349	else if (elem.data.integer != 0) {
1350		if (!error)
1351			printf("[errorIndex(%d) w/o errorStatus]",
1352				elem.data.integer);
1353		else {
1354			printf("@%d", elem.data.integer);
1355			error = elem.data.integer;
1356		}
1357	} else if (error) {
1358		fputs("[errorIndex==0]", stdout);
1359		error = 0;
1360	}
1361	length -= count;
1362	np += count;
1363
1364	varbind_print(pduid, np, length);
1365	return;
1366}
1367
1368/*
1369 * Decode SNMP Trap PDU
1370 */
1371static void
1372trappdu_print(const u_char *np, u_int length)
1373{
1374	struct be elem;
1375	int count = 0, generic;
1376
1377	putchar(' ');
1378
1379	/* enterprise (oid) */
1380	if ((count = asn1_parse(np, length, &elem)) < 0)
1381		return;
1382	if (elem.type != BE_OID) {
1383		fputs("[enterprise!=OID]", stdout);
1384		asn1_print(&elem);
1385		return;
1386	}
1387	asn1_print(&elem);
1388	length -= count;
1389	np += count;
1390
1391	putchar(' ');
1392
1393	/* agent-addr (inetaddr) */
1394	if ((count = asn1_parse(np, length, &elem)) < 0)
1395		return;
1396	if (elem.type != BE_INETADDR) {
1397		fputs("[agent-addr!=INETADDR]", stdout);
1398		asn1_print(&elem);
1399		return;
1400	}
1401	asn1_print(&elem);
1402	length -= count;
1403	np += count;
1404
1405	/* generic-trap (Integer) */
1406	if ((count = asn1_parse(np, length, &elem)) < 0)
1407		return;
1408	if (elem.type != BE_INT) {
1409		fputs("[generic-trap!=INT]", stdout);
1410		asn1_print(&elem);
1411		return;
1412	}
1413	generic = elem.data.integer;
1414	{
1415		char buf[10];
1416		printf(" %s", DECODE_GenericTrap(generic));
1417	}
1418	length -= count;
1419	np += count;
1420
1421	/* specific-trap (Integer) */
1422	if ((count = asn1_parse(np, length, &elem)) < 0)
1423		return;
1424	if (elem.type != BE_INT) {
1425		fputs("[specific-trap!=INT]", stdout);
1426		asn1_print(&elem);
1427		return;
1428	}
1429	if (generic != GT_ENTERPRISE) {
1430		if (elem.data.integer != 0)
1431			printf("[specific-trap(%d)!=0]", elem.data.integer);
1432	} else
1433		printf(" s=%d", elem.data.integer);
1434	length -= count;
1435	np += count;
1436
1437	putchar(' ');
1438
1439	/* time-stamp (TimeTicks) */
1440	if ((count = asn1_parse(np, length, &elem)) < 0)
1441		return;
1442	if (elem.type != BE_UNS) {			/* XXX */
1443		fputs("[time-stamp!=TIMETICKS]", stdout);
1444		asn1_print(&elem);
1445		return;
1446	}
1447	asn1_print(&elem);
1448	length -= count;
1449	np += count;
1450
1451	varbind_print (TRAP, np, length);
1452	return;
1453}
1454
1455/*
1456 * Decode arbitrary SNMP PDUs.
1457 */
1458static void
1459pdu_print(const u_char *np, u_int length, int version)
1460{
1461	struct be pdu;
1462	int count = 0;
1463
1464	/* PDU (Context) */
1465	if ((count = asn1_parse(np, length, &pdu)) < 0)
1466		return;
1467	if (pdu.type != BE_PDU) {
1468		fputs("[no PDU]", stdout);
1469		return;
1470	}
1471	if (count < length)
1472		printf("[%d extra after PDU]", length - count);
1473	if (vflag) {
1474		fputs("{ ", stdout);
1475	}
1476	asn1_print(&pdu);
1477	fputs(" ", stdout);
1478	/* descend into PDU */
1479	length = pdu.asnlen;
1480	np = (u_char *)pdu.data.raw;
1481
1482	if (version == SNMP_VERSION_1 &&
1483	    (pdu.id == GETBULKREQ || pdu.id == INFORMREQ ||
1484	     pdu.id == V2TRAP || pdu.id == REPORT)) {
1485	        printf("[v2 PDU in v1 message]");
1486		return;
1487	}
1488
1489	if (version == SNMP_VERSION_2 && pdu.id == TRAP) {
1490	        printf("[v1 PDU in v2 message]");
1491		return;
1492	}
1493
1494	switch (pdu.id) {
1495	case TRAP:
1496		trappdu_print(np, length);
1497		break;
1498	case GETREQ:
1499	case GETNEXTREQ:
1500	case GETRESP:
1501	case SETREQ:
1502	case GETBULKREQ:
1503	case INFORMREQ:
1504	case V2TRAP:
1505	case REPORT:
1506		snmppdu_print(pdu.id, np, length);
1507		break;
1508	}
1509
1510	if (vflag) {
1511		fputs("} ", stdout);
1512	}
1513}
1514
1515/*
1516 * Decode a scoped SNMP PDU.
1517 */
1518static void
1519scopedpdu_print(const u_char *np, u_int length, int version)
1520{
1521	struct be elem;
1522	int i, count = 0;
1523
1524	/* Sequence */
1525	if ((count = asn1_parse(np, length, &elem)) < 0)
1526		return;
1527	if (elem.type != BE_SEQ) {
1528		fputs("[!scoped PDU]", stdout);
1529		asn1_print(&elem);
1530		return;
1531	}
1532	length = elem.asnlen;
1533	np = (u_char *)elem.data.raw;
1534
1535	/* contextEngineID (OCTET STRING) */
1536	if ((count = asn1_parse(np, length, &elem)) < 0)
1537		return;
1538	if (elem.type != BE_STR) {
1539		fputs("[contextEngineID!=STR]", stdout);
1540		asn1_print(&elem);
1541		return;
1542	}
1543	length -= count;
1544	np += count;
1545
1546	fputs("E= ", stdout);
1547	for (i = 0; i < (int)elem.asnlen; i++) {
1548            printf("0x%02X", elem.data.str[i]);
1549        }
1550	fputs(" ", stdout);
1551
1552	/* contextName (OCTET STRING) */
1553	if ((count = asn1_parse(np, length, &elem)) < 0)
1554		return;
1555	if (elem.type != BE_STR) {
1556		fputs("[contextName!=STR]", stdout);
1557		asn1_print(&elem);
1558		return;
1559	}
1560	length -= count;
1561	np += count;
1562
1563	printf("C=%.*s ", (int)elem.asnlen, elem.data.str);
1564
1565	pdu_print(np, length, version);
1566}
1567
1568/*
1569 * Decode SNMP Community Header (SNMPv1 and SNMPv2c)
1570 */
1571static void
1572community_print(const u_char *np, u_int length, int version)
1573{
1574	struct be elem;
1575	int count = 0;
1576
1577	/* Community (String) */
1578	if ((count = asn1_parse(np, length, &elem)) < 0)
1579		return;
1580	if (elem.type != BE_STR) {
1581		fputs("[comm!=STR]", stdout);
1582		asn1_print(&elem);
1583		return;
1584	}
1585	/* default community */
1586	if (strncmp((char *)elem.data.str, DEF_COMMUNITY,
1587	    sizeof(DEF_COMMUNITY) - 1))
1588		/* ! "public" */
1589		printf("C=%.*s ", (int)elem.asnlen, elem.data.str);
1590	length -= count;
1591	np += count;
1592
1593	pdu_print(np, length, version);
1594}
1595
1596/*
1597 * Decode SNMPv3 User-based Security Message Header (SNMPv3)
1598 */
1599static void
1600usm_print(const u_char *np, u_int length)
1601{
1602        struct be elem;
1603	int count = 0;
1604
1605	/* Sequence */
1606	if ((count = asn1_parse(np, length, &elem)) < 0)
1607		return;
1608	if (elem.type != BE_SEQ) {
1609		fputs("[!usm]", stdout);
1610		asn1_print(&elem);
1611		return;
1612	}
1613	length = elem.asnlen;
1614	np = (u_char *)elem.data.raw;
1615
1616	/* msgAuthoritativeEngineID (OCTET STRING) */
1617	if ((count = asn1_parse(np, length, &elem)) < 0)
1618		return;
1619	if (elem.type != BE_STR) {
1620		fputs("[msgAuthoritativeEngineID!=STR]", stdout);
1621		asn1_print(&elem);
1622		return;
1623	}
1624	length -= count;
1625	np += count;
1626
1627	/* msgAuthoritativeEngineBoots (INTEGER) */
1628	if ((count = asn1_parse(np, length, &elem)) < 0)
1629		return;
1630	if (elem.type != BE_INT) {
1631		fputs("[msgAuthoritativeEngineBoots!=INT]", stdout);
1632		asn1_print(&elem);
1633		return;
1634	}
1635	if (vflag)
1636	        printf("B=%d ", elem.data.integer);
1637	length -= count;
1638	np += count;
1639
1640	/* msgAuthoritativeEngineTime (INTEGER) */
1641	if ((count = asn1_parse(np, length, &elem)) < 0)
1642		return;
1643	if (elem.type != BE_INT) {
1644		fputs("[msgAuthoritativeEngineTime!=INT]", stdout);
1645		asn1_print(&elem);
1646		return;
1647	}
1648	if (vflag)
1649	        printf("T=%d ", elem.data.integer);
1650	length -= count;
1651	np += count;
1652
1653	/* msgUserName (OCTET STRING) */
1654	if ((count = asn1_parse(np, length, &elem)) < 0)
1655		return;
1656	if (elem.type != BE_STR) {
1657		fputs("[msgUserName!=STR]", stdout);
1658		asn1_print(&elem);
1659		return;
1660	}
1661	length -= count;
1662        np += count;
1663
1664	printf("U=%.*s ", (int)elem.asnlen, elem.data.str);
1665
1666	/* msgAuthenticationParameters (OCTET STRING) */
1667	if ((count = asn1_parse(np, length, &elem)) < 0)
1668		return;
1669	if (elem.type != BE_STR) {
1670		fputs("[msgAuthenticationParameters!=STR]", stdout);
1671		asn1_print(&elem);
1672		return;
1673	}
1674	length -= count;
1675        np += count;
1676
1677	/* msgPrivacyParameters (OCTET STRING) */
1678	if ((count = asn1_parse(np, length, &elem)) < 0)
1679		return;
1680	if (elem.type != BE_STR) {
1681		fputs("[msgPrivacyParameters!=STR]", stdout);
1682		asn1_print(&elem);
1683		return;
1684	}
1685	length -= count;
1686        np += count;
1687
1688	if (count < length)
1689		printf("[%d extra after usm SEQ]", length - count);
1690}
1691
1692/*
1693 * Decode SNMPv3 Message Header (SNMPv3)
1694 */
1695static void
1696v3msg_print(const u_char *np, u_int length)
1697{
1698	struct be elem;
1699	int count = 0;
1700	u_char flags;
1701	int model;
1702	const u_char *xnp = np;
1703	int xlength = length;
1704
1705	/* Sequence */
1706	if ((count = asn1_parse(np, length, &elem)) < 0)
1707		return;
1708	if (elem.type != BE_SEQ) {
1709		fputs("[!message]", stdout);
1710		asn1_print(&elem);
1711		return;
1712	}
1713	length = elem.asnlen;
1714	np = (u_char *)elem.data.raw;
1715
1716	if (vflag) {
1717		fputs("{ ", stdout);
1718	}
1719
1720	/* msgID (INTEGER) */
1721	if ((count = asn1_parse(np, length, &elem)) < 0)
1722		return;
1723	if (elem.type != BE_INT) {
1724		fputs("[msgID!=INT]", stdout);
1725		asn1_print(&elem);
1726		return;
1727	}
1728	length -= count;
1729	np += count;
1730
1731	/* msgMaxSize (INTEGER) */
1732	if ((count = asn1_parse(np, length, &elem)) < 0)
1733		return;
1734	if (elem.type != BE_INT) {
1735		fputs("[msgMaxSize!=INT]", stdout);
1736		asn1_print(&elem);
1737		return;
1738	}
1739	length -= count;
1740	np += count;
1741
1742	/* msgFlags (OCTET STRING) */
1743	if ((count = asn1_parse(np, length, &elem)) < 0)
1744		return;
1745	if (elem.type != BE_STR) {
1746		fputs("[msgFlags!=STR]", stdout);
1747		asn1_print(&elem);
1748		return;
1749	}
1750	if (elem.asnlen != 1) {
1751	        printf("[msgFlags size %d]", elem.asnlen);
1752		return;
1753	}
1754	flags = elem.data.str[0];
1755	if (flags != 0x00 && flags != 0x01 && flags != 0x03
1756	    && flags != 0x04 && flags != 0x05 && flags != 0x07) {
1757		printf("[msgFlags=0x%02X]", flags);
1758		return;
1759	}
1760	length -= count;
1761	np += count;
1762
1763	fputs("F=", stdout);
1764	if (flags & 0x01) fputs("a", stdout);
1765	if (flags & 0x02) fputs("p", stdout);
1766	if (flags & 0x04) fputs("r", stdout);
1767	fputs(" ", stdout);
1768
1769	/* msgSecurityModel (INTEGER) */
1770	if ((count = asn1_parse(np, length, &elem)) < 0)
1771		return;
1772	if (elem.type != BE_INT) {
1773		fputs("[msgSecurityModel!=INT]", stdout);
1774		asn1_print(&elem);
1775		return;
1776	}
1777	model = elem.data.integer;
1778	length -= count;
1779	np += count;
1780
1781	if (count < length)
1782		printf("[%d extra after message SEQ]", length - count);
1783
1784	if (vflag) {
1785		fputs("} ", stdout);
1786	}
1787
1788	if (model == 3) {
1789	    if (vflag) {
1790		fputs("{ USM ", stdout);
1791	    }
1792	} else {
1793	    printf("[security model %d]", model);
1794            return;
1795	}
1796
1797	np = xnp + (np - xnp);
1798	length = xlength - (np - xnp);
1799
1800	/* msgSecurityParameters (OCTET STRING) */
1801	if ((count = asn1_parse(np, length, &elem)) < 0)
1802		return;
1803	if (elem.type != BE_STR) {
1804		fputs("[msgSecurityParameters!=STR]", stdout);
1805		asn1_print(&elem);
1806		return;
1807	}
1808	length -= count;
1809	np += count;
1810
1811	if (model == 3) {
1812	    usm_print(elem.data.str, elem.asnlen);
1813	    if (vflag) {
1814		fputs("} ", stdout);
1815	    }
1816	}
1817
1818	if (vflag) {
1819	    fputs("{ ScopedPDU ", stdout);
1820	}
1821
1822	scopedpdu_print(np, length, 3);
1823
1824	if (vflag) {
1825		fputs("} ", stdout);
1826	}
1827}
1828
1829/*
1830 * Decode SNMP header and pass on to PDU printing routines
1831 */
1832void
1833snmp_print(const u_char *np, u_int length)
1834{
1835	struct be elem;
1836	int count = 0;
1837	int version = 0;
1838
1839	truncated = 0;
1840
1841	/* truncated packet? */
1842	if (np + length > snapend) {
1843		truncated = 1;
1844		length = snapend - np;
1845	}
1846
1847	putchar(' ');
1848
1849	/* initial Sequence */
1850	if ((count = asn1_parse(np, length, &elem)) < 0)
1851		return;
1852	if (elem.type != BE_SEQ) {
1853		fputs("[!init SEQ]", stdout);
1854		asn1_print(&elem);
1855		return;
1856	}
1857	if (count < length)
1858		printf("[%d extra after iSEQ]", length - count);
1859	/* descend */
1860	length = elem.asnlen;
1861	np = (u_char *)elem.data.raw;
1862
1863	/* Version (INTEGER) */
1864	if ((count = asn1_parse(np, length, &elem)) < 0)
1865		return;
1866	if (elem.type != BE_INT) {
1867		fputs("[version!=INT]", stdout);
1868		asn1_print(&elem);
1869		return;
1870	}
1871
1872	switch (elem.data.integer) {
1873	case SNMP_VERSION_1:
1874	case SNMP_VERSION_2:
1875	case SNMP_VERSION_3:
1876	        if (vflag)
1877		        printf("{ %s ", SnmpVersion[elem.data.integer]);
1878		break;
1879	default:
1880	        printf("[version = %d]", elem.data.integer);
1881		return;
1882	}
1883	version = elem.data.integer;
1884	length -= count;
1885	np += count;
1886
1887	switch (version) {
1888	case SNMP_VERSION_1:
1889        case SNMP_VERSION_2:
1890		community_print(np, length, version);
1891		break;
1892	case SNMP_VERSION_3:
1893		v3msg_print(np, length);
1894		break;
1895	default:
1896	        printf("[version = %d]", elem.data.integer);
1897		break;
1898	}
1899
1900	if (vflag) {
1901		fputs("} ", stdout);
1902	}
1903}
1904