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