Break proto_tree_add_item_format() into multiple functions:
[obnox/wireshark/wip.git] / packet-snmp.c
1 /* packet-snmp.c
2  * Routines for SNMP (simple network management protocol)
3  * D.Jorand (c) 1998
4  *
5  * $Id: packet-snmp.c,v 1.24 2000/02/20 03:32:29 guy Exp $
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@zing.org>
9  * Copyright 1998 Didier Jorand
10  *
11  * Some stuff from:
12  * 
13  * GXSNMP -- An snmp mangament application
14  * Copyright (C) 1998 Gregory McLean & Jochen Friedrich
15  * Beholder RMON ethernet network monitor,Copyright (C) 1993 DNPAP group
16  *
17  * This program is free software; you can redistribute it and/or
18  * modify it under the terms of the GNU General Public License
19  * as published by the Free Software Foundation; either version 2
20  * of the License, or (at your option) any later version.
21  * 
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  * 
27  * You should have received a copy of the GNU General Public License
28  * along with this program; if not, write to the Free Software
29  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
30  */
31
32 #ifdef HAVE_CONFIG_H
33 # include "config.h"
34 #endif
35
36 #include <stdio.h>
37 #include <string.h>
38 #include <ctype.h>
39
40 #ifdef HAVE_SYS_TYPES_H
41 # include <sys/types.h>
42 #endif
43
44 #ifdef HAVE_NETINET_IN_H
45 # include <netinet/in.h>
46 #endif
47
48 #define MAX_STRING_LEN 1024     /* TBC */
49
50 #include <glib.h>
51
52 #include "packet.h"
53
54 #if defined(HAVE_UCD_SNMP_SNMP_H) || defined(HAVE_SNMP_SNMP_H)
55  /*
56   * UCD or CMU SNMP?
57   */
58 # if defined(HAVE_UCD_SNMP_SNMP_H)
59    /*
60     * UCD SNMP.
61     */
62 #  include <ucd-snmp/asn1.h>
63 #  include <ucd-snmp/snmp_api.h>
64 #  include <ucd-snmp/snmp_impl.h>
65 #  include <ucd-snmp/mib.h>
66
67    /*
68     * Sigh.  UCD SNMP 4.1[.x] makes "snmp_set_full_objid()" a macro
69     * that calls "ds_set_boolean()" with the first two arguments
70     * being DS_LIBRARY_ID and DS_LIB_PRINT_FULL_OID; this means that,
71     * when building with 4.1[.x], we need to arrange that
72     * <ucd-snmp/default_store.h> is included, to define those two values
73     * and to declare "ds_set_boolean()".
74     *
75     * However:
76     *
77     *   1) we can't include it on earlier versions (at least not 3.6.2),
78     *      as it doesn't exist in those versions;
79     *
80     *   2) we don't want to include <ucd-snmp/ucd-snmp-includes.h>,
81     *      as that includes <ucd-snmp/snmp.h>, and that defines a whole
82     *      bunch of values that we also define ourselves.
83     *
84     * So we only include it if "snmp_set_full_objid" is defined as
85     * a macro.
86     */
87 #  ifdef snmp_set_full_objid
88 #   include <ucd-snmp/default_store.h>
89 #  endif
90
91    /*
92     * XXX - for now, we assume all versions of UCD SNMP have it.
93     */
94 #  define HAVE_SPRINT_VALUE
95
96    /*
97     * Define values "sprint_value()" expects.
98     */
99 #  define VALTYPE_INTEGER       ASN_INTEGER
100 #  define VALTYPE_COUNTER       ASN_COUNTER
101 #  define VALTYPE_GAUGE         ASN_GAUGE
102 #  define VALTYPE_TIMETICKS     ASN_TIMETICKS
103 #  define VALTYPE_STRING        ASN_OCTET_STR
104 #  define VALTYPE_IPADDR        ASN_IPADDRESS
105 #  define VALTYPE_OPAQUE        ASN_OPAQUE
106 #  define VALTYPE_NSAP          ASN_NSAP
107 #  define VALTYPE_OBJECTID      ASN_OBJECT_ID
108 #  define VALTYPE_BITSTR        ASN_BIT_STR
109 #  define VALTYPE_COUNTER64     ASN_COUNTER64
110 # elif defined(HAVE_SNMP_SNMP_H)
111    /*
112     * CMU SNMP.
113     */
114 #  include <snmp/snmp.h>
115
116    /*
117     * Some older versions of CMU SNMP may lack these values (e.g., the
118     * "libsnmp3.6" package for Debian, which is based on some old
119     * CMU SNMP, perhaps 1.0); for now, we assume they also lack
120     * "sprint_value()".
121     */
122 #  ifdef SMI_INTEGER
123 #   define HAVE_SPRINT_VALUE
124     /*
125      * Define values "sprint_value()" expects.
126      */
127 #   define VALTYPE_INTEGER      SMI_INTEGER
128 #   define VALTYPE_COUNTER      SMI_COUNTER32
129 #   define VALTYPE_GAUGE        SMI_GAUGE32
130 #   define VALTYPE_TIMETICKS    SMI_TIMETICKS
131 #   define VALTYPE_STRING       SMI_STRING
132 #   define VALTYPE_IPADDR       SMI_IPADDRESS
133 #   define VALTYPE_OPAQUE       SMI_OPAQUE
134 #   define VALTYPE_NSAP         SMI_STRING
135 #   define VALTYPE_OBJECTID     SMI_OBJID
136 #   define VALTYPE_BITSTR       ASN_BIT_STR
137 #   define VALTYPE_COUNTER64    SMI_COUNTER64
138 #  endif
139   /*
140    * Now undo all the definitions they "helpfully" gave us, so we don't get
141    * complaints about redefining them.
142    *
143    * Why, oh why, is there no library that provides code to
144    *
145    *    1) read MIB files;
146    *
147    *    2) translate object IDs into names;
148    *
149    *    3) let you find out, for a given object ID, what the type, enum
150    *       values, display hint, etc. are;
151    *
152    * in a *simple* fashion, without assuming that your code is part of an
153    * SNMP agent or client that wants a pile of definitions of PDU types,
154    * etc.?  Is it just that 99 44/100% of the code that uses an SNMP library
155    * *is* part of an agent or client, and really *does* need that stuff,
156    * and *doesn't* need the interfaces we want?
157    */
158 #  undef SNMP_ERR_NOERROR
159 #  undef SNMP_ERR_TOOBIG
160 #  undef SNMP_ERR_NOSUCHNAME
161 #  undef SNMP_ERR_BADVALUE
162 #  undef SNMP_ERR_READONLY
163 #  undef SNMP_ERR_NOACCESS
164 #  undef SNMP_ERR_WRONGTYPE
165 #  undef SNMP_ERR_WRONGLENGTH
166 #  undef SNMP_ERR_WRONGENCODING
167 #  undef SNMP_ERR_WRONGVALUE
168 #  undef SNMP_ERR_NOCREATION
169 #  undef SNMP_ERR_INCONSISTENTVALUE
170 #  undef SNMP_ERR_RESOURCEUNAVAILABLE
171 #  undef SNMP_ERR_COMMITFAILED
172 #  undef SNMP_ERR_UNDOFAILED
173 #  undef SNMP_ERR_AUTHORIZATIONERROR
174 #  undef SNMP_ERR_NOTWRITABLE
175 #  undef SNMP_ERR_INCONSISTENTNAME
176 #  undef SNMP_TRAP_COLDSTART
177 #  undef SNMP_TRAP_WARMSTART
178 #  undef SNMP_TRAP_LINKDOWN
179 #  undef SNMP_TRAP_LINKUP
180 #  undef SNMP_TRAP_EGPNEIGHBORLOSS
181 #  undef SNMP_TRAP_ENTERPRISESPECIFIC
182 # endif
183 #endif
184
185 #include "asn1.h"
186
187 #include "packet-snmp.h"
188
189 static int proto_snmp = -1;
190
191 static gint ett_snmp = -1;
192
193 /* Protocol version numbers */
194 #define SNMP_VERSION_1  0
195 #define SNMP_VERSION_2c 1
196 #define SNMP_VERSION_2u 2
197 #define SNMP_VERSION_3  3
198
199 static const value_string versions[] = {
200         { SNMP_VERSION_1,       "1" },
201         { SNMP_VERSION_2c,      "2C" },
202         { SNMP_VERSION_2u,      "2U" },
203         { SNMP_VERSION_3,       "3" },
204         { 0,                    NULL },
205 };
206
207 /* PDU types */
208 #define SNMP_MSG_GET            0
209 #define SNMP_MSG_GETNEXT        1
210 #define SNMP_MSG_RESPONSE       2
211 #define SNMP_MSG_SET            3
212 #define SNMP_MSG_TRAP           4
213
214 #define SNMP_MSG_GETBULK        5
215 #define SNMP_MSG_INFORM         6
216 #define SNMP_MSG_TRAP2          7
217 #define SNMP_MSG_REPORT         8
218
219 static const value_string pdu_types[] = {
220         { SNMP_MSG_GET,         "GET" },
221         { SNMP_MSG_GETNEXT,     "GET-NEXT" },
222         { SNMP_MSG_SET,         "SET" },
223         { SNMP_MSG_RESPONSE,    "RESPONSE" },
224         { SNMP_MSG_TRAP,        "TRAP-V1" },
225         { SNMP_MSG_GETBULK,     "GETBULK" },
226         { SNMP_MSG_INFORM,      "INFORM" },
227         { SNMP_MSG_TRAP2,       "TRAP-V2" },
228         { SNMP_MSG_REPORT,      "REPORT" },
229         { 0,                    NULL }
230 };
231
232 /* Error status values */
233 #define SNMP_ERR_NOERROR                0
234 #define SNMP_ERR_TOOBIG                 1
235 #define SNMP_ERR_NOSUCHNAME             2
236 #define SNMP_ERR_BADVALUE               3
237 #define SNMP_ERR_READONLY               4
238 #define SNMP_ERR_GENERROR               5
239
240 #define SNMP_ERR_NOACCESS               6
241 #define SNMP_ERR_WRONGTYPE              7
242 #define SNMP_ERR_WRONGLENGTH            8
243 #define SNMP_ERR_WRONGENCODING          9
244 #define SNMP_ERR_WRONGVALUE             10
245 #define SNMP_ERR_NOCREATION             11
246 #define SNMP_ERR_INCONSISTENTVALUE      12
247 #define SNMP_ERR_RESOURCEUNAVAILABLE    13
248 #define SNMP_ERR_COMMITFAILED           14
249 #define SNMP_ERR_UNDOFAILED             15
250 #define SNMP_ERR_AUTHORIZATIONERROR     16
251 #define SNMP_ERR_NOTWRITABLE            17
252 #define SNMP_ERR_INCONSISTENTNAME       18
253
254 static const value_string error_statuses[] = {
255         { SNMP_ERR_NOERROR,             "NO ERROR" },
256         { SNMP_ERR_TOOBIG,              "TOOBIG" },
257         { SNMP_ERR_NOSUCHNAME,          "NO SUCH NAME" },
258         { SNMP_ERR_BADVALUE,            "BAD VALUE" },
259         { SNMP_ERR_READONLY,            "READ ONLY" },
260         { SNMP_ERR_GENERROR,            "GENERIC ERROR" },
261         { SNMP_ERR_NOACCESS,            "NO ACCESS" },
262         { SNMP_ERR_WRONGTYPE,           "WRONG TYPE" },
263         { SNMP_ERR_WRONGLENGTH,         "WRONG LENGTH" },
264         { SNMP_ERR_WRONGENCODING,       "WRONG ENCODING" },
265         { SNMP_ERR_WRONGVALUE,          "WRONG VALUE" },
266         { SNMP_ERR_NOCREATION,          "NO CREATION" },
267         { SNMP_ERR_INCONSISTENTVALUE,   "INCONSISTENT VALUE" },
268         { SNMP_ERR_RESOURCEUNAVAILABLE, "RESOURCE UNAVAILABLE" },
269         { SNMP_ERR_COMMITFAILED,        "COMMIT FAILED" },
270         { SNMP_ERR_UNDOFAILED,          "UNDO FAILED" },
271         { SNMP_ERR_AUTHORIZATIONERROR,  "AUTHORIZATION ERROR" },
272         { SNMP_ERR_NOTWRITABLE,         "NOT WRITABLE" },
273         { SNMP_ERR_INCONSISTENTNAME,    "INCONSISTENT NAME" },
274         { 0,                            NULL }
275 };
276
277 /* General SNMP V1 Traps */
278
279 #define SNMP_TRAP_COLDSTART             0
280 #define SNMP_TRAP_WARMSTART             1
281 #define SNMP_TRAP_LINKDOWN              2
282 #define SNMP_TRAP_LINKUP                3
283 #define SNMP_TRAP_AUTHFAIL              4
284 #define SNMP_TRAP_EGPNEIGHBORLOSS       5
285 #define SNMP_TRAP_ENTERPRISESPECIFIC    6
286
287 static const value_string trap_types[] = {
288         { SNMP_TRAP_COLDSTART,          "COLD START" },
289         { SNMP_TRAP_WARMSTART,          "WARM START" },
290         { SNMP_TRAP_LINKDOWN,           "LINK DOWN" },
291         { SNMP_TRAP_LINKUP,             "LINK UP" },
292         { SNMP_TRAP_AUTHFAIL,           "AUTHENTICATION FAILED" },
293         { SNMP_TRAP_EGPNEIGHBORLOSS,    "EGP NEIGHBORLOSS" },
294         { SNMP_TRAP_ENTERPRISESPECIFIC, "ENTERPRISE SPECIFIC" },
295         { 0,                            NULL }
296 };
297
298 /* SNMP Tags */
299
300 #define SNMP_IPA    0           /* IP Address */
301 #define SNMP_CNT    1           /* Counter (Counter32) */
302 #define SNMP_GGE    2           /* Gauge (Gauge32) */
303 #define SNMP_TIT    3           /* TimeTicks */
304 #define SNMP_OPQ    4           /* Opaque */
305 #define SNMP_NSP    5           /* NsapAddress */
306 #define SNMP_C64    6           /* Counter64 */
307 #define SNMP_U32    7           /* Uinteger32 */
308
309 #define SERR_NSO    0
310 #define SERR_NSI    1
311 #define SERR_EOM    2
312
313 /* SNMPv1 Types */
314
315 #define SNMP_NULL                0
316 #define SNMP_INTEGER             1    /* l  */
317 #define SNMP_OCTETSTR            2    /* c  */
318 #define SNMP_DISPLAYSTR          2    /* c  */
319 #define SNMP_OBJECTID            3    /* ul */
320 #define SNMP_IPADDR              4    /* uc */
321 #define SNMP_COUNTER             5    /* ul */
322 #define SNMP_GAUGE               6    /* ul */
323 #define SNMP_TIMETICKS           7    /* ul */
324 #define SNMP_OPAQUE              8    /* c  */
325
326 /* additional SNMPv2 Types */
327
328 #define SNMP_UINTEGER            5    /* ul */
329 #define SNMP_BITSTR              9    /* uc */
330 #define SNMP_NSAP               10    /* uc */
331 #define SNMP_COUNTER64          11    /* ul */
332 #define SNMP_NOSUCHOBJECT       12
333 #define SNMP_NOSUCHINSTANCE     13
334 #define SNMP_ENDOFMIBVIEW       14
335
336 typedef struct _SNMP_CNV SNMP_CNV;
337
338 struct _SNMP_CNV
339 {
340   guint class;
341   guint tag;
342   gint  syntax;
343   gchar *name;
344 };
345
346 static SNMP_CNV SnmpCnv [] =
347 {
348   {ASN1_UNI, ASN1_NUL, SNMP_NULL,      "NULL"},
349   {ASN1_UNI, ASN1_INT, SNMP_INTEGER,   "INTEGER"},
350   {ASN1_UNI, ASN1_OTS, SNMP_OCTETSTR,  "OCTET STRING"},
351   {ASN1_UNI, ASN1_OJI, SNMP_OBJECTID,  "OBJECTID"},
352   {ASN1_APL, SNMP_IPA, SNMP_IPADDR,    "IPADDR"},
353   {ASN1_APL, SNMP_CNT, SNMP_COUNTER,   "COUNTER"},  /* Counter32 */
354   {ASN1_APL, SNMP_GGE, SNMP_GAUGE,     "GAUGE"},    /* Gauge32 == Unsigned32  */
355   {ASN1_APL, SNMP_TIT, SNMP_TIMETICKS, "TIMETICKS"},
356   {ASN1_APL, SNMP_OPQ, SNMP_OPAQUE,    "OPAQUE"},
357
358 /* SNMPv2 data types and errors */
359
360   {ASN1_UNI, ASN1_BTS, SNMP_BITSTR,         "BITSTR"},
361   {ASN1_APL, SNMP_C64, SNMP_COUNTER64,      "COUNTER64"},
362   {ASN1_CTX, SERR_NSO, SNMP_NOSUCHOBJECT,   "NOSUCHOBJECT"},
363   {ASN1_CTX, SERR_NSI, SNMP_NOSUCHINSTANCE, "NOSUCHINSTANCE"},
364   {ASN1_CTX, SERR_EOM, SNMP_ENDOFMIBVIEW,   "ENDOFMIBVIEW"},
365   {0,       0,         -1,                  NULL}
366 };
367
368 /*
369  * NAME:        g_snmp_tag_cls2syntax
370  * SYNOPSIS:    gboolean g_snmp_tag_cls2syntax
371  *                  (
372  *                      guint    tag,
373  *                      guint    cls,
374  *                      gushort *syntax
375  *                  )
376  * DESCRIPTION: Converts ASN1 tag and class to Syntax tag and name.
377  *              See SnmpCnv for conversion.
378  * RETURNS:     name on success, NULL on failure
379  */
380
381 static gchar *
382 snmp_tag_cls2syntax ( guint tag, guint cls, gushort *syntax)
383 {
384     SNMP_CNV *cnv;
385
386     cnv = SnmpCnv;
387     while (cnv->syntax != -1)
388     {
389         if (cnv->tag == tag && cnv->class == cls)
390         {
391             *syntax = cnv->syntax;
392             return cnv->name;
393         }
394         cnv++;
395     }
396     return NULL;
397 }
398
399 static void
400 dissect_snmp_parse_error(const u_char *pd, int offset, frame_data *fd,
401                    proto_tree *tree, const char *field_name, int ret)
402 {
403         const gchar *errstr;
404
405         if (check_col(fd, COL_INFO)) {
406                 switch (ret) {
407
408                 case ASN1_ERR_EMPTY:
409                         errstr = "Ran out of data";
410                         break;
411
412                 case ASN1_ERR_EOC_MISMATCH:
413                         errstr = "EOC mismatch";
414                         break;
415
416                 case ASN1_ERR_WRONG_TYPE:
417                         errstr = "Wrong type for that item";
418                         break;
419
420                 case ASN1_ERR_LENGTH_NOT_DEFINITE:
421                         errstr = "Length was indefinite";
422                         break;
423
424                 case ASN1_ERR_LENGTH_MISMATCH:
425                         errstr = "Length mismatch";
426                         break;
427
428                 case ASN1_ERR_WRONG_LENGTH_FOR_TYPE:
429                         errstr = "Wrong length for that item's type";
430                         break;
431
432                 default:
433                         errstr = "Unknown error";
434                         break;
435                 }
436                 col_add_fstr(fd, COL_INFO,
437                     "ERROR: Couldn't parse %s: %s", field_name, errstr);
438         }
439
440         dissect_data(pd, offset, fd, tree);
441 }
442
443 static void
444 dissect_snmp_error(const u_char *pd, int offset, frame_data *fd,
445                    proto_tree *tree, const char *message)
446 {
447         if (check_col(fd, COL_INFO))
448                 col_add_str(fd, COL_INFO, message);
449
450         dissect_data(pd, offset, fd, tree);
451 }
452
453 static void
454 format_oid(gchar *buf, subid_t *oid, guint oid_length)
455 {
456         int i;
457         int len;
458
459         len = sprintf(buf, "%lu", (unsigned long)oid[0]);
460         buf += len;
461         for (i = 1; i < oid_length;i++) {
462                 len = sprintf(buf, ".%lu", (unsigned long)oid[i]);
463                 buf += len;
464         }
465 }
466
467 #ifdef HAVE_SPRINT_VALUE
468 static void
469 format_value(gchar *buf, struct variable_list *variable, subid_t *variable_oid,
470     guint variable_oid_length, gushort vb_type, guint vb_length)
471 {
472         variable->next_variable = NULL;
473         variable->name = variable_oid;
474         variable->name_length = variable_oid_length;
475         switch (vb_type) {
476
477         case SNMP_INTEGER:
478                 variable->type = VALTYPE_INTEGER;
479                 break;
480
481         case SNMP_COUNTER:
482                 variable->type = VALTYPE_COUNTER;
483                 break;
484
485         case SNMP_GAUGE:
486                 variable->type = VALTYPE_GAUGE;
487                 break;
488
489         case SNMP_TIMETICKS:
490                 variable->type = VALTYPE_TIMETICKS;
491                 break;
492
493         case SNMP_OCTETSTR:
494                 variable->type = VALTYPE_STRING;
495                 break;
496
497         case SNMP_IPADDR:
498                 variable->type = VALTYPE_IPADDR;
499                 break;
500
501         case SNMP_OPAQUE:
502                 variable->type = VALTYPE_OPAQUE;
503                 break;
504
505         case SNMP_NSAP:
506                 variable->type = VALTYPE_NSAP;
507                 break;
508
509         case SNMP_OBJECTID:
510                 variable->type = VALTYPE_OBJECTID;
511                 break;
512
513         case SNMP_BITSTR:
514                 variable->type = VALTYPE_BITSTR;
515                 break;
516
517         case SNMP_COUNTER64:
518                 variable->type = VALTYPE_COUNTER64;
519                 break;
520         }
521         variable->val_len = vb_length;
522         sprint_value(buf, variable_oid, variable_oid_length, variable);
523 }
524 #endif
525
526 static int
527 snmp_variable_decode(proto_tree *snmp_tree, subid_t *variable_oid,
528     guint variable_oid_length, ASN1_SCK *asn1, int offset, guint *lengthp)
529 {
530         const guchar *start;
531         guint length;
532         gboolean def;
533         guint vb_length;
534         gushort vb_type;
535         gchar *vb_type_name;
536         int ret;
537         guint cls, con, tag;
538
539         gint32 vb_integer_value;
540         guint32 vb_uinteger_value;
541
542         guint8 *vb_octet_string;
543
544         subid_t *vb_oid;
545         guint vb_oid_length;
546
547         gchar vb_display_string[MAX_STRING_LEN]; /* TBC */
548
549 #ifdef HAVE_SPRINT_VALUE
550         struct variable_list variable;
551 #if defined(HAVE_UCD_SNMP_SNMP_H)
552         long value;
553 #endif
554 #else   /* HAVE_SPRINT_VALUE */
555         int i;
556         gchar *buf;
557         int len;
558 #endif  /* HAVE_SPRINT_VALUE */
559
560         /* parse the type of the object */
561         start = asn1->pointer;
562         ret = asn1_header_decode (asn1, &cls, &con, &tag, &def, &vb_length);
563         if (ret != ASN1_ERR_NOERROR)
564                 return ret;
565         if (!def)
566                 return ASN1_ERR_LENGTH_NOT_DEFINITE;
567
568         /* Convert the class, constructed flag, and tag to a type. */
569         vb_type_name = snmp_tag_cls2syntax(tag, cls, &vb_type);
570         if (vb_type_name == NULL) {
571                 /*
572                  * Unsupported type.
573                  * Dissect the value as an opaque string of octets.
574                  */
575                 vb_type_name = "unsupported type";
576                 vb_type = SNMP_OPAQUE;
577         }
578
579         /* parse the value */
580         switch (vb_type) {
581
582         case SNMP_INTEGER:
583                 ret = asn1_int32_value_decode(asn1, vb_length,
584                     &vb_integer_value);
585                 if (ret != ASN1_ERR_NOERROR)
586                         return ret;
587                 length = asn1->pointer - start;
588                 if (snmp_tree) {
589 #ifdef HAVE_SPRINT_VALUE
590 #if defined(HAVE_UCD_SNMP_SNMP_H)
591                         value = vb_integer_value;
592                         variable.val.integer = &value;
593 #elif defined(HAVE_SNMP_SNMP_H)
594                         variable.val.integer = &vb_integer_value;
595 #endif
596                         format_value(vb_display_string, &variable,
597                             variable_oid, variable_oid_length, vb_type,
598                             vb_length);
599                         proto_tree_add_text(snmp_tree, offset, length,
600                             "Value: %s", vb_display_string);
601 #else
602                         proto_tree_add_text(snmp_tree, offset, length,
603                             "Value: %s: %d (%#x)", vb_type_name,
604                             vb_integer_value, vb_integer_value);
605 #endif
606                 }
607                 break;
608
609         case SNMP_COUNTER:
610         case SNMP_GAUGE:
611         case SNMP_TIMETICKS:
612                 ret = asn1_uint32_value_decode(asn1, vb_length,
613                     &vb_uinteger_value);
614                 if (ret != ASN1_ERR_NOERROR)
615                         return ret;
616                 length = asn1->pointer - start;
617                 if (snmp_tree) {
618 #ifdef HAVE_SPRINT_VALUE
619 #if defined(HAVE_UCD_SNMP_SNMP_H)
620                         value = vb_uinteger_value;
621                         variable.val.integer = &value;
622 #elif defined(HAVE_SNMP_SNMP_H)
623                         variable.val.integer = &vb_uinteger_value;
624 #endif
625                         format_value(vb_display_string, &variable,
626                             variable_oid, variable_oid_length, vb_type,
627                             vb_length);
628                         proto_tree_add_text(snmp_tree, offset, length,
629                             "Value: %s", vb_display_string);
630 #else
631                         proto_tree_add_text(snmp_tree, offset, length,
632                             "Value: %s: %u (%#x)", vb_type_name,
633                             vb_uinteger_value, vb_uinteger_value);
634 #endif
635                 }
636                 break;
637
638         case SNMP_OCTETSTR:
639         case SNMP_IPADDR:
640         case SNMP_OPAQUE:
641         case SNMP_NSAP:
642         case SNMP_BITSTR:
643         case SNMP_COUNTER64:
644                 ret = asn1_octet_string_value_decode (asn1, vb_length,
645                     &vb_octet_string);
646                 if (ret != ASN1_ERR_NOERROR)
647                         return ret;
648                 length = asn1->pointer - start;
649                 if (snmp_tree) {
650 #ifdef HAVE_SPRINT_VALUE
651                         variable.val.string = vb_octet_string;
652                         format_value(vb_display_string, &variable,
653                             variable_oid, variable_oid_length, vb_type,
654                             vb_length);
655                         proto_tree_add_text(snmp_tree, offset, length,
656                             "Value: %s", vb_display_string);
657 #else
658                         /*
659                          * If some characters are not printable, display
660                          * the string as bytes.
661                          */
662                         for (i = 0; i < vb_length; i++) {
663                                 if (!(isprint(vb_octet_string[i])
664                                     || isspace(vb_octet_string[i])))
665                                         break;
666                         }
667                         if (i < vb_length) {
668                                 /*
669                                  * We stopped, due to a non-printable
670                                  * character, before we got to the end
671                                  * of the string.
672                                  */
673                                 buf = &vb_display_string[0];
674                                 len = sprintf(buf, "%03u", vb_octet_string[0]);
675                                 buf += len;
676                                 for (i = 1; i < vb_length; i++) {
677                                         len = sprintf(buf, ".%03u",
678                                             vb_octet_string[i]);
679                                         buf += len;
680                                 }
681                                 proto_tree_add_text(snmp_tree, offset, length,
682                                     "Value: %s: %s", vb_type_name,
683                                     vb_display_string);
684                         } else {
685                                 proto_tree_add_text(snmp_tree, offset, length,
686                                     "Value: %s: %.*s", vb_type_name, vb_length,
687                                     vb_octet_string);
688                         }
689 #endif
690                 }
691                 g_free(vb_octet_string);
692                 break;
693
694         case SNMP_NULL:
695                 ret = asn1_null_decode (asn1, vb_length);
696                 if (ret != ASN1_ERR_NOERROR)
697                         return ret;
698                 length = asn1->pointer - start;
699                 if (snmp_tree) {
700                         proto_tree_add_text(snmp_tree, offset, length,
701                             "Value: %s", vb_type_name);
702                 }
703                 break;
704
705         case SNMP_OBJECTID:
706                 ret = asn1_oid_value_decode (asn1, vb_length, &vb_oid,
707                     &vb_oid_length);
708                 if (ret != ASN1_ERR_NOERROR)
709                         return ret;
710                 length = asn1->pointer - start;
711                 if (snmp_tree) {
712 #ifdef HAVE_SPRINT_VALUE
713                         variable.val.objid = vb_oid;
714                         format_value(vb_display_string, &variable,
715                             variable_oid, variable_oid_length, vb_type,
716                             vb_length*sizeof (subid_t));
717                         proto_tree_add_text(snmp_tree, offset, length,
718                             "Value: %s", vb_display_string);
719 #else
720                         format_oid(vb_display_string, vb_oid, vb_oid_length);
721                         proto_tree_add_text(snmp_tree, offset, length,
722                             "Value: %s: %s", vb_type_name, vb_display_string);
723 #endif
724                 }
725                 g_free(vb_oid);
726                 break;
727
728         case SNMP_NOSUCHOBJECT:
729                 length = asn1->pointer - start;
730                 if (snmp_tree) {
731                         proto_tree_add_text(snmp_tree, offset, length,
732                             "Value: %s: no such object", vb_type_name);
733                 }
734                 break;
735
736         case SNMP_NOSUCHINSTANCE:
737                 length = asn1->pointer - start;
738                 if (snmp_tree) {
739                         proto_tree_add_text(snmp_tree, offset, length,
740                             "Value: %s: no such instance", vb_type_name);
741                 }
742                 break;
743
744         case SNMP_ENDOFMIBVIEW:
745                 length = asn1->pointer - start;
746                 if (snmp_tree) {
747                         proto_tree_add_text(snmp_tree, offset, length,
748                             "Value: %s: end of mib view", vb_type_name);
749                 }
750                 break;
751
752         default:
753                 g_assert_not_reached();
754                 return ASN1_ERR_WRONG_TYPE;
755         }
756         *lengthp = length;
757         return ASN1_ERR_NOERROR;
758 }
759
760 void
761 dissect_snmp_pdu(const u_char *pd, int offset, frame_data *fd,
762     proto_tree *tree, char *proto_name, int proto, gint ett)
763 {
764         ASN1_SCK asn1;
765         const guchar *start;
766         gboolean def;
767         guint length;
768         guint sequence_length;
769
770         guint message_length;
771
772         guint32 version;
773
774         guchar *community;
775         int community_length;
776
777         guint pdu_type;
778         char *pdu_type_string;
779         guint pdu_length;
780
781         guint32 request_id;
782
783         guint32 error_status;
784
785         guint32 error_index;
786
787         subid_t *enterprise;
788         guint enterprise_length;
789
790         guint8 *agent_address;
791         guint agent_address_length;
792
793         guint32 trap_type;
794
795         guint32 specific_type;
796
797         guint timestamp;
798         guint timestamp_length;
799
800         gchar oid_string[MAX_STRING_LEN]; /* TBC */
801
802         guint variable_bindings_length;
803
804         int vb_index;
805         guint variable_length;
806         subid_t *variable_oid;
807         guint variable_oid_length;
808 #if defined(HAVE_UCD_SNMP_SNMP_H) || defined(HAVE_SNMP_SNMP_H)
809         gchar vb_oid_string[MAX_STRING_LEN]; /* TBC */
810 #endif
811
812         proto_tree *snmp_tree = NULL;
813         proto_item *item = NULL;
814         int ret;
815         guint cls, con, tag;
816
817         if (check_col(fd, COL_PROTOCOL))
818                 col_add_str(fd, COL_PROTOCOL, proto_name);
819
820         if (tree) {
821                 item = proto_tree_add_item(tree, proto, offset, END_OF_FRAME, NULL);
822                 snmp_tree = proto_item_add_subtree(item, ett);
823         }
824
825         /* NOTE: we have to parse the message piece by piece, since the
826          * capture length may be less than the message length: a 'global'
827          * parsing is likely to fail.
828          */
829         /* parse the SNMP header */
830         asn1_open(&asn1, &pd[offset], END_OF_FRAME);
831         ret = asn1_sequence_decode(&asn1, &message_length, &length);
832         if (ret != ASN1_ERR_NOERROR) {
833                 dissect_snmp_parse_error(pd, offset, fd, tree,
834                         "message header", ret);
835                 return;
836         }
837         offset += length;
838
839         ret = asn1_uint32_decode (&asn1, &version, &length);
840         if (ret != ASN1_ERR_NOERROR) {
841                 dissect_snmp_parse_error(pd, offset, fd, tree, "version number",
842                     ret);
843                 return;
844         }
845         if (tree) {
846                 proto_tree_add_text(snmp_tree, offset, length,
847                     "Version: %s",
848                     val_to_str(version, versions, "Unknown version %#x"));
849         }
850         offset += length;
851
852         ret = asn1_octet_string_decode (&asn1, &community, &community_length,
853             &length);
854         if (ret != ASN1_ERR_NOERROR) {
855                 dissect_snmp_parse_error(pd, offset, fd, tree, "community",
856                     ret);
857                 return;
858         }
859         if (tree) {
860                 proto_tree_add_text(snmp_tree, offset, length,
861                     "Community: %.*s", community_length, community);
862         }
863         g_free(community);
864         offset += length;
865
866         switch (version) {
867
868         case SNMP_VERSION_1:
869         case SNMP_VERSION_2c:
870         case SNMP_VERSION_2u:
871         case SNMP_VERSION_3:
872                 break;
873
874         default:
875                 dissect_snmp_error(pd, offset, fd, tree,
876                     "PDU for unknown version of SNMP");
877                 return;
878         }
879
880         start = asn1.pointer;
881         ret = asn1_header_decode (&asn1, &cls, &con, &pdu_type, &def,
882             &pdu_length);
883         if (ret != ASN1_ERR_NOERROR) {
884                 dissect_snmp_parse_error(pd, offset, fd, tree,
885                     "PDU type", ret);
886                 return;
887         }
888         if (cls != ASN1_CTX || con != ASN1_CON) {
889                 dissect_snmp_parse_error(pd, offset, fd, tree,
890                     "PDU type", ASN1_ERR_WRONG_TYPE);
891                 return;
892         }
893         pdu_type_string = val_to_str(pdu_type, pdu_types,
894             "Unknown PDU type %#x");
895         if (check_col(fd, COL_INFO))
896                 col_add_str(fd, COL_INFO, pdu_type_string);
897         length = asn1.pointer - start;
898         if (tree) {
899                 proto_tree_add_text(snmp_tree, offset, length,
900                     "PDU type: %s", pdu_type_string);
901         }
902         offset += length;
903
904         /* get the fields in the PDU preceeding the variable-bindings sequence */
905         switch (pdu_type) {
906
907         case SNMP_MSG_GET:
908         case SNMP_MSG_GETNEXT:
909         case SNMP_MSG_RESPONSE:
910         case SNMP_MSG_SET:
911         /* XXX - are they like V1 non-trap PDUs? */
912         case SNMP_MSG_GETBULK:
913         case SNMP_MSG_INFORM:
914                 /* request id */
915                 ret = asn1_uint32_decode (&asn1, &request_id, &length);
916                 if (ret != ASN1_ERR_NOERROR) {
917                         dissect_snmp_parse_error(pd, offset, fd, tree,
918                             "request ID", ret);
919                         return;
920                 }
921                 if (tree) {
922                         proto_tree_add_text(snmp_tree, offset, length,
923                             "Request Id: %#x", request_id);
924                 }
925                 offset += length;
926                 
927                 /* error status (getbulk non-repeaters) */
928                 ret = asn1_uint32_decode (&asn1, &error_status, &length);
929                 if (ret != ASN1_ERR_NOERROR) {
930                         dissect_snmp_parse_error(pd, offset, fd, tree,
931                             "error status", ret);
932                         return;
933                 }
934                 if (tree) {
935                         proto_tree_add_text(snmp_tree, offset, length,
936                             "Error Status: %s",
937                             val_to_str(error_status, error_statuses,
938                               "Unknown (%d)"));
939                 }
940                 offset += length;
941
942                 /* error index (getbulk max-repetitions) */
943                 ret = asn1_uint32_decode (&asn1, &error_index, &length);
944                 if (ret != ASN1_ERR_NOERROR) {
945                         dissect_snmp_parse_error(pd, offset, fd, tree,
946                             "error index", ret);
947                         return;
948                 }
949                 if (tree) {
950                         proto_tree_add_text(snmp_tree, offset, length,
951                             "Error Index: %u", error_index);
952                 }
953                 offset += length;
954                 break;
955
956         case SNMP_MSG_TRAP:
957                 /* enterprise */
958                 ret = asn1_oid_decode (&asn1, &enterprise, &enterprise_length,
959                     &length);
960                 if (ret != ASN1_ERR_NOERROR) {
961                         dissect_snmp_parse_error(pd, offset, fd, tree,
962                             "enterprise OID", ret);
963                         return;
964                 }
965                 if (tree) {
966                         format_oid(oid_string, enterprise, enterprise_length);
967                         proto_tree_add_text(snmp_tree, offset, length,
968                             "Enterprise: %s", oid_string);
969                 }
970                 g_free(enterprise);
971                 offset += length;
972
973                 /* agent address */
974                 start = asn1.pointer;
975                 ret = asn1_header_decode (&asn1, &cls, &con, &tag,
976                     &def, &agent_address_length);
977                 if (ret != ASN1_ERR_NOERROR) {
978                         dissect_snmp_parse_error(pd, offset, fd, tree,
979                             "agent address", ret);
980                         return;
981                 }
982                 if (!((cls == ASN1_APL && con == ASN1_PRI && tag == SNMP_IPA) ||
983                     (cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_OTS))) {
984                         /* GXSNMP 0.0.15 says the latter is "needed for
985                            Banyan" */
986                         dissect_snmp_parse_error(pd, offset, fd, tree,
987                             "agent_address", ASN1_ERR_WRONG_TYPE);
988                         return;
989                 }
990                 if (agent_address_length != 4) {
991                         dissect_snmp_parse_error(pd, offset, fd, tree,
992                             "agent_address", ASN1_ERR_WRONG_LENGTH_FOR_TYPE);
993                         return;
994                 }
995                 ret = asn1_octet_string_value_decode (&asn1,
996                     agent_address_length, &agent_address);
997                 if (ret != ASN1_ERR_NOERROR) {
998                         dissect_snmp_parse_error(pd, offset, fd, tree,
999                             "agent address", ret);
1000                         return;
1001                 }
1002                 length = asn1.pointer - start;
1003                 if (tree) {
1004                         proto_tree_add_text(snmp_tree, offset, agent_address_length,
1005                             "Agent address: %s", ip_to_str(agent_address));
1006                 }
1007                 g_free(agent_address);
1008                 offset += length;
1009                 
1010                 /* generic trap type */
1011                 ret = asn1_uint32_decode (&asn1, &trap_type, &length);
1012                 if (ret != ASN1_ERR_NOERROR) {
1013                         dissect_snmp_parse_error(pd, offset, fd, tree,
1014                             "generic trap type", ret);
1015                         return;
1016                 }
1017                 if (tree) {
1018                         proto_tree_add_text(snmp_tree, offset, length,
1019                             "Trap type: %s",
1020                             val_to_str(trap_type, trap_types, "Unknown (%u)"));
1021                 }               
1022                 offset += length;
1023                 
1024                 /* specific trap type */
1025                 ret = asn1_uint32_decode (&asn1, &specific_type, &length);
1026                 if (ret != ASN1_ERR_NOERROR) {
1027                         dissect_snmp_parse_error(pd, offset, fd, tree,
1028                             "specific trap type", ret);
1029                         return;
1030                 }
1031                 if (tree) {
1032                         proto_tree_add_text(snmp_tree, offset, length,
1033                             "Specific trap type: %u (%#x)",
1034                             specific_type, specific_type);
1035                 }               
1036                 offset += length;
1037                 
1038                 /* timestamp */
1039                 start = asn1.pointer;
1040                 ret = asn1_header_decode (&asn1, &cls, &con, &tag,
1041                     &def, &timestamp_length);
1042                 if (ret != ASN1_ERR_NOERROR) {
1043                         dissect_snmp_parse_error(pd, offset, fd, tree,
1044                             "timestamp", ret);
1045                         return;
1046                 }
1047                 if (!((cls == ASN1_APL && con == ASN1_PRI && tag == SNMP_TIT) ||
1048                     (cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_INT))) {
1049                         dissect_snmp_parse_error(pd, offset, fd, tree,
1050                             "timestamp", ASN1_ERR_WRONG_TYPE);
1051                         return;
1052                 }
1053                 ret = asn1_uint32_value_decode(&asn1, timestamp_length,
1054                     &timestamp);
1055                 if (ret != ASN1_ERR_NOERROR) {
1056                         dissect_snmp_parse_error(pd, offset, fd, tree,
1057                             "timestamp", ret);
1058                         return;
1059                 }
1060                 length = asn1.pointer - start;
1061                 if (tree) {
1062                         proto_tree_add_text(snmp_tree, offset, length,
1063                             "Timestamp: %u", timestamp);
1064                 }               
1065                 offset += length;
1066                 break;
1067         }
1068
1069         /* variable bindings */
1070         /* get header for variable-bindings sequence */
1071         ret = asn1_sequence_decode(&asn1, &variable_bindings_length, &length);
1072         if (ret != ASN1_ERR_NOERROR) {
1073                 dissect_snmp_parse_error(pd, offset, fd, tree,
1074                         "variable bindings header", ret);
1075                 return;
1076         }
1077         offset += length;
1078
1079         /* loop on variable bindings */
1080         vb_index = 0;
1081         while (variable_bindings_length > 0) {
1082                 vb_index++;
1083                 sequence_length = 0;
1084
1085                 /* parse type */
1086                 ret = asn1_sequence_decode(&asn1, &variable_length, &length);
1087                 if (ret != ASN1_ERR_NOERROR) {
1088                         dissect_snmp_parse_error(pd, offset, fd, tree,
1089                                 "variable binding header", ret);
1090                         return;
1091                 }
1092                 sequence_length += length;
1093
1094                 /* parse object identifier */
1095                 ret = asn1_oid_decode (&asn1, &variable_oid,
1096                     &variable_oid_length, &length);
1097                 if (ret != ASN1_ERR_NOERROR) {
1098                         dissect_snmp_parse_error(pd, offset, fd, tree,
1099                             "variable binding OID", ret);
1100                         return;
1101                 }
1102                 sequence_length += length;
1103
1104                 if (tree) {
1105                         format_oid(oid_string, variable_oid,
1106                             variable_oid_length);
1107                         
1108 #if defined(HAVE_UCD_SNMP_SNMP_H) || defined(HAVE_SNMP_SNMP_H)
1109                         sprint_objid(vb_oid_string, variable_oid,
1110                             variable_oid_length);
1111                         proto_tree_add_text(snmp_tree, offset, sequence_length,
1112                             "Object identifier %d: %s (%s)", vb_index,
1113                             oid_string, vb_oid_string);
1114 #else
1115                         
1116                         proto_tree_add_text(snmp_tree, offset, sequence_length,
1117                             "Object identifier %d: %s", vb_index,
1118                             oid_string);
1119 #endif
1120                 }
1121                 offset += sequence_length;
1122                 variable_bindings_length -= sequence_length;
1123                                 
1124                 /* Parse the variable's value */
1125                 ret = snmp_variable_decode(snmp_tree, variable_oid,
1126                     variable_oid_length, &asn1, offset, &length);
1127                 if (ret != ASN1_ERR_NOERROR) {
1128                         dissect_snmp_parse_error(pd, offset, fd, tree,
1129                             "variable", ret);
1130                         return;
1131                 }
1132                 offset += length;
1133                 variable_bindings_length -= length;
1134         }
1135 }
1136
1137 void
1138 dissect_snmp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) 
1139 {
1140         dissect_snmp_pdu(pd, offset, fd, tree, "SNMP", proto_snmp, ett_snmp);
1141 }
1142
1143 void
1144 proto_register_snmp(void)
1145 {
1146 /*        static hf_register_info hf[] = {
1147                 { &variable,
1148                 { "Name",           "snmp.abbreviation", TYPE, VALS_POINTER }},
1149         };*/
1150         static gint *ett[] = {
1151                 &ett_snmp,
1152         };
1153
1154 #if defined(HAVE_UCD_SNMP_SNMP_H) || defined(HAVE_SNMP_SNMP_H)
1155         /* UCD or CMU SNMP */
1156         init_mib();
1157 #ifdef HAVE_UCD_SNMP_SNMP_H
1158         snmp_set_full_objid(TRUE);
1159 #endif
1160 #endif
1161         proto_snmp = proto_register_protocol("Simple Network Management Protocol", "snmp");
1162  /*       proto_register_field_array(proto_snmp, hf, array_length(hf));*/
1163         proto_register_subtree_array(ett, array_length(ett));
1164 }