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[] _U_ =
61    "@(#) $Header: /tcpdump/master/tcpdump/print-snmp.c,v 1.64 2005-05-06 07:56:53 guy Exp $ (LBL)";
62#endif
63
64#ifdef HAVE_CONFIG_H
65#include "config.h"
66#endif
67
68#include <tcpdump-stdinc.h>
69
70#include <stdio.h>
71#include <string.h>
72
73#ifdef HAVE_SMI_H
74#include <smi.h>
75#endif
76
77#include "interface.h"
78#include "addrtoname.h"
79
80#undef OPAQUE  /* defined in <wingdi.h> */
81
82/*
83 * Universal ASN.1 types
84 * (we only care about the tag values for those allowed in the Internet SMI)
85 */
86const char *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 */
109const char *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 */
128const char *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 */
158const char *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 */
171const char *Private[] = {
172	"P-0"
173};
174
175/*
176 * error-status values for any SNMP PDU
177 */
178const char *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 && (size_t)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 */
207const char *GenericTrap[] = {
208	"coldStart",
209	"warmStart",
210	"linkDown",
211	"linkUp",
212	"authenticationFailure",
213	"egpNeighborLoss",
214	"enterpriseSpecific"
215#define GT_ENTERPRISE 6
216};
217#define DECODE_GenericTrap(t) \
218	( t >= 0 && (size_t)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	const char	*name;
230	const 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 */
248const char *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	const 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	const char *prefix;		/* prefix for this abrev */
283	struct obj *node;		/* pointer into object table */
284	const 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 */
363const char *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 * This decodes the next ASN.1 object in the stream pointed to by "p"
399 * (and of real-length "len") and stores the intermediate data in the
400 * provided BE object.
401 *
402 * This returns -l if it fails (i.e., the ASN.1 stream is not valid).
403 * O/w, this returns the number of bytes parsed from "p".
404 */
405static int
406asn1_parse(register const u_char *p, u_int len, struct be *elem)
407{
408	u_char form, class, id;
409	int i, hdr;
410
411	elem->asnlen = 0;
412	elem->type = BE_ANY;
413	if (len < 1) {
414		fputs("[nothing to parse]", stdout);
415		return -1;
416	}
417	TCHECK(*p);
418
419	/*
420	 * it would be nice to use a bit field, but you can't depend on them.
421	 *  +---+---+---+---+---+---+---+---+
422	 *  + class |frm|        id         |
423	 *  +---+---+---+---+---+---+---+---+
424	 *    7   6   5   4   3   2   1   0
425	 */
426	id = *p & ASN_ID_BITS;		/* lower 5 bits, range 00-1f */
427#ifdef notdef
428	form = (*p & 0xe0) >> 5;	/* move upper 3 bits to lower 3 */
429	class = form >> 1;		/* bits 7&6 -> bits 1&0, range 0-3 */
430	form &= 0x1;			/* bit 5 -> bit 0, range 0-1 */
431#else
432	form = (u_char)(*p & ASN_FORM_BITS) >> ASN_FORM_SHIFT;
433	class = (u_char)(*p & ASN_CLASS_BITS) >> ASN_CLASS_SHIFT;
434#endif
435	elem->form = form;
436	elem->class = class;
437	elem->id = id;
438	p++; len--; hdr = 1;
439	/* extended tag field */
440	if (id == ASN_ID_EXT) {
441		/*
442		 * The ID follows, as a sequence of octets with the
443		 * 8th bit set and the remaining 7 bits being
444		 * the next 7 bits of the value, terminated with
445		 * an octet with the 8th bit not set.
446		 *
447		 * First, assemble all the octets with the 8th
448		 * bit set.  XXX - this doesn't handle a value
449		 * that won't fit in 32 bits.
450		 */
451		for (id = 0; *p & ASN_BIT8; len--, hdr++, p++) {
452			if (len < 1) {
453				fputs("[Xtagfield?]", stdout);
454				return -1;
455			}
456			TCHECK(*p);
457			id = (id << 7) | (*p & ~ASN_BIT8);
458		}
459		if (len < 1) {
460			fputs("[Xtagfield?]", stdout);
461			return -1;
462		}
463		TCHECK(*p);
464		elem->id = id = (id << 7) | *p;
465		--len;
466		++hdr;
467		++p;
468	}
469	if (len < 1) {
470		fputs("[no asnlen]", stdout);
471		return -1;
472	}
473	TCHECK(*p);
474	elem->asnlen = *p;
475	p++; len--; hdr++;
476	if (elem->asnlen & ASN_BIT8) {
477		u_int32_t noct = elem->asnlen % ASN_BIT8;
478		elem->asnlen = 0;
479		if (len < noct) {
480			printf("[asnlen? %d<%d]", len, noct);
481			return -1;
482		}
483		TCHECK2(*p, noct);
484		for (; noct-- > 0; len--, hdr++)
485			elem->asnlen = (elem->asnlen << ASN_SHIFT8) | *p++;
486	}
487	if (len < elem->asnlen) {
488		printf("[len%d<asnlen%u]", len, elem->asnlen);
489		return -1;
490	}
491	if (form >= sizeof(Form)/sizeof(Form[0])) {
492		printf("[form?%d]", form);
493		return -1;
494	}
495	if (class >= sizeof(Class)/sizeof(Class[0])) {
496		printf("[class?%c/%d]", *Form[form], class);
497		return -1;
498	}
499	if ((int)id >= Class[class].numIDs) {
500		printf("[id?%c/%s/%d]", *Form[form], Class[class].name, id);
501		return -1;
502	}
503
504	switch (form) {
505	case PRIMITIVE:
506		switch (class) {
507		case UNIVERSAL:
508			switch (id) {
509			case STRING:
510				elem->type = BE_STR;
511				elem->data.str = p;
512				break;
513
514			case INTEGER: {
515				register int32_t data;
516				elem->type = BE_INT;
517				data = 0;
518
519				TCHECK2(*p, elem->asnlen);
520				if (*p & ASN_BIT8)	/* negative */
521					data = -1;
522				for (i = elem->asnlen; i-- > 0; p++)
523					data = (data << ASN_SHIFT8) | *p;
524				elem->data.integer = data;
525				break;
526			}
527
528			case OBJECTID:
529				elem->type = BE_OID;
530				elem->data.raw = (caddr_t)p;
531				break;
532
533			case ASN_NULL:
534				elem->type = BE_NULL;
535				elem->data.raw = NULL;
536				break;
537
538			default:
539				elem->type = BE_OCTET;
540				elem->data.raw = (caddr_t)p;
541				printf("[P/U/%s]",
542					Class[class].Id[id]);
543				break;
544			}
545			break;
546
547		case APPLICATION:
548			switch (id) {
549			case IPADDR:
550				elem->type = BE_INETADDR;
551				elem->data.raw = (caddr_t)p;
552				break;
553
554			case COUNTER:
555			case GAUGE:
556			case TIMETICKS: {
557				register u_int32_t data;
558				TCHECK2(*p, elem->asnlen);
559				elem->type = BE_UNS;
560				data = 0;
561				for (i = elem->asnlen; i-- > 0; p++)
562					data = (data << 8) + *p;
563				elem->data.uns = data;
564				break;
565			}
566
567			case COUNTER64: {
568				register u_int32_t high, low;
569				TCHECK2(*p, elem->asnlen);
570			        elem->type = BE_UNS64;
571				high = 0, low = 0;
572				for (i = elem->asnlen; i-- > 0; p++) {
573				        high = (high << 8) |
574					    ((low & 0xFF000000) >> 24);
575					low = (low << 8) | *p;
576				}
577				elem->data.uns64.high = high;
578				elem->data.uns64.low = low;
579				break;
580			}
581
582			default:
583				elem->type = BE_OCTET;
584				elem->data.raw = (caddr_t)p;
585				printf("[P/A/%s]",
586					Class[class].Id[id]);
587				break;
588			}
589			break;
590
591		case CONTEXT:
592			switch (id) {
593			case NOSUCHOBJECT:
594				elem->type = BE_NOSUCHOBJECT;
595				elem->data.raw = NULL;
596				break;
597
598			case NOSUCHINSTANCE:
599				elem->type = BE_NOSUCHINST;
600				elem->data.raw = NULL;
601				break;
602
603			case ENDOFMIBVIEW:
604				elem->type = BE_ENDOFMIBVIEW;
605				elem->data.raw = NULL;
606				break;
607			}
608			break;
609
610		default:
611			printf("[P/%s/%s]",
612				Class[class].name, Class[class].Id[id]);
613			TCHECK2(*p, elem->asnlen);
614			elem->type = BE_OCTET;
615			elem->data.raw = (caddr_t)p;
616			break;
617		}
618		break;
619
620	case CONSTRUCTED:
621		switch (class) {
622		case UNIVERSAL:
623			switch (id) {
624			case SEQUENCE:
625				elem->type = BE_SEQ;
626				elem->data.raw = (caddr_t)p;
627				break;
628
629			default:
630				elem->type = BE_OCTET;
631				elem->data.raw = (caddr_t)p;
632				printf("C/U/%s", Class[class].Id[id]);
633				break;
634			}
635			break;
636
637		case CONTEXT:
638			elem->type = BE_PDU;
639			elem->data.raw = (caddr_t)p;
640			break;
641
642		default:
643			elem->type = BE_OCTET;
644			elem->data.raw = (caddr_t)p;
645			printf("C/%s/%s",
646				Class[class].name, Class[class].Id[id]);
647			break;
648		}
649		break;
650	}
651	p += elem->asnlen;
652	len -= elem->asnlen;
653	return elem->asnlen + hdr;
654
655trunc:
656	fputs("[|snmp]", stdout);
657	return -1;
658}
659
660/*
661 * Display the ASN.1 object represented by the BE object.
662 * This used to be an integral part of asn1_parse() before the intermediate
663 * BE form was added.
664 */
665static int
666asn1_print(struct be *elem)
667{
668	u_char *p = (u_char *)elem->data.raw;
669	u_int32_t asnlen = elem->asnlen;
670	u_int32_t i;
671
672	switch (elem->type) {
673
674	case BE_OCTET:
675		TCHECK2(*p, asnlen);
676		for (i = asnlen; i-- > 0; p++)
677			printf("_%.2x", *p);
678		break;
679
680	case BE_NULL:
681		break;
682
683	case BE_OID: {
684		int o = 0, first = -1, i = asnlen;
685
686		if (!sflag && !nflag && asnlen > 2) {
687			struct obj_abrev *a = &obj_abrev_list[0];
688			size_t a_len = strlen(a->oid);
689			for (; a->node; a++) {
690				TCHECK2(*p, a_len);
691				if (memcmp(a->oid, (char *)p, a_len) == 0) {
692					objp = a->node->child;
693					i -= strlen(a->oid);
694					p += strlen(a->oid);
695					fputs(a->prefix, stdout);
696					first = 1;
697					break;
698				}
699			}
700		}
701
702		for (; !sflag && i-- > 0; p++) {
703			TCHECK(*p);
704			o = (o << ASN_SHIFT7) + (*p & ~ASN_BIT8);
705			if (*p & ASN_LONGLEN)
706			        continue;
707
708			/*
709			 * first subitem encodes two items with 1st*OIDMUX+2nd
710			 * (see X.690:1997 clause 8.19 for the details)
711			 */
712			if (first < 0) {
713			        int s;
714				if (!nflag)
715					objp = mibroot;
716				first = 0;
717				s = o / OIDMUX;
718				if (s > 2) s = 2;
719				OBJ_PRINT(s, first);
720				o -= s * OIDMUX;
721			}
722			OBJ_PRINT(o, first);
723			if (--first < 0)
724				first = 0;
725			o = 0;
726		}
727		break;
728	}
729
730	case BE_INT:
731		printf("%d", elem->data.integer);
732		break;
733
734	case BE_UNS:
735		printf("%u", elem->data.uns);
736		break;
737
738	case BE_UNS64: {	/* idea borrowed from by Marshall Rose */
739	        double d;
740		int j, carry;
741		char *cpf, *cpl, last[6], first[30];
742		if (elem->data.uns64.high == 0) {
743		        printf("%u", elem->data.uns64.low);
744		        break;
745		}
746		d = elem->data.uns64.high * 4294967296.0;	/* 2^32 */
747		if (elem->data.uns64.high <= 0x1fffff) {
748		        d += elem->data.uns64.low;
749#if 0 /*is looks illegal, but what is the intention?*/
750			printf("%.f", d);
751#else
752			printf("%f", d);
753#endif
754			break;
755		}
756		d += (elem->data.uns64.low & 0xfffff000);
757#if 0 /*is looks illegal, but what is the intention?*/
758		snprintf(first, sizeof(first), "%.f", d);
759#else
760		snprintf(first, sizeof(first), "%f", d);
761#endif
762		snprintf(last, sizeof(last), "%5.5d",
763		    elem->data.uns64.low & 0xfff);
764		for (carry = 0, cpf = first+strlen(first)-1, cpl = last+4;
765		     cpl >= last;
766		     cpf--, cpl--) {
767		        j = carry + (*cpf - '0') + (*cpl - '0');
768			if (j > 9) {
769			        j -= 10;
770				carry = 1;
771			} else {
772			        carry = 0;
773		        }
774			*cpf = j + '0';
775		}
776		fputs(first, stdout);
777		break;
778	}
779
780	case BE_STR: {
781		register int printable = 1, first = 1;
782		const u_char *p = elem->data.str;
783		TCHECK2(*p, asnlen);
784		for (i = asnlen; printable && i-- > 0; p++)
785			printable = ND_ISPRINT(*p);
786		p = elem->data.str;
787		if (printable) {
788			putchar('"');
789			if (fn_printn(p, asnlen, snapend)) {
790				putchar('"');
791				goto trunc;
792			}
793			putchar('"');
794		} else
795			for (i = asnlen; i-- > 0; p++) {
796				printf(first ? "%.2x" : "_%.2x", *p);
797				first = 0;
798			}
799		break;
800	}
801
802	case BE_SEQ:
803		printf("Seq(%u)", elem->asnlen);
804		break;
805
806	case BE_INETADDR:
807		if (asnlen != ASNLEN_INETADDR)
808			printf("[inetaddr len!=%d]", ASNLEN_INETADDR);
809		TCHECK2(*p, asnlen);
810		for (i = asnlen; i-- != 0; p++) {
811			printf((i == asnlen-1) ? "%u" : ".%u", *p);
812		}
813		break;
814
815	case BE_NOSUCHOBJECT:
816	case BE_NOSUCHINST:
817	case BE_ENDOFMIBVIEW:
818	        printf("[%s]", Class[EXCEPTIONS].Id[elem->id]);
819		break;
820
821	case BE_PDU:
822		printf("%s(%u)",
823			Class[CONTEXT].Id[elem->id], elem->asnlen);
824		break;
825
826	case BE_ANY:
827		fputs("[BE_ANY!?]", stdout);
828		break;
829
830	default:
831		fputs("[be!?]", stdout);
832		break;
833	}
834	return 0;
835
836trunc:
837	fputs("[|snmp]", stdout);
838	return -1;
839}
840
841#ifdef notdef
842/*
843 * This is a brute force ASN.1 printer: recurses to dump an entire structure.
844 * This will work for any ASN.1 stream, not just an SNMP PDU.
845 *
846 * By adding newlines and spaces at the correct places, this would print in
847 * Rose-Normal-Form.
848 *
849 * This is not currently used.
850 */
851static void
852asn1_decode(u_char *p, u_int length)
853{
854	struct be elem;
855	int i = 0;
856
857	while (i >= 0 && length > 0) {
858		i = asn1_parse(p, length, &elem);
859		if (i >= 0) {
860			fputs(" ", stdout);
861			if (asn1_print(&elem) < 0)
862				return;
863			if (elem.type == BE_SEQ || elem.type == BE_PDU) {
864				fputs(" {", stdout);
865				asn1_decode(elem.data.raw, elem.asnlen);
866				fputs(" }", stdout);
867			}
868			length -= i;
869			p += i;
870		}
871	}
872}
873#endif
874
875#ifdef LIBSMI
876
877struct smi2be {
878    SmiBasetype basetype;
879    int be;
880};
881
882static struct smi2be smi2betab[] = {
883    { SMI_BASETYPE_INTEGER32,		BE_INT },
884    { SMI_BASETYPE_OCTETSTRING,		BE_STR },
885    { SMI_BASETYPE_OCTETSTRING,		BE_INETADDR },
886    { SMI_BASETYPE_OBJECTIDENTIFIER,	BE_OID },
887    { SMI_BASETYPE_UNSIGNED32,		BE_UNS },
888    { SMI_BASETYPE_INTEGER64,		BE_NONE },
889    { SMI_BASETYPE_UNSIGNED64,		BE_UNS64 },
890    { SMI_BASETYPE_FLOAT32,		BE_NONE },
891    { SMI_BASETYPE_FLOAT64,		BE_NONE },
892    { SMI_BASETYPE_FLOAT128,		BE_NONE },
893    { SMI_BASETYPE_ENUM,		BE_INT },
894    { SMI_BASETYPE_BITS,		BE_STR },
895    { SMI_BASETYPE_UNKNOWN,		BE_NONE }
896};
897
898static int
899smi_decode_oid(struct be *elem, unsigned int *oid,
900	       unsigned int oidsize, unsigned int *oidlen)
901{
902	u_char *p = (u_char *)elem->data.raw;
903	u_int32_t asnlen = elem->asnlen;
904	int o = 0, first = -1, i = asnlen;
905
906	for (*oidlen = 0; sflag && i-- > 0; p++) {
907		TCHECK(*p);
908	        o = (o << ASN_SHIFT7) + (*p & ~ASN_BIT8);
909		if (*p & ASN_LONGLEN)
910		    continue;
911
912		/*
913		 * first subitem encodes two items with 1st*OIDMUX+2nd
914		 * (see X.690:1997 clause 8.19 for the details)
915		 */
916		if (first < 0) {
917		        first = 0;
918			if (*oidlen < oidsize) {
919			    oid[*oidlen] = o / OIDMUX;
920			    if (oid[*oidlen] > 2) oid[*oidlen] = 2;
921			}
922			o -= oid[*oidlen] * OIDMUX;
923			if (*oidlen < oidsize) (*oidlen)++;
924		}
925		if (*oidlen < oidsize) {
926			oid[(*oidlen)++] = o;
927		}
928		o = 0;
929	}
930	return 0;
931
932trunc:
933	fputs("[|snmp]", stdout);
934	return -1;
935}
936
937static int smi_check_type(SmiBasetype basetype, int be)
938{
939    int i;
940
941    for (i = 0; smi2betab[i].basetype != SMI_BASETYPE_UNKNOWN; i++) {
942	if (smi2betab[i].basetype == basetype && smi2betab[i].be == be) {
943	    return 1;
944	}
945    }
946
947    return 0;
948}
949
950static int smi_check_a_range(SmiType *smiType, SmiRange *smiRange,
951			     struct be *elem)
952{
953    int ok = 1;
954
955    switch (smiType->basetype) {
956    case SMI_BASETYPE_OBJECTIDENTIFIER:
957    case SMI_BASETYPE_OCTETSTRING:
958	if (smiRange->minValue.value.unsigned32
959	    == smiRange->maxValue.value.unsigned32) {
960	    ok = (elem->asnlen == smiRange->minValue.value.unsigned32);
961	} else {
962	    ok = (elem->asnlen >= smiRange->minValue.value.unsigned32
963		  && elem->asnlen <= smiRange->maxValue.value.unsigned32);
964	}
965	break;
966
967    case SMI_BASETYPE_INTEGER32:
968	ok = (elem->data.integer >= smiRange->minValue.value.integer32
969	      && elem->data.integer <= smiRange->maxValue.value.integer32);
970	break;
971
972    case SMI_BASETYPE_UNSIGNED32:
973	ok = (elem->data.uns >= smiRange->minValue.value.unsigned32
974	      && elem->data.uns <= smiRange->maxValue.value.unsigned32);
975	break;
976
977    case SMI_BASETYPE_UNSIGNED64:
978	/* XXX */
979	break;
980
981	/* case SMI_BASETYPE_INTEGER64: SMIng */
982	/* case SMI_BASETYPE_FLOAT32: SMIng */
983	/* case SMI_BASETYPE_FLOAT64: SMIng */
984	/* case SMI_BASETYPE_FLOAT128: SMIng */
985
986    case SMI_BASETYPE_ENUM:
987    case SMI_BASETYPE_BITS:
988    case SMI_BASETYPE_UNKNOWN:
989	ok = 1;
990	break;
991
992    default:
993	ok = 0;
994	break;
995    }
996
997    return ok;
998}
999
1000static int smi_check_range(SmiType *smiType, struct be *elem)
1001{
1002        SmiRange *smiRange;
1003	int ok = 1;
1004
1005	for (smiRange = smiGetFirstRange(smiType);
1006	     smiRange;
1007	     smiRange = smiGetNextRange(smiRange)) {
1008
1009	    ok = smi_check_a_range(smiType, smiRange, elem);
1010
1011	    if (ok) {
1012		break;
1013	    }
1014	}
1015
1016	if (ok) {
1017	    SmiType *parentType;
1018	    parentType = smiGetParentType(smiType);
1019	    if (parentType) {
1020		ok = smi_check_range(parentType, elem);
1021	    }
1022	}
1023
1024	return ok;
1025}
1026
1027static SmiNode *smi_print_variable(struct be *elem, int *status)
1028{
1029	unsigned int oid[128], oidlen;
1030	SmiNode *smiNode = NULL;
1031	unsigned int i;
1032
1033	*status = smi_decode_oid(elem, oid, sizeof(oid)/sizeof(unsigned int),
1034	    &oidlen);
1035	if (*status < 0)
1036		return NULL;
1037	smiNode = smiGetNodeByOID(oidlen, oid);
1038	if (! smiNode) {
1039		*status = asn1_print(elem);
1040		return NULL;
1041	}
1042	if (vflag) {
1043		fputs(smiGetNodeModule(smiNode)->name, stdout);
1044		fputs("::", stdout);
1045	}
1046	fputs(smiNode->name, stdout);
1047	if (smiNode->oidlen < oidlen) {
1048	        for (i = smiNode->oidlen; i < oidlen; i++) {
1049		        printf(".%u", oid[i]);
1050		}
1051	}
1052	*status = 0;
1053	return smiNode;
1054}
1055
1056static int
1057smi_print_value(SmiNode *smiNode, u_char pduid, struct be *elem)
1058{
1059	unsigned int i, oid[128], oidlen;
1060	SmiType *smiType;
1061	SmiNamedNumber *nn;
1062	int done = 0;
1063
1064	if (! smiNode || ! (smiNode->nodekind
1065			    & (SMI_NODEKIND_SCALAR | SMI_NODEKIND_COLUMN))) {
1066	    return asn1_print(elem);
1067	}
1068
1069	if (elem->type == BE_NOSUCHOBJECT
1070	    || elem->type == BE_NOSUCHINST
1071	    || elem->type == BE_ENDOFMIBVIEW) {
1072	    return asn1_print(elem);
1073	}
1074
1075	if (NOTIFY_CLASS(pduid) && smiNode->access < SMI_ACCESS_NOTIFY) {
1076	    fputs("[notNotifyable]", stdout);
1077	}
1078
1079	if (READ_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_ONLY) {
1080	    fputs("[notReadable]", stdout);
1081	}
1082
1083	if (WRITE_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_WRITE) {
1084	    fputs("[notWritable]", stdout);
1085	}
1086
1087	if (RESPONSE_CLASS(pduid)
1088	    && smiNode->access == SMI_ACCESS_NOT_ACCESSIBLE) {
1089	    fputs("[noAccess]", stdout);
1090	}
1091
1092	smiType = smiGetNodeType(smiNode);
1093	if (! smiType) {
1094	    return asn1_print(elem);
1095	}
1096
1097	if (! smi_check_type(smiType->basetype, elem->type)) {
1098	    fputs("[wrongType]", stdout);
1099	}
1100
1101	if (! smi_check_range(smiType, elem)) {
1102	    fputs("[outOfRange]", stdout);
1103	}
1104
1105	/* resolve bits to named bits */
1106
1107	/* check whether instance identifier is valid */
1108
1109	/* apply display hints (integer, octetstring) */
1110
1111	/* convert instance identifier to index type values */
1112
1113	switch (elem->type) {
1114	case BE_OID:
1115	        if (smiType->basetype == SMI_BASETYPE_BITS) {
1116		        /* print bit labels */
1117		} else {
1118		        smi_decode_oid(elem, oid,
1119				       sizeof(oid)/sizeof(unsigned int),
1120				       &oidlen);
1121			smiNode = smiGetNodeByOID(oidlen, oid);
1122			if (smiNode) {
1123			        if (vflag) {
1124					fputs(smiGetNodeModule(smiNode)->name, stdout);
1125					fputs("::", stdout);
1126				}
1127				fputs(smiNode->name, stdout);
1128				if (smiNode->oidlen < oidlen) {
1129				        for (i = smiNode->oidlen;
1130					     i < oidlen; i++) {
1131					        printf(".%u", oid[i]);
1132					}
1133				}
1134				done++;
1135			}
1136		}
1137		break;
1138
1139	case BE_INT:
1140	        if (smiType->basetype == SMI_BASETYPE_ENUM) {
1141		        for (nn = smiGetFirstNamedNumber(smiType);
1142			     nn;
1143			     nn = smiGetNextNamedNumber(nn)) {
1144			         if (nn->value.value.integer32
1145				     == elem->data.integer) {
1146				         fputs(nn->name, stdout);
1147					 printf("(%d)", elem->data.integer);
1148					 done++;
1149					 break;
1150				}
1151			}
1152		}
1153		break;
1154	}
1155
1156	if (! done) {
1157		return asn1_print(elem);
1158	}
1159	return 0;
1160}
1161#endif
1162
1163/*
1164 * General SNMP header
1165 *	SEQUENCE {
1166 *		version INTEGER {version-1(0)},
1167 *		community OCTET STRING,
1168 *		data ANY	-- PDUs
1169 *	}
1170 * PDUs for all but Trap: (see rfc1157 from page 15 on)
1171 *	SEQUENCE {
1172 *		request-id INTEGER,
1173 *		error-status INTEGER,
1174 *		error-index INTEGER,
1175 *		varbindlist SEQUENCE OF
1176 *			SEQUENCE {
1177 *				name ObjectName,
1178 *				value ObjectValue
1179 *			}
1180 *	}
1181 * PDU for Trap:
1182 *	SEQUENCE {
1183 *		enterprise OBJECT IDENTIFIER,
1184 *		agent-addr NetworkAddress,
1185 *		generic-trap INTEGER,
1186 *		specific-trap INTEGER,
1187 *		time-stamp TimeTicks,
1188 *		varbindlist SEQUENCE OF
1189 *			SEQUENCE {
1190 *				name ObjectName,
1191 *				value ObjectValue
1192 *			}
1193 *	}
1194 */
1195
1196/*
1197 * Decode SNMP varBind
1198 */
1199static void
1200varbind_print(u_char pduid, const u_char *np, u_int length)
1201{
1202	struct be elem;
1203	int count = 0, ind;
1204#ifdef LIBSMI
1205	SmiNode *smiNode = NULL;
1206#endif
1207	int status;
1208
1209	/* Sequence of varBind */
1210	if ((count = asn1_parse(np, length, &elem)) < 0)
1211		return;
1212	if (elem.type != BE_SEQ) {
1213		fputs("[!SEQ of varbind]", stdout);
1214		asn1_print(&elem);
1215		return;
1216	}
1217	if ((u_int)count < length)
1218		printf("[%d extra after SEQ of varbind]", length - count);
1219	/* descend */
1220	length = elem.asnlen;
1221	np = (u_char *)elem.data.raw;
1222
1223	for (ind = 1; length > 0; ind++) {
1224		const u_char *vbend;
1225		u_int vblength;
1226
1227		fputs(" ", stdout);
1228
1229		/* Sequence */
1230		if ((count = asn1_parse(np, length, &elem)) < 0)
1231			return;
1232		if (elem.type != BE_SEQ) {
1233			fputs("[!varbind]", stdout);
1234			asn1_print(&elem);
1235			return;
1236		}
1237		vbend = np + count;
1238		vblength = length - count;
1239		/* descend */
1240		length = elem.asnlen;
1241		np = (u_char *)elem.data.raw;
1242
1243		/* objName (OID) */
1244		if ((count = asn1_parse(np, length, &elem)) < 0)
1245			return;
1246		if (elem.type != BE_OID) {
1247			fputs("[objName!=OID]", stdout);
1248			asn1_print(&elem);
1249			return;
1250		}
1251#ifdef LIBSMI
1252		smiNode = smi_print_variable(&elem, &status);
1253#else
1254		status = asn1_print(&elem);
1255#endif
1256		if (status < 0)
1257			return;
1258		length -= count;
1259		np += count;
1260
1261		if (pduid != GETREQ && pduid != GETNEXTREQ
1262		    && pduid != GETBULKREQ)
1263			fputs("=", stdout);
1264
1265		/* objVal (ANY) */
1266		if ((count = asn1_parse(np, length, &elem)) < 0)
1267			return;
1268		if (pduid == GETREQ || pduid == GETNEXTREQ
1269		    || pduid == GETBULKREQ) {
1270			if (elem.type != BE_NULL) {
1271				fputs("[objVal!=NULL]", stdout);
1272				if (asn1_print(&elem) < 0)
1273					return;
1274			}
1275		} else {
1276		        if (elem.type != BE_NULL) {
1277#ifdef LIBSMI
1278				status = smi_print_value(smiNode, pduid, &elem);
1279#else
1280				status = asn1_print(&elem);
1281#endif
1282			}
1283			if (status < 0)
1284				return;
1285		}
1286		length = vblength;
1287		np = vbend;
1288	}
1289}
1290
1291/*
1292 * Decode SNMP PDUs: GetRequest, GetNextRequest, GetResponse, SetRequest,
1293 * GetBulk, Inform, V2Trap, and Report
1294 */
1295static void
1296snmppdu_print(u_short pduid, const u_char *np, u_int length)
1297{
1298	struct be elem;
1299	int count = 0, error;
1300
1301	/* reqId (Integer) */
1302	if ((count = asn1_parse(np, length, &elem)) < 0)
1303		return;
1304	if (elem.type != BE_INT) {
1305		fputs("[reqId!=INT]", stdout);
1306		asn1_print(&elem);
1307		return;
1308	}
1309	if (vflag)
1310		printf("R=%d ", elem.data.integer);
1311	length -= count;
1312	np += count;
1313
1314	/* errorStatus (Integer) */
1315	if ((count = asn1_parse(np, length, &elem)) < 0)
1316		return;
1317	if (elem.type != BE_INT) {
1318		fputs("[errorStatus!=INT]", stdout);
1319		asn1_print(&elem);
1320		return;
1321	}
1322	error = 0;
1323	if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
1324	    || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
1325	    && elem.data.integer != 0) {
1326		char errbuf[20];
1327		printf("[errorStatus(%s)!=0]",
1328			DECODE_ErrorStatus(elem.data.integer));
1329	} else if (pduid == GETBULKREQ) {
1330	        printf(" N=%d", elem.data.integer);
1331	} else if (elem.data.integer != 0) {
1332		char errbuf[20];
1333		printf(" %s", DECODE_ErrorStatus(elem.data.integer));
1334		error = elem.data.integer;
1335	}
1336	length -= count;
1337	np += count;
1338
1339	/* errorIndex (Integer) */
1340	if ((count = asn1_parse(np, length, &elem)) < 0)
1341		return;
1342	if (elem.type != BE_INT) {
1343		fputs("[errorIndex!=INT]", stdout);
1344		asn1_print(&elem);
1345		return;
1346	}
1347	if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
1348	    || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
1349	    && elem.data.integer != 0)
1350		printf("[errorIndex(%d)!=0]", elem.data.integer);
1351	else if (pduid == GETBULKREQ)
1352	        printf(" M=%d", elem.data.integer);
1353	else if (elem.data.integer != 0) {
1354		if (!error)
1355			printf("[errorIndex(%d) w/o errorStatus]",
1356				elem.data.integer);
1357		else {
1358			printf("@%d", elem.data.integer);
1359			error = elem.data.integer;
1360		}
1361	} else if (error) {
1362		fputs("[errorIndex==0]", stdout);
1363		error = 0;
1364	}
1365	length -= count;
1366	np += count;
1367
1368	varbind_print(pduid, np, length);
1369	return;
1370}
1371
1372/*
1373 * Decode SNMP Trap PDU
1374 */
1375static void
1376trappdu_print(const u_char *np, u_int length)
1377{
1378	struct be elem;
1379	int count = 0, generic;
1380
1381	putchar(' ');
1382
1383	/* enterprise (oid) */
1384	if ((count = asn1_parse(np, length, &elem)) < 0)
1385		return;
1386	if (elem.type != BE_OID) {
1387		fputs("[enterprise!=OID]", stdout);
1388		asn1_print(&elem);
1389		return;
1390	}
1391	if (asn1_print(&elem) < 0)
1392		return;
1393	length -= count;
1394	np += count;
1395
1396	putchar(' ');
1397
1398	/* agent-addr (inetaddr) */
1399	if ((count = asn1_parse(np, length, &elem)) < 0)
1400		return;
1401	if (elem.type != BE_INETADDR) {
1402		fputs("[agent-addr!=INETADDR]", stdout);
1403		asn1_print(&elem);
1404		return;
1405	}
1406	if (asn1_print(&elem) < 0)
1407		return;
1408	length -= count;
1409	np += count;
1410
1411	/* generic-trap (Integer) */
1412	if ((count = asn1_parse(np, length, &elem)) < 0)
1413		return;
1414	if (elem.type != BE_INT) {
1415		fputs("[generic-trap!=INT]", stdout);
1416		asn1_print(&elem);
1417		return;
1418	}
1419	generic = elem.data.integer;
1420	{
1421		char buf[20];
1422		printf(" %s", DECODE_GenericTrap(generic));
1423	}
1424	length -= count;
1425	np += count;
1426
1427	/* specific-trap (Integer) */
1428	if ((count = asn1_parse(np, length, &elem)) < 0)
1429		return;
1430	if (elem.type != BE_INT) {
1431		fputs("[specific-trap!=INT]", stdout);
1432		asn1_print(&elem);
1433		return;
1434	}
1435	if (generic != GT_ENTERPRISE) {
1436		if (elem.data.integer != 0)
1437			printf("[specific-trap(%d)!=0]", elem.data.integer);
1438	} else
1439		printf(" s=%d", elem.data.integer);
1440	length -= count;
1441	np += count;
1442
1443	putchar(' ');
1444
1445	/* time-stamp (TimeTicks) */
1446	if ((count = asn1_parse(np, length, &elem)) < 0)
1447		return;
1448	if (elem.type != BE_UNS) {			/* XXX */
1449		fputs("[time-stamp!=TIMETICKS]", stdout);
1450		asn1_print(&elem);
1451		return;
1452	}
1453	if (asn1_print(&elem) < 0)
1454		return;
1455	length -= count;
1456	np += count;
1457
1458	varbind_print (TRAP, np, length);
1459	return;
1460}
1461
1462/*
1463 * Decode arbitrary SNMP PDUs.
1464 */
1465static void
1466pdu_print(const u_char *np, u_int length, int version)
1467{
1468	struct be pdu;
1469	int count = 0;
1470
1471	/* PDU (Context) */
1472	if ((count = asn1_parse(np, length, &pdu)) < 0)
1473		return;
1474	if (pdu.type != BE_PDU) {
1475		fputs("[no PDU]", stdout);
1476		return;
1477	}
1478	if ((u_int)count < length)
1479		printf("[%d extra after PDU]", length - count);
1480	if (vflag) {
1481		fputs("{ ", stdout);
1482	}
1483	if (asn1_print(&pdu) < 0)
1484		return;
1485	fputs(" ", stdout);
1486	/* descend into PDU */
1487	length = pdu.asnlen;
1488	np = (u_char *)pdu.data.raw;
1489
1490	if (version == SNMP_VERSION_1 &&
1491	    (pdu.id == GETBULKREQ || pdu.id == INFORMREQ ||
1492	     pdu.id == V2TRAP || pdu.id == REPORT)) {
1493	        printf("[v2 PDU in v1 message]");
1494		return;
1495	}
1496
1497	if (version == SNMP_VERSION_2 && pdu.id == TRAP) {
1498	        printf("[v1 PDU in v2 message]");
1499		return;
1500	}
1501
1502	switch (pdu.id) {
1503	case TRAP:
1504		trappdu_print(np, length);
1505		break;
1506	case GETREQ:
1507	case GETNEXTREQ:
1508	case GETRESP:
1509	case SETREQ:
1510	case GETBULKREQ:
1511	case INFORMREQ:
1512	case V2TRAP:
1513	case REPORT:
1514		snmppdu_print(pdu.id, np, length);
1515		break;
1516	}
1517
1518	if (vflag) {
1519		fputs(" } ", stdout);
1520	}
1521}
1522
1523/*
1524 * Decode a scoped SNMP PDU.
1525 */
1526static void
1527scopedpdu_print(const u_char *np, u_int length, int version)
1528{
1529	struct be elem;
1530	int i, count = 0;
1531
1532	/* Sequence */
1533	if ((count = asn1_parse(np, length, &elem)) < 0)
1534		return;
1535	if (elem.type != BE_SEQ) {
1536		fputs("[!scoped PDU]", stdout);
1537		asn1_print(&elem);
1538		return;
1539	}
1540	length = elem.asnlen;
1541	np = (u_char *)elem.data.raw;
1542
1543	/* contextEngineID (OCTET STRING) */
1544	if ((count = asn1_parse(np, length, &elem)) < 0)
1545		return;
1546	if (elem.type != BE_STR) {
1547		fputs("[contextEngineID!=STR]", stdout);
1548		asn1_print(&elem);
1549		return;
1550	}
1551	length -= count;
1552	np += count;
1553
1554	fputs("E= ", stdout);
1555	for (i = 0; i < (int)elem.asnlen; i++) {
1556            printf("0x%02X", elem.data.str[i]);
1557        }
1558	fputs(" ", stdout);
1559
1560	/* contextName (OCTET STRING) */
1561	if ((count = asn1_parse(np, length, &elem)) < 0)
1562		return;
1563	if (elem.type != BE_STR) {
1564		fputs("[contextName!=STR]", stdout);
1565		asn1_print(&elem);
1566		return;
1567	}
1568	length -= count;
1569	np += count;
1570
1571	printf("C=%.*s ", (int)elem.asnlen, elem.data.str);
1572
1573	pdu_print(np, length, version);
1574}
1575
1576/*
1577 * Decode SNMP Community Header (SNMPv1 and SNMPv2c)
1578 */
1579static void
1580community_print(const u_char *np, u_int length, int version)
1581{
1582	struct be elem;
1583	int count = 0;
1584
1585	/* Community (String) */
1586	if ((count = asn1_parse(np, length, &elem)) < 0)
1587		return;
1588	if (elem.type != BE_STR) {
1589		fputs("[comm!=STR]", stdout);
1590		asn1_print(&elem);
1591		return;
1592	}
1593	/* default community */
1594	if (!(elem.asnlen == sizeof(DEF_COMMUNITY) - 1 &&
1595	    strncmp((char *)elem.data.str, DEF_COMMUNITY,
1596	            sizeof(DEF_COMMUNITY) - 1) == 0))
1597		/* ! "public" */
1598		printf("C=%.*s ", (int)elem.asnlen, elem.data.str);
1599	length -= count;
1600	np += count;
1601
1602	pdu_print(np, length, version);
1603}
1604
1605/*
1606 * Decode SNMPv3 User-based Security Message Header (SNMPv3)
1607 */
1608static void
1609usm_print(const u_char *np, u_int length)
1610{
1611        struct be elem;
1612	int count = 0;
1613
1614	/* Sequence */
1615	if ((count = asn1_parse(np, length, &elem)) < 0)
1616		return;
1617	if (elem.type != BE_SEQ) {
1618		fputs("[!usm]", stdout);
1619		asn1_print(&elem);
1620		return;
1621	}
1622	length = elem.asnlen;
1623	np = (u_char *)elem.data.raw;
1624
1625	/* msgAuthoritativeEngineID (OCTET STRING) */
1626	if ((count = asn1_parse(np, length, &elem)) < 0)
1627		return;
1628	if (elem.type != BE_STR) {
1629		fputs("[msgAuthoritativeEngineID!=STR]", stdout);
1630		asn1_print(&elem);
1631		return;
1632	}
1633	length -= count;
1634	np += count;
1635
1636	/* msgAuthoritativeEngineBoots (INTEGER) */
1637	if ((count = asn1_parse(np, length, &elem)) < 0)
1638		return;
1639	if (elem.type != BE_INT) {
1640		fputs("[msgAuthoritativeEngineBoots!=INT]", stdout);
1641		asn1_print(&elem);
1642		return;
1643	}
1644	if (vflag)
1645	        printf("B=%d ", elem.data.integer);
1646	length -= count;
1647	np += count;
1648
1649	/* msgAuthoritativeEngineTime (INTEGER) */
1650	if ((count = asn1_parse(np, length, &elem)) < 0)
1651		return;
1652	if (elem.type != BE_INT) {
1653		fputs("[msgAuthoritativeEngineTime!=INT]", stdout);
1654		asn1_print(&elem);
1655		return;
1656	}
1657	if (vflag)
1658	        printf("T=%d ", elem.data.integer);
1659	length -= count;
1660	np += count;
1661
1662	/* msgUserName (OCTET STRING) */
1663	if ((count = asn1_parse(np, length, &elem)) < 0)
1664		return;
1665	if (elem.type != BE_STR) {
1666		fputs("[msgUserName!=STR]", stdout);
1667		asn1_print(&elem);
1668		return;
1669	}
1670	length -= count;
1671        np += count;
1672
1673	printf("U=%.*s ", (int)elem.asnlen, elem.data.str);
1674
1675	/* msgAuthenticationParameters (OCTET STRING) */
1676	if ((count = asn1_parse(np, length, &elem)) < 0)
1677		return;
1678	if (elem.type != BE_STR) {
1679		fputs("[msgAuthenticationParameters!=STR]", stdout);
1680		asn1_print(&elem);
1681		return;
1682	}
1683	length -= count;
1684        np += count;
1685
1686	/* msgPrivacyParameters (OCTET STRING) */
1687	if ((count = asn1_parse(np, length, &elem)) < 0)
1688		return;
1689	if (elem.type != BE_STR) {
1690		fputs("[msgPrivacyParameters!=STR]", stdout);
1691		asn1_print(&elem);
1692		return;
1693	}
1694	length -= count;
1695        np += count;
1696
1697	if ((u_int)count < length)
1698		printf("[%d extra after usm SEQ]", length - count);
1699}
1700
1701/*
1702 * Decode SNMPv3 Message Header (SNMPv3)
1703 */
1704static void
1705v3msg_print(const u_char *np, u_int length)
1706{
1707	struct be elem;
1708	int count = 0;
1709	u_char flags;
1710	int model;
1711	const u_char *xnp = np;
1712	int xlength = length;
1713
1714	/* Sequence */
1715	if ((count = asn1_parse(np, length, &elem)) < 0)
1716		return;
1717	if (elem.type != BE_SEQ) {
1718		fputs("[!message]", stdout);
1719		asn1_print(&elem);
1720		return;
1721	}
1722	length = elem.asnlen;
1723	np = (u_char *)elem.data.raw;
1724
1725	if (vflag) {
1726		fputs("{ ", stdout);
1727	}
1728
1729	/* msgID (INTEGER) */
1730	if ((count = asn1_parse(np, length, &elem)) < 0)
1731		return;
1732	if (elem.type != BE_INT) {
1733		fputs("[msgID!=INT]", stdout);
1734		asn1_print(&elem);
1735		return;
1736	}
1737	length -= count;
1738	np += count;
1739
1740	/* msgMaxSize (INTEGER) */
1741	if ((count = asn1_parse(np, length, &elem)) < 0)
1742		return;
1743	if (elem.type != BE_INT) {
1744		fputs("[msgMaxSize!=INT]", stdout);
1745		asn1_print(&elem);
1746		return;
1747	}
1748	length -= count;
1749	np += count;
1750
1751	/* msgFlags (OCTET STRING) */
1752	if ((count = asn1_parse(np, length, &elem)) < 0)
1753		return;
1754	if (elem.type != BE_STR) {
1755		fputs("[msgFlags!=STR]", stdout);
1756		asn1_print(&elem);
1757		return;
1758	}
1759	if (elem.asnlen != 1) {
1760	        printf("[msgFlags size %d]", elem.asnlen);
1761		return;
1762	}
1763	flags = elem.data.str[0];
1764	if (flags != 0x00 && flags != 0x01 && flags != 0x03
1765	    && flags != 0x04 && flags != 0x05 && flags != 0x07) {
1766		printf("[msgFlags=0x%02X]", flags);
1767		return;
1768	}
1769	length -= count;
1770	np += count;
1771
1772	fputs("F=", stdout);
1773	if (flags & 0x01) fputs("a", stdout);
1774	if (flags & 0x02) fputs("p", stdout);
1775	if (flags & 0x04) fputs("r", stdout);
1776	fputs(" ", stdout);
1777
1778	/* msgSecurityModel (INTEGER) */
1779	if ((count = asn1_parse(np, length, &elem)) < 0)
1780		return;
1781	if (elem.type != BE_INT) {
1782		fputs("[msgSecurityModel!=INT]", stdout);
1783		asn1_print(&elem);
1784		return;
1785	}
1786	model = elem.data.integer;
1787	length -= count;
1788	np += count;
1789
1790	if ((u_int)count < length)
1791		printf("[%d extra after message SEQ]", length - count);
1792
1793	if (vflag) {
1794		fputs("} ", stdout);
1795	}
1796
1797	if (model == 3) {
1798	    if (vflag) {
1799		fputs("{ USM ", stdout);
1800	    }
1801	} else {
1802	    printf("[security model %d]", model);
1803            return;
1804	}
1805
1806	np = xnp + (np - xnp);
1807	length = xlength - (np - xnp);
1808
1809	/* msgSecurityParameters (OCTET STRING) */
1810	if ((count = asn1_parse(np, length, &elem)) < 0)
1811		return;
1812	if (elem.type != BE_STR) {
1813		fputs("[msgSecurityParameters!=STR]", stdout);
1814		asn1_print(&elem);
1815		return;
1816	}
1817	length -= count;
1818	np += count;
1819
1820	if (model == 3) {
1821	    usm_print(elem.data.str, elem.asnlen);
1822	    if (vflag) {
1823		fputs("} ", stdout);
1824	    }
1825	}
1826
1827	if (vflag) {
1828	    fputs("{ ScopedPDU ", stdout);
1829	}
1830
1831	scopedpdu_print(np, length, 3);
1832
1833	if (vflag) {
1834		fputs("} ", stdout);
1835	}
1836}
1837
1838/*
1839 * Decode SNMP header and pass on to PDU printing routines
1840 */
1841void
1842snmp_print(const u_char *np, u_int length)
1843{
1844	struct be elem;
1845	int count = 0;
1846	int version = 0;
1847
1848	putchar(' ');
1849
1850	/* initial Sequence */
1851	if ((count = asn1_parse(np, length, &elem)) < 0)
1852		return;
1853	if (elem.type != BE_SEQ) {
1854		fputs("[!init SEQ]", stdout);
1855		asn1_print(&elem);
1856		return;
1857	}
1858	if ((u_int)count < length)
1859		printf("[%d extra after iSEQ]", length - count);
1860	/* descend */
1861	length = elem.asnlen;
1862	np = (u_char *)elem.data.raw;
1863
1864	/* Version (INTEGER) */
1865	if ((count = asn1_parse(np, length, &elem)) < 0)
1866		return;
1867	if (elem.type != BE_INT) {
1868		fputs("[version!=INT]", stdout);
1869		asn1_print(&elem);
1870		return;
1871	}
1872
1873	switch (elem.data.integer) {
1874	case SNMP_VERSION_1:
1875	case SNMP_VERSION_2:
1876	case SNMP_VERSION_3:
1877	        if (vflag)
1878		        printf("{ %s ", SnmpVersion[elem.data.integer]);
1879		break;
1880	default:
1881	        printf("[version = %d]", elem.data.integer);
1882		return;
1883	}
1884	version = elem.data.integer;
1885	length -= count;
1886	np += count;
1887
1888	switch (version) {
1889	case SNMP_VERSION_1:
1890        case SNMP_VERSION_2:
1891		community_print(np, length, version);
1892		break;
1893	case SNMP_VERSION_3:
1894		v3msg_print(np, length);
1895		break;
1896	default:
1897	        printf("[version = %d]", elem.data.integer);
1898		break;
1899	}
1900
1901	if (vflag) {
1902		fputs("} ", stdout);
1903	}
1904}
1905