Move calls to "dissector_add()" out of the register routines for TCP and
[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.27 2000/04/08 07:07:36 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 #define UDP_PORT_SNMP           161
194 #define UDP_PORT_SNMP_TRAP      162
195
196 /* Protocol version numbers */
197 #define SNMP_VERSION_1  0
198 #define SNMP_VERSION_2c 1
199 #define SNMP_VERSION_2u 2
200 #define SNMP_VERSION_3  3
201
202 static const value_string versions[] = {
203         { SNMP_VERSION_1,       "1" },
204         { SNMP_VERSION_2c,      "2C" },
205         { SNMP_VERSION_2u,      "2U" },
206         { SNMP_VERSION_3,       "3" },
207         { 0,                    NULL },
208 };
209
210 /* PDU types */
211 #define SNMP_MSG_GET            0
212 #define SNMP_MSG_GETNEXT        1
213 #define SNMP_MSG_RESPONSE       2
214 #define SNMP_MSG_SET            3
215 #define SNMP_MSG_TRAP           4
216
217 #define SNMP_MSG_GETBULK        5
218 #define SNMP_MSG_INFORM         6
219 #define SNMP_MSG_TRAP2          7
220 #define SNMP_MSG_REPORT         8
221
222 static const value_string pdu_types[] = {
223         { SNMP_MSG_GET,         "GET" },
224         { SNMP_MSG_GETNEXT,     "GET-NEXT" },
225         { SNMP_MSG_SET,         "SET" },
226         { SNMP_MSG_RESPONSE,    "RESPONSE" },
227         { SNMP_MSG_TRAP,        "TRAP-V1" },
228         { SNMP_MSG_GETBULK,     "GETBULK" },
229         { SNMP_MSG_INFORM,      "INFORM" },
230         { SNMP_MSG_TRAP2,       "TRAP-V2" },
231         { SNMP_MSG_REPORT,      "REPORT" },
232         { 0,                    NULL }
233 };
234
235 /* Error status values */
236 #define SNMP_ERR_NOERROR                0
237 #define SNMP_ERR_TOOBIG                 1
238 #define SNMP_ERR_NOSUCHNAME             2
239 #define SNMP_ERR_BADVALUE               3
240 #define SNMP_ERR_READONLY               4
241 #define SNMP_ERR_GENERROR               5
242
243 #define SNMP_ERR_NOACCESS               6
244 #define SNMP_ERR_WRONGTYPE              7
245 #define SNMP_ERR_WRONGLENGTH            8
246 #define SNMP_ERR_WRONGENCODING          9
247 #define SNMP_ERR_WRONGVALUE             10
248 #define SNMP_ERR_NOCREATION             11
249 #define SNMP_ERR_INCONSISTENTVALUE      12
250 #define SNMP_ERR_RESOURCEUNAVAILABLE    13
251 #define SNMP_ERR_COMMITFAILED           14
252 #define SNMP_ERR_UNDOFAILED             15
253 #define SNMP_ERR_AUTHORIZATIONERROR     16
254 #define SNMP_ERR_NOTWRITABLE            17
255 #define SNMP_ERR_INCONSISTENTNAME       18
256
257 static const value_string error_statuses[] = {
258         { SNMP_ERR_NOERROR,             "NO ERROR" },
259         { SNMP_ERR_TOOBIG,              "TOOBIG" },
260         { SNMP_ERR_NOSUCHNAME,          "NO SUCH NAME" },
261         { SNMP_ERR_BADVALUE,            "BAD VALUE" },
262         { SNMP_ERR_READONLY,            "READ ONLY" },
263         { SNMP_ERR_GENERROR,            "GENERIC ERROR" },
264         { SNMP_ERR_NOACCESS,            "NO ACCESS" },
265         { SNMP_ERR_WRONGTYPE,           "WRONG TYPE" },
266         { SNMP_ERR_WRONGLENGTH,         "WRONG LENGTH" },
267         { SNMP_ERR_WRONGENCODING,       "WRONG ENCODING" },
268         { SNMP_ERR_WRONGVALUE,          "WRONG VALUE" },
269         { SNMP_ERR_NOCREATION,          "NO CREATION" },
270         { SNMP_ERR_INCONSISTENTVALUE,   "INCONSISTENT VALUE" },
271         { SNMP_ERR_RESOURCEUNAVAILABLE, "RESOURCE UNAVAILABLE" },
272         { SNMP_ERR_COMMITFAILED,        "COMMIT FAILED" },
273         { SNMP_ERR_UNDOFAILED,          "UNDO FAILED" },
274         { SNMP_ERR_AUTHORIZATIONERROR,  "AUTHORIZATION ERROR" },
275         { SNMP_ERR_NOTWRITABLE,         "NOT WRITABLE" },
276         { SNMP_ERR_INCONSISTENTNAME,    "INCONSISTENT NAME" },
277         { 0,                            NULL }
278 };
279
280 /* General SNMP V1 Traps */
281
282 #define SNMP_TRAP_COLDSTART             0
283 #define SNMP_TRAP_WARMSTART             1
284 #define SNMP_TRAP_LINKDOWN              2
285 #define SNMP_TRAP_LINKUP                3
286 #define SNMP_TRAP_AUTHFAIL              4
287 #define SNMP_TRAP_EGPNEIGHBORLOSS       5
288 #define SNMP_TRAP_ENTERPRISESPECIFIC    6
289
290 static const value_string trap_types[] = {
291         { SNMP_TRAP_COLDSTART,          "COLD START" },
292         { SNMP_TRAP_WARMSTART,          "WARM START" },
293         { SNMP_TRAP_LINKDOWN,           "LINK DOWN" },
294         { SNMP_TRAP_LINKUP,             "LINK UP" },
295         { SNMP_TRAP_AUTHFAIL,           "AUTHENTICATION FAILED" },
296         { SNMP_TRAP_EGPNEIGHBORLOSS,    "EGP NEIGHBORLOSS" },
297         { SNMP_TRAP_ENTERPRISESPECIFIC, "ENTERPRISE SPECIFIC" },
298         { 0,                            NULL }
299 };
300
301 /* SNMP Tags */
302
303 #define SNMP_IPA    0           /* IP Address */
304 #define SNMP_CNT    1           /* Counter (Counter32) */
305 #define SNMP_GGE    2           /* Gauge (Gauge32) */
306 #define SNMP_TIT    3           /* TimeTicks */
307 #define SNMP_OPQ    4           /* Opaque */
308 #define SNMP_NSP    5           /* NsapAddress */
309 #define SNMP_C64    6           /* Counter64 */
310 #define SNMP_U32    7           /* Uinteger32 */
311
312 #define SERR_NSO    0
313 #define SERR_NSI    1
314 #define SERR_EOM    2
315
316 /* SNMPv1 Types */
317
318 #define SNMP_NULL                0
319 #define SNMP_INTEGER             1    /* l  */
320 #define SNMP_OCTETSTR            2    /* c  */
321 #define SNMP_DISPLAYSTR          2    /* c  */
322 #define SNMP_OBJECTID            3    /* ul */
323 #define SNMP_IPADDR              4    /* uc */
324 #define SNMP_COUNTER             5    /* ul */
325 #define SNMP_GAUGE               6    /* ul */
326 #define SNMP_TIMETICKS           7    /* ul */
327 #define SNMP_OPAQUE              8    /* c  */
328
329 /* additional SNMPv2 Types */
330
331 #define SNMP_UINTEGER            5    /* ul */
332 #define SNMP_BITSTR              9    /* uc */
333 #define SNMP_NSAP               10    /* uc */
334 #define SNMP_COUNTER64          11    /* ul */
335 #define SNMP_NOSUCHOBJECT       12
336 #define SNMP_NOSUCHINSTANCE     13
337 #define SNMP_ENDOFMIBVIEW       14
338
339 typedef struct _SNMP_CNV SNMP_CNV;
340
341 struct _SNMP_CNV
342 {
343   guint class;
344   guint tag;
345   gint  syntax;
346   gchar *name;
347 };
348
349 static SNMP_CNV SnmpCnv [] =
350 {
351   {ASN1_UNI, ASN1_NUL, SNMP_NULL,      "NULL"},
352   {ASN1_UNI, ASN1_INT, SNMP_INTEGER,   "INTEGER"},
353   {ASN1_UNI, ASN1_OTS, SNMP_OCTETSTR,  "OCTET STRING"},
354   {ASN1_UNI, ASN1_OJI, SNMP_OBJECTID,  "OBJECTID"},
355   {ASN1_APL, SNMP_IPA, SNMP_IPADDR,    "IPADDR"},
356   {ASN1_APL, SNMP_CNT, SNMP_COUNTER,   "COUNTER"},  /* Counter32 */
357   {ASN1_APL, SNMP_GGE, SNMP_GAUGE,     "GAUGE"},    /* Gauge32 == Unsigned32  */
358   {ASN1_APL, SNMP_TIT, SNMP_TIMETICKS, "TIMETICKS"},
359   {ASN1_APL, SNMP_OPQ, SNMP_OPAQUE,    "OPAQUE"},
360
361 /* SNMPv2 data types and errors */
362
363   {ASN1_UNI, ASN1_BTS, SNMP_BITSTR,         "BITSTR"},
364   {ASN1_APL, SNMP_C64, SNMP_COUNTER64,      "COUNTER64"},
365   {ASN1_CTX, SERR_NSO, SNMP_NOSUCHOBJECT,   "NOSUCHOBJECT"},
366   {ASN1_CTX, SERR_NSI, SNMP_NOSUCHINSTANCE, "NOSUCHINSTANCE"},
367   {ASN1_CTX, SERR_EOM, SNMP_ENDOFMIBVIEW,   "ENDOFMIBVIEW"},
368   {0,       0,         -1,                  NULL}
369 };
370
371 /*
372  * NAME:        g_snmp_tag_cls2syntax
373  * SYNOPSIS:    gboolean g_snmp_tag_cls2syntax
374  *                  (
375  *                      guint    tag,
376  *                      guint    cls,
377  *                      gushort *syntax
378  *                  )
379  * DESCRIPTION: Converts ASN1 tag and class to Syntax tag and name.
380  *              See SnmpCnv for conversion.
381  * RETURNS:     name on success, NULL on failure
382  */
383
384 static gchar *
385 snmp_tag_cls2syntax ( guint tag, guint cls, gushort *syntax)
386 {
387     SNMP_CNV *cnv;
388
389     cnv = SnmpCnv;
390     while (cnv->syntax != -1)
391     {
392         if (cnv->tag == tag && cnv->class == cls)
393         {
394             *syntax = cnv->syntax;
395             return cnv->name;
396         }
397         cnv++;
398     }
399     return NULL;
400 }
401
402 static void
403 dissect_snmp_parse_error(const u_char *pd, int offset, frame_data *fd,
404                    proto_tree *tree, const char *field_name, int ret)
405 {
406         const gchar *errstr;
407
408         if (check_col(fd, COL_INFO)) {
409                 switch (ret) {
410
411                 case ASN1_ERR_EMPTY:
412                         errstr = "Ran out of data";
413                         break;
414
415                 case ASN1_ERR_EOC_MISMATCH:
416                         errstr = "EOC mismatch";
417                         break;
418
419                 case ASN1_ERR_WRONG_TYPE:
420                         errstr = "Wrong type for that item";
421                         break;
422
423                 case ASN1_ERR_LENGTH_NOT_DEFINITE:
424                         errstr = "Length was indefinite";
425                         break;
426
427                 case ASN1_ERR_LENGTH_MISMATCH:
428                         errstr = "Length mismatch";
429                         break;
430
431                 case ASN1_ERR_WRONG_LENGTH_FOR_TYPE:
432                         errstr = "Wrong length for that item's type";
433                         break;
434
435                 default:
436                         errstr = "Unknown error";
437                         break;
438                 }
439                 col_add_fstr(fd, COL_INFO,
440                     "ERROR: Couldn't parse %s: %s", field_name, errstr);
441         }
442
443         dissect_data(pd, offset, fd, tree);
444 }
445
446 static void
447 dissect_snmp_error(const u_char *pd, int offset, frame_data *fd,
448                    proto_tree *tree, const char *message)
449 {
450         if (check_col(fd, COL_INFO))
451                 col_add_str(fd, COL_INFO, message);
452
453         dissect_data(pd, offset, fd, tree);
454 }
455
456 static void
457 format_oid(gchar *buf, subid_t *oid, guint oid_length)
458 {
459         int i;
460         int len;
461
462         len = sprintf(buf, "%lu", (unsigned long)oid[0]);
463         buf += len;
464         for (i = 1; i < oid_length;i++) {
465                 len = sprintf(buf, ".%lu", (unsigned long)oid[i]);
466                 buf += len;
467         }
468 }
469
470 #ifdef HAVE_SPRINT_VALUE
471 static void
472 format_value(gchar *buf, struct variable_list *variable, subid_t *variable_oid,
473     guint variable_oid_length, gushort vb_type, guint vb_length)
474 {
475         variable->next_variable = NULL;
476         variable->name = variable_oid;
477         variable->name_length = variable_oid_length;
478         switch (vb_type) {
479
480         case SNMP_INTEGER:
481                 variable->type = VALTYPE_INTEGER;
482                 break;
483
484         case SNMP_COUNTER:
485                 variable->type = VALTYPE_COUNTER;
486                 break;
487
488         case SNMP_GAUGE:
489                 variable->type = VALTYPE_GAUGE;
490                 break;
491
492         case SNMP_TIMETICKS:
493                 variable->type = VALTYPE_TIMETICKS;
494                 break;
495
496         case SNMP_OCTETSTR:
497                 variable->type = VALTYPE_STRING;
498                 break;
499
500         case SNMP_IPADDR:
501                 variable->type = VALTYPE_IPADDR;
502                 break;
503
504         case SNMP_OPAQUE:
505                 variable->type = VALTYPE_OPAQUE;
506                 break;
507
508         case SNMP_NSAP:
509                 variable->type = VALTYPE_NSAP;
510                 break;
511
512         case SNMP_OBJECTID:
513                 variable->type = VALTYPE_OBJECTID;
514                 break;
515
516         case SNMP_BITSTR:
517                 variable->type = VALTYPE_BITSTR;
518                 break;
519
520         case SNMP_COUNTER64:
521                 variable->type = VALTYPE_COUNTER64;
522                 break;
523         }
524         variable->val_len = vb_length;
525         sprint_value(buf, variable_oid, variable_oid_length, variable);
526 }
527 #endif
528
529 static int
530 snmp_variable_decode(proto_tree *snmp_tree, subid_t *variable_oid,
531     guint variable_oid_length, ASN1_SCK *asn1, int offset, guint *lengthp)
532 {
533         const guchar *start;
534         guint length;
535         gboolean def;
536         guint vb_length;
537         gushort vb_type;
538         gchar *vb_type_name;
539         int ret;
540         guint cls, con, tag;
541
542         gint32 vb_integer_value;
543         guint32 vb_uinteger_value;
544
545         guint8 *vb_octet_string;
546
547         subid_t *vb_oid;
548         guint vb_oid_length;
549
550         gchar vb_display_string[MAX_STRING_LEN]; /* TBC */
551
552 #ifdef HAVE_SPRINT_VALUE
553         struct variable_list variable;
554 #if defined(HAVE_UCD_SNMP_SNMP_H)
555         long value;
556 #endif
557 #else   /* HAVE_SPRINT_VALUE */
558         int i;
559         gchar *buf;
560         int len;
561 #endif  /* HAVE_SPRINT_VALUE */
562
563         /* parse the type of the object */
564         start = asn1->pointer;
565         ret = asn1_header_decode (asn1, &cls, &con, &tag, &def, &vb_length);
566         if (ret != ASN1_ERR_NOERROR)
567                 return ret;
568         if (!def)
569                 return ASN1_ERR_LENGTH_NOT_DEFINITE;
570
571         /* Convert the class, constructed flag, and tag to a type. */
572         vb_type_name = snmp_tag_cls2syntax(tag, cls, &vb_type);
573         if (vb_type_name == NULL) {
574                 /*
575                  * Unsupported type.
576                  * Dissect the value as an opaque string of octets.
577                  */
578                 vb_type_name = "unsupported type";
579                 vb_type = SNMP_OPAQUE;
580         }
581
582         /* parse the value */
583         switch (vb_type) {
584
585         case SNMP_INTEGER:
586                 ret = asn1_int32_value_decode(asn1, vb_length,
587                     &vb_integer_value);
588                 if (ret != ASN1_ERR_NOERROR)
589                         return ret;
590                 length = asn1->pointer - start;
591                 if (snmp_tree) {
592 #ifdef HAVE_SPRINT_VALUE
593 #if defined(HAVE_UCD_SNMP_SNMP_H)
594                         value = vb_integer_value;
595                         variable.val.integer = &value;
596 #elif defined(HAVE_SNMP_SNMP_H)
597                         variable.val.integer = &vb_integer_value;
598 #endif
599                         format_value(vb_display_string, &variable,
600                             variable_oid, variable_oid_length, vb_type,
601                             vb_length);
602                         proto_tree_add_text(snmp_tree, offset, length,
603                             "Value: %s", vb_display_string);
604 #else
605                         proto_tree_add_text(snmp_tree, offset, length,
606                             "Value: %s: %d (%#x)", vb_type_name,
607                             vb_integer_value, vb_integer_value);
608 #endif
609                 }
610                 break;
611
612         case SNMP_COUNTER:
613         case SNMP_GAUGE:
614         case SNMP_TIMETICKS:
615                 ret = asn1_uint32_value_decode(asn1, vb_length,
616                     &vb_uinteger_value);
617                 if (ret != ASN1_ERR_NOERROR)
618                         return ret;
619                 length = asn1->pointer - start;
620                 if (snmp_tree) {
621 #ifdef HAVE_SPRINT_VALUE
622 #if defined(HAVE_UCD_SNMP_SNMP_H)
623                         value = vb_uinteger_value;
624                         variable.val.integer = &value;
625 #elif defined(HAVE_SNMP_SNMP_H)
626                         variable.val.integer = &vb_uinteger_value;
627 #endif
628                         format_value(vb_display_string, &variable,
629                             variable_oid, variable_oid_length, vb_type,
630                             vb_length);
631                         proto_tree_add_text(snmp_tree, offset, length,
632                             "Value: %s", vb_display_string);
633 #else
634                         proto_tree_add_text(snmp_tree, offset, length,
635                             "Value: %s: %u (%#x)", vb_type_name,
636                             vb_uinteger_value, vb_uinteger_value);
637 #endif
638                 }
639                 break;
640
641         case SNMP_OCTETSTR:
642         case SNMP_IPADDR:
643         case SNMP_OPAQUE:
644         case SNMP_NSAP:
645         case SNMP_BITSTR:
646         case SNMP_COUNTER64:
647                 ret = asn1_octet_string_value_decode (asn1, vb_length,
648                     &vb_octet_string);
649                 if (ret != ASN1_ERR_NOERROR)
650                         return ret;
651                 length = asn1->pointer - start;
652                 if (snmp_tree) {
653 #ifdef HAVE_SPRINT_VALUE
654                         variable.val.string = vb_octet_string;
655                         format_value(vb_display_string, &variable,
656                             variable_oid, variable_oid_length, vb_type,
657                             vb_length);
658                         proto_tree_add_text(snmp_tree, offset, length,
659                             "Value: %s", vb_display_string);
660 #else
661                         /*
662                          * If some characters are not printable, display
663                          * the string as bytes.
664                          */
665                         for (i = 0; i < vb_length; i++) {
666                                 if (!(isprint(vb_octet_string[i])
667                                     || isspace(vb_octet_string[i])))
668                                         break;
669                         }
670                         if (i < vb_length) {
671                                 /*
672                                  * We stopped, due to a non-printable
673                                  * character, before we got to the end
674                                  * of the string.
675                                  */
676                                 buf = &vb_display_string[0];
677                                 len = sprintf(buf, "%03u", vb_octet_string[0]);
678                                 buf += len;
679                                 for (i = 1; i < vb_length; i++) {
680                                         len = sprintf(buf, ".%03u",
681                                             vb_octet_string[i]);
682                                         buf += len;
683                                 }
684                                 proto_tree_add_text(snmp_tree, offset, length,
685                                     "Value: %s: %s", vb_type_name,
686                                     vb_display_string);
687                         } else {
688                                 proto_tree_add_text(snmp_tree, offset, length,
689                                     "Value: %s: %.*s", vb_type_name, vb_length,
690                                     vb_octet_string);
691                         }
692 #endif
693                 }
694                 g_free(vb_octet_string);
695                 break;
696
697         case SNMP_NULL:
698                 ret = asn1_null_decode (asn1, vb_length);
699                 if (ret != ASN1_ERR_NOERROR)
700                         return ret;
701                 length = asn1->pointer - start;
702                 if (snmp_tree) {
703                         proto_tree_add_text(snmp_tree, offset, length,
704                             "Value: %s", vb_type_name);
705                 }
706                 break;
707
708         case SNMP_OBJECTID:
709                 ret = asn1_oid_value_decode (asn1, vb_length, &vb_oid,
710                     &vb_oid_length);
711                 if (ret != ASN1_ERR_NOERROR)
712                         return ret;
713                 length = asn1->pointer - start;
714                 if (snmp_tree) {
715 #ifdef HAVE_SPRINT_VALUE
716                         variable.val.objid = vb_oid;
717                         format_value(vb_display_string, &variable,
718                             variable_oid, variable_oid_length, vb_type,
719                             vb_length*sizeof (subid_t));
720                         proto_tree_add_text(snmp_tree, offset, length,
721                             "Value: %s", vb_display_string);
722 #else
723                         format_oid(vb_display_string, vb_oid, vb_oid_length);
724                         proto_tree_add_text(snmp_tree, offset, length,
725                             "Value: %s: %s", vb_type_name, vb_display_string);
726 #endif
727                 }
728                 g_free(vb_oid);
729                 break;
730
731         case SNMP_NOSUCHOBJECT:
732                 length = asn1->pointer - start;
733                 if (snmp_tree) {
734                         proto_tree_add_text(snmp_tree, offset, length,
735                             "Value: %s: no such object", vb_type_name);
736                 }
737                 break;
738
739         case SNMP_NOSUCHINSTANCE:
740                 length = asn1->pointer - start;
741                 if (snmp_tree) {
742                         proto_tree_add_text(snmp_tree, offset, length,
743                             "Value: %s: no such instance", vb_type_name);
744                 }
745                 break;
746
747         case SNMP_ENDOFMIBVIEW:
748                 length = asn1->pointer - start;
749                 if (snmp_tree) {
750                         proto_tree_add_text(snmp_tree, offset, length,
751                             "Value: %s: end of mib view", vb_type_name);
752                 }
753                 break;
754
755         default:
756                 g_assert_not_reached();
757                 return ASN1_ERR_WRONG_TYPE;
758         }
759         *lengthp = length;
760         return ASN1_ERR_NOERROR;
761 }
762
763 void
764 dissect_snmp_pdu(const u_char *pd, int offset, frame_data *fd,
765     proto_tree *tree, char *proto_name, int proto, gint ett)
766 {
767         ASN1_SCK asn1;
768         const guchar *start;
769         gboolean def;
770         guint length;
771         guint sequence_length;
772
773         guint message_length;
774
775         guint32 version;
776
777         guchar *community;
778         int community_length;
779
780         guint pdu_type;
781         char *pdu_type_string;
782         guint pdu_length;
783
784         guint32 request_id;
785
786         guint32 error_status;
787
788         guint32 error_index;
789
790         subid_t *enterprise;
791         guint enterprise_length;
792
793         guint8 *agent_address;
794         guint agent_address_length;
795
796         guint32 trap_type;
797
798         guint32 specific_type;
799
800         guint timestamp;
801         guint timestamp_length;
802
803         gchar oid_string[MAX_STRING_LEN]; /* TBC */
804
805         guint variable_bindings_length;
806
807         int vb_index;
808         guint variable_length;
809         subid_t *variable_oid;
810         guint variable_oid_length;
811 #if defined(HAVE_UCD_SNMP_SNMP_H) || defined(HAVE_SNMP_SNMP_H)
812         gchar vb_oid_string[MAX_STRING_LEN]; /* TBC */
813 #endif
814
815         proto_tree *snmp_tree = NULL;
816         proto_item *item = NULL;
817         int ret;
818         guint cls, con, tag;
819
820         if (check_col(fd, COL_PROTOCOL))
821                 col_add_str(fd, COL_PROTOCOL, proto_name);
822
823         if (tree) {
824                 item = proto_tree_add_item(tree, proto, offset, END_OF_FRAME, NULL);
825                 snmp_tree = proto_item_add_subtree(item, ett);
826         }
827
828         /* NOTE: we have to parse the message piece by piece, since the
829          * capture length may be less than the message length: a 'global'
830          * parsing is likely to fail.
831          */
832         /* parse the SNMP header */
833         asn1_open(&asn1, &pd[offset], END_OF_FRAME);
834         ret = asn1_sequence_decode(&asn1, &message_length, &length);
835         if (ret != ASN1_ERR_NOERROR) {
836                 dissect_snmp_parse_error(pd, offset, fd, tree,
837                         "message header", ret);
838                 return;
839         }
840         offset += length;
841
842         ret = asn1_uint32_decode (&asn1, &version, &length);
843         if (ret != ASN1_ERR_NOERROR) {
844                 dissect_snmp_parse_error(pd, offset, fd, tree, "version number",
845                     ret);
846                 return;
847         }
848         if (tree) {
849                 proto_tree_add_text(snmp_tree, offset, length,
850                     "Version: %s",
851                     val_to_str(version, versions, "Unknown version %#x"));
852         }
853         offset += length;
854
855         ret = asn1_octet_string_decode (&asn1, &community, &community_length,
856             &length);
857         if (ret != ASN1_ERR_NOERROR) {
858                 dissect_snmp_parse_error(pd, offset, fd, tree, "community",
859                     ret);
860                 return;
861         }
862         if (tree) {
863                 proto_tree_add_text(snmp_tree, offset, length,
864                     "Community: %.*s", community_length, community);
865         }
866         g_free(community);
867         offset += length;
868
869         switch (version) {
870
871         case SNMP_VERSION_1:
872         case SNMP_VERSION_2c:
873         case SNMP_VERSION_2u:
874         case SNMP_VERSION_3:
875                 break;
876
877         default:
878                 dissect_snmp_error(pd, offset, fd, tree,
879                     "PDU for unknown version of SNMP");
880                 return;
881         }
882
883         start = asn1.pointer;
884         ret = asn1_header_decode (&asn1, &cls, &con, &pdu_type, &def,
885             &pdu_length);
886         if (ret != ASN1_ERR_NOERROR) {
887                 dissect_snmp_parse_error(pd, offset, fd, tree,
888                     "PDU type", ret);
889                 return;
890         }
891         if (cls != ASN1_CTX || con != ASN1_CON) {
892                 dissect_snmp_parse_error(pd, offset, fd, tree,
893                     "PDU type", ASN1_ERR_WRONG_TYPE);
894                 return;
895         }
896         pdu_type_string = val_to_str(pdu_type, pdu_types,
897             "Unknown PDU type %#x");
898         if (check_col(fd, COL_INFO))
899                 col_add_str(fd, COL_INFO, pdu_type_string);
900         length = asn1.pointer - start;
901         if (tree) {
902                 proto_tree_add_text(snmp_tree, offset, length,
903                     "PDU type: %s", pdu_type_string);
904         }
905         offset += length;
906
907         /* get the fields in the PDU preceeding the variable-bindings sequence */
908         switch (pdu_type) {
909
910         case SNMP_MSG_GET:
911         case SNMP_MSG_GETNEXT:
912         case SNMP_MSG_RESPONSE:
913         case SNMP_MSG_SET:
914         case SNMP_MSG_GETBULK:
915         case SNMP_MSG_INFORM:
916         case SNMP_MSG_TRAP2:
917                 /* request id */
918                 ret = asn1_uint32_decode (&asn1, &request_id, &length);
919                 if (ret != ASN1_ERR_NOERROR) {
920                         dissect_snmp_parse_error(pd, offset, fd, tree,
921                             "request ID", ret);
922                         return;
923                 }
924                 if (tree) {
925                         proto_tree_add_text(snmp_tree, offset, length,
926                             "Request Id: %#x", request_id);
927                 }
928                 offset += length;
929                 
930                 /* error status, or getbulk non-repeaters */
931                 ret = asn1_uint32_decode (&asn1, &error_status, &length);
932                 if (ret != ASN1_ERR_NOERROR) {
933                         dissect_snmp_parse_error(pd, offset, fd, tree,
934                             (pdu_type == SNMP_MSG_GETBULK) ? "non-repeaters"
935                                                            : "error status",
936                             ret);
937                         return;
938                 }
939                 if (tree) {
940                         if (pdu_type == SNMP_MSG_GETBULK) {
941                                 proto_tree_add_text(snmp_tree, offset, length,
942                                     "Non-repeaters: %u", error_status);
943                         } else {
944                                 proto_tree_add_text(snmp_tree, offset, length,
945                                     "Error Status: %s",
946                                     val_to_str(error_status, error_statuses,
947                                       "Unknown (%d)"));
948                         }
949                 }
950                 offset += length;
951
952                 /* error index, or getbulk max-repetitions */
953                 ret = asn1_uint32_decode (&asn1, &error_index, &length);
954                 if (ret != ASN1_ERR_NOERROR) {
955                         dissect_snmp_parse_error(pd, offset, fd, tree,
956                             (pdu_type == SNMP_MSG_GETBULK) ? "max repetitions"
957                                                            : "error index",
958                             ret);
959                         return;
960                 }
961                 if (tree) {
962                         if (pdu_type == SNMP_MSG_GETBULK) {
963                                 proto_tree_add_text(snmp_tree, offset, length,
964                                     "Max repetitions: %u", error_index);
965                         } else {
966                                 proto_tree_add_text(snmp_tree, offset, length,
967                                     "Error Index: %u", error_index);
968                         }
969                 }
970                 offset += length;
971                 break;
972
973         case SNMP_MSG_TRAP:
974                 /* enterprise */
975                 ret = asn1_oid_decode (&asn1, &enterprise, &enterprise_length,
976                     &length);
977                 if (ret != ASN1_ERR_NOERROR) {
978                         dissect_snmp_parse_error(pd, offset, fd, tree,
979                             "enterprise OID", ret);
980                         return;
981                 }
982                 if (tree) {
983                         format_oid(oid_string, enterprise, enterprise_length);
984                         proto_tree_add_text(snmp_tree, offset, length,
985                             "Enterprise: %s", oid_string);
986                 }
987                 g_free(enterprise);
988                 offset += length;
989
990                 /* agent address */
991                 start = asn1.pointer;
992                 ret = asn1_header_decode (&asn1, &cls, &con, &tag,
993                     &def, &agent_address_length);
994                 if (ret != ASN1_ERR_NOERROR) {
995                         dissect_snmp_parse_error(pd, offset, fd, tree,
996                             "agent address", ret);
997                         return;
998                 }
999                 if (!((cls == ASN1_APL && con == ASN1_PRI && tag == SNMP_IPA) ||
1000                     (cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_OTS))) {
1001                         /* GXSNMP 0.0.15 says the latter is "needed for
1002                            Banyan" */
1003                         dissect_snmp_parse_error(pd, offset, fd, tree,
1004                             "agent_address", ASN1_ERR_WRONG_TYPE);
1005                         return;
1006                 }
1007                 if (agent_address_length != 4) {
1008                         dissect_snmp_parse_error(pd, offset, fd, tree,
1009                             "agent_address", ASN1_ERR_WRONG_LENGTH_FOR_TYPE);
1010                         return;
1011                 }
1012                 ret = asn1_octet_string_value_decode (&asn1,
1013                     agent_address_length, &agent_address);
1014                 if (ret != ASN1_ERR_NOERROR) {
1015                         dissect_snmp_parse_error(pd, offset, fd, tree,
1016                             "agent address", ret);
1017                         return;
1018                 }
1019                 length = asn1.pointer - start;
1020                 if (tree) {
1021                         proto_tree_add_text(snmp_tree, offset, agent_address_length,
1022                             "Agent address: %s", ip_to_str(agent_address));
1023                 }
1024                 g_free(agent_address);
1025                 offset += length;
1026                 
1027                 /* generic trap type */
1028                 ret = asn1_uint32_decode (&asn1, &trap_type, &length);
1029                 if (ret != ASN1_ERR_NOERROR) {
1030                         dissect_snmp_parse_error(pd, offset, fd, tree,
1031                             "generic trap type", ret);
1032                         return;
1033                 }
1034                 if (tree) {
1035                         proto_tree_add_text(snmp_tree, offset, length,
1036                             "Trap type: %s",
1037                             val_to_str(trap_type, trap_types, "Unknown (%u)"));
1038                 }               
1039                 offset += length;
1040                 
1041                 /* specific trap type */
1042                 ret = asn1_uint32_decode (&asn1, &specific_type, &length);
1043                 if (ret != ASN1_ERR_NOERROR) {
1044                         dissect_snmp_parse_error(pd, offset, fd, tree,
1045                             "specific trap type", ret);
1046                         return;
1047                 }
1048                 if (tree) {
1049                         proto_tree_add_text(snmp_tree, offset, length,
1050                             "Specific trap type: %u (%#x)",
1051                             specific_type, specific_type);
1052                 }               
1053                 offset += length;
1054                 
1055                 /* timestamp */
1056                 start = asn1.pointer;
1057                 ret = asn1_header_decode (&asn1, &cls, &con, &tag,
1058                     &def, &timestamp_length);
1059                 if (ret != ASN1_ERR_NOERROR) {
1060                         dissect_snmp_parse_error(pd, offset, fd, tree,
1061                             "timestamp", ret);
1062                         return;
1063                 }
1064                 if (!((cls == ASN1_APL && con == ASN1_PRI && tag == SNMP_TIT) ||
1065                     (cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_INT))) {
1066                         dissect_snmp_parse_error(pd, offset, fd, tree,
1067                             "timestamp", ASN1_ERR_WRONG_TYPE);
1068                         return;
1069                 }
1070                 ret = asn1_uint32_value_decode(&asn1, timestamp_length,
1071                     &timestamp);
1072                 if (ret != ASN1_ERR_NOERROR) {
1073                         dissect_snmp_parse_error(pd, offset, fd, tree,
1074                             "timestamp", ret);
1075                         return;
1076                 }
1077                 length = asn1.pointer - start;
1078                 if (tree) {
1079                         proto_tree_add_text(snmp_tree, offset, length,
1080                             "Timestamp: %u", timestamp);
1081                 }               
1082                 offset += length;
1083                 break;
1084         }
1085
1086         /* variable bindings */
1087         /* get header for variable-bindings sequence */
1088         ret = asn1_sequence_decode(&asn1, &variable_bindings_length, &length);
1089         if (ret != ASN1_ERR_NOERROR) {
1090                 dissect_snmp_parse_error(pd, offset, fd, tree,
1091                         "variable bindings header", ret);
1092                 return;
1093         }
1094         offset += length;
1095
1096         /* loop on variable bindings */
1097         vb_index = 0;
1098         while (variable_bindings_length > 0) {
1099                 vb_index++;
1100                 sequence_length = 0;
1101
1102                 /* parse type */
1103                 ret = asn1_sequence_decode(&asn1, &variable_length, &length);
1104                 if (ret != ASN1_ERR_NOERROR) {
1105                         dissect_snmp_parse_error(pd, offset, fd, tree,
1106                                 "variable binding header", ret);
1107                         return;
1108                 }
1109                 sequence_length += length;
1110
1111                 /* parse object identifier */
1112                 ret = asn1_oid_decode (&asn1, &variable_oid,
1113                     &variable_oid_length, &length);
1114                 if (ret != ASN1_ERR_NOERROR) {
1115                         dissect_snmp_parse_error(pd, offset, fd, tree,
1116                             "variable binding OID", ret);
1117                         return;
1118                 }
1119                 sequence_length += length;
1120
1121                 if (tree) {
1122                         format_oid(oid_string, variable_oid,
1123                             variable_oid_length);
1124                         
1125 #if defined(HAVE_UCD_SNMP_SNMP_H) || defined(HAVE_SNMP_SNMP_H)
1126                         sprint_objid(vb_oid_string, variable_oid,
1127                             variable_oid_length);
1128                         proto_tree_add_text(snmp_tree, offset, sequence_length,
1129                             "Object identifier %d: %s (%s)", vb_index,
1130                             oid_string, vb_oid_string);
1131 #else
1132                         
1133                         proto_tree_add_text(snmp_tree, offset, sequence_length,
1134                             "Object identifier %d: %s", vb_index,
1135                             oid_string);
1136 #endif
1137                 }
1138                 offset += sequence_length;
1139                 variable_bindings_length -= sequence_length;
1140                                 
1141                 /* Parse the variable's value */
1142                 ret = snmp_variable_decode(snmp_tree, variable_oid,
1143                     variable_oid_length, &asn1, offset, &length);
1144                 if (ret != ASN1_ERR_NOERROR) {
1145                         dissect_snmp_parse_error(pd, offset, fd, tree,
1146                             "variable", ret);
1147                         return;
1148                 }
1149                 offset += length;
1150                 variable_bindings_length -= length;
1151         }
1152 }
1153
1154 void
1155 dissect_snmp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) 
1156 {
1157         dissect_snmp_pdu(pd, offset, fd, tree, "SNMP", proto_snmp, ett_snmp);
1158 }
1159
1160 void
1161 proto_register_snmp(void)
1162 {
1163 /*        static hf_register_info hf[] = {
1164                 { &variable,
1165                 { "Name",           "snmp.abbreviation", TYPE, VALS_POINTER }},
1166         };*/
1167         static gint *ett[] = {
1168                 &ett_snmp,
1169         };
1170
1171 #if defined(HAVE_UCD_SNMP_SNMP_H) || defined(HAVE_SNMP_SNMP_H)
1172         /* UCD or CMU SNMP */
1173         init_mib();
1174 #ifdef HAVE_UCD_SNMP_SNMP_H
1175         snmp_set_full_objid(TRUE);
1176 #endif
1177 #endif
1178         proto_snmp = proto_register_protocol("Simple Network Management Protocol", "snmp");
1179  /*       proto_register_field_array(proto_snmp, hf, array_length(hf));*/
1180         proto_register_subtree_array(ett, array_length(ett));
1181 }
1182
1183 void
1184 proto_reg_handoff_snmp(void)
1185 {
1186         dissector_add("udp.port", UDP_PORT_SNMP, dissect_snmp);
1187         dissector_add("udp.port", UDP_PORT_SNMP_TRAP, dissect_snmp);
1188 }