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