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