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