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