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