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