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