GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/tcpdump/print-snmp.c Lines: 0 393 0.0 %
Date: 2017-11-13 Branches: 0 313 0.0 %

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