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