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