xref: /freebsd/contrib/tcpdump/print-snmp.c (revision 09e8dea79366f1e5b3a73e8a271b26e4b6bf2e6a)
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 2001/09/17 22:16:53 fenner 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 			 */
692 			if (first < 0) {
693 				if (!nflag)
694 					objp = mibroot;
695 				first = 0;
696 				OBJ_PRINT(o/OIDMUX, first);
697 				o %= OIDMUX;
698 			}
699 			OBJ_PRINT(o, first);
700 			if (--first < 0)
701 				first = 0;
702 			o = 0;
703 		}
704 		break;
705 	}
706 
707 	case BE_INT:
708 		printf("%d", elem->data.integer);
709 		break;
710 
711 	case BE_UNS:
712 		printf("%u", elem->data.uns);
713 		break;
714 
715 	case BE_UNS64: {	/* idea borrowed from by Marshall Rose */
716 	        double d;
717 		int j, carry;
718 		char *cpf, *cpl, last[6], first[30];
719 		if (elem->data.uns64.high == 0) {
720 		        printf("%u", elem->data.uns64.low);
721 		        break;
722 		}
723 		d = elem->data.uns64.high * 4294967296.0;	/* 2^32 */
724 		if (elem->data.uns64.high <= 0x1fffff) {
725 		        d += elem->data.uns64.low;
726 #if 0 /*is looks illegal, but what is the intention?*/
727 			printf("%.f", d);
728 #else
729 			printf("%f", d);
730 #endif
731 			break;
732 		}
733 		d += (elem->data.uns64.low & 0xfffff000);
734 #if 0 /*is looks illegal, but what is the intention?*/
735 		snprintf(first, sizeof(first), "%.f", d);
736 #else
737 		snprintf(first, sizeof(first), "%f", d);
738 #endif
739 		snprintf(last, sizeof(last), "%5.5d",
740 		    elem->data.uns64.low & 0xfff);
741 		for (carry = 0, cpf = first+strlen(first)-1, cpl = last+4;
742 		     cpl >= last;
743 		     cpf--, cpl--) {
744 		        j = carry + (*cpf - '0') + (*cpl - '0');
745 			if (j > 9) {
746 			        j -= 10;
747 				carry = 1;
748 			} else {
749 			        carry = 0;
750 		        }
751 			*cpf = j + '0';
752 		}
753 		fputs(first, stdout);
754 		break;
755 	}
756 
757 	case BE_STR: {
758 		register int printable = 1, first = 1;
759 		const u_char *p = elem->data.str;
760 		for (i = asnlen; printable && i-- > 0; p++)
761 			printable = isprint(*p) || isspace(*p);
762 		p = elem->data.str;
763 		if (printable) {
764 			putchar('"');
765 			(void)fn_print(p, p + asnlen);
766 			putchar('"');
767 		} else
768 			for (i = asnlen; i-- > 0; p++) {
769 				printf(first ? "%.2x" : "_%.2x", *p);
770 				first = 0;
771 			}
772 		break;
773 	}
774 
775 	case BE_SEQ:
776 		printf("Seq(%u)", elem->asnlen);
777 		break;
778 
779 	case BE_INETADDR:
780 		if (asnlen != ASNLEN_INETADDR)
781 			printf("[inetaddr len!=%d]", ASNLEN_INETADDR);
782 		for (i = asnlen; i-- > 0; p++) {
783 			printf((i == asnlen-1) ? "%u" : ".%u", *p);
784 		}
785 		break;
786 
787 	case BE_NOSUCHOBJECT:
788 	case BE_NOSUCHINST:
789 	case BE_ENDOFMIBVIEW:
790 	        printf("[%s]", Class[EXCEPTIONS].Id[elem->id]);
791 		break;
792 
793 	case BE_PDU:
794 		printf("%s(%u)",
795 			Class[CONTEXT].Id[elem->id], elem->asnlen);
796 		break;
797 
798 	case BE_ANY:
799 		fputs("[BE_ANY!?]", stdout);
800 		break;
801 
802 	default:
803 		fputs("[be!?]", stdout);
804 		break;
805 	}
806 }
807 
808 #ifdef notdef
809 /*
810  * This is a brute force ASN.1 printer: recurses to dump an entire structure.
811  * This will work for any ASN.1 stream, not just an SNMP PDU.
812  *
813  * By adding newlines and spaces at the correct places, this would print in
814  * Rose-Normal-Form.
815  *
816  * This is not currently used.
817  */
818 static void
819 asn1_decode(u_char *p, u_int length)
820 {
821 	struct be elem;
822 	int i = 0;
823 
824 	while (i >= 0 && length > 0) {
825 		i = asn1_parse(p, length, &elem);
826 		if (i >= 0) {
827 			fputs(" ", stdout);
828 			asn1_print(&elem);
829 			if (elem.type == BE_SEQ || elem.type == BE_PDU) {
830 				fputs(" {", stdout);
831 				asn1_decode(elem.data.raw, elem.asnlen);
832 				fputs(" }", stdout);
833 			}
834 			length -= i;
835 			p += i;
836 		}
837 	}
838 }
839 #endif
840 
841 #ifdef LIBSMI
842 
843 struct smi2be {
844     SmiBasetype basetype;
845     int be;
846 };
847 
848 static struct smi2be smi2betab[] = {
849     { SMI_BASETYPE_INTEGER32,		BE_INT },
850     { SMI_BASETYPE_OCTETSTRING,		BE_STR },
851     { SMI_BASETYPE_OCTETSTRING,		BE_INETADDR },
852     { SMI_BASETYPE_OBJECTIDENTIFIER,	BE_OID },
853     { SMI_BASETYPE_UNSIGNED32,		BE_UNS },
854     { SMI_BASETYPE_INTEGER64,		BE_NONE },
855     { SMI_BASETYPE_UNSIGNED64,		BE_UNS64 },
856     { SMI_BASETYPE_FLOAT32,		BE_NONE },
857     { SMI_BASETYPE_FLOAT64,		BE_NONE },
858     { SMI_BASETYPE_FLOAT128,		BE_NONE },
859     { SMI_BASETYPE_ENUM,		BE_INT },
860     { SMI_BASETYPE_BITS,		BE_STR },
861     { SMI_BASETYPE_UNKNOWN,		BE_NONE }
862 };
863 
864 static void smi_decode_oid(struct be *elem, unsigned int *oid,
865 			   unsigned int oidsize, unsigned int *oidlen)
866 {
867 	u_char *p = (u_char *)elem->data.raw;
868 	u_int32_t asnlen = elem->asnlen;
869 	int o = 0, first = -1, i = asnlen;
870 
871 	for (*oidlen = 0; sflag && i-- > 0; p++) {
872 	        o = (o << ASN_SHIFT7) + (*p & ~ASN_BIT8);
873 		if (*p & ASN_LONGLEN)
874 		    continue;
875 
876 		/*
877 		 * first subitem encodes two items with 1st*OIDMUX+2nd
878 		 */
879 		if (first < 0) {
880 		        first = 0;
881 			if (*oidlen < oidsize) {
882 			    oid[(*oidlen)++] = o/OIDMUX;
883 			}
884 			o %= OIDMUX;
885 		}
886 		if (*oidlen < oidsize) {
887 		    oid[(*oidlen)++] = o;
888 		}
889 		o = 0;
890 	}
891 }
892 
893 static int smi_check_type(SmiBasetype basetype, int be)
894 {
895     int i;
896 
897     for (i = 0; smi2betab[i].basetype != SMI_BASETYPE_UNKNOWN; i++) {
898 	if (smi2betab[i].basetype == basetype && smi2betab[i].be == be) {
899 	    return 1;
900 	}
901     }
902 
903     return 0;
904 }
905 
906 static int smi_check_a_range(SmiType *smiType, SmiRange *smiRange,
907 			     struct be *elem)
908 {
909     int ok = 1;
910 
911     switch (smiType->basetype) {
912     case SMI_BASETYPE_OBJECTIDENTIFIER:
913     case SMI_BASETYPE_OCTETSTRING:
914 	if (smiRange->minValue.value.unsigned32
915 	    == smiRange->maxValue.value.unsigned32) {
916 	    ok = (elem->asnlen == smiRange->minValue.value.unsigned32);
917 	} else {
918 	    ok = (elem->asnlen >= smiRange->minValue.value.unsigned32
919 		  && elem->asnlen <= smiRange->maxValue.value.unsigned32);
920 	}
921 	break;
922 
923     case SMI_BASETYPE_INTEGER32:
924 	ok = (elem->data.integer >= smiRange->minValue.value.integer32
925 	      && elem->data.integer <= smiRange->maxValue.value.integer32);
926 	break;
927 
928     case SMI_BASETYPE_UNSIGNED32:
929 	ok = (elem->data.uns >= smiRange->minValue.value.unsigned32
930 	      && elem->data.uns <= smiRange->maxValue.value.unsigned32);
931 	break;
932 
933     case SMI_BASETYPE_UNSIGNED64:
934 	/* XXX */
935 	break;
936 
937 	/* case SMI_BASETYPE_INTEGER64: SMIng */
938 	/* case SMI_BASETYPE_FLOAT32: SMIng */
939 	/* case SMI_BASETYPE_FLOAT64: SMIng */
940 	/* case SMI_BASETYPE_FLOAT128: SMIng */
941 
942     case SMI_BASETYPE_ENUM:
943     case SMI_BASETYPE_BITS:
944     case SMI_BASETYPE_UNKNOWN:
945 	ok = 1;
946 	break;
947     }
948 
949     return ok;
950 }
951 
952 static int smi_check_range(SmiType *smiType, struct be *elem)
953 {
954         SmiRange *smiRange;
955 	int ok = 1;
956 
957 	for (smiRange = smiGetFirstRange(smiType);
958 	     smiRange;
959 	     smiRange = smiGetNextRange(smiRange)) {
960 
961 	    ok = smi_check_a_range(smiType, smiRange, elem);
962 
963 	    if (ok) {
964 		break;
965 	    }
966 	}
967 
968 	if (ok) {
969 	    SmiType *parentType;
970 	    parentType = smiGetParentType(smiType);
971 	    if (parentType) {
972 		ok = smi_check_range(parentType, elem);
973 	    }
974 	}
975 
976 	return ok;
977 }
978 
979 static SmiNode *smi_print_variable(struct be *elem)
980 {
981 	unsigned int oid[128], oidlen;
982 	SmiNode *smiNode = NULL;
983 	int i;
984 
985 	smi_decode_oid(elem, oid, sizeof(oid)/sizeof(unsigned int), &oidlen);
986 	smiNode = smiGetNodeByOID(oidlen, oid);
987 	if (! smiNode) {
988 	        asn1_print(elem);
989 		return NULL;
990 	}
991 	if (vflag) {
992 		fputs(smiGetNodeModule(smiNode)->name, stdout);
993 		fputs("::", stdout);
994 	}
995 	fputs(smiNode->name, stdout);
996 	if (smiNode->oidlen < oidlen) {
997 	        for (i = smiNode->oidlen; i < oidlen; i++) {
998 		        printf(".%u", oid[i]);
999 		}
1000 	}
1001 	return smiNode;
1002 }
1003 
1004 static void smi_print_value(SmiNode *smiNode, u_char pduid, struct be *elem)
1005 {
1006 	unsigned int oid[128], oidlen;
1007 	SmiType *smiType;
1008 	SmiNamedNumber *nn;
1009 	int i, done = 0;
1010 
1011 	if (! smiNode || ! (smiNode->nodekind
1012 			    & (SMI_NODEKIND_SCALAR | SMI_NODEKIND_COLUMN))) {
1013 	    asn1_print(elem);
1014 	    return;
1015 	}
1016 
1017 	if (elem->type == BE_NOSUCHOBJECT
1018 	    || elem->type == BE_NOSUCHINST
1019 	    || elem->type == BE_ENDOFMIBVIEW) {
1020 	    asn1_print(elem);
1021 	    return;
1022 	}
1023 
1024 	if (NOTIFY_CLASS(pduid) && smiNode->access < SMI_ACCESS_NOTIFY) {
1025 	    fputs("[notNotifyable]", stdout);
1026 	}
1027 
1028 	if (READ_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_ONLY) {
1029 	    fputs("[notReadable]", stdout);
1030 	}
1031 
1032 	if (WRITE_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_WRITE) {
1033 	    fputs("[notWritable]", stdout);
1034 	}
1035 
1036 	if (RESPONSE_CLASS(pduid)
1037 	    && smiNode->access == SMI_ACCESS_NOT_ACCESSIBLE) {
1038 	    fputs("[noAccess]", stdout);
1039 	}
1040 
1041 	smiType = smiGetNodeType(smiNode);
1042 	if (! smiType) {
1043 	    asn1_print(elem);
1044 	    return;
1045 	}
1046 
1047 	if (! smi_check_type(smiType->basetype, elem->type)) {
1048 	    fputs("[wrongType]", stdout);
1049 	}
1050 
1051 	if (! smi_check_range(smiType, elem)) {
1052 	    fputs("[outOfRange]", stdout);
1053 	}
1054 
1055 	/* resolve bits to named bits */
1056 
1057 	/* check whether instance identifier is valid */
1058 
1059 	/* apply display hints (integer, octetstring) */
1060 
1061 	/* convert instance identifier to index type values */
1062 
1063 	switch (elem->type) {
1064 	case BE_OID:
1065 	        if (smiType->basetype == SMI_BASETYPE_BITS) {
1066 		        /* print bit labels */
1067 		} else {
1068 		        smi_decode_oid(elem, oid,
1069 				       sizeof(oid)/sizeof(unsigned int),
1070 				       &oidlen);
1071 			smiNode = smiGetNodeByOID(oidlen, oid);
1072 			if (smiNode) {
1073 			        if (vflag) {
1074 					fputs(smiGetNodeModule(smiNode)->name, stdout);
1075 					fputs("::", stdout);
1076 				}
1077 				fputs(smiNode->name, stdout);
1078 				if (smiNode->oidlen < oidlen) {
1079 				        for (i = smiNode->oidlen;
1080 					     i < oidlen; i++) {
1081 					        printf(".%u", oid[i]);
1082 					}
1083 				}
1084 				done++;
1085 			}
1086 		}
1087 		break;
1088 
1089 	case BE_INT:
1090 	        if (smiType->basetype == SMI_BASETYPE_ENUM) {
1091 		        for (nn = smiGetFirstNamedNumber(smiType);
1092 			     nn;
1093 			     nn = smiGetNextNamedNumber(nn)) {
1094 			         if (nn->value.value.integer32
1095 				     == elem->data.integer) {
1096 				         fputs(nn->name, stdout);
1097 					 printf("(%d)", elem->data.integer);
1098 					 done++;
1099 					 break;
1100 				}
1101 			}
1102 		}
1103 		break;
1104 	}
1105 
1106 	if (! done) {
1107 	        asn1_print(elem);
1108 	}
1109 }
1110 #endif
1111 
1112 /*
1113  * General SNMP header
1114  *	SEQUENCE {
1115  *		version INTEGER {version-1(0)},
1116  *		community OCTET STRING,
1117  *		data ANY	-- PDUs
1118  *	}
1119  * PDUs for all but Trap: (see rfc1157 from page 15 on)
1120  *	SEQUENCE {
1121  *		request-id INTEGER,
1122  *		error-status INTEGER,
1123  *		error-index INTEGER,
1124  *		varbindlist SEQUENCE OF
1125  *			SEQUENCE {
1126  *				name ObjectName,
1127  *				value ObjectValue
1128  *			}
1129  *	}
1130  * PDU for Trap:
1131  *	SEQUENCE {
1132  *		enterprise OBJECT IDENTIFIER,
1133  *		agent-addr NetworkAddress,
1134  *		generic-trap INTEGER,
1135  *		specific-trap INTEGER,
1136  *		time-stamp TimeTicks,
1137  *		varbindlist SEQUENCE OF
1138  *			SEQUENCE {
1139  *				name ObjectName,
1140  *				value ObjectValue
1141  *			}
1142  *	}
1143  */
1144 
1145 /*
1146  * Decode SNMP varBind
1147  */
1148 static void
1149 varbind_print(u_char pduid, const u_char *np, u_int length)
1150 {
1151 	struct be elem;
1152 	int count = 0, ind;
1153 #ifdef LIBSMI
1154 	SmiNode *smiNode = NULL;
1155 #endif
1156 
1157 	/* Sequence of varBind */
1158 	if ((count = asn1_parse(np, length, &elem)) < 0)
1159 		return;
1160 	if (elem.type != BE_SEQ) {
1161 		fputs("[!SEQ of varbind]", stdout);
1162 		asn1_print(&elem);
1163 		return;
1164 	}
1165 	if (count < length)
1166 		printf("[%d extra after SEQ of varbind]", length - count);
1167 	/* descend */
1168 	length = elem.asnlen;
1169 	np = (u_char *)elem.data.raw;
1170 
1171 	for (ind = 1; length > 0; ind++) {
1172 		const u_char *vbend;
1173 		u_int vblength;
1174 
1175 		fputs(" ", stdout);
1176 
1177 		/* Sequence */
1178 		if ((count = asn1_parse(np, length, &elem)) < 0)
1179 			return;
1180 		if (elem.type != BE_SEQ) {
1181 			fputs("[!varbind]", stdout);
1182 			asn1_print(&elem);
1183 			return;
1184 		}
1185 		vbend = np + count;
1186 		vblength = length - count;
1187 		/* descend */
1188 		length = elem.asnlen;
1189 		np = (u_char *)elem.data.raw;
1190 
1191 		/* objName (OID) */
1192 		if ((count = asn1_parse(np, length, &elem)) < 0)
1193 			return;
1194 		if (elem.type != BE_OID) {
1195 			fputs("[objName!=OID]", stdout);
1196 			asn1_print(&elem);
1197 			return;
1198 		}
1199 #ifdef LIBSMI
1200 		smiNode = smi_print_variable(&elem);
1201 #else
1202 		asn1_print(&elem);
1203 #endif
1204 		length -= count;
1205 		np += count;
1206 
1207 		if (pduid != GETREQ && pduid != GETNEXTREQ
1208 		    && pduid != GETBULKREQ)
1209 				fputs("=", stdout);
1210 
1211 		/* objVal (ANY) */
1212 		if ((count = asn1_parse(np, length, &elem)) < 0)
1213 			return;
1214 		if (pduid == GETREQ || pduid == GETNEXTREQ
1215 		    || pduid == GETBULKREQ) {
1216 			if (elem.type != BE_NULL) {
1217 				fputs("[objVal!=NULL]", stdout);
1218 				asn1_print(&elem);
1219 			}
1220 		} else {
1221 		        if (elem.type != BE_NULL) {
1222 #ifdef LIBSMI
1223 			        smi_print_value(smiNode, pduid, &elem);
1224 #else
1225 				asn1_print(&elem);
1226 #endif
1227 			}
1228 		}
1229 		length = vblength;
1230 		np = vbend;
1231 	}
1232 }
1233 
1234 /*
1235  * Decode SNMP PDUs: GetRequest, GetNextRequest, GetResponse, SetRequest,
1236  * GetBulk, Inform, V2Trap, and Report
1237  */
1238 static void
1239 snmppdu_print(u_char pduid, const u_char *np, u_int length)
1240 {
1241 	struct be elem;
1242 	int count = 0, error;
1243 
1244 	/* reqId (Integer) */
1245 	if ((count = asn1_parse(np, length, &elem)) < 0)
1246 		return;
1247 	if (elem.type != BE_INT) {
1248 		fputs("[reqId!=INT]", stdout);
1249 		asn1_print(&elem);
1250 		return;
1251 	}
1252 	if (vflag)
1253 		printf("R=%d ", elem.data.integer);
1254 	length -= count;
1255 	np += count;
1256 
1257 	/* errorStatus (Integer) */
1258 	if ((count = asn1_parse(np, length, &elem)) < 0)
1259 		return;
1260 	if (elem.type != BE_INT) {
1261 		fputs("[errorStatus!=INT]", stdout);
1262 		asn1_print(&elem);
1263 		return;
1264 	}
1265 	error = 0;
1266 	if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
1267 	    || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
1268 	    && elem.data.integer != 0) {
1269 		char errbuf[10];
1270 		printf("[errorStatus(%s)!=0]",
1271 			DECODE_ErrorStatus(elem.data.integer));
1272 	} else if (pduid == GETBULKREQ) {
1273 	        printf(" N=%d", elem.data.integer);
1274 	} else if (elem.data.integer != 0) {
1275 		char errbuf[10];
1276 		printf(" %s", DECODE_ErrorStatus(elem.data.integer));
1277 		error = elem.data.integer;
1278 	}
1279 	length -= count;
1280 	np += count;
1281 
1282 	/* errorIndex (Integer) */
1283 	if ((count = asn1_parse(np, length, &elem)) < 0)
1284 		return;
1285 	if (elem.type != BE_INT) {
1286 		fputs("[errorIndex!=INT]", stdout);
1287 		asn1_print(&elem);
1288 		return;
1289 	}
1290 	if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
1291 	    || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
1292 	    && elem.data.integer != 0)
1293 		printf("[errorIndex(%d)!=0]", elem.data.integer);
1294 	else if (pduid == GETBULKREQ)
1295 	        printf(" M=%d", elem.data.integer);
1296 	else if (elem.data.integer != 0) {
1297 		if (!error)
1298 			printf("[errorIndex(%d) w/o errorStatus]",
1299 				elem.data.integer);
1300 		else {
1301 			printf("@%d", elem.data.integer);
1302 			error = elem.data.integer;
1303 		}
1304 	} else if (error) {
1305 		fputs("[errorIndex==0]", stdout);
1306 		error = 0;
1307 	}
1308 	length -= count;
1309 	np += count;
1310 
1311 	varbind_print(pduid, np, length);
1312 	return;
1313 }
1314 
1315 /*
1316  * Decode SNMP Trap PDU
1317  */
1318 static void
1319 trappdu_print(const u_char *np, u_int length)
1320 {
1321 	struct be elem;
1322 	int count = 0, generic;
1323 
1324 	putchar(' ');
1325 
1326 	/* enterprise (oid) */
1327 	if ((count = asn1_parse(np, length, &elem)) < 0)
1328 		return;
1329 	if (elem.type != BE_OID) {
1330 		fputs("[enterprise!=OID]", stdout);
1331 		asn1_print(&elem);
1332 		return;
1333 	}
1334 	asn1_print(&elem);
1335 	length -= count;
1336 	np += count;
1337 
1338 	putchar(' ');
1339 
1340 	/* agent-addr (inetaddr) */
1341 	if ((count = asn1_parse(np, length, &elem)) < 0)
1342 		return;
1343 	if (elem.type != BE_INETADDR) {
1344 		fputs("[agent-addr!=INETADDR]", stdout);
1345 		asn1_print(&elem);
1346 		return;
1347 	}
1348 	asn1_print(&elem);
1349 	length -= count;
1350 	np += count;
1351 
1352 	/* generic-trap (Integer) */
1353 	if ((count = asn1_parse(np, length, &elem)) < 0)
1354 		return;
1355 	if (elem.type != BE_INT) {
1356 		fputs("[generic-trap!=INT]", stdout);
1357 		asn1_print(&elem);
1358 		return;
1359 	}
1360 	generic = elem.data.integer;
1361 	{
1362 		char buf[10];
1363 		printf(" %s", DECODE_GenericTrap(generic));
1364 	}
1365 	length -= count;
1366 	np += count;
1367 
1368 	/* specific-trap (Integer) */
1369 	if ((count = asn1_parse(np, length, &elem)) < 0)
1370 		return;
1371 	if (elem.type != BE_INT) {
1372 		fputs("[specific-trap!=INT]", stdout);
1373 		asn1_print(&elem);
1374 		return;
1375 	}
1376 	if (generic != GT_ENTERPRISE) {
1377 		if (elem.data.integer != 0)
1378 			printf("[specific-trap(%d)!=0]", elem.data.integer);
1379 	} else
1380 		printf(" s=%d", elem.data.integer);
1381 	length -= count;
1382 	np += count;
1383 
1384 	putchar(' ');
1385 
1386 	/* time-stamp (TimeTicks) */
1387 	if ((count = asn1_parse(np, length, &elem)) < 0)
1388 		return;
1389 	if (elem.type != BE_UNS) {			/* XXX */
1390 		fputs("[time-stamp!=TIMETICKS]", stdout);
1391 		asn1_print(&elem);
1392 		return;
1393 	}
1394 	asn1_print(&elem);
1395 	length -= count;
1396 	np += count;
1397 
1398 	varbind_print (TRAP, np, length);
1399 	return;
1400 }
1401 
1402 /*
1403  * Decode arbitrary SNMP PDUs.
1404  */
1405 static void
1406 pdu_print(const u_char *np, u_int length, int version)
1407 {
1408 	struct be pdu;
1409 	int count = 0;
1410 
1411 	/* PDU (Context) */
1412 	if ((count = asn1_parse(np, length, &pdu)) < 0)
1413 		return;
1414 	if (pdu.type != BE_PDU) {
1415 		fputs("[no PDU]", stdout);
1416 		return;
1417 	}
1418 	if (count < length)
1419 		printf("[%d extra after PDU]", length - count);
1420 	if (vflag) {
1421 		fputs("{ ", stdout);
1422 	}
1423 	asn1_print(&pdu);
1424 	fputs(" ", stdout);
1425 	/* descend into PDU */
1426 	length = pdu.asnlen;
1427 	np = (u_char *)pdu.data.raw;
1428 
1429 	if (version == SNMP_VERSION_1 &&
1430 	    (pdu.id == GETBULKREQ || pdu.id == INFORMREQ ||
1431 	     pdu.id == V2TRAP || pdu.id == REPORT)) {
1432 	        printf("[v2 PDU in v1 message]");
1433 		return;
1434 	}
1435 
1436 	if (version == SNMP_VERSION_2 && pdu.id == TRAP) {
1437 	        printf("[v1 PDU in v2 message]");
1438 		return;
1439 	}
1440 
1441 	switch (pdu.id) {
1442 	case TRAP:
1443 		trappdu_print(np, length);
1444 		break;
1445 	case GETREQ:
1446 	case GETNEXTREQ:
1447 	case GETRESP:
1448 	case SETREQ:
1449 	case GETBULKREQ:
1450 	case INFORMREQ:
1451 	case V2TRAP:
1452 	case REPORT:
1453 		snmppdu_print(pdu.id, np, length);
1454 		break;
1455 	}
1456 
1457 	if (vflag) {
1458 		fputs("} ", stdout);
1459 	}
1460 }
1461 
1462 /*
1463  * Decode a scoped SNMP PDU.
1464  */
1465 static void
1466 scopedpdu_print(const u_char *np, u_int length, int version)
1467 {
1468 	struct be elem;
1469 	int i, count = 0;
1470 
1471 	/* Sequence */
1472 	if ((count = asn1_parse(np, length, &elem)) < 0)
1473 		return;
1474 	if (elem.type != BE_SEQ) {
1475 		fputs("[!scoped PDU]", stdout);
1476 		asn1_print(&elem);
1477 		return;
1478 	}
1479 	length = elem.asnlen;
1480 	np = (u_char *)elem.data.raw;
1481 
1482 	/* contextEngineID (OCTET STRING) */
1483 	if ((count = asn1_parse(np, length, &elem)) < 0)
1484 		return;
1485 	if (elem.type != BE_STR) {
1486 		fputs("[contextEngineID!=STR]", stdout);
1487 		asn1_print(&elem);
1488 		return;
1489 	}
1490 	length -= count;
1491 	np += count;
1492 
1493 	fputs("E= ", stdout);
1494 	for (i = 0; i < (int)elem.asnlen; i++) {
1495             printf("0x%02X", elem.data.str[i]);
1496         }
1497 	fputs(" ", stdout);
1498 
1499 	/* contextName (OCTET STRING) */
1500 	if ((count = asn1_parse(np, length, &elem)) < 0)
1501 		return;
1502 	if (elem.type != BE_STR) {
1503 		fputs("[contextName!=STR]", stdout);
1504 		asn1_print(&elem);
1505 		return;
1506 	}
1507 	length -= count;
1508 	np += count;
1509 
1510 	printf("C=%.*s ", (int)elem.asnlen, elem.data.str);
1511 
1512 	pdu_print(np, length, version);
1513 }
1514 
1515 /*
1516  * Decode SNMP Community Header (SNMPv1 and SNMPv2c)
1517  */
1518 static void
1519 community_print(const u_char *np, u_int length, int version)
1520 {
1521 	struct be elem;
1522 	int count = 0;
1523 
1524 	/* Community (String) */
1525 	if ((count = asn1_parse(np, length, &elem)) < 0)
1526 		return;
1527 	if (elem.type != BE_STR) {
1528 		fputs("[comm!=STR]", stdout);
1529 		asn1_print(&elem);
1530 		return;
1531 	}
1532 	/* default community */
1533 	if (strncmp((char *)elem.data.str, DEF_COMMUNITY,
1534 	    sizeof(DEF_COMMUNITY) - 1))
1535 		/* ! "public" */
1536 		printf("C=%.*s ", (int)elem.asnlen, elem.data.str);
1537 	length -= count;
1538 	np += count;
1539 
1540 	pdu_print(np, length, version);
1541 }
1542 
1543 /*
1544  * Decode SNMPv3 User-based Security Message Header (SNMPv3)
1545  */
1546 static void
1547 usm_print(const u_char *np, u_int length)
1548 {
1549         struct be elem;
1550 	int count = 0;
1551 
1552 	/* Sequence */
1553 	if ((count = asn1_parse(np, length, &elem)) < 0)
1554 		return;
1555 	if (elem.type != BE_SEQ) {
1556 		fputs("[!usm]", stdout);
1557 		asn1_print(&elem);
1558 		return;
1559 	}
1560 	length = elem.asnlen;
1561 	np = (u_char *)elem.data.raw;
1562 
1563 	/* msgAuthoritativeEngineID (OCTET STRING) */
1564 	if ((count = asn1_parse(np, length, &elem)) < 0)
1565 		return;
1566 	if (elem.type != BE_STR) {
1567 		fputs("[msgAuthoritativeEngineID!=STR]", stdout);
1568 		asn1_print(&elem);
1569 		return;
1570 	}
1571 	length -= count;
1572 	np += count;
1573 
1574 	/* msgAuthoritativeEngineBoots (INTEGER) */
1575 	if ((count = asn1_parse(np, length, &elem)) < 0)
1576 		return;
1577 	if (elem.type != BE_INT) {
1578 		fputs("[msgAuthoritativeEngineBoots!=INT]", stdout);
1579 		asn1_print(&elem);
1580 		return;
1581 	}
1582 	if (vflag)
1583 	        printf("B=%d ", elem.data.integer);
1584 	length -= count;
1585 	np += count;
1586 
1587 	/* msgAuthoritativeEngineTime (INTEGER) */
1588 	if ((count = asn1_parse(np, length, &elem)) < 0)
1589 		return;
1590 	if (elem.type != BE_INT) {
1591 		fputs("[msgAuthoritativeEngineTime!=INT]", stdout);
1592 		asn1_print(&elem);
1593 		return;
1594 	}
1595 	if (vflag)
1596 	        printf("T=%d ", elem.data.integer);
1597 	length -= count;
1598 	np += count;
1599 
1600 	/* msgUserName (OCTET STRING) */
1601 	if ((count = asn1_parse(np, length, &elem)) < 0)
1602 		return;
1603 	if (elem.type != BE_STR) {
1604 		fputs("[msgUserName!=STR]", stdout);
1605 		asn1_print(&elem);
1606 		return;
1607 	}
1608 	length -= count;
1609         np += count;
1610 
1611 	printf("U=%.*s ", (int)elem.asnlen, elem.data.str);
1612 
1613 	/* msgAuthenticationParameters (OCTET STRING) */
1614 	if ((count = asn1_parse(np, length, &elem)) < 0)
1615 		return;
1616 	if (elem.type != BE_STR) {
1617 		fputs("[msgAuthenticationParameters!=STR]", stdout);
1618 		asn1_print(&elem);
1619 		return;
1620 	}
1621 	length -= count;
1622         np += count;
1623 
1624 	/* msgPrivacyParameters (OCTET STRING) */
1625 	if ((count = asn1_parse(np, length, &elem)) < 0)
1626 		return;
1627 	if (elem.type != BE_STR) {
1628 		fputs("[msgPrivacyParameters!=STR]", stdout);
1629 		asn1_print(&elem);
1630 		return;
1631 	}
1632 	length -= count;
1633         np += count;
1634 
1635 	if (count < length)
1636 		printf("[%d extra after usm SEQ]", length - count);
1637 }
1638 
1639 /*
1640  * Decode SNMPv3 Message Header (SNMPv3)
1641  */
1642 static void
1643 v3msg_print(const u_char *np, u_int length)
1644 {
1645 	struct be elem;
1646 	int count = 0;
1647 	u_char flags;
1648 	int model;
1649 	const u_char *xnp = np;
1650 	int xlength = length;
1651 
1652 	/* Sequence */
1653 	if ((count = asn1_parse(np, length, &elem)) < 0)
1654 		return;
1655 	if (elem.type != BE_SEQ) {
1656 		fputs("[!message]", stdout);
1657 		asn1_print(&elem);
1658 		return;
1659 	}
1660 	length = elem.asnlen;
1661 	np = (u_char *)elem.data.raw;
1662 
1663 	if (vflag) {
1664 		fputs("{ ", stdout);
1665 	}
1666 
1667 	/* msgID (INTEGER) */
1668 	if ((count = asn1_parse(np, length, &elem)) < 0)
1669 		return;
1670 	if (elem.type != BE_INT) {
1671 		fputs("[msgID!=INT]", stdout);
1672 		asn1_print(&elem);
1673 		return;
1674 	}
1675 	length -= count;
1676 	np += count;
1677 
1678 	/* msgMaxSize (INTEGER) */
1679 	if ((count = asn1_parse(np, length, &elem)) < 0)
1680 		return;
1681 	if (elem.type != BE_INT) {
1682 		fputs("[msgMaxSize!=INT]", stdout);
1683 		asn1_print(&elem);
1684 		return;
1685 	}
1686 	length -= count;
1687 	np += count;
1688 
1689 	/* msgFlags (OCTET STRING) */
1690 	if ((count = asn1_parse(np, length, &elem)) < 0)
1691 		return;
1692 	if (elem.type != BE_STR) {
1693 		fputs("[msgFlags!=STR]", stdout);
1694 		asn1_print(&elem);
1695 		return;
1696 	}
1697 	if (elem.asnlen != 1) {
1698 	        printf("[msgFlags size %d]", elem.asnlen);
1699 		return;
1700 	}
1701 	flags = elem.data.str[0];
1702 	if (flags != 0x00 && flags != 0x01 && flags != 0x03
1703 	    && flags != 0x04 && flags != 0x05 && flags != 0x07) {
1704 		printf("[msgFlags=0x%02X]", flags);
1705 		return;
1706 	}
1707 	length -= count;
1708 	np += count;
1709 
1710 	fputs("F=", stdout);
1711 	if (flags & 0x01) fputs("a", stdout);
1712 	if (flags & 0x02) fputs("p", stdout);
1713 	if (flags & 0x04) fputs("r", stdout);
1714 	fputs(" ", stdout);
1715 
1716 	/* msgSecurityModel (INTEGER) */
1717 	if ((count = asn1_parse(np, length, &elem)) < 0)
1718 		return;
1719 	if (elem.type != BE_INT) {
1720 		fputs("[msgSecurityModel!=INT]", stdout);
1721 		asn1_print(&elem);
1722 		return;
1723 	}
1724 	model = elem.data.integer;
1725 	length -= count;
1726 	np += count;
1727 
1728 	if (count < length)
1729 		printf("[%d extra after message SEQ]", length - count);
1730 
1731 	if (vflag) {
1732 		fputs("} ", stdout);
1733 	}
1734 
1735 	if (model == 3) {
1736 	    if (vflag) {
1737 		fputs("{ USM ", stdout);
1738 	    }
1739 	} else {
1740 	    printf("[security model %d]", model);
1741             return;
1742 	}
1743 
1744 	np = xnp + (np - xnp);
1745 	length = xlength - (np - xnp);
1746 
1747 	/* msgSecurityParameters (OCTET STRING) */
1748 	if ((count = asn1_parse(np, length, &elem)) < 0)
1749 		return;
1750 	if (elem.type != BE_STR) {
1751 		fputs("[msgSecurityParameters!=STR]", stdout);
1752 		asn1_print(&elem);
1753 		return;
1754 	}
1755 	length -= count;
1756 	np += count;
1757 
1758 	if (model == 3) {
1759 	    usm_print(elem.data.str, elem.asnlen);
1760 	    if (vflag) {
1761 		fputs("} ", stdout);
1762 	    }
1763 	}
1764 
1765 	if (vflag) {
1766 	    fputs("{ ScopedPDU ", stdout);
1767 	}
1768 
1769 	scopedpdu_print(np, length, 3);
1770 
1771 	if (vflag) {
1772 		fputs("} ", stdout);
1773 	}
1774 }
1775 
1776 /*
1777  * Decode SNMP header and pass on to PDU printing routines
1778  */
1779 void
1780 snmp_print(const u_char *np, u_int length)
1781 {
1782 	struct be elem;
1783 	int count = 0;
1784 	int version = 0;
1785 
1786 	truncated = 0;
1787 
1788 	/* truncated packet? */
1789 	if (np + length > snapend) {
1790 		truncated = 1;
1791 		length = snapend - np;
1792 	}
1793 
1794 	putchar(' ');
1795 
1796 	/* initial Sequence */
1797 	if ((count = asn1_parse(np, length, &elem)) < 0)
1798 		return;
1799 	if (elem.type != BE_SEQ) {
1800 		fputs("[!init SEQ]", stdout);
1801 		asn1_print(&elem);
1802 		return;
1803 	}
1804 	if (count < length)
1805 		printf("[%d extra after iSEQ]", length - count);
1806 	/* descend */
1807 	length = elem.asnlen;
1808 	np = (u_char *)elem.data.raw;
1809 
1810 	/* Version (INTEGER) */
1811 	if ((count = asn1_parse(np, length, &elem)) < 0)
1812 		return;
1813 	if (elem.type != BE_INT) {
1814 		fputs("[version!=INT]", stdout);
1815 		asn1_print(&elem);
1816 		return;
1817 	}
1818 
1819 	switch (elem.data.integer) {
1820 	case SNMP_VERSION_1:
1821 	case SNMP_VERSION_2:
1822 	case SNMP_VERSION_3:
1823 	        if (vflag)
1824 		        printf("{ %s ", SnmpVersion[elem.data.integer]);
1825 		break;
1826 	default:
1827 	        printf("[version = %d]", elem.data.integer);
1828 		return;
1829 	}
1830 	version = elem.data.integer;
1831 	length -= count;
1832 	np += count;
1833 
1834 	switch (version) {
1835 	case SNMP_VERSION_1:
1836         case SNMP_VERSION_2:
1837 		community_print(np, length, version);
1838 		break;
1839 	case SNMP_VERSION_3:
1840 		v3msg_print(np, length);
1841 		break;
1842 	default:
1843 	        printf("[version = %d]", elem.data.integer);
1844 		break;
1845 	}
1846 
1847 	if (vflag) {
1848 		fputs("} ", stdout);
1849 	}
1850 }
1851