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