Merge branch 'vendor/TCPDUMP'
[dragonfly.git] / contrib / tcpdump / print-snmp.c
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
60 static const char rcsid[] _U_ =
61     "@(#) $Header: /tcpdump/master/tcpdump/print-snmp.c,v 1.62.2.2 2005/05/06 07:57:19 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  */
86 const 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  */
109 const 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  */
128 const 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  */
158 const 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  */
171 const char *Private[] = {
172         "P-0"
173 };
174
175 /*
176  * error-status values for any SNMP PDU
177  */
178 const 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  */
207 const 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 */
228 struct {
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  */
248 const 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  */
259 struct 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  */
281 struct 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  */
328 struct 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  */
363 const 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  */
405 static int
406 asn1_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
655 trunc:
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  */
665 static int
666 asn1_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 = isprint(*p) || isspace(*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
836 trunc:
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  */
851 static void
852 asn1_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
877 struct smi2be {
878     SmiBasetype basetype;
879     int be;
880 };
881
882 static 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
898 static int
899 smi_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
932 trunc:
933         fputs("[|snmp]", stdout);
934         return -1;
935 }
936
937 static 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
950 static 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
1000 static 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
1027 static 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
1056 static int
1057 smi_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  */
1199 static void
1200 varbind_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  */
1295 static void
1296 snmppdu_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  */
1375 static void
1376 trappdu_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  */
1465 static void
1466 pdu_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  */
1526 static void
1527 scopedpdu_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  */
1579 static void
1580 community_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  */
1608 static void
1609 usm_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  */
1704 static void
1705 v3msg_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  */
1841 void
1842 snmp_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 }