xref: /openbsd/usr.sbin/tcpdump/print-snmp.c (revision 404b540a)
1 /*	$OpenBSD: print-snmp.c,v 1.15 2008/01/17 17:33:15 reyk Exp $	*/
2 
3 /*
4  * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
5  *     John Robert LoVerso. All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  *
30  * This implementation has been influenced by the CMU SNMP release,
31  * by Steve Waldbusser.  However, this shares no code with that system.
32  * Additional ASN.1 insight gained from Marshall T. Rose's _The_Open_Book_.
33  * Earlier forms of this implementation were derived and/or inspired by an
34  * awk script originally written by C. Philip Wood of LANL (but later
35  * heavily modified by John Robert LoVerso).  The copyright notice for
36  * that work is preserved below, even though it may not rightly apply
37  * to this file.
38  *
39  * This started out as a very simple program, but the incremental decoding
40  * (into the BE structure) complicated things.
41  *
42  #			Los Alamos National Laboratory
43  #
44  #	Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
45  #	This software was produced under a U.S. Government contract
46  #	(W-7405-ENG-36) by Los Alamos National Laboratory, which is
47  #	operated by the	University of California for the U.S. Department
48  #	of Energy.  The U.S. Government is licensed to use, reproduce,
49  #	and distribute this software.  Permission is granted to the
50  #	public to copy and use this software without charge, provided
51  #	that this Notice and any statement of authorship are reproduced
52  #	on all copies.  Neither the Government nor the University makes
53  #	any warranty, express or implied, or assumes any liability or
54  #	responsibility for the use of this software.
55  #	@(#)snmp.awk.x	1.1 (LANL) 1/15/90
56  */
57 
58 #ifndef lint
59 static const char rcsid[] =
60     "@(#) $Id: print-snmp.c,v 1.15 2008/01/17 17:33:15 reyk Exp $ (LBL)";
61 #endif
62 
63 #include <sys/param.h>
64 #include <sys/time.h>
65 
66 #include <ctype.h>
67 #ifdef HAVE_MEMORY_H
68 #include <memory.h>
69 #endif
70 #include <stdio.h>
71 #include <string.h>
72 
73 #include "interface.h"
74 #include "addrtoname.h"
75 
76 /*
77  * Universal ASN.1 types
78  * (we only care about the tag values for those allowed in the Internet SMI)
79  */
80 char *Universal[] = {
81 	"U-0",
82 	"Boolean",
83 	"Integer",
84 #define INTEGER 2
85 	"Bitstring",
86 	"String",
87 #define STRING 4
88 	"Null",
89 #define ASN_NULL 5
90 	"ObjID",
91 #define OBJECTID 6
92 	"ObjectDes",
93 	"U-8","U-9","U-10","U-11",	/* 8-11 */
94 	"U-12","U-13","U-14","U-15",	/* 12-15 */
95 	"Sequence",
96 #define SEQUENCE 16
97 	"Set"
98 };
99 
100 /*
101  * Application-wide ASN.1 types from the Internet SMI and their tags
102  */
103 char *Application[] = {
104 	"IpAddress",
105 #define IPADDR 0
106 	"Counter",
107 #define COUNTER 1
108 	"Gauge",
109 #define GAUGE 2
110 	"TimeTicks",
111 #define TIMETICKS 3
112 	"Opaque",
113 #define OPAQUE 4
114 	"NsapAddress",
115 #define NSAPADDR 5
116 	"Counter64",
117 #define COUNTER64 6
118 	"UInteger32"
119 #define UINTEGER32 7
120 };
121 
122 /*
123  * Context-specific ASN.1 types for the SNMP PDUs and their tags
124  */
125 char *Context[] = {
126 	"GetRequest",
127 #define GETREQ 0
128 	"GetNextRequest",
129 #define GETNEXTREQ 1
130 	"GetResponse",
131 #define GETRESP 2
132 	"SetRequest",
133 #define SETREQ 3
134 	"Trap",
135 #define TRAP 4
136 	"GetBulkReq",
137 #define GETBULKREQ 5
138 	"InformReq",
139 #define INFORMREQ 6
140 	"TrapV2",
141 #define TRAPV2 7
142 	"Report"
143 #define REPORT 8
144 };
145 
146 /*
147  * Private ASN.1 types
148  * The Internet SMI does not specify any
149  */
150 char *Private[] = {
151 	"P-0"
152 };
153 
154 /*
155  * error-status values for any SNMP PDU
156  */
157 char *ErrorStatus[] = {
158 	"noError",
159 	"tooBig",
160 	"noSuchName",
161 	"badValue",
162 	"readOnly",
163 	"genErr",
164 	"noAccess",
165 	"wrongType",
166 	"wrongLength",
167 	"wrongEnc",
168 	"wrongValue",
169 	"noCreation",
170 	"inconValue",
171 	"resUnavail",
172 	"commitFailed",
173 	"undoFailed",
174 	"authError",
175 	"notWritable",
176 	"inconName"
177 };
178 #define DECODE_ErrorStatus(e) \
179 	( e >= 0 && e <= sizeof(ErrorStatus)/sizeof(ErrorStatus[0]) \
180 	? ErrorStatus[e] : (snprintf(errbuf, sizeof(errbuf), "err=%u", e), errbuf))
181 
182 /*
183  * generic-trap values in the SNMP Trap-PDU
184  */
185 char *GenericTrap[] = {
186 	"coldStart",
187 	"warmStart",
188 	"linkDown",
189 	"linkUp",
190 	"authenticationFailure",
191 	"egpNeighborLoss",
192 	"enterpriseSpecific"
193 #define GT_ENTERPRISE 6
194 };
195 #define DECODE_GenericTrap(t) \
196 	( t >= 0 && t <= sizeof(GenericTrap)/sizeof(GenericTrap[0]) \
197 	? GenericTrap[t] : (snprintf(buf, sizeof(buf), "gt=%d", t), buf))
198 
199 /*
200  * ASN.1 type class table
201  * Ties together the preceding Universal, Application, Context, and Private
202  * type definitions.
203  */
204 #define defineCLASS(x) { "x", x, sizeof(x)/sizeof(x[0]) } /* not ANSI-C */
205 struct {
206 	char	*name;
207 	char	**Id;
208 	    int	numIDs;
209     } Class[] = {
210 	defineCLASS(Universal),
211 #define	UNIVERSAL	0
212 	defineCLASS(Application),
213 #define	APPLICATION	1
214 	defineCLASS(Context),
215 #define	CONTEXT		2
216 	defineCLASS(Private),
217 #define	PRIVATE		3
218 };
219 
220 /*
221  * defined forms for ASN.1 types
222  */
223 char *Form[] = {
224 	"Primitive",
225 #define PRIMITIVE	0
226 	"Constructed",
227 #define CONSTRUCTED	1
228 };
229 
230 /*
231  * A structure for the OID tree for the compiled-in MIB.
232  * This is stored as a general-order tree.
233  */
234 struct obj {
235 	char	*desc;			/* name of object */
236 	u_int	oid;			/* sub-id following parent */
237 	u_char	type;			/* object type (unused) */
238 	struct obj *child, *next;	/* child and next sibling pointers */
239 } *objp = NULL;
240 
241 /*
242  * Include the compiled in SNMP MIB.  "mib.h" is produced by feeding
243  * RFC-1156 format files into "makemib".  "mib.h" MUST define at least
244  * a value for `mibroot'.
245  *
246  * In particular, this is gross, as this is including initialized structures,
247  * and by right shouldn't be an "include" file.
248  */
249 #include "mib.h"
250 
251 /*
252  * This defines a list of OIDs which will be abbreviated on output.
253  * Currently, this includes the prefixes for the Internet MIB, the
254  * private enterprises tree, and the experimental tree.
255  */
256 struct obj_abrev {
257 	char *prefix;			/* prefix for this abrev */
258 	struct obj *node;		/* pointer into object table */
259 	char *oid;			/* ASN.1 encoded OID */
260 } obj_abrev_list[] = {
261 #ifndef NO_ABREV_MIB
262 	/* .iso.org.dod.internet.mgmt.mib */
263 	{ "",	&_mib_obj,		"\53\6\1\2\1" },
264 #endif
265 #ifndef NO_ABREV_ENTER
266 	/* .iso.org.dod.internet.private.enterprises */
267 	{ "E:",	&_enterprises_obj,	"\53\6\1\4\1" },
268 #endif
269 #ifndef NO_ABREV_EXPERI
270 	/* .iso.org.dod.internet.experimental */
271 	{ "X:",	&_experimental_obj,	"\53\6\1\3" },
272 #endif
273 #ifndef NO_ABREV_SNMPMIBOBJECTS
274 	/* .iso.org.dod.internet.snmpV2.snmpModules.snmpMIB.snmpMIBObjects */
275 	{ "S:", &_snmpmibobjects_obj,	"\53\6\1\6\3\1\1" },
276 #endif
277 	{ 0,0,0 }
278 };
279 
280 /*
281  * This is used in the OID print routine to walk down the object tree
282  * rooted at `mibroot'.
283  */
284 #define OBJ_PRINT(o, suppressdot) \
285 { \
286 	if (objp) { \
287 		do { \
288 			if ((o) == objp->oid) \
289 				break; \
290 		} while ((objp = objp->next) != NULL); \
291 	} \
292 	if (objp) { \
293 		printf(suppressdot?"%s":".%s", objp->desc); \
294 		objp = objp->child; \
295 	} else \
296 		printf(suppressdot?"%u":".%u", (o)); \
297 }
298 
299 /*
300  * This is the definition for the Any-Data-Type storage used purely for
301  * temporary internal representation while decoding an ASN.1 data stream.
302  */
303 struct be {
304 	u_int32_t asnlen;
305 	union {
306 		caddr_t raw;
307 		int32_t integer;
308 		u_int32_t uns;
309 		u_int64_t uns64;
310 		const u_char *str;
311 	} data;
312 	u_short id;
313 	u_char form, class;		/* tag info */
314 	u_char type;
315 #define BE_ANY		255
316 #define BE_NONE		0
317 #define BE_NULL		1
318 #define BE_OCTET	2
319 #define BE_OID		3
320 #define BE_INT		4
321 #define BE_UNS		5
322 #define BE_STR		6
323 #define BE_SEQ		7
324 #define BE_INETADDR	8
325 #define BE_PDU		9
326 #define BE_UNS64	10
327 };
328 
329 /*
330  * Defaults for SNMP PDU components
331  */
332 #define DEF_COMMUNITY "public"
333 #define DEF_VERSION 1
334 
335 /*
336  * constants for ASN.1 decoding
337  */
338 #define OIDMUX 40
339 #define ASNLEN_INETADDR 4
340 #define ASN_SHIFT7 7
341 #define ASN_SHIFT8 8
342 #define ASN_BIT8 0x80
343 #define ASN_LONGLEN 0x80
344 
345 #define ASN_ID_BITS 0x1f
346 #define ASN_FORM_BITS 0x20
347 #define ASN_FORM_SHIFT 5
348 #define ASN_CLASS_BITS 0xc0
349 #define ASN_CLASS_SHIFT 6
350 
351 #define ASN_ID_EXT 0x1f		/* extension ID in tag field */
352 
353 /*
354  * truncated==1 means the packet was complete, but we don't have all of
355  * it to decode.
356  */
357 static int truncated;
358 #define ifNotTruncated if (truncated) fputs("[|snmp]", stdout); else
359 
360 /*
361  * This decodes the next ASN.1 object in the stream pointed to by "p"
362  * (and of real-length "len") and stores the intermediate data in the
363  * provided BE object.
364  *
365  * This returns -l if it fails (i.e., the ASN.1 stream is not valid).
366  * O/w, this returns the number of bytes parsed from "p".
367  */
368 static int
369 asn1_parse(register const u_char *p, u_int len, struct be *elem)
370 {
371 	u_char form, class, id;
372 	int i, hdr;
373 
374 	elem->asnlen = 0;
375 	elem->type = BE_ANY;
376 	if (len < 1) {
377 		ifNotTruncated puts("[nothing to parse], stdout");
378 		return -1;
379 	}
380 
381 	/*
382 	 * it would be nice to use a bit field, but you can't depend on them.
383 	 *  +---+---+---+---+---+---+---+---+
384 	 *  + class |frm|        id         |
385 	 *  +---+---+---+---+---+---+---+---+
386 	 *    7   6   5   4   3   2   1   0
387 	 */
388 	id = *p & ASN_ID_BITS;		/* lower 5 bits, range 00-1f */
389 #ifdef notdef
390 	form = (*p & 0xe0) >> 5;	/* move upper 3 bits to lower 3 */
391 	class = form >> 1;		/* bits 7&6 -> bits 1&0, range 0-3 */
392 	form &= 0x1;			/* bit 5 -> bit 0, range 0-1 */
393 #else
394 	form = (u_char)(*p & ASN_FORM_BITS) >> ASN_FORM_SHIFT;
395 	class = (u_char)(*p & ASN_CLASS_BITS) >> ASN_CLASS_SHIFT;
396 #endif
397 	elem->form = form;
398 	elem->class = class;
399 	elem->id = id;
400 	if (vflag)
401 		printf("|%.2x", *p);
402 	p++; len--; hdr = 1;
403 	/* extended tag field */
404 	if (id == ASN_ID_EXT) {
405 		for (id = 0; *p & ASN_BIT8 && len > 0; len--, hdr++, p++) {
406 			if (vflag)
407 				printf("|%.2x", *p);
408 			id = (id << 7) | (*p & ~ASN_BIT8);
409 		}
410 		if (len == 0 && *p & ASN_BIT8) {
411 			ifNotTruncated fputs("[Xtagfield?]", stdout);
412 			return -1;
413 		}
414 		elem->id = id = (id << 7) | *p;
415 		--len;
416 		++hdr;
417 		++p;
418 	}
419 	if (len < 1) {
420 		ifNotTruncated fputs("[no asnlen]", stdout);
421 		return -1;
422 	}
423 	elem->asnlen = *p;
424 	if (vflag)
425 		printf("|%.2x", *p);
426 	p++; len--; hdr++;
427 	if (elem->asnlen & ASN_BIT8) {
428 		int noct = elem->asnlen % ASN_BIT8;
429 		elem->asnlen = 0;
430 		if (len < noct) {
431 			ifNotTruncated printf("[asnlen? %d<%d]", len, noct);
432 			return -1;
433 		}
434 		for (; noct-- > 0; len--, hdr++) {
435 			if (vflag)
436 				printf("|%.2x", *p);
437 			elem->asnlen = (elem->asnlen << ASN_SHIFT8) | *p++;
438 		}
439 	}
440 	if (len < elem->asnlen) {
441 		if (!truncated) {
442 			printf("[len%d<asnlen%u]", len, elem->asnlen);
443 			return -1;
444 		}
445 		/* maybe should check at least 4? */
446 		elem->asnlen = len;
447 	}
448 	if (form >= sizeof(Form)/sizeof(Form[0])) {
449 		ifNotTruncated printf("[form?%d]", form);
450 		return -1;
451 	}
452 	if (class >= sizeof(Class)/sizeof(Class[0])) {
453 		ifNotTruncated printf("[class?%c/%d]", *Form[form], class);
454 		return -1;
455 	}
456 	if ((int)id >= Class[class].numIDs) {
457 		ifNotTruncated printf("[id?%c/%s/%d]", *Form[form],
458 			Class[class].name, id);
459 		return -1;
460 	}
461 
462 	switch (form) {
463 	case PRIMITIVE:
464 		switch (class) {
465 		case UNIVERSAL:
466 			switch (id) {
467 			case STRING:
468 				elem->type = BE_STR;
469 				elem->data.str = p;
470 				break;
471 
472 			case INTEGER: {
473 				register int32_t data;
474 				elem->type = BE_INT;
475 				data = 0;
476 
477 				if (*p & ASN_BIT8)	/* negative */
478 					data = -1;
479 				for (i = elem->asnlen; i-- > 0; p++)
480 					data = (data << ASN_SHIFT8) | *p;
481 				elem->data.integer = data;
482 				break;
483 			}
484 
485 			case OBJECTID:
486 				elem->type = BE_OID;
487 				elem->data.raw = (caddr_t)p;
488 				break;
489 
490 			case ASN_NULL:
491 				elem->type = BE_NULL;
492 				elem->data.raw = NULL;
493 				break;
494 
495 			default:
496 				elem->type = BE_OCTET;
497 				elem->data.raw = (caddr_t)p;
498 				printf("[P/U/%s]",
499 					Class[class].Id[id]);
500 				break;
501 			}
502 			break;
503 
504 		case APPLICATION:
505 			switch (id) {
506 			case IPADDR:
507 				elem->type = BE_INETADDR;
508 				elem->data.raw = (caddr_t)p;
509 				break;
510 
511 			case COUNTER:
512 			case GAUGE:
513 			case TIMETICKS:
514 			case OPAQUE:
515 			case NSAPADDR:
516 			case UINTEGER32: {
517 				register u_int32_t data;
518 				elem->type = BE_UNS;
519 				data = 0;
520 				for (i = elem->asnlen; i-- > 0; p++)
521 					data = (data << 8) + *p;
522 				elem->data.uns = data;
523 				break;
524 			}
525 
526 			case COUNTER64: {
527 				register u_int64_t data;
528 				elem->type = BE_UNS64;
529 				data = 0;
530 				for (i = elem->asnlen; i-- > 0; p++)
531 					data = (data << 8) + *p;
532 				elem->data.uns64 = data;
533 				break;
534 			}
535 
536 			default:
537 				elem->type = BE_OCTET;
538 				elem->data.raw = (caddr_t)p;
539 				printf("[P/A/%s]",
540 					Class[class].Id[id]);
541 				break;
542 			}
543 			break;
544 
545 		default:
546 			elem->type = BE_OCTET;
547 			elem->data.raw = (caddr_t)p;
548 			printf("[P/%s/%s]",
549 				Class[class].name, Class[class].Id[id]);
550 			break;
551 		}
552 		break;
553 
554 	case CONSTRUCTED:
555 		switch (class) {
556 		case UNIVERSAL:
557 			switch (id) {
558 			case SEQUENCE:
559 				elem->type = BE_SEQ;
560 				elem->data.raw = (caddr_t)p;
561 				break;
562 
563 			default:
564 				elem->type = BE_OCTET;
565 				elem->data.raw = (caddr_t)p;
566 				printf("C/U/%s", Class[class].Id[id]);
567 				break;
568 			}
569 			break;
570 
571 		case CONTEXT:
572 			elem->type = BE_PDU;
573 			elem->data.raw = (caddr_t)p;
574 			break;
575 
576 		default:
577 			elem->type = BE_OCTET;
578 			elem->data.raw = (caddr_t)p;
579 			printf("C/%s/%s",
580 				Class[class].name, Class[class].Id[id]);
581 			break;
582 		}
583 		break;
584 	}
585 	p += elem->asnlen;
586 	len -= elem->asnlen;
587 	return elem->asnlen + hdr;
588 }
589 
590 /*
591  * Display the ASN.1 object represented by the BE object.
592  * This used to be an integral part of asn1_parse() before the intermediate
593  * BE form was added.
594  */
595 static void
596 asn1_print(struct be *elem)
597 {
598 	u_char *p = (u_char *)elem->data.raw;
599 	u_int32_t asnlen = elem->asnlen;
600 	int i;
601 
602 	switch (elem->type) {
603 
604 	case BE_OCTET:
605 		for (i = asnlen; i-- > 0; p++)
606 			printf("_%.2x", *p);
607 		break;
608 
609 	case BE_NULL:
610 		break;
611 
612 	case BE_OID: {
613 	int o = 0, first = -1, i = asnlen;
614 
615 		if (!nflag && asnlen > 2) {
616 			struct obj_abrev *a = &obj_abrev_list[0];
617 			for (; a->node; a++) {
618 				if (!memcmp(a->oid, (char *)p,
619 				    strlen(a->oid))) {
620 					objp = a->node->child;
621 					i -= strlen(a->oid);
622 					p += strlen(a->oid);
623 					fputs(a->prefix, stdout);
624 					first = 1;
625 					break;
626 				}
627 			}
628 		}
629 		for (; i-- > 0; p++) {
630 			o = (o << ASN_SHIFT7) + (*p & ~ASN_BIT8);
631 			if (*p & ASN_LONGLEN)
632 				continue;
633 
634 			/*
635 			 * first subitem encodes two items with 1st*OIDMUX+2nd
636 			 */
637 			if (first < 0) {
638 				if (!nflag)
639 					objp = mibroot;
640 				first = 0;
641 				OBJ_PRINT(o/OIDMUX, first);
642 				o %= OIDMUX;
643 			}
644 			OBJ_PRINT(o, first);
645 			if (--first < 0)
646 				first = 0;
647 			o = 0;
648 		}
649 		break;
650 	}
651 
652 	case BE_INT:
653 		printf("%d", elem->data.integer);
654 		break;
655 
656 	case BE_UNS:
657 		printf("%d", elem->data.uns);
658 		break;
659 
660 	case BE_UNS64:
661 		printf("%lld", elem->data.uns64);
662 		break;
663 
664 	case BE_STR: {
665 		register int printable = 1, first = 1;
666 		const u_char *p = elem->data.str;
667 		for (i = asnlen; printable && i-- > 0; p++)
668 			printable = isprint(*p) || isspace(*p);
669 		p = elem->data.str;
670 		if (printable) {
671 			putchar('"');
672 			(void)fn_print(p, p + asnlen);
673 			putchar('"');
674 		} else
675 			for (i = asnlen; i-- > 0; p++) {
676 				printf(first ? "%.2x" : "_%.2x", *p);
677 				first = 0;
678 			}
679 		break;
680 	}
681 
682 	case BE_SEQ:
683 		printf("Seq(%u)", elem->asnlen);
684 		break;
685 
686 	case BE_INETADDR: {
687 		char sep;
688 		if (asnlen != ASNLEN_INETADDR)
689 			printf("[inetaddr len!=%d]", ASNLEN_INETADDR);
690 		sep='[';
691 		for (i = asnlen; i-- > 0; p++) {
692 			printf("%c%u", sep, *p);
693 			sep='.';
694 		}
695 		putchar(']');
696 		break;
697 	}
698 
699 	case BE_PDU:
700 		printf("%s(%u)",
701 			Class[CONTEXT].Id[elem->id], elem->asnlen);
702 		break;
703 
704 	case BE_ANY:
705 		fputs("[BE_ANY!?]", stdout);
706 		break;
707 
708 	default:
709 		fputs("[be!?]", stdout);
710 		break;
711 	}
712 }
713 
714 #ifdef notdef
715 /*
716  * This is a brute force ASN.1 printer: recurses to dump an entire structure.
717  * This will work for any ASN.1 stream, not just an SNMP PDU.
718  *
719  * By adding newlines and spaces at the correct places, this would print in
720  * Rose-Normal-Form.
721  *
722  * This is not currently used.
723  */
724 static void
725 asn1_decode(u_char *p, u_int length)
726 {
727 	struct be elem;
728 	int i = 0;
729 
730 	while (i >= 0 && length > 0) {
731 		i = asn1_parse(p, length, &elem);
732 		if (i >= 0) {
733 			fputs(" ", stdout);
734 			asn1_print(&elem);
735 			if (elem.type == BE_SEQ || elem.type == BE_PDU) {
736 				fputs(" {", stdout);
737 				asn1_decode(elem.data.raw, elem.asnlen);
738 				fputs(" }", stdout);
739 			}
740 			length -= i;
741 			p += i;
742 		}
743 	}
744 }
745 #endif
746 
747 /*
748  * General SNMP header
749  *	SEQUENCE {
750  *		version INTEGER {version-1(0)},
751  *		community OCTET STRING,
752  *		data ANY	-- PDUs
753  *	}
754  * PDUs for all but Trap: (see rfc1157 from page 15 on)
755  *	SEQUENCE {
756  *		request-id INTEGER,
757  *		error-status INTEGER,
758  *		error-index INTEGER,
759  *		varbindlist SEQUENCE OF
760  *			SEQUENCE {
761  *				name ObjectName,
762  *				value ObjectValue
763  *			}
764  *	}
765  * PDU for Trap:
766  *	SEQUENCE {
767  *		enterprise OBJECT IDENTIFIER,
768  *		agent-addr NetworkAddress,
769  *		generic-trap INTEGER,
770  *		specific-trap INTEGER,
771  *		time-stamp TimeTicks,
772  *		varbindlist SEQUENCE OF
773  *			SEQUENCE {
774  *				name ObjectName,
775  *				value ObjectValue
776  *			}
777  *	}
778  */
779 
780 /*
781  * Decode SNMP varBind
782  */
783 static void
784 varbind_print(u_char pduid, const u_char *np, u_int length, int error)
785 {
786 	struct be elem;
787 	int count = 0, ind;
788 
789 	/* Sequence of varBind */
790 	if ((count = asn1_parse(np, length, &elem)) < 0)
791 		return;
792 	if (elem.type != BE_SEQ) {
793 		fputs("[!SEQ of varbind]", stdout);
794 		asn1_print(&elem);
795 		return;
796 	}
797 	if (count < length)
798 		printf("[%d extra after SEQ of varbind]", length - count);
799 	/* descend */
800 	length = elem.asnlen;
801 	np = (u_char *)elem.data.raw;
802 
803 	for (ind = 1; length > 0; ind++) {
804 		const u_char *vbend;
805 		u_int vblength;
806 
807 		if (!error || ind == error)
808 			fputs(" ", stdout);
809 
810 		/* Sequence */
811 		if ((count = asn1_parse(np, length, &elem)) < 0)
812 			return;
813 		if (elem.type != BE_SEQ) {
814 			fputs("[!varbind]", stdout);
815 			asn1_print(&elem);
816 			return;
817 		}
818 		vbend = np + count;
819 		vblength = length - count;
820 		/* descend */
821 		length = elem.asnlen;
822 		np = (u_char *)elem.data.raw;
823 
824 		/* objName (OID) */
825 		if ((count = asn1_parse(np, length, &elem)) < 0)
826 			return;
827 		if (elem.type != BE_OID) {
828 			fputs("[objName!=OID]", stdout);
829 			asn1_print(&elem);
830 			return;
831 		}
832 		if (!error || ind == error)
833 			asn1_print(&elem);
834 		length -= count;
835 		np += count;
836 
837 		if (pduid != GETREQ && pduid != GETNEXTREQ && !error)
838 				fputs("=", stdout);
839 
840 		/* objVal (ANY) */
841 		if ((count = asn1_parse(np, length, &elem)) < 0)
842 			return;
843 		if (pduid == GETREQ || pduid == GETNEXTREQ || pduid == GETBULKREQ) {
844 			if (elem.type != BE_NULL) {
845 				fputs("[objVal!=NULL]", stdout);
846 				asn1_print(&elem);
847 			}
848 		} else
849 			if (error && ind == error && elem.type != BE_NULL)
850 				fputs("[err objVal!=NULL]", stdout);
851 			if (!error || ind == error)
852 				asn1_print(&elem);
853 
854 		length = vblength;
855 		np = vbend;
856 	}
857 }
858 
859 /*
860  * Decode SNMP PDUs: GetRequest, GetNextRequest, GetResponse, and SetRequest
861  */
862 static void
863 snmppdu_print(u_char pduid, const u_char *np, u_int length)
864 {
865 	struct be elem;
866 	int count = 0, error;
867 
868 	/* reqId (Integer) */
869 	if ((count = asn1_parse(np, length, &elem)) < 0)
870 		return;
871 	if (elem.type != BE_INT) {
872 		fputs("[reqId!=INT]", stdout);
873 		asn1_print(&elem);
874 		return;
875 	}
876 	/* ignore the reqId */
877 	length -= count;
878 	np += count;
879 
880 	/* errorStatus (Integer) */
881 	if ((count = asn1_parse(np, length, &elem)) < 0)
882 		return;
883 	if (elem.type != BE_INT) {
884 		fputs("[errorStatus!=INT]", stdout);
885 		asn1_print(&elem);
886 		return;
887 	}
888 	error = 0;
889 	if ((pduid == GETREQ || pduid == GETNEXTREQ)
890 	    && elem.data.integer != 0) {
891 		char errbuf[20];
892 		printf("[errorStatus(%s)!=0]",
893 			DECODE_ErrorStatus(elem.data.integer));
894 	} else if (pduid == GETBULKREQ)
895 		printf(" non-repeaters=%d", elem.data.integer);
896 	else if (elem.data.integer != 0) {
897 		char errbuf[20];
898 		printf(" %s", DECODE_ErrorStatus(elem.data.integer));
899 		error = elem.data.integer;
900 	}
901 	length -= count;
902 	np += count;
903 
904 	/* errorIndex (Integer) */
905 	if ((count = asn1_parse(np, length, &elem)) < 0)
906 		return;
907 	if (elem.type != BE_INT) {
908 		fputs("[errorIndex!=INT]", stdout);
909 		asn1_print(&elem);
910 		return;
911 	}
912 	if ((pduid == GETREQ || pduid == GETNEXTREQ)
913 	    && elem.data.integer != 0)
914 		printf("[errorIndex(%d)!=0]", elem.data.integer);
915 	else if (pduid == GETBULKREQ)
916 		printf(" max-repetitions=%d", elem.data.integer);
917 	else if (elem.data.integer != 0) {
918 		if (!error)
919 			printf("[errorIndex(%d) w/o errorStatus]",
920 				elem.data.integer);
921 		else {
922 			printf("@%d", elem.data.integer);
923 			error = elem.data.integer;
924 		}
925 	} else if (error) {
926 		fputs("[errorIndex==0]", stdout);
927 		error = 0;
928 	}
929 	length -= count;
930 	np += count;
931 
932 	varbind_print(pduid, np, length, error);
933 	return;
934 }
935 
936 /*
937  * Decode SNMP Trap PDU
938  */
939 static void
940 trap_print(const u_char *np, u_int length)
941 {
942 	struct be elem;
943 	int count = 0, generic;
944 
945 	putchar(' ');
946 
947 	/* enterprise (oid) */
948 	if ((count = asn1_parse(np, length, &elem)) < 0)
949 		return;
950 	if (elem.type != BE_OID) {
951 		fputs("[enterprise!=OID]", stdout);
952 		asn1_print(&elem);
953 		return;
954 	}
955 	asn1_print(&elem);
956 	length -= count;
957 	np += count;
958 
959 	putchar(' ');
960 
961 	/* agent-addr (inetaddr) */
962 	if ((count = asn1_parse(np, length, &elem)) < 0)
963 		return;
964 	if (elem.type != BE_INETADDR) {
965 		fputs("[agent-addr!=INETADDR]", stdout);
966 		asn1_print(&elem);
967 		return;
968 	}
969 	asn1_print(&elem);
970 	length -= count;
971 	np += count;
972 
973 	/* generic-trap (Integer) */
974 	if ((count = asn1_parse(np, length, &elem)) < 0)
975 		return;
976 	if (elem.type != BE_INT) {
977 		fputs("[generic-trap!=INT]", stdout);
978 		asn1_print(&elem);
979 		return;
980 	}
981 	generic = elem.data.integer;
982 	{
983 		char buf[20];
984 		printf(" %s", DECODE_GenericTrap(generic));
985 	}
986 	length -= count;
987 	np += count;
988 
989 	/* specific-trap (Integer) */
990 	if ((count = asn1_parse(np, length, &elem)) < 0)
991 		return;
992 	if (elem.type != BE_INT) {
993 		fputs("[specific-trap!=INT]", stdout);
994 		asn1_print(&elem);
995 		return;
996 	}
997 	if (generic != GT_ENTERPRISE) {
998 		if (elem.data.integer != 0)
999 			printf("[specific-trap(%d)!=0]", elem.data.integer);
1000 	} else
1001 		printf(" s=%d", elem.data.integer);
1002 	length -= count;
1003 	np += count;
1004 
1005 	putchar(' ');
1006 
1007 	/* time-stamp (TimeTicks) */
1008 	if ((count = asn1_parse(np, length, &elem)) < 0)
1009 		return;
1010 	if (elem.type != BE_UNS) {			/* XXX */
1011 		fputs("[time-stamp!=TIMETICKS]", stdout);
1012 		asn1_print(&elem);
1013 		return;
1014 	}
1015 	asn1_print(&elem);
1016 	length -= count;
1017 	np += count;
1018 
1019 	varbind_print (TRAP, np, length, 0);
1020 	return;
1021 }
1022 
1023 /*
1024  * Decode SNMP header and pass on to PDU printing routines
1025  */
1026 void
1027 snmp_print(const u_char *np, u_int length)
1028 {
1029 	struct be elem, pdu;
1030 	int count = 0;
1031 
1032 	truncated = 0;
1033 
1034 	/* truncated packet? */
1035 	if (np + length > snapend) {
1036 		truncated = 1;
1037 		length = snapend - np;
1038 	}
1039 
1040 	putchar(' ');
1041 
1042 	/* initial Sequence */
1043 	if ((count = asn1_parse(np, length, &elem)) < 0)
1044 		return;
1045 	if (elem.type != BE_SEQ) {
1046 		fputs("[!init SEQ]", stdout);
1047 		asn1_print(&elem);
1048 		return;
1049 	}
1050 	if (count < length)
1051 		printf("[%d extra after iSEQ]", length - count);
1052 	/* descend */
1053 	length = elem.asnlen;
1054 	np = (u_char *)elem.data.raw;
1055 	/* Version (Integer) */
1056 	if ((count = asn1_parse(np, length, &elem)) < 0)
1057 		return;
1058 	if (elem.type != BE_INT) {
1059 		fputs("[version!=INT]", stdout);
1060 		asn1_print(&elem);
1061 		return;
1062 	}
1063 	/* only handle version 1 and 2 */
1064 	if (elem.data.integer > DEF_VERSION) {
1065 		printf("[version(%d)>%d]", elem.data.integer, DEF_VERSION);
1066 		return;
1067 	}
1068 	length -= count;
1069 	np += count;
1070 
1071 	/* Community (String) */
1072 	if ((count = asn1_parse(np, length, &elem)) < 0)
1073 		return;
1074 	if (elem.type != BE_STR) {
1075 		fputs("[comm!=STR]", stdout);
1076 		asn1_print(&elem);
1077 		return;
1078 	}
1079 	/* default community */
1080 	if (strncmp((char *)elem.data.str, DEF_COMMUNITY,
1081 	    sizeof(DEF_COMMUNITY) - 1))
1082 		/* ! "public" */
1083 		printf("C=%.*s ", (int)elem.asnlen, elem.data.str);
1084 	length -= count;
1085 	np += count;
1086 
1087 	/* PDU (Context) */
1088 	if ((count = asn1_parse(np, length, &pdu)) < 0)
1089 		return;
1090 	if (pdu.type != BE_PDU) {
1091 		fputs("[no PDU]", stdout);
1092 		return;
1093 	}
1094 	if (count < length)
1095 		printf("[%d extra after PDU]", length - count);
1096 	asn1_print(&pdu);
1097 	/* descend into PDU */
1098 	length = pdu.asnlen;
1099 	np = (u_char *)pdu.data.raw;
1100 
1101 	switch (pdu.id) {
1102 	case TRAP:
1103 		trap_print(np, length);
1104 		break;
1105 	case GETREQ:
1106 	case GETNEXTREQ:
1107 	case GETRESP:
1108 	case SETREQ:
1109 	case GETBULKREQ:
1110 	case INFORMREQ:
1111 	case TRAPV2:
1112 	case REPORT:
1113 		snmppdu_print(pdu.id, np, length);
1114 		break;
1115 	}
1116 	return;
1117 }
1118