xref: /freebsd/contrib/tcpdump/print-snmp.c (revision f9218d3d4fd34f082473b3a021c6d4d109fb47cf)
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[] =
61     "@(#) $Header: /tcpdump/master/tcpdump/print-snmp.c,v 1.50.4.2 2002/07/20 23:33:08 guy Exp $ (LBL)";
62 #endif
63 
64 #ifdef HAVE_CONFIG_H
65 #include "config.h"
66 #endif
67 
68 #include <sys/param.h>
69 #include <sys/time.h>
70 
71 #include <ctype.h>
72 #include <stdio.h>
73 #include <string.h>
74 
75 #ifdef HAVE_SMI_H
76 #include <smi.h>
77 #endif
78 
79 #include "interface.h"
80 #include "addrtoname.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 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 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 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 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 char *Private[] = {
172 	"P-0"
173 };
174 
175 /*
176  * error-status values for any SNMP PDU
177  */
178 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 && 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 char *GenericTrap[] = {
208 	"coldStart",
209 	"warmStart",
210 	"linkDown",
211 	"linkUp",
212 	"authenticationFailure",
213 	"egpNeighborLoss",
214 	"enterpriseSpecific"
215 #define GT_ENTERPRISE 7
216 };
217 #define DECODE_GenericTrap(t) \
218 	( t >= 0 && 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 	char	*name;
230 	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 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 	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 	char *prefix;			/* prefix for this abrev */
283 	struct obj *node;		/* pointer into object table */
284 	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 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  * truncated==1 means the packet was complete, but we don't have all of
399  * it to decode.
400  */
401 static int truncated;
402 #define ifNotTruncated if (truncated) fputs("[|snmp]", stdout); else
403 
404 /*
405  * This decodes the next ASN.1 object in the stream pointed to by "p"
406  * (and of real-length "len") and stores the intermediate data in the
407  * provided BE object.
408  *
409  * This returns -l if it fails (i.e., the ASN.1 stream is not valid).
410  * O/w, this returns the number of bytes parsed from "p".
411  */
412 static int
413 asn1_parse(register const u_char *p, u_int len, struct be *elem)
414 {
415 	u_char form, class, id;
416 	int i, hdr;
417 
418 	elem->asnlen = 0;
419 	elem->type = BE_ANY;
420 	if (len < 1) {
421 		ifNotTruncated fputs("[nothing to parse]", stdout);
422 		return -1;
423 	}
424 
425 	/*
426 	 * it would be nice to use a bit field, but you can't depend on them.
427 	 *  +---+---+---+---+---+---+---+---+
428 	 *  + class |frm|        id         |
429 	 *  +---+---+---+---+---+---+---+---+
430 	 *    7   6   5   4   3   2   1   0
431 	 */
432 	id = *p & ASN_ID_BITS;		/* lower 5 bits, range 00-1f */
433 #ifdef notdef
434 	form = (*p & 0xe0) >> 5;	/* move upper 3 bits to lower 3 */
435 	class = form >> 1;		/* bits 7&6 -> bits 1&0, range 0-3 */
436 	form &= 0x1;			/* bit 5 -> bit 0, range 0-1 */
437 #else
438 	form = (u_char)(*p & ASN_FORM_BITS) >> ASN_FORM_SHIFT;
439 	class = (u_char)(*p & ASN_CLASS_BITS) >> ASN_CLASS_SHIFT;
440 #endif
441 	elem->form = form;
442 	elem->class = class;
443 	elem->id = id;
444 	p++; len--; hdr = 1;
445 	/* extended tag field */
446 	if (id == ASN_ID_EXT) {
447 		for (id = 0; *p & ASN_BIT8 && len > 0; len--, hdr++, p++)
448 			id = (id << 7) | (*p & ~ASN_BIT8);
449 		if (len == 0 && *p & ASN_BIT8) {
450 			ifNotTruncated fputs("[Xtagfield?]", stdout);
451 			return -1;
452 		}
453 		elem->id = id = (id << 7) | *p;
454 		--len;
455 		++hdr;
456 		++p;
457 	}
458 	if (len < 1) {
459 		ifNotTruncated fputs("[no asnlen]", stdout);
460 		return -1;
461 	}
462 	elem->asnlen = *p;
463 	p++; len--; hdr++;
464 	if (elem->asnlen & ASN_BIT8) {
465 		int noct = elem->asnlen % ASN_BIT8;
466 		elem->asnlen = 0;
467 		if (len < noct) {
468 			ifNotTruncated printf("[asnlen? %d<%d]", len, noct);
469 			return -1;
470 		}
471 		for (; noct-- > 0; len--, hdr++)
472 			elem->asnlen = (elem->asnlen << ASN_SHIFT8) | *p++;
473 	}
474 	if (len < elem->asnlen) {
475 		if (!truncated) {
476 			printf("[len%d<asnlen%u]", len, elem->asnlen);
477 			return -1;
478 		}
479 		/* maybe should check at least 4? */
480 		elem->asnlen = len;
481 	}
482 	if (form >= sizeof(Form)/sizeof(Form[0])) {
483 		ifNotTruncated printf("[form?%d]", form);
484 		return -1;
485 	}
486 	if (class >= sizeof(Class)/sizeof(Class[0])) {
487 		ifNotTruncated printf("[class?%c/%d]", *Form[form], class);
488 		return -1;
489 	}
490 	if ((int)id >= Class[class].numIDs) {
491 		ifNotTruncated printf("[id?%c/%s/%d]", *Form[form],
492 			Class[class].name, id);
493 		return -1;
494 	}
495 
496 	switch (form) {
497 	case PRIMITIVE:
498 		switch (class) {
499 		case UNIVERSAL:
500 			switch (id) {
501 			case STRING:
502 				elem->type = BE_STR;
503 				elem->data.str = p;
504 				break;
505 
506 			case INTEGER: {
507 				register int32_t data;
508 				elem->type = BE_INT;
509 				data = 0;
510 
511 				if (*p & ASN_BIT8)	/* negative */
512 					data = -1;
513 				for (i = elem->asnlen; i-- > 0; p++)
514 					data = (data << ASN_SHIFT8) | *p;
515 				elem->data.integer = data;
516 				break;
517 			}
518 
519 			case OBJECTID:
520 				elem->type = BE_OID;
521 				elem->data.raw = (caddr_t)p;
522 				break;
523 
524 			case ASN_NULL:
525 				elem->type = BE_NULL;
526 				elem->data.raw = NULL;
527 				break;
528 
529 			default:
530 				elem->type = BE_OCTET;
531 				elem->data.raw = (caddr_t)p;
532 				printf("[P/U/%s]",
533 					Class[class].Id[id]);
534 				break;
535 			}
536 			break;
537 
538 		case APPLICATION:
539 			switch (id) {
540 			case IPADDR:
541 				elem->type = BE_INETADDR;
542 				elem->data.raw = (caddr_t)p;
543 				break;
544 
545 			case COUNTER:
546 			case GAUGE:
547 			case TIMETICKS: {
548 				register u_int32_t data;
549 				elem->type = BE_UNS;
550 				data = 0;
551 				for (i = elem->asnlen; i-- > 0; p++)
552 					data = (data << 8) + *p;
553 				elem->data.uns = data;
554 				break;
555 			}
556 
557 			case COUNTER64: {
558 				register u_int32_t high, low;
559 			        elem->type = BE_UNS64;
560 				high = 0, low = 0;
561 				for (i = elem->asnlen; i-- > 0; p++) {
562 				        high = (high << 8) |
563 					    ((low & 0xFF000000) >> 24);
564 					low = (low << 8) | *p;
565 				}
566 				elem->data.uns64.high = high;
567 				elem->data.uns64.low = low;
568 				break;
569 			}
570 
571 			default:
572 				elem->type = BE_OCTET;
573 				elem->data.raw = (caddr_t)p;
574 				printf("[P/A/%s]",
575 					Class[class].Id[id]);
576 				break;
577 			}
578 			break;
579 
580 		case CONTEXT:
581 			switch (id) {
582 			case NOSUCHOBJECT:
583 				elem->type = BE_NOSUCHOBJECT;
584 				elem->data.raw = NULL;
585 				break;
586 
587 			case NOSUCHINSTANCE:
588 				elem->type = BE_NOSUCHINST;
589 				elem->data.raw = NULL;
590 				break;
591 
592 			case ENDOFMIBVIEW:
593 				elem->type = BE_ENDOFMIBVIEW;
594 				elem->data.raw = NULL;
595 				break;
596 			}
597 			break;
598 
599 		default:
600 			elem->type = BE_OCTET;
601 			elem->data.raw = (caddr_t)p;
602 			printf("[P/%s/%s]",
603 				Class[class].name, Class[class].Id[id]);
604 			break;
605 		}
606 		break;
607 
608 	case CONSTRUCTED:
609 		switch (class) {
610 		case UNIVERSAL:
611 			switch (id) {
612 			case SEQUENCE:
613 				elem->type = BE_SEQ;
614 				elem->data.raw = (caddr_t)p;
615 				break;
616 
617 			default:
618 				elem->type = BE_OCTET;
619 				elem->data.raw = (caddr_t)p;
620 				printf("C/U/%s", Class[class].Id[id]);
621 				break;
622 			}
623 			break;
624 
625 		case CONTEXT:
626 			elem->type = BE_PDU;
627 			elem->data.raw = (caddr_t)p;
628 			break;
629 
630 		default:
631 			elem->type = BE_OCTET;
632 			elem->data.raw = (caddr_t)p;
633 			printf("C/%s/%s",
634 				Class[class].name, Class[class].Id[id]);
635 			break;
636 		}
637 		break;
638 	}
639 	p += elem->asnlen;
640 	len -= elem->asnlen;
641 	return elem->asnlen + hdr;
642 }
643 
644 /*
645  * Display the ASN.1 object represented by the BE object.
646  * This used to be an integral part of asn1_parse() before the intermediate
647  * BE form was added.
648  */
649 static void
650 asn1_print(struct be *elem)
651 {
652 	u_char *p = (u_char *)elem->data.raw;
653 	u_int32_t asnlen = elem->asnlen;
654 	int i;
655 
656 	switch (elem->type) {
657 
658 	case BE_OCTET:
659 		for (i = asnlen; i-- > 0; p++)
660 			printf("_%.2x", *p);
661 		break;
662 
663 	case BE_NULL:
664 		break;
665 
666 	case BE_OID: {
667 	int o = 0, first = -1, i = asnlen;
668 
669 		if (!sflag && !nflag && asnlen > 2) {
670 			struct obj_abrev *a = &obj_abrev_list[0];
671 			for (; a->node; a++) {
672 				if (!memcmp(a->oid, (char *)p,
673 				    strlen(a->oid))) {
674 					objp = a->node->child;
675 					i -= strlen(a->oid);
676 					p += strlen(a->oid);
677 					fputs(a->prefix, stdout);
678 					first = 1;
679 					break;
680 				}
681 			}
682 		}
683 
684 		for (; !sflag && i-- > 0; p++) {
685 			o = (o << ASN_SHIFT7) + (*p & ~ASN_BIT8);
686 			if (*p & ASN_LONGLEN)
687 			        continue;
688 
689 			/*
690 			 * first subitem encodes two items with 1st*OIDMUX+2nd
691 			 * (see X.690:1997 clause 8.19 for the details)
692 			 */
693 			if (first < 0) {
694 			        int s;
695 				if (!nflag)
696 					objp = mibroot;
697 				first = 0;
698 				s = o / OIDMUX;
699 				if (s > 2) s = 2;
700 				OBJ_PRINT(s, first);
701 				o -= s * OIDMUX;
702 			}
703 			OBJ_PRINT(o, first);
704 			if (--first < 0)
705 				first = 0;
706 			o = 0;
707 		}
708 		break;
709 	}
710 
711 	case BE_INT:
712 		printf("%d", elem->data.integer);
713 		break;
714 
715 	case BE_UNS:
716 		printf("%u", elem->data.uns);
717 		break;
718 
719 	case BE_UNS64: {	/* idea borrowed from by Marshall Rose */
720 	        double d;
721 		int j, carry;
722 		char *cpf, *cpl, last[6], first[30];
723 		if (elem->data.uns64.high == 0) {
724 		        printf("%u", elem->data.uns64.low);
725 		        break;
726 		}
727 		d = elem->data.uns64.high * 4294967296.0;	/* 2^32 */
728 		if (elem->data.uns64.high <= 0x1fffff) {
729 		        d += elem->data.uns64.low;
730 #if 0 /*is looks illegal, but what is the intention?*/
731 			printf("%.f", d);
732 #else
733 			printf("%f", d);
734 #endif
735 			break;
736 		}
737 		d += (elem->data.uns64.low & 0xfffff000);
738 #if 0 /*is looks illegal, but what is the intention?*/
739 		snprintf(first, sizeof(first), "%.f", d);
740 #else
741 		snprintf(first, sizeof(first), "%f", d);
742 #endif
743 		snprintf(last, sizeof(last), "%5.5d",
744 		    elem->data.uns64.low & 0xfff);
745 		for (carry = 0, cpf = first+strlen(first)-1, cpl = last+4;
746 		     cpl >= last;
747 		     cpf--, cpl--) {
748 		        j = carry + (*cpf - '0') + (*cpl - '0');
749 			if (j > 9) {
750 			        j -= 10;
751 				carry = 1;
752 			} else {
753 			        carry = 0;
754 		        }
755 			*cpf = j + '0';
756 		}
757 		fputs(first, stdout);
758 		break;
759 	}
760 
761 	case BE_STR: {
762 		register int printable = 1, first = 1;
763 		const u_char *p = elem->data.str;
764 		for (i = asnlen; printable && i-- > 0; p++)
765 			printable = isprint(*p) || isspace(*p);
766 		p = elem->data.str;
767 		if (printable) {
768 			putchar('"');
769 			(void)fn_print(p, p + asnlen);
770 			putchar('"');
771 		} else
772 			for (i = asnlen; i-- > 0; p++) {
773 				printf(first ? "%.2x" : "_%.2x", *p);
774 				first = 0;
775 			}
776 		break;
777 	}
778 
779 	case BE_SEQ:
780 		printf("Seq(%u)", elem->asnlen);
781 		break;
782 
783 	case BE_INETADDR:
784 		if (asnlen != ASNLEN_INETADDR)
785 			printf("[inetaddr len!=%d]", ASNLEN_INETADDR);
786 		for (i = asnlen; i-- > 0; p++) {
787 			printf((i == asnlen-1) ? "%u" : ".%u", *p);
788 		}
789 		break;
790 
791 	case BE_NOSUCHOBJECT:
792 	case BE_NOSUCHINST:
793 	case BE_ENDOFMIBVIEW:
794 	        printf("[%s]", Class[EXCEPTIONS].Id[elem->id]);
795 		break;
796 
797 	case BE_PDU:
798 		printf("%s(%u)",
799 			Class[CONTEXT].Id[elem->id], elem->asnlen);
800 		break;
801 
802 	case BE_ANY:
803 		fputs("[BE_ANY!?]", stdout);
804 		break;
805 
806 	default:
807 		fputs("[be!?]", stdout);
808 		break;
809 	}
810 }
811 
812 #ifdef notdef
813 /*
814  * This is a brute force ASN.1 printer: recurses to dump an entire structure.
815  * This will work for any ASN.1 stream, not just an SNMP PDU.
816  *
817  * By adding newlines and spaces at the correct places, this would print in
818  * Rose-Normal-Form.
819  *
820  * This is not currently used.
821  */
822 static void
823 asn1_decode(u_char *p, u_int length)
824 {
825 	struct be elem;
826 	int i = 0;
827 
828 	while (i >= 0 && length > 0) {
829 		i = asn1_parse(p, length, &elem);
830 		if (i >= 0) {
831 			fputs(" ", stdout);
832 			asn1_print(&elem);
833 			if (elem.type == BE_SEQ || elem.type == BE_PDU) {
834 				fputs(" {", stdout);
835 				asn1_decode(elem.data.raw, elem.asnlen);
836 				fputs(" }", stdout);
837 			}
838 			length -= i;
839 			p += i;
840 		}
841 	}
842 }
843 #endif
844 
845 #ifdef LIBSMI
846 
847 struct smi2be {
848     SmiBasetype basetype;
849     int be;
850 };
851 
852 static struct smi2be smi2betab[] = {
853     { SMI_BASETYPE_INTEGER32,		BE_INT },
854     { SMI_BASETYPE_OCTETSTRING,		BE_STR },
855     { SMI_BASETYPE_OCTETSTRING,		BE_INETADDR },
856     { SMI_BASETYPE_OBJECTIDENTIFIER,	BE_OID },
857     { SMI_BASETYPE_UNSIGNED32,		BE_UNS },
858     { SMI_BASETYPE_INTEGER64,		BE_NONE },
859     { SMI_BASETYPE_UNSIGNED64,		BE_UNS64 },
860     { SMI_BASETYPE_FLOAT32,		BE_NONE },
861     { SMI_BASETYPE_FLOAT64,		BE_NONE },
862     { SMI_BASETYPE_FLOAT128,		BE_NONE },
863     { SMI_BASETYPE_ENUM,		BE_INT },
864     { SMI_BASETYPE_BITS,		BE_STR },
865     { SMI_BASETYPE_UNKNOWN,		BE_NONE }
866 };
867 
868 static void smi_decode_oid(struct be *elem, unsigned int *oid,
869 			   unsigned int oidsize, unsigned int *oidlen)
870 {
871 	u_char *p = (u_char *)elem->data.raw;
872 	u_int32_t asnlen = elem->asnlen;
873 	int o = 0, first = -1, i = asnlen;
874 
875 	for (*oidlen = 0; sflag && i-- > 0; p++) {
876 	        o = (o << ASN_SHIFT7) + (*p & ~ASN_BIT8);
877 		if (*p & ASN_LONGLEN)
878 		    continue;
879 
880 		/*
881 		 * first subitem encodes two items with 1st*OIDMUX+2nd
882 		 * (see X.690:1997 clause 8.19 for the details)
883 		 */
884 		if (first < 0) {
885 		        first = 0;
886 			if (*oidlen < oidsize) {
887 			    oid[*oidlen] = o / OIDMUX;
888 			    if (oid[*oidlen] > 2) oid[*oidlen] = 2;
889 			}
890 			o -= oid[*oidlen] * OIDMUX;
891 			if (*oidlen < oidsize) (*oidlen)++;
892 		}
893 		if (*oidlen < oidsize) {
894 			oid[(*oidlen)++] = o;
895 		}
896 		o = 0;
897 	}
898 }
899 
900 static int smi_check_type(SmiBasetype basetype, int be)
901 {
902     int i;
903 
904     for (i = 0; smi2betab[i].basetype != SMI_BASETYPE_UNKNOWN; i++) {
905 	if (smi2betab[i].basetype == basetype && smi2betab[i].be == be) {
906 	    return 1;
907 	}
908     }
909 
910     return 0;
911 }
912 
913 static int smi_check_a_range(SmiType *smiType, SmiRange *smiRange,
914 			     struct be *elem)
915 {
916     int ok = 1;
917 
918     switch (smiType->basetype) {
919     case SMI_BASETYPE_OBJECTIDENTIFIER:
920     case SMI_BASETYPE_OCTETSTRING:
921 	if (smiRange->minValue.value.unsigned32
922 	    == smiRange->maxValue.value.unsigned32) {
923 	    ok = (elem->asnlen == smiRange->minValue.value.unsigned32);
924 	} else {
925 	    ok = (elem->asnlen >= smiRange->minValue.value.unsigned32
926 		  && elem->asnlen <= smiRange->maxValue.value.unsigned32);
927 	}
928 	break;
929 
930     case SMI_BASETYPE_INTEGER32:
931 	ok = (elem->data.integer >= smiRange->minValue.value.integer32
932 	      && elem->data.integer <= smiRange->maxValue.value.integer32);
933 	break;
934 
935     case SMI_BASETYPE_UNSIGNED32:
936 	ok = (elem->data.uns >= smiRange->minValue.value.unsigned32
937 	      && elem->data.uns <= smiRange->maxValue.value.unsigned32);
938 	break;
939 
940     case SMI_BASETYPE_UNSIGNED64:
941 	/* XXX */
942 	break;
943 
944 	/* case SMI_BASETYPE_INTEGER64: SMIng */
945 	/* case SMI_BASETYPE_FLOAT32: SMIng */
946 	/* case SMI_BASETYPE_FLOAT64: SMIng */
947 	/* case SMI_BASETYPE_FLOAT128: SMIng */
948 
949     case SMI_BASETYPE_ENUM:
950     case SMI_BASETYPE_BITS:
951     case SMI_BASETYPE_UNKNOWN:
952 	ok = 1;
953 	break;
954     }
955 
956     return ok;
957 }
958 
959 static int smi_check_range(SmiType *smiType, struct be *elem)
960 {
961         SmiRange *smiRange;
962 	int ok = 1;
963 
964 	for (smiRange = smiGetFirstRange(smiType);
965 	     smiRange;
966 	     smiRange = smiGetNextRange(smiRange)) {
967 
968 	    ok = smi_check_a_range(smiType, smiRange, elem);
969 
970 	    if (ok) {
971 		break;
972 	    }
973 	}
974 
975 	if (ok) {
976 	    SmiType *parentType;
977 	    parentType = smiGetParentType(smiType);
978 	    if (parentType) {
979 		ok = smi_check_range(parentType, elem);
980 	    }
981 	}
982 
983 	return ok;
984 }
985 
986 static SmiNode *smi_print_variable(struct be *elem)
987 {
988 	unsigned int oid[128], oidlen;
989 	SmiNode *smiNode = NULL;
990 	int i;
991 
992 	smi_decode_oid(elem, oid, sizeof(oid)/sizeof(unsigned int), &oidlen);
993 	smiNode = smiGetNodeByOID(oidlen, oid);
994 	if (! smiNode) {
995 	        asn1_print(elem);
996 		return NULL;
997 	}
998 	if (vflag) {
999 		fputs(smiGetNodeModule(smiNode)->name, stdout);
1000 		fputs("::", stdout);
1001 	}
1002 	fputs(smiNode->name, stdout);
1003 	if (smiNode->oidlen < oidlen) {
1004 	        for (i = smiNode->oidlen; i < oidlen; i++) {
1005 		        printf(".%u", oid[i]);
1006 		}
1007 	}
1008 	return smiNode;
1009 }
1010 
1011 static void smi_print_value(SmiNode *smiNode, u_char pduid, struct be *elem)
1012 {
1013 	unsigned int oid[128], oidlen;
1014 	SmiType *smiType;
1015 	SmiNamedNumber *nn;
1016 	int i, done = 0;
1017 
1018 	if (! smiNode || ! (smiNode->nodekind
1019 			    & (SMI_NODEKIND_SCALAR | SMI_NODEKIND_COLUMN))) {
1020 	    asn1_print(elem);
1021 	    return;
1022 	}
1023 
1024 	if (elem->type == BE_NOSUCHOBJECT
1025 	    || elem->type == BE_NOSUCHINST
1026 	    || elem->type == BE_ENDOFMIBVIEW) {
1027 	    asn1_print(elem);
1028 	    return;
1029 	}
1030 
1031 	if (NOTIFY_CLASS(pduid) && smiNode->access < SMI_ACCESS_NOTIFY) {
1032 	    fputs("[notNotifyable]", stdout);
1033 	}
1034 
1035 	if (READ_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_ONLY) {
1036 	    fputs("[notReadable]", stdout);
1037 	}
1038 
1039 	if (WRITE_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_WRITE) {
1040 	    fputs("[notWritable]", stdout);
1041 	}
1042 
1043 	if (RESPONSE_CLASS(pduid)
1044 	    && smiNode->access == SMI_ACCESS_NOT_ACCESSIBLE) {
1045 	    fputs("[noAccess]", stdout);
1046 	}
1047 
1048 	smiType = smiGetNodeType(smiNode);
1049 	if (! smiType) {
1050 	    asn1_print(elem);
1051 	    return;
1052 	}
1053 
1054 	if (! smi_check_type(smiType->basetype, elem->type)) {
1055 	    fputs("[wrongType]", stdout);
1056 	}
1057 
1058 	if (! smi_check_range(smiType, elem)) {
1059 	    fputs("[outOfRange]", stdout);
1060 	}
1061 
1062 	/* resolve bits to named bits */
1063 
1064 	/* check whether instance identifier is valid */
1065 
1066 	/* apply display hints (integer, octetstring) */
1067 
1068 	/* convert instance identifier to index type values */
1069 
1070 	switch (elem->type) {
1071 	case BE_OID:
1072 	        if (smiType->basetype == SMI_BASETYPE_BITS) {
1073 		        /* print bit labels */
1074 		} else {
1075 		        smi_decode_oid(elem, oid,
1076 				       sizeof(oid)/sizeof(unsigned int),
1077 				       &oidlen);
1078 			smiNode = smiGetNodeByOID(oidlen, oid);
1079 			if (smiNode) {
1080 			        if (vflag) {
1081 					fputs(smiGetNodeModule(smiNode)->name, stdout);
1082 					fputs("::", stdout);
1083 				}
1084 				fputs(smiNode->name, stdout);
1085 				if (smiNode->oidlen < oidlen) {
1086 				        for (i = smiNode->oidlen;
1087 					     i < oidlen; i++) {
1088 					        printf(".%u", oid[i]);
1089 					}
1090 				}
1091 				done++;
1092 			}
1093 		}
1094 		break;
1095 
1096 	case BE_INT:
1097 	        if (smiType->basetype == SMI_BASETYPE_ENUM) {
1098 		        for (nn = smiGetFirstNamedNumber(smiType);
1099 			     nn;
1100 			     nn = smiGetNextNamedNumber(nn)) {
1101 			         if (nn->value.value.integer32
1102 				     == elem->data.integer) {
1103 				         fputs(nn->name, stdout);
1104 					 printf("(%d)", elem->data.integer);
1105 					 done++;
1106 					 break;
1107 				}
1108 			}
1109 		}
1110 		break;
1111 	}
1112 
1113 	if (! done) {
1114 	        asn1_print(elem);
1115 	}
1116 }
1117 #endif
1118 
1119 /*
1120  * General SNMP header
1121  *	SEQUENCE {
1122  *		version INTEGER {version-1(0)},
1123  *		community OCTET STRING,
1124  *		data ANY	-- PDUs
1125  *	}
1126  * PDUs for all but Trap: (see rfc1157 from page 15 on)
1127  *	SEQUENCE {
1128  *		request-id INTEGER,
1129  *		error-status INTEGER,
1130  *		error-index INTEGER,
1131  *		varbindlist SEQUENCE OF
1132  *			SEQUENCE {
1133  *				name ObjectName,
1134  *				value ObjectValue
1135  *			}
1136  *	}
1137  * PDU for Trap:
1138  *	SEQUENCE {
1139  *		enterprise OBJECT IDENTIFIER,
1140  *		agent-addr NetworkAddress,
1141  *		generic-trap INTEGER,
1142  *		specific-trap INTEGER,
1143  *		time-stamp TimeTicks,
1144  *		varbindlist SEQUENCE OF
1145  *			SEQUENCE {
1146  *				name ObjectName,
1147  *				value ObjectValue
1148  *			}
1149  *	}
1150  */
1151 
1152 /*
1153  * Decode SNMP varBind
1154  */
1155 static void
1156 varbind_print(u_char pduid, const u_char *np, u_int length)
1157 {
1158 	struct be elem;
1159 	int count = 0, ind;
1160 #ifdef LIBSMI
1161 	SmiNode *smiNode = NULL;
1162 #endif
1163 
1164 	/* Sequence of varBind */
1165 	if ((count = asn1_parse(np, length, &elem)) < 0)
1166 		return;
1167 	if (elem.type != BE_SEQ) {
1168 		fputs("[!SEQ of varbind]", stdout);
1169 		asn1_print(&elem);
1170 		return;
1171 	}
1172 	if (count < length)
1173 		printf("[%d extra after SEQ of varbind]", length - count);
1174 	/* descend */
1175 	length = elem.asnlen;
1176 	np = (u_char *)elem.data.raw;
1177 
1178 	for (ind = 1; length > 0; ind++) {
1179 		const u_char *vbend;
1180 		u_int vblength;
1181 
1182 		fputs(" ", stdout);
1183 
1184 		/* Sequence */
1185 		if ((count = asn1_parse(np, length, &elem)) < 0)
1186 			return;
1187 		if (elem.type != BE_SEQ) {
1188 			fputs("[!varbind]", stdout);
1189 			asn1_print(&elem);
1190 			return;
1191 		}
1192 		vbend = np + count;
1193 		vblength = length - count;
1194 		/* descend */
1195 		length = elem.asnlen;
1196 		np = (u_char *)elem.data.raw;
1197 
1198 		/* objName (OID) */
1199 		if ((count = asn1_parse(np, length, &elem)) < 0)
1200 			return;
1201 		if (elem.type != BE_OID) {
1202 			fputs("[objName!=OID]", stdout);
1203 			asn1_print(&elem);
1204 			return;
1205 		}
1206 #ifdef LIBSMI
1207 		smiNode = smi_print_variable(&elem);
1208 #else
1209 		asn1_print(&elem);
1210 #endif
1211 		length -= count;
1212 		np += count;
1213 
1214 		if (pduid != GETREQ && pduid != GETNEXTREQ
1215 		    && pduid != GETBULKREQ)
1216 				fputs("=", stdout);
1217 
1218 		/* objVal (ANY) */
1219 		if ((count = asn1_parse(np, length, &elem)) < 0)
1220 			return;
1221 		if (pduid == GETREQ || pduid == GETNEXTREQ
1222 		    || pduid == GETBULKREQ) {
1223 			if (elem.type != BE_NULL) {
1224 				fputs("[objVal!=NULL]", stdout);
1225 				asn1_print(&elem);
1226 			}
1227 		} else {
1228 		        if (elem.type != BE_NULL) {
1229 #ifdef LIBSMI
1230 			        smi_print_value(smiNode, pduid, &elem);
1231 #else
1232 				asn1_print(&elem);
1233 #endif
1234 			}
1235 		}
1236 		length = vblength;
1237 		np = vbend;
1238 	}
1239 }
1240 
1241 /*
1242  * Decode SNMP PDUs: GetRequest, GetNextRequest, GetResponse, SetRequest,
1243  * GetBulk, Inform, V2Trap, and Report
1244  */
1245 static void
1246 snmppdu_print(u_char pduid, const u_char *np, u_int length)
1247 {
1248 	struct be elem;
1249 	int count = 0, error;
1250 
1251 	/* reqId (Integer) */
1252 	if ((count = asn1_parse(np, length, &elem)) < 0)
1253 		return;
1254 	if (elem.type != BE_INT) {
1255 		fputs("[reqId!=INT]", stdout);
1256 		asn1_print(&elem);
1257 		return;
1258 	}
1259 	if (vflag)
1260 		printf("R=%d ", elem.data.integer);
1261 	length -= count;
1262 	np += count;
1263 
1264 	/* errorStatus (Integer) */
1265 	if ((count = asn1_parse(np, length, &elem)) < 0)
1266 		return;
1267 	if (elem.type != BE_INT) {
1268 		fputs("[errorStatus!=INT]", stdout);
1269 		asn1_print(&elem);
1270 		return;
1271 	}
1272 	error = 0;
1273 	if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
1274 	    || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
1275 	    && elem.data.integer != 0) {
1276 		char errbuf[10];
1277 		printf("[errorStatus(%s)!=0]",
1278 			DECODE_ErrorStatus(elem.data.integer));
1279 	} else if (pduid == GETBULKREQ) {
1280 	        printf(" N=%d", elem.data.integer);
1281 	} else if (elem.data.integer != 0) {
1282 		char errbuf[10];
1283 		printf(" %s", DECODE_ErrorStatus(elem.data.integer));
1284 		error = elem.data.integer;
1285 	}
1286 	length -= count;
1287 	np += count;
1288 
1289 	/* errorIndex (Integer) */
1290 	if ((count = asn1_parse(np, length, &elem)) < 0)
1291 		return;
1292 	if (elem.type != BE_INT) {
1293 		fputs("[errorIndex!=INT]", stdout);
1294 		asn1_print(&elem);
1295 		return;
1296 	}
1297 	if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
1298 	    || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
1299 	    && elem.data.integer != 0)
1300 		printf("[errorIndex(%d)!=0]", elem.data.integer);
1301 	else if (pduid == GETBULKREQ)
1302 	        printf(" M=%d", elem.data.integer);
1303 	else if (elem.data.integer != 0) {
1304 		if (!error)
1305 			printf("[errorIndex(%d) w/o errorStatus]",
1306 				elem.data.integer);
1307 		else {
1308 			printf("@%d", elem.data.integer);
1309 			error = elem.data.integer;
1310 		}
1311 	} else if (error) {
1312 		fputs("[errorIndex==0]", stdout);
1313 		error = 0;
1314 	}
1315 	length -= count;
1316 	np += count;
1317 
1318 	varbind_print(pduid, np, length);
1319 	return;
1320 }
1321 
1322 /*
1323  * Decode SNMP Trap PDU
1324  */
1325 static void
1326 trappdu_print(const u_char *np, u_int length)
1327 {
1328 	struct be elem;
1329 	int count = 0, generic;
1330 
1331 	putchar(' ');
1332 
1333 	/* enterprise (oid) */
1334 	if ((count = asn1_parse(np, length, &elem)) < 0)
1335 		return;
1336 	if (elem.type != BE_OID) {
1337 		fputs("[enterprise!=OID]", stdout);
1338 		asn1_print(&elem);
1339 		return;
1340 	}
1341 	asn1_print(&elem);
1342 	length -= count;
1343 	np += count;
1344 
1345 	putchar(' ');
1346 
1347 	/* agent-addr (inetaddr) */
1348 	if ((count = asn1_parse(np, length, &elem)) < 0)
1349 		return;
1350 	if (elem.type != BE_INETADDR) {
1351 		fputs("[agent-addr!=INETADDR]", stdout);
1352 		asn1_print(&elem);
1353 		return;
1354 	}
1355 	asn1_print(&elem);
1356 	length -= count;
1357 	np += count;
1358 
1359 	/* generic-trap (Integer) */
1360 	if ((count = asn1_parse(np, length, &elem)) < 0)
1361 		return;
1362 	if (elem.type != BE_INT) {
1363 		fputs("[generic-trap!=INT]", stdout);
1364 		asn1_print(&elem);
1365 		return;
1366 	}
1367 	generic = elem.data.integer;
1368 	{
1369 		char buf[10];
1370 		printf(" %s", DECODE_GenericTrap(generic));
1371 	}
1372 	length -= count;
1373 	np += count;
1374 
1375 	/* specific-trap (Integer) */
1376 	if ((count = asn1_parse(np, length, &elem)) < 0)
1377 		return;
1378 	if (elem.type != BE_INT) {
1379 		fputs("[specific-trap!=INT]", stdout);
1380 		asn1_print(&elem);
1381 		return;
1382 	}
1383 	if (generic != GT_ENTERPRISE) {
1384 		if (elem.data.integer != 0)
1385 			printf("[specific-trap(%d)!=0]", elem.data.integer);
1386 	} else
1387 		printf(" s=%d", elem.data.integer);
1388 	length -= count;
1389 	np += count;
1390 
1391 	putchar(' ');
1392 
1393 	/* time-stamp (TimeTicks) */
1394 	if ((count = asn1_parse(np, length, &elem)) < 0)
1395 		return;
1396 	if (elem.type != BE_UNS) {			/* XXX */
1397 		fputs("[time-stamp!=TIMETICKS]", stdout);
1398 		asn1_print(&elem);
1399 		return;
1400 	}
1401 	asn1_print(&elem);
1402 	length -= count;
1403 	np += count;
1404 
1405 	varbind_print (TRAP, np, length);
1406 	return;
1407 }
1408 
1409 /*
1410  * Decode arbitrary SNMP PDUs.
1411  */
1412 static void
1413 pdu_print(const u_char *np, u_int length, int version)
1414 {
1415 	struct be pdu;
1416 	int count = 0;
1417 
1418 	/* PDU (Context) */
1419 	if ((count = asn1_parse(np, length, &pdu)) < 0)
1420 		return;
1421 	if (pdu.type != BE_PDU) {
1422 		fputs("[no PDU]", stdout);
1423 		return;
1424 	}
1425 	if (count < length)
1426 		printf("[%d extra after PDU]", length - count);
1427 	if (vflag) {
1428 		fputs("{ ", stdout);
1429 	}
1430 	asn1_print(&pdu);
1431 	fputs(" ", stdout);
1432 	/* descend into PDU */
1433 	length = pdu.asnlen;
1434 	np = (u_char *)pdu.data.raw;
1435 
1436 	if (version == SNMP_VERSION_1 &&
1437 	    (pdu.id == GETBULKREQ || pdu.id == INFORMREQ ||
1438 	     pdu.id == V2TRAP || pdu.id == REPORT)) {
1439 	        printf("[v2 PDU in v1 message]");
1440 		return;
1441 	}
1442 
1443 	if (version == SNMP_VERSION_2 && pdu.id == TRAP) {
1444 	        printf("[v1 PDU in v2 message]");
1445 		return;
1446 	}
1447 
1448 	switch (pdu.id) {
1449 	case TRAP:
1450 		trappdu_print(np, length);
1451 		break;
1452 	case GETREQ:
1453 	case GETNEXTREQ:
1454 	case GETRESP:
1455 	case SETREQ:
1456 	case GETBULKREQ:
1457 	case INFORMREQ:
1458 	case V2TRAP:
1459 	case REPORT:
1460 		snmppdu_print(pdu.id, np, length);
1461 		break;
1462 	}
1463 
1464 	if (vflag) {
1465 		fputs("} ", stdout);
1466 	}
1467 }
1468 
1469 /*
1470  * Decode a scoped SNMP PDU.
1471  */
1472 static void
1473 scopedpdu_print(const u_char *np, u_int length, int version)
1474 {
1475 	struct be elem;
1476 	int i, count = 0;
1477 
1478 	/* Sequence */
1479 	if ((count = asn1_parse(np, length, &elem)) < 0)
1480 		return;
1481 	if (elem.type != BE_SEQ) {
1482 		fputs("[!scoped PDU]", stdout);
1483 		asn1_print(&elem);
1484 		return;
1485 	}
1486 	length = elem.asnlen;
1487 	np = (u_char *)elem.data.raw;
1488 
1489 	/* contextEngineID (OCTET STRING) */
1490 	if ((count = asn1_parse(np, length, &elem)) < 0)
1491 		return;
1492 	if (elem.type != BE_STR) {
1493 		fputs("[contextEngineID!=STR]", stdout);
1494 		asn1_print(&elem);
1495 		return;
1496 	}
1497 	length -= count;
1498 	np += count;
1499 
1500 	fputs("E= ", stdout);
1501 	for (i = 0; i < (int)elem.asnlen; i++) {
1502             printf("0x%02X", elem.data.str[i]);
1503         }
1504 	fputs(" ", stdout);
1505 
1506 	/* contextName (OCTET STRING) */
1507 	if ((count = asn1_parse(np, length, &elem)) < 0)
1508 		return;
1509 	if (elem.type != BE_STR) {
1510 		fputs("[contextName!=STR]", stdout);
1511 		asn1_print(&elem);
1512 		return;
1513 	}
1514 	length -= count;
1515 	np += count;
1516 
1517 	printf("C=%.*s ", (int)elem.asnlen, elem.data.str);
1518 
1519 	pdu_print(np, length, version);
1520 }
1521 
1522 /*
1523  * Decode SNMP Community Header (SNMPv1 and SNMPv2c)
1524  */
1525 static void
1526 community_print(const u_char *np, u_int length, int version)
1527 {
1528 	struct be elem;
1529 	int count = 0;
1530 
1531 	/* Community (String) */
1532 	if ((count = asn1_parse(np, length, &elem)) < 0)
1533 		return;
1534 	if (elem.type != BE_STR) {
1535 		fputs("[comm!=STR]", stdout);
1536 		asn1_print(&elem);
1537 		return;
1538 	}
1539 	/* default community */
1540 	if (!(elem.asnlen == sizeof(DEF_COMMUNITY) - 1 &&
1541 	    strncmp((char *)elem.data.str, DEF_COMMUNITY,
1542 	            sizeof(DEF_COMMUNITY) - 1) == 0))
1543 		/* ! "public" */
1544 		printf("C=%.*s ", (int)elem.asnlen, elem.data.str);
1545 	length -= count;
1546 	np += count;
1547 
1548 	pdu_print(np, length, version);
1549 }
1550 
1551 /*
1552  * Decode SNMPv3 User-based Security Message Header (SNMPv3)
1553  */
1554 static void
1555 usm_print(const u_char *np, u_int length)
1556 {
1557         struct be elem;
1558 	int count = 0;
1559 
1560 	/* Sequence */
1561 	if ((count = asn1_parse(np, length, &elem)) < 0)
1562 		return;
1563 	if (elem.type != BE_SEQ) {
1564 		fputs("[!usm]", stdout);
1565 		asn1_print(&elem);
1566 		return;
1567 	}
1568 	length = elem.asnlen;
1569 	np = (u_char *)elem.data.raw;
1570 
1571 	/* msgAuthoritativeEngineID (OCTET STRING) */
1572 	if ((count = asn1_parse(np, length, &elem)) < 0)
1573 		return;
1574 	if (elem.type != BE_STR) {
1575 		fputs("[msgAuthoritativeEngineID!=STR]", stdout);
1576 		asn1_print(&elem);
1577 		return;
1578 	}
1579 	length -= count;
1580 	np += count;
1581 
1582 	/* msgAuthoritativeEngineBoots (INTEGER) */
1583 	if ((count = asn1_parse(np, length, &elem)) < 0)
1584 		return;
1585 	if (elem.type != BE_INT) {
1586 		fputs("[msgAuthoritativeEngineBoots!=INT]", stdout);
1587 		asn1_print(&elem);
1588 		return;
1589 	}
1590 	if (vflag)
1591 	        printf("B=%d ", elem.data.integer);
1592 	length -= count;
1593 	np += count;
1594 
1595 	/* msgAuthoritativeEngineTime (INTEGER) */
1596 	if ((count = asn1_parse(np, length, &elem)) < 0)
1597 		return;
1598 	if (elem.type != BE_INT) {
1599 		fputs("[msgAuthoritativeEngineTime!=INT]", stdout);
1600 		asn1_print(&elem);
1601 		return;
1602 	}
1603 	if (vflag)
1604 	        printf("T=%d ", elem.data.integer);
1605 	length -= count;
1606 	np += count;
1607 
1608 	/* msgUserName (OCTET STRING) */
1609 	if ((count = asn1_parse(np, length, &elem)) < 0)
1610 		return;
1611 	if (elem.type != BE_STR) {
1612 		fputs("[msgUserName!=STR]", stdout);
1613 		asn1_print(&elem);
1614 		return;
1615 	}
1616 	length -= count;
1617         np += count;
1618 
1619 	printf("U=%.*s ", (int)elem.asnlen, elem.data.str);
1620 
1621 	/* msgAuthenticationParameters (OCTET STRING) */
1622 	if ((count = asn1_parse(np, length, &elem)) < 0)
1623 		return;
1624 	if (elem.type != BE_STR) {
1625 		fputs("[msgAuthenticationParameters!=STR]", stdout);
1626 		asn1_print(&elem);
1627 		return;
1628 	}
1629 	length -= count;
1630         np += count;
1631 
1632 	/* msgPrivacyParameters (OCTET STRING) */
1633 	if ((count = asn1_parse(np, length, &elem)) < 0)
1634 		return;
1635 	if (elem.type != BE_STR) {
1636 		fputs("[msgPrivacyParameters!=STR]", stdout);
1637 		asn1_print(&elem);
1638 		return;
1639 	}
1640 	length -= count;
1641         np += count;
1642 
1643 	if (count < length)
1644 		printf("[%d extra after usm SEQ]", length - count);
1645 }
1646 
1647 /*
1648  * Decode SNMPv3 Message Header (SNMPv3)
1649  */
1650 static void
1651 v3msg_print(const u_char *np, u_int length)
1652 {
1653 	struct be elem;
1654 	int count = 0;
1655 	u_char flags;
1656 	int model;
1657 	const u_char *xnp = np;
1658 	int xlength = length;
1659 
1660 	/* Sequence */
1661 	if ((count = asn1_parse(np, length, &elem)) < 0)
1662 		return;
1663 	if (elem.type != BE_SEQ) {
1664 		fputs("[!message]", stdout);
1665 		asn1_print(&elem);
1666 		return;
1667 	}
1668 	length = elem.asnlen;
1669 	np = (u_char *)elem.data.raw;
1670 
1671 	if (vflag) {
1672 		fputs("{ ", stdout);
1673 	}
1674 
1675 	/* msgID (INTEGER) */
1676 	if ((count = asn1_parse(np, length, &elem)) < 0)
1677 		return;
1678 	if (elem.type != BE_INT) {
1679 		fputs("[msgID!=INT]", stdout);
1680 		asn1_print(&elem);
1681 		return;
1682 	}
1683 	length -= count;
1684 	np += count;
1685 
1686 	/* msgMaxSize (INTEGER) */
1687 	if ((count = asn1_parse(np, length, &elem)) < 0)
1688 		return;
1689 	if (elem.type != BE_INT) {
1690 		fputs("[msgMaxSize!=INT]", stdout);
1691 		asn1_print(&elem);
1692 		return;
1693 	}
1694 	length -= count;
1695 	np += count;
1696 
1697 	/* msgFlags (OCTET STRING) */
1698 	if ((count = asn1_parse(np, length, &elem)) < 0)
1699 		return;
1700 	if (elem.type != BE_STR) {
1701 		fputs("[msgFlags!=STR]", stdout);
1702 		asn1_print(&elem);
1703 		return;
1704 	}
1705 	if (elem.asnlen != 1) {
1706 	        printf("[msgFlags size %d]", elem.asnlen);
1707 		return;
1708 	}
1709 	flags = elem.data.str[0];
1710 	if (flags != 0x00 && flags != 0x01 && flags != 0x03
1711 	    && flags != 0x04 && flags != 0x05 && flags != 0x07) {
1712 		printf("[msgFlags=0x%02X]", flags);
1713 		return;
1714 	}
1715 	length -= count;
1716 	np += count;
1717 
1718 	fputs("F=", stdout);
1719 	if (flags & 0x01) fputs("a", stdout);
1720 	if (flags & 0x02) fputs("p", stdout);
1721 	if (flags & 0x04) fputs("r", stdout);
1722 	fputs(" ", stdout);
1723 
1724 	/* msgSecurityModel (INTEGER) */
1725 	if ((count = asn1_parse(np, length, &elem)) < 0)
1726 		return;
1727 	if (elem.type != BE_INT) {
1728 		fputs("[msgSecurityModel!=INT]", stdout);
1729 		asn1_print(&elem);
1730 		return;
1731 	}
1732 	model = elem.data.integer;
1733 	length -= count;
1734 	np += count;
1735 
1736 	if (count < length)
1737 		printf("[%d extra after message SEQ]", length - count);
1738 
1739 	if (vflag) {
1740 		fputs("} ", stdout);
1741 	}
1742 
1743 	if (model == 3) {
1744 	    if (vflag) {
1745 		fputs("{ USM ", stdout);
1746 	    }
1747 	} else {
1748 	    printf("[security model %d]", model);
1749             return;
1750 	}
1751 
1752 	np = xnp + (np - xnp);
1753 	length = xlength - (np - xnp);
1754 
1755 	/* msgSecurityParameters (OCTET STRING) */
1756 	if ((count = asn1_parse(np, length, &elem)) < 0)
1757 		return;
1758 	if (elem.type != BE_STR) {
1759 		fputs("[msgSecurityParameters!=STR]", stdout);
1760 		asn1_print(&elem);
1761 		return;
1762 	}
1763 	length -= count;
1764 	np += count;
1765 
1766 	if (model == 3) {
1767 	    usm_print(elem.data.str, elem.asnlen);
1768 	    if (vflag) {
1769 		fputs("} ", stdout);
1770 	    }
1771 	}
1772 
1773 	if (vflag) {
1774 	    fputs("{ ScopedPDU ", stdout);
1775 	}
1776 
1777 	scopedpdu_print(np, length, 3);
1778 
1779 	if (vflag) {
1780 		fputs("} ", stdout);
1781 	}
1782 }
1783 
1784 /*
1785  * Decode SNMP header and pass on to PDU printing routines
1786  */
1787 void
1788 snmp_print(const u_char *np, u_int length)
1789 {
1790 	struct be elem;
1791 	int count = 0;
1792 	int version = 0;
1793 
1794 	truncated = 0;
1795 
1796 	/* truncated packet? */
1797 	if (np + length > snapend) {
1798 		truncated = 1;
1799 		length = snapend - np;
1800 	}
1801 
1802 	putchar(' ');
1803 
1804 	/* initial Sequence */
1805 	if ((count = asn1_parse(np, length, &elem)) < 0)
1806 		return;
1807 	if (elem.type != BE_SEQ) {
1808 		fputs("[!init SEQ]", stdout);
1809 		asn1_print(&elem);
1810 		return;
1811 	}
1812 	if (count < length)
1813 		printf("[%d extra after iSEQ]", length - count);
1814 	/* descend */
1815 	length = elem.asnlen;
1816 	np = (u_char *)elem.data.raw;
1817 
1818 	/* Version (INTEGER) */
1819 	if ((count = asn1_parse(np, length, &elem)) < 0)
1820 		return;
1821 	if (elem.type != BE_INT) {
1822 		fputs("[version!=INT]", stdout);
1823 		asn1_print(&elem);
1824 		return;
1825 	}
1826 
1827 	switch (elem.data.integer) {
1828 	case SNMP_VERSION_1:
1829 	case SNMP_VERSION_2:
1830 	case SNMP_VERSION_3:
1831 	        if (vflag)
1832 		        printf("{ %s ", SnmpVersion[elem.data.integer]);
1833 		break;
1834 	default:
1835 	        printf("[version = %d]", elem.data.integer);
1836 		return;
1837 	}
1838 	version = elem.data.integer;
1839 	length -= count;
1840 	np += count;
1841 
1842 	switch (version) {
1843 	case SNMP_VERSION_1:
1844         case SNMP_VERSION_2:
1845 		community_print(np, length, version);
1846 		break;
1847 	case SNMP_VERSION_3:
1848 		v3msg_print(np, length);
1849 		break;
1850 	default:
1851 	        printf("[version = %d]", elem.data.integer);
1852 		break;
1853 	}
1854 
1855 	if (vflag) {
1856 		fputs("} ", stdout);
1857 	}
1858 }
1859