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