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