understand TCP MD5 signature. Greg Hankins <gregh@twoguys.org>
[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.54 2000/12/24 09:10:12 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 2048     /* TBC */
49
50 #ifdef linux
51 #include <dlfcn.h>
52 #endif
53
54 #include <glib.h>
55
56 #include "packet.h"
57 #include "strutil.h"
58 #include "conversation.h"
59 #include "etypes.h"
60 #include "packet-ipx.h"
61
62 #if defined(HAVE_UCD_SNMP_SNMP_H) || defined(HAVE_SNMP_SNMP_H)
63  /*
64   * UCD or CMU SNMP?
65   */
66 # if defined(HAVE_UCD_SNMP_SNMP_H)
67    /*
68     * UCD SNMP.
69     */
70 #  include <ucd-snmp/asn1.h>
71 #  include <ucd-snmp/snmp_api.h>
72 #  include <ucd-snmp/snmp_impl.h>
73 #  include <ucd-snmp/mib.h>
74
75    /*
76     * Sigh.  UCD SNMP 4.1.1 makes "snmp_set_suffix_only()" a macro
77     * that calls "ds_set_int()" with the first two arguments
78     * being DS_LIBRARY_ID and DS_LIB_PRINT_SUFFIX_ONLY; this means that,
79     * when building with 4.1.1, we need to arrange that
80     * <ucd-snmp/default_store.h> is included, to define those two values
81     * and to declare "ds_int()".
82     *
83     * However:
84     *
85     *   1) we can't include it on earlier versions (at least not 3.6.2),
86     *      as it doesn't exist in those versions;
87     *
88     *   2) we don't want to include <ucd-snmp/ucd-snmp-includes.h>,
89     *      as that includes <ucd-snmp/snmp.h>, and that defines a whole
90     *      bunch of values that we also define ourselves.
91     *
92     * So we only include it if "snmp_set_suffix_only" is defined as
93     * a macro.
94     */
95 #  ifdef snmp_set_suffix_only
96 #   include <ucd-snmp/default_store.h>
97 #  endif
98
99    /*
100     * XXX - for now, we assume all versions of UCD SNMP have it.
101     */
102 #  define HAVE_SPRINT_VALUE
103
104    /*
105     * Define values "sprint_value()" expects.
106     */
107 #  define VALTYPE_INTEGER       ASN_INTEGER
108 #  define VALTYPE_COUNTER       ASN_COUNTER
109 #  define VALTYPE_GAUGE         ASN_GAUGE
110 #  define VALTYPE_TIMETICKS     ASN_TIMETICKS
111 #  define VALTYPE_STRING        ASN_OCTET_STR
112 #  define VALTYPE_IPADDR        ASN_IPADDRESS
113 #  define VALTYPE_OPAQUE        ASN_OPAQUE
114 #  define VALTYPE_NSAP          ASN_NSAP
115 #  define VALTYPE_OBJECTID      ASN_OBJECT_ID
116 #  define VALTYPE_BITSTR        ASN_BIT_STR
117 #  define VALTYPE_COUNTER64     ASN_COUNTER64
118 # elif defined(HAVE_SNMP_SNMP_H)
119    /*
120     * CMU SNMP.
121     */
122 #  include <snmp/snmp.h>
123
124    /*
125     * Some older versions of CMU SNMP may lack these values (e.g., the
126     * "libsnmp3.6" package for Debian, which is based on some old
127     * CMU SNMP, perhaps 1.0); for now, we assume they also lack
128     * "sprint_value()".
129     */
130 #  ifdef SMI_INTEGER
131 #   define HAVE_SPRINT_VALUE
132     /*
133      * Define values "sprint_value()" expects.
134      */
135 #   define VALTYPE_INTEGER      SMI_INTEGER
136 #   define VALTYPE_COUNTER      SMI_COUNTER32
137 #   define VALTYPE_GAUGE        SMI_GAUGE32
138 #   define VALTYPE_TIMETICKS    SMI_TIMETICKS
139 #   define VALTYPE_STRING       SMI_STRING
140 #   define VALTYPE_IPADDR       SMI_IPADDRESS
141 #   define VALTYPE_OPAQUE       SMI_OPAQUE
142 #   define VALTYPE_NSAP         SMI_STRING
143 #   define VALTYPE_OBJECTID     SMI_OBJID
144 #   define VALTYPE_BITSTR       ASN_BIT_STR
145 #   define VALTYPE_COUNTER64    SMI_COUNTER64
146 #  endif
147   /*
148    * Now undo all the definitions they "helpfully" gave us, so we don't get
149    * complaints about redefining them.
150    *
151    * Why, oh why, is there no library that provides code to
152    *
153    *    1) read MIB files;
154    *
155    *    2) translate object IDs into names;
156    *
157    *    3) let you find out, for a given object ID, what the type, enum
158    *       values, display hint, etc. are;
159    *
160    * in a *simple* fashion, without assuming that your code is part of an
161    * SNMP agent or client that wants a pile of definitions of PDU types,
162    * etc.?  Is it just that 99 44/100% of the code that uses an SNMP library
163    * *is* part of an agent or client, and really *does* need that stuff,
164    * and *doesn't* need the interfaces we want?
165    */
166 #  undef SNMP_ERR_NOERROR
167 #  undef SNMP_ERR_TOOBIG
168 #  undef SNMP_ERR_NOSUCHNAME
169 #  undef SNMP_ERR_BADVALUE
170 #  undef SNMP_ERR_READONLY
171 #  undef SNMP_ERR_NOACCESS
172 #  undef SNMP_ERR_WRONGTYPE
173 #  undef SNMP_ERR_WRONGLENGTH
174 #  undef SNMP_ERR_WRONGENCODING
175 #  undef SNMP_ERR_WRONGVALUE
176 #  undef SNMP_ERR_NOCREATION
177 #  undef SNMP_ERR_INCONSISTENTVALUE
178 #  undef SNMP_ERR_RESOURCEUNAVAILABLE
179 #  undef SNMP_ERR_COMMITFAILED
180 #  undef SNMP_ERR_UNDOFAILED
181 #  undef SNMP_ERR_AUTHORIZATIONERROR
182 #  undef SNMP_ERR_NOTWRITABLE
183 #  undef SNMP_ERR_INCONSISTENTNAME
184 #  undef SNMP_TRAP_COLDSTART
185 #  undef SNMP_TRAP_WARMSTART
186 #  undef SNMP_TRAP_LINKDOWN
187 #  undef SNMP_TRAP_LINKUP
188 #  undef SNMP_TRAP_EGPNEIGHBORLOSS
189 #  undef SNMP_TRAP_ENTERPRISESPECIFIC
190 # endif
191 #endif
192
193 #include "asn1.h"
194
195 #include "packet-snmp.h"
196
197 /* Null string of type "guchar[]". */
198 static const guchar nullstring[] = "";
199
200 /* Take a pointer that may be null and return a pointer that's not null
201    by turning null pointers into pointers to the above null string. */
202 #define SAFE_STRING(s)  (((s) != NULL) ? (s) : nullstring)
203
204 static int proto_snmp = -1;
205 static int proto_smux = -1;
206
207 static gint ett_snmp = -1;
208 static gint ett_smux = -1;
209 static gint ett_global = -1;
210 static gint ett_flags = -1;
211 static gint ett_secur = -1;
212
213 static int hf_snmpv3_flags = -1;
214 static int hf_snmpv3_flags_auth = -1;
215 static int hf_snmpv3_flags_crypt = -1;
216 static int hf_snmpv3_flags_report = -1;
217
218 #define TH_AUTH   0x01
219 #define TH_CRYPT  0x02
220 #define TH_REPORT 0x04
221
222 static const true_false_string flags_set_truth = {
223   "Set",
224   "Not set"
225 };
226
227 #define UDP_PORT_SNMP           161
228 #define UDP_PORT_SNMP_TRAP      162
229 #define TCP_PORT_SMUX           199
230
231 /* Protocol version numbers */
232 #define SNMP_VERSION_1  0
233 #define SNMP_VERSION_2c 1
234 #define SNMP_VERSION_2u 2
235 #define SNMP_VERSION_3  3
236
237 static const value_string versions[] = {
238         { SNMP_VERSION_1,       "1" },
239         { SNMP_VERSION_2c,      "2C" },
240         { SNMP_VERSION_2u,      "2U" },
241         { SNMP_VERSION_3,       "3" },
242         { 0,                    NULL },
243 };
244
245 /* PDU types */
246 #define SNMP_MSG_GET            0
247 #define SNMP_MSG_GETNEXT        1
248 #define SNMP_MSG_RESPONSE       2
249 #define SNMP_MSG_SET            3
250 #define SNMP_MSG_TRAP           4
251
252 #define SNMP_MSG_GETBULK        5
253 #define SNMP_MSG_INFORM         6
254 #define SNMP_MSG_TRAP2          7
255 #define SNMP_MSG_REPORT         8
256
257 static const value_string pdu_types[] = {
258         { SNMP_MSG_GET,         "GET" },
259         { SNMP_MSG_GETNEXT,     "GET-NEXT" },
260         { SNMP_MSG_SET,         "SET" },
261         { SNMP_MSG_RESPONSE,    "RESPONSE" },
262         { SNMP_MSG_TRAP,        "TRAP-V1" },
263         { SNMP_MSG_GETBULK,     "GETBULK" },
264         { SNMP_MSG_INFORM,      "INFORM" },
265         { SNMP_MSG_TRAP2,       "TRAP-V2" },
266         { SNMP_MSG_REPORT,      "REPORT" },
267         { 0,                    NULL }
268 };
269
270 /* SMUX PDU types */
271 #define SMUX_MSG_OPEN           0
272 #define SMUX_MSG_CLOSE          1
273 #define SMUX_MSG_RREQ           2
274 #define SMUX_MSG_RRSP           3
275 #define SMUX_MSG_SOUT           4
276
277 static const value_string smux_types[] = {
278         { SMUX_MSG_OPEN,        "Open" },
279         { SMUX_MSG_CLOSE,       "Close" },
280         { SMUX_MSG_RREQ,        "Registration Request" },
281         { SMUX_MSG_RRSP,        "Registration Response" },
282         { SMUX_MSG_SOUT,        "Commit Or Rollback" },
283         { 0,                    NULL }
284 };
285
286 /* SMUX Closing causes */
287 #define SMUX_CLOSE_DOWN                 0
288 #define SMUX_CLOSE_VERSION              1
289 #define SMUX_CLOSE_PACKET               2
290 #define SMUX_CLOSE_PROTOCOL             3
291 #define SMUX_CLOSE_INTERNAL             4
292 #define SMUX_CLOSE_NOAUTH               5
293
294 static const value_string smux_close[] = {
295         { SMUX_CLOSE_DOWN,      "Going down" },
296         { SMUX_CLOSE_VERSION,   "Unsupported Version" },
297         { SMUX_CLOSE_PACKET,    "Packet Format Error" },
298         { SMUX_CLOSE_PROTOCOL,  "Protocol Error" },
299         { SMUX_CLOSE_INTERNAL,  "Internal Error" },
300         { SMUX_CLOSE_NOAUTH,    "Unauthorized" },
301         { 0,                    NULL }
302 };
303
304 /* SMUX Request codes */
305 #define SMUX_RREQ_DELETE                0
306 #define SMUX_RREQ_READONLY              1
307 #define SMUX_RREQ_READWRITE             2
308
309 static const value_string smux_rreq[] = {
310         { SMUX_RREQ_DELETE,     "Delete" },
311         { SMUX_RREQ_READONLY,   "Read Only" },
312         { SMUX_RREQ_READWRITE,  "Read Write" },
313         { 0,                    NULL }
314 };
315
316 static const value_string smux_prio[] = {
317         { -1,                           "Failure" },
318         { 0,                            NULL }
319 };
320
321 /* SMUX SOut codes */
322 #define SMUX_SOUT_COMMIT                0
323 #define SMUX_SOUT_ROLLBACK              1
324
325 static const value_string smux_sout[] = {
326         { SMUX_SOUT_COMMIT,             "Commit" },
327         { SMUX_SOUT_ROLLBACK,           "Rollback" }
328 };
329
330 /* Error status values */
331 #define SNMP_ERR_NOERROR                0
332 #define SNMP_ERR_TOOBIG                 1
333 #define SNMP_ERR_NOSUCHNAME             2
334 #define SNMP_ERR_BADVALUE               3
335 #define SNMP_ERR_READONLY               4
336 #define SNMP_ERR_GENERROR               5
337
338 #define SNMP_ERR_NOACCESS               6
339 #define SNMP_ERR_WRONGTYPE              7
340 #define SNMP_ERR_WRONGLENGTH            8
341 #define SNMP_ERR_WRONGENCODING          9
342 #define SNMP_ERR_WRONGVALUE             10
343 #define SNMP_ERR_NOCREATION             11
344 #define SNMP_ERR_INCONSISTENTVALUE      12
345 #define SNMP_ERR_RESOURCEUNAVAILABLE    13
346 #define SNMP_ERR_COMMITFAILED           14
347 #define SNMP_ERR_UNDOFAILED             15
348 #define SNMP_ERR_AUTHORIZATIONERROR     16
349 #define SNMP_ERR_NOTWRITABLE            17
350 #define SNMP_ERR_INCONSISTENTNAME       18
351
352 static const value_string error_statuses[] = {
353         { SNMP_ERR_NOERROR,             "NO ERROR" },
354         { SNMP_ERR_TOOBIG,              "TOOBIG" },
355         { SNMP_ERR_NOSUCHNAME,          "NO SUCH NAME" },
356         { SNMP_ERR_BADVALUE,            "BAD VALUE" },
357         { SNMP_ERR_READONLY,            "READ ONLY" },
358         { SNMP_ERR_GENERROR,            "GENERIC ERROR" },
359         { SNMP_ERR_NOACCESS,            "NO ACCESS" },
360         { SNMP_ERR_WRONGTYPE,           "WRONG TYPE" },
361         { SNMP_ERR_WRONGLENGTH,         "WRONG LENGTH" },
362         { SNMP_ERR_WRONGENCODING,       "WRONG ENCODING" },
363         { SNMP_ERR_WRONGVALUE,          "WRONG VALUE" },
364         { SNMP_ERR_NOCREATION,          "NO CREATION" },
365         { SNMP_ERR_INCONSISTENTVALUE,   "INCONSISTENT VALUE" },
366         { SNMP_ERR_RESOURCEUNAVAILABLE, "RESOURCE UNAVAILABLE" },
367         { SNMP_ERR_COMMITFAILED,        "COMMIT FAILED" },
368         { SNMP_ERR_UNDOFAILED,          "UNDO FAILED" },
369         { SNMP_ERR_AUTHORIZATIONERROR,  "AUTHORIZATION ERROR" },
370         { SNMP_ERR_NOTWRITABLE,         "NOT WRITABLE" },
371         { SNMP_ERR_INCONSISTENTNAME,    "INCONSISTENT NAME" },
372         { 0,                            NULL }
373 };
374
375 /* General SNMP V1 Traps */
376
377 #define SNMP_TRAP_COLDSTART             0
378 #define SNMP_TRAP_WARMSTART             1
379 #define SNMP_TRAP_LINKDOWN              2
380 #define SNMP_TRAP_LINKUP                3
381 #define SNMP_TRAP_AUTHFAIL              4
382 #define SNMP_TRAP_EGPNEIGHBORLOSS       5
383 #define SNMP_TRAP_ENTERPRISESPECIFIC    6
384
385 static const value_string trap_types[] = {
386         { SNMP_TRAP_COLDSTART,          "COLD START" },
387         { SNMP_TRAP_WARMSTART,          "WARM START" },
388         { SNMP_TRAP_LINKDOWN,           "LINK DOWN" },
389         { SNMP_TRAP_LINKUP,             "LINK UP" },
390         { SNMP_TRAP_AUTHFAIL,           "AUTHENTICATION FAILED" },
391         { SNMP_TRAP_EGPNEIGHBORLOSS,    "EGP NEIGHBORLOSS" },
392         { SNMP_TRAP_ENTERPRISESPECIFIC, "ENTERPRISE SPECIFIC" },
393         { 0,                            NULL }
394 };
395
396 /* Security Models */
397
398 #define SNMP_SEC_ANY                    0
399 #define SNMP_SEC_V1                     1
400 #define SNMP_SEC_V2C                    2
401 #define SNMP_SEC_USM                    3
402
403 static const value_string sec_models[] = {
404         { SNMP_SEC_ANY,                 "Any" },
405         { SNMP_SEC_V1,                  "V1" },
406         { SNMP_SEC_V2C,                 "V2C" },
407         { SNMP_SEC_USM,                 "USM" },
408         { 0,                            NULL }
409 };
410
411 /* SNMP Tags */
412
413 #define SNMP_IPA    0           /* IP Address */
414 #define SNMP_CNT    1           /* Counter (Counter32) */
415 #define SNMP_GGE    2           /* Gauge (Gauge32) */
416 #define SNMP_TIT    3           /* TimeTicks */
417 #define SNMP_OPQ    4           /* Opaque */
418 #define SNMP_NSP    5           /* NsapAddress */
419 #define SNMP_C64    6           /* Counter64 */
420 #define SNMP_U32    7           /* Uinteger32 */
421
422 #define SERR_NSO    0
423 #define SERR_NSI    1
424 #define SERR_EOM    2
425
426 /* SNMPv1 Types */
427
428 #define SNMP_NULL                0
429 #define SNMP_INTEGER             1    /* l  */
430 #define SNMP_OCTETSTR            2    /* c  */
431 #define SNMP_DISPLAYSTR          2    /* c  */
432 #define SNMP_OBJECTID            3    /* ul */
433 #define SNMP_IPADDR              4    /* uc */
434 #define SNMP_COUNTER             5    /* ul */
435 #define SNMP_GAUGE               6    /* ul */
436 #define SNMP_TIMETICKS           7    /* ul */
437 #define SNMP_OPAQUE              8    /* c  */
438
439 /* additional SNMPv2 Types */
440
441 #define SNMP_UINTEGER            5    /* ul */
442 #define SNMP_BITSTR              9    /* uc */
443 #define SNMP_NSAP               10    /* uc */
444 #define SNMP_COUNTER64          11    /* ul */
445 #define SNMP_NOSUCHOBJECT       12
446 #define SNMP_NOSUCHINSTANCE     13
447 #define SNMP_ENDOFMIBVIEW       14
448
449 typedef struct _SNMP_CNV SNMP_CNV;
450
451 struct _SNMP_CNV
452 {
453   guint class;
454   guint tag;
455   gint  syntax;
456   gchar *name;
457 };
458
459 static SNMP_CNV SnmpCnv [] =
460 {
461   {ASN1_UNI, ASN1_NUL, SNMP_NULL,      "NULL"},
462   {ASN1_UNI, ASN1_INT, SNMP_INTEGER,   "INTEGER"},
463   {ASN1_UNI, ASN1_OTS, SNMP_OCTETSTR,  "OCTET STRING"},
464   {ASN1_UNI, ASN1_OJI, SNMP_OBJECTID,  "OBJECTID"},
465   {ASN1_APL, SNMP_IPA, SNMP_IPADDR,    "IPADDR"},
466   {ASN1_APL, SNMP_CNT, SNMP_COUNTER,   "COUNTER"},  /* Counter32 */
467   {ASN1_APL, SNMP_GGE, SNMP_GAUGE,     "GAUGE"},    /* Gauge32 == Unsigned32  */
468   {ASN1_APL, SNMP_TIT, SNMP_TIMETICKS, "TIMETICKS"},
469   {ASN1_APL, SNMP_OPQ, SNMP_OPAQUE,    "OPAQUE"},
470
471 /* SNMPv2 data types and errors */
472
473   {ASN1_UNI, ASN1_BTS, SNMP_BITSTR,         "BITSTR"},
474   {ASN1_APL, SNMP_C64, SNMP_COUNTER64,      "COUNTER64"},
475   {ASN1_CTX, SERR_NSO, SNMP_NOSUCHOBJECT,   "NOSUCHOBJECT"},
476   {ASN1_CTX, SERR_NSI, SNMP_NOSUCHINSTANCE, "NOSUCHINSTANCE"},
477   {ASN1_CTX, SERR_EOM, SNMP_ENDOFMIBVIEW,   "ENDOFMIBVIEW"},
478   {0,       0,         -1,                  NULL}
479 };
480
481 /*
482  * NAME:        g_snmp_tag_cls2syntax
483  * SYNOPSIS:    gboolean g_snmp_tag_cls2syntax
484  *                  (
485  *                      guint    tag,
486  *                      guint    cls,
487  *                      gushort *syntax
488  *                  )
489  * DESCRIPTION: Converts ASN1 tag and class to Syntax tag and name.
490  *              See SnmpCnv for conversion.
491  * RETURNS:     name on success, NULL on failure
492  */
493
494 static gchar *
495 snmp_tag_cls2syntax ( guint tag, guint cls, gushort *syntax)
496 {
497     SNMP_CNV *cnv;
498
499     cnv = SnmpCnv;
500     while (cnv->syntax != -1)
501     {
502         if (cnv->tag == tag && cnv->class == cls)
503         {
504             *syntax = cnv->syntax;
505             return cnv->name;
506         }
507         cnv++;
508     }
509     return NULL;
510 }
511
512 static void
513 dissect_snmp_parse_error(const u_char *pd, int offset, frame_data *fd,
514                    proto_tree *tree, const char *field_name, int ret)
515 {
516         const gchar *errstr;
517
518         if (check_col(fd, COL_INFO)) {
519                 switch (ret) {
520
521                 case ASN1_ERR_EMPTY:
522                         errstr = "Ran out of data";
523                         break;
524
525                 case ASN1_ERR_EOC_MISMATCH:
526                         errstr = "EOC mismatch";
527                         break;
528
529                 case ASN1_ERR_WRONG_TYPE:
530                         errstr = "Wrong type for that item";
531                         break;
532
533                 case ASN1_ERR_LENGTH_NOT_DEFINITE:
534                         errstr = "Length was indefinite";
535                         break;
536
537                 case ASN1_ERR_LENGTH_MISMATCH:
538                         errstr = "Length mismatch";
539                         break;
540
541                 case ASN1_ERR_WRONG_LENGTH_FOR_TYPE:
542                         errstr = "Wrong length for that item's type";
543                         break;
544
545                 default:
546                         errstr = "Unknown error";
547                         break;
548                 }
549                 col_add_fstr(fd, COL_INFO,
550                     "ERROR: Couldn't parse %s: %s", field_name, errstr);
551         }
552
553         old_dissect_data(pd, offset, fd, tree);
554 }
555
556 static void
557 dissect_snmp_error(const u_char *pd, int offset, frame_data *fd,
558                    proto_tree *tree, const char *message)
559 {
560         if (check_col(fd, COL_INFO))
561                 col_add_str(fd, COL_INFO, message);
562
563         old_dissect_data(pd, offset, fd, tree);
564 }
565
566 static gchar *
567 format_oid(subid_t *oid, guint oid_length)
568 {
569         char *result;
570         int result_len;
571         int len, i;
572         char *buf;
573
574         result_len = oid_length * 22;
575         result = g_malloc(result_len + 1);
576         buf = result;
577         len = sprintf(buf, "%lu", (unsigned long)oid[0]);
578         buf += len;
579         for (i = 1; i < oid_length;i++) {
580                 len = sprintf(buf, ".%lu", (unsigned long)oid[i]);
581                 buf += len;
582         }
583         return result;
584 }
585
586 #ifdef HAVE_SPRINT_VALUE
587 static gchar *
588 format_var(struct variable_list *variable, subid_t *variable_oid,
589     guint variable_oid_length, gushort vb_type, guint vb_length)
590 {
591         gchar *buf;
592
593         switch (vb_type) {
594
595         case SNMP_INTEGER:
596         case SNMP_COUNTER:
597         case SNMP_GAUGE:
598         case SNMP_TIMETICKS:
599                 /* We don't know how long this will be, but let's guess it
600                    fits within 128 characters; that should be enough for an
601                    integral value plus some sort of type indication. */
602                 buf = g_malloc(128);
603                 break;
604
605         case SNMP_OCTETSTR:
606         case SNMP_IPADDR:
607         case SNMP_OPAQUE:
608         case SNMP_NSAP:
609         case SNMP_BITSTR:
610         case SNMP_COUNTER64:
611                 /* We don't know how long this will be, but let's guess it
612                    fits within 128 characters plus 4 characters per octet. */
613                 buf = g_malloc(128 + 4*vb_length);
614                 break;
615
616         case SNMP_OBJECTID:
617                 /* We don't know how long this will be, but let's guess it
618                    fits within 128 characters plus 32 characters per subid
619                    (10 digits plus period, or a subid name). */
620                 buf = g_malloc(1024 + 32*vb_length);
621                 break;
622
623         default:
624                 /* Should not happen. */
625                 g_assert_not_reached();
626                 buf = NULL;
627                 break;
628         }
629
630         variable->next_variable = NULL;
631         variable->name = variable_oid;
632         variable->name_length = variable_oid_length;
633         switch (vb_type) {
634
635         case SNMP_INTEGER:
636                 variable->type = VALTYPE_INTEGER;
637                 break;
638
639         case SNMP_COUNTER:
640                 variable->type = VALTYPE_COUNTER;
641                 break;
642
643         case SNMP_GAUGE:
644                 variable->type = VALTYPE_GAUGE;
645                 break;
646
647         case SNMP_TIMETICKS:
648                 variable->type = VALTYPE_TIMETICKS;
649                 break;
650
651         case SNMP_OCTETSTR:
652                 variable->type = VALTYPE_STRING;
653                 break;
654
655         case SNMP_IPADDR:
656                 variable->type = VALTYPE_IPADDR;
657                 break;
658
659         case SNMP_OPAQUE:
660                 variable->type = VALTYPE_OPAQUE;
661                 break;
662
663         case SNMP_NSAP:
664                 variable->type = VALTYPE_NSAP;
665                 break;
666
667         case SNMP_OBJECTID:
668                 variable->type = VALTYPE_OBJECTID;
669                 vb_length *= sizeof (subid_t);  /* XXX - necessary? */
670                 break;
671
672         case SNMP_BITSTR:
673                 variable->type = VALTYPE_BITSTR;
674                 break;
675
676         case SNMP_COUNTER64:
677                 variable->type = VALTYPE_COUNTER64;
678                 break;
679         }
680         variable->val_len = vb_length;
681
682         sprint_value(buf, variable_oid, variable_oid_length, variable);
683         return buf;
684 }
685 #endif
686
687 static int
688 snmp_variable_decode(proto_tree *snmp_tree, subid_t *variable_oid,
689     guint variable_oid_length, ASN1_SCK *asn1, int offset, guint *lengthp)
690 {
691         const guchar *start;
692         guint length;
693         gboolean def;
694         guint vb_length;
695         gushort vb_type;
696         gchar *vb_type_name;
697         int ret;
698         guint cls, con, tag;
699
700         gint32 vb_integer_value;
701         guint32 vb_uinteger_value;
702
703         guint8 *vb_octet_string;
704
705         subid_t *vb_oid;
706         guint vb_oid_length;
707
708         gchar *vb_display_string;
709
710 #ifdef HAVE_SPRINT_VALUE
711         struct variable_list variable;
712 #if defined(HAVE_UCD_SNMP_SNMP_H)
713         long value;
714 #endif
715 #else   /* HAVE_SPRINT_VALUE */
716         int i;
717         gchar *buf;
718         int len;
719 #endif  /* HAVE_SPRINT_VALUE */
720
721         /* parse the type of the object */
722         start = asn1->pointer;
723         ret = asn1_header_decode (asn1, &cls, &con, &tag, &def, &vb_length);
724         if (ret != ASN1_ERR_NOERROR)
725                 return ret;
726         if (!def)
727                 return ASN1_ERR_LENGTH_NOT_DEFINITE;
728
729         /* Convert the class, constructed flag, and tag to a type. */
730         vb_type_name = snmp_tag_cls2syntax(tag, cls, &vb_type);
731         if (vb_type_name == NULL) {
732                 /*
733                  * Unsupported type.
734                  * Dissect the value as an opaque string of octets.
735                  */
736                 vb_type_name = "unsupported type";
737                 vb_type = SNMP_OPAQUE;
738         }
739
740         /* parse the value */
741         switch (vb_type) {
742
743         case SNMP_INTEGER:
744                 ret = asn1_int32_value_decode(asn1, vb_length,
745                     &vb_integer_value);
746                 if (ret != ASN1_ERR_NOERROR)
747                         return ret;
748                 length = asn1->pointer - start;
749                 if (snmp_tree) {
750 #ifdef HAVE_SPRINT_VALUE
751 #if defined(HAVE_UCD_SNMP_SNMP_H)
752                         value = vb_integer_value;
753                         variable.val.integer = &value;
754 #elif defined(HAVE_SNMP_SNMP_H)
755                         variable.val.integer = &vb_integer_value;
756 #endif
757                         vb_display_string = format_var(&variable,
758                             variable_oid, variable_oid_length, vb_type,
759                             vb_length);
760                         proto_tree_add_text(snmp_tree, NullTVB, offset, length,
761                             "Value: %s", vb_display_string);
762                         g_free(vb_display_string);
763 #else
764                         proto_tree_add_text(snmp_tree, NullTVB, offset, length,
765                             "Value: %s: %d (%#x)", vb_type_name,
766                             vb_integer_value, vb_integer_value);
767 #endif
768                 }
769                 break;
770
771         case SNMP_COUNTER:
772         case SNMP_GAUGE:
773         case SNMP_TIMETICKS:
774                 ret = asn1_uint32_value_decode(asn1, vb_length,
775                     &vb_uinteger_value);
776                 if (ret != ASN1_ERR_NOERROR)
777                         return ret;
778                 length = asn1->pointer - start;
779                 if (snmp_tree) {
780 #ifdef HAVE_SPRINT_VALUE
781 #if defined(HAVE_UCD_SNMP_SNMP_H)
782                         value = vb_uinteger_value;
783                         variable.val.integer = &value;
784 #elif defined(HAVE_SNMP_SNMP_H)
785                         variable.val.integer = &vb_uinteger_value;
786 #endif
787                         vb_display_string = format_var(&variable,
788                             variable_oid, variable_oid_length, vb_type,
789                             vb_length);
790                         proto_tree_add_text(snmp_tree, NullTVB, offset, length,
791                             "Value: %s", vb_display_string);
792                         g_free(vb_display_string);
793 #else
794                         proto_tree_add_text(snmp_tree, NullTVB, offset, length,
795                             "Value: %s: %u (%#x)", vb_type_name,
796                             vb_uinteger_value, vb_uinteger_value);
797 #endif
798                 }
799                 break;
800
801         case SNMP_OCTETSTR:
802         case SNMP_IPADDR:
803         case SNMP_OPAQUE:
804         case SNMP_NSAP:
805         case SNMP_BITSTR:
806         case SNMP_COUNTER64:
807                 ret = asn1_string_value_decode (asn1, vb_length,
808                     &vb_octet_string);
809                 if (ret != ASN1_ERR_NOERROR)
810                         return ret;
811                 length = asn1->pointer - start;
812                 if (snmp_tree) {
813 #ifdef HAVE_SPRINT_VALUE
814                         variable.val.string = vb_octet_string;
815                         vb_display_string = format_var(&variable,
816                             variable_oid, variable_oid_length, vb_type,
817                             vb_length);
818                         proto_tree_add_text(snmp_tree, NullTVB, offset, length,
819                             "Value: %s", vb_display_string);
820                         g_free(vb_display_string);
821 #else
822                         /*
823                          * If some characters are not printable, display
824                          * the string as bytes.
825                          */
826                         for (i = 0; i < vb_length; i++) {
827                                 if (!(isprint(vb_octet_string[i])
828                                     || isspace(vb_octet_string[i])))
829                                         break;
830                         }
831                         if (i < vb_length) {
832                                 /*
833                                  * We stopped, due to a non-printable
834                                  * character, before we got to the end
835                                  * of the string.
836                                  */
837                                 vb_display_string = g_malloc(4*vb_length);
838                                 buf = &vb_display_string[0];
839                                 len = sprintf(buf, "%03u", vb_octet_string[0]);
840                                 buf += len;
841                                 for (i = 1; i < vb_length; i++) {
842                                         len = sprintf(buf, ".%03u",
843                                             vb_octet_string[i]);
844                                         buf += len;
845                                 }
846                                 proto_tree_add_text(snmp_tree, NullTVB, offset, length,
847                                     "Value: %s: %s", vb_type_name,
848                                     vb_display_string);
849                                 g_free(vb_display_string);
850                         } else {
851                                 proto_tree_add_text(snmp_tree, NullTVB, offset, length,
852                                     "Value: %s: %.*s", vb_type_name,
853                                     (int)vb_length, vb_octet_string);
854                         }
855 #endif
856                 }
857                 g_free(vb_octet_string);
858                 break;
859
860         case SNMP_NULL:
861                 ret = asn1_null_decode (asn1, vb_length);
862                 if (ret != ASN1_ERR_NOERROR)
863                         return ret;
864                 length = asn1->pointer - start;
865                 if (snmp_tree) {
866                         proto_tree_add_text(snmp_tree, NullTVB, offset, length,
867                             "Value: %s", vb_type_name);
868                 }
869                 break;
870
871         case SNMP_OBJECTID:
872                 ret = asn1_oid_value_decode (asn1, vb_length, &vb_oid,
873                     &vb_oid_length);
874                 if (ret != ASN1_ERR_NOERROR)
875                         return ret;
876                 length = asn1->pointer - start;
877                 if (snmp_tree) {
878 #ifdef HAVE_SPRINT_VALUE
879                         variable.val.objid = vb_oid;
880                         vb_display_string = format_var(&variable,
881                             variable_oid, variable_oid_length, vb_type,
882                             vb_length);
883                         proto_tree_add_text(snmp_tree, NullTVB, offset, length,
884                             "Value: %s", vb_display_string);
885 #else
886                         vb_display_string = format_oid(vb_oid, vb_oid_length);
887                         proto_tree_add_text(snmp_tree, NullTVB, offset, length,
888                             "Value: %s: %s", vb_type_name, vb_display_string);
889 #endif
890                         g_free(vb_display_string);
891                 }
892                 g_free(vb_oid);
893                 break;
894
895         case SNMP_NOSUCHOBJECT:
896                 length = asn1->pointer - start;
897                 if (snmp_tree) {
898                         proto_tree_add_text(snmp_tree, NullTVB, offset, length,
899                             "Value: %s: no such object", vb_type_name);
900                 }
901                 break;
902
903         case SNMP_NOSUCHINSTANCE:
904                 length = asn1->pointer - start;
905                 if (snmp_tree) {
906                         proto_tree_add_text(snmp_tree, NullTVB, offset, length,
907                             "Value: %s: no such instance", vb_type_name);
908                 }
909                 break;
910
911         case SNMP_ENDOFMIBVIEW:
912                 length = asn1->pointer - start;
913                 if (snmp_tree) {
914                         proto_tree_add_text(snmp_tree, NullTVB, offset, length,
915                             "Value: %s: end of mib view", vb_type_name);
916                 }
917                 break;
918
919         default:
920                 g_assert_not_reached();
921                 return ASN1_ERR_WRONG_TYPE;
922         }
923         *lengthp = length;
924         return ASN1_ERR_NOERROR;
925 }
926
927 static void
928 dissect_common_pdu(const u_char *pd, int offset, frame_data *fd,
929     proto_tree *tree, ASN1_SCK asn1, guint pdu_type, const guchar *start)
930 {
931         gboolean def;
932         guint length;
933         guint sequence_length;
934
935         guint32 request_id;
936
937         guint32 error_status;
938
939         guint32 error_index;
940
941         char *pdu_type_string;
942
943         subid_t *enterprise;
944         guint enterprise_length;
945
946         guint8 *agent_address;
947         guint agent_address_length;
948
949         guint32 trap_type;
950
951         guint32 specific_type;
952
953         guint timestamp;
954         guint timestamp_length;
955
956         gchar *oid_string;
957
958         guint variable_bindings_length;
959
960         int vb_index;
961         guint variable_length;
962         subid_t *variable_oid;
963         guint variable_oid_length;
964 #if defined(HAVE_UCD_SNMP_SNMP_H) || defined(HAVE_SNMP_SNMP_H)
965         gchar vb_oid_string[MAX_STRING_LEN]; /* TBC */
966 #endif
967
968         int ret;
969         guint cls, con, tag;
970
971         pdu_type_string = val_to_str(pdu_type, pdu_types,
972             "Unknown PDU type %#x");
973         if (check_col(fd, COL_INFO))
974                 col_add_str(fd, COL_INFO, pdu_type_string);
975         length = asn1.pointer - start;
976         if (tree) {
977                 proto_tree_add_text(tree, NullTVB, offset, length,
978                     "PDU type: %s", pdu_type_string);
979         }
980         offset += length;
981
982         /* get the fields in the PDU preceeding the variable-bindings sequence */
983         switch (pdu_type) {
984
985         case SNMP_MSG_GET:
986         case SNMP_MSG_GETNEXT:
987         case SNMP_MSG_RESPONSE:
988         case SNMP_MSG_SET:
989         case SNMP_MSG_GETBULK:
990         case SNMP_MSG_INFORM:
991         case SNMP_MSG_TRAP2:
992         case SNMP_MSG_REPORT:
993                 /* request id */
994                 ret = asn1_uint32_decode (&asn1, &request_id, &length);
995                 if (ret != ASN1_ERR_NOERROR) {
996                         dissect_snmp_parse_error(pd, offset, fd, tree,
997                             "request ID", ret);
998                         return;
999                 }
1000                 if (tree) {
1001                         proto_tree_add_text(tree, NullTVB, offset, length,
1002                             "Request Id: %#x", request_id);
1003                 }
1004                 offset += length;
1005                 
1006                 /* error status, or getbulk non-repeaters */
1007                 ret = asn1_uint32_decode (&asn1, &error_status, &length);
1008                 if (ret != ASN1_ERR_NOERROR) {
1009                         dissect_snmp_parse_error(pd, offset, fd, tree,
1010                             (pdu_type == SNMP_MSG_GETBULK) ? "non-repeaters"
1011                                                            : "error status",
1012                             ret);
1013                         return;
1014                 }
1015                 if (tree) {
1016                         if (pdu_type == SNMP_MSG_GETBULK) {
1017                                 proto_tree_add_text(tree, NullTVB, offset,
1018                                     length, "Non-repeaters: %u", error_status);
1019                         } else {
1020                                 proto_tree_add_text(tree, NullTVB, offset,
1021                                     length, "Error Status: %s",
1022                                     val_to_str(error_status, error_statuses,
1023                                       "Unknown (%d)"));
1024                         }
1025                 }
1026                 offset += length;
1027
1028                 /* error index, or getbulk max-repetitions */
1029                 ret = asn1_uint32_decode (&asn1, &error_index, &length);
1030                 if (ret != ASN1_ERR_NOERROR) {
1031                         dissect_snmp_parse_error(pd, offset, fd, tree,
1032                             (pdu_type == SNMP_MSG_GETBULK) ? "max repetitions"
1033                                                            : "error index",
1034                             ret);
1035                         return;
1036                 }
1037                 if (tree) {
1038                         if (pdu_type == SNMP_MSG_GETBULK) {
1039                                 proto_tree_add_text(tree, NullTVB, offset,
1040                                     length, "Max repetitions: %u", error_index);
1041                         } else {
1042                                 proto_tree_add_text(tree, NullTVB, offset,
1043                                     length, "Error Index: %u", error_index);
1044                         }
1045                 }
1046                 offset += length;
1047                 break;
1048
1049         case SNMP_MSG_TRAP:
1050                 /* enterprise */
1051                 ret = asn1_oid_decode (&asn1, &enterprise, &enterprise_length,
1052                     &length);
1053                 if (ret != ASN1_ERR_NOERROR) {
1054                         dissect_snmp_parse_error(pd, offset, fd, tree,
1055                             "enterprise OID", ret);
1056                         return;
1057                 }
1058                 if (tree) {
1059                         oid_string = format_oid(enterprise, enterprise_length);
1060                         proto_tree_add_text(tree, NullTVB, offset, length,
1061                             "Enterprise: %s", oid_string);
1062                         g_free(oid_string);
1063                 }
1064                 g_free(enterprise);
1065                 offset += length;
1066
1067                 /* agent address */
1068                 start = asn1.pointer;
1069                 ret = asn1_header_decode (&asn1, &cls, &con, &tag,
1070                     &def, &agent_address_length);
1071                 if (ret != ASN1_ERR_NOERROR) {
1072                         dissect_snmp_parse_error(pd, offset, fd, tree,
1073                             "agent address", ret);
1074                         return;
1075                 }
1076                 if (!((cls == ASN1_APL && con == ASN1_PRI && tag == SNMP_IPA) ||
1077                     (cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_OTS))) {
1078                         /* GXSNMP 0.0.15 says the latter is "needed for
1079                            Banyan" */
1080                         dissect_snmp_parse_error(pd, offset, fd, tree,
1081                             "agent_address", ASN1_ERR_WRONG_TYPE);
1082                         return;
1083                 }
1084                 if (agent_address_length != 4) {
1085                         dissect_snmp_parse_error(pd, offset, fd, tree,
1086                             "agent_address", ASN1_ERR_WRONG_LENGTH_FOR_TYPE);
1087                         return;
1088                 }
1089                 ret = asn1_string_value_decode (&asn1,
1090                     agent_address_length, &agent_address);
1091                 if (ret != ASN1_ERR_NOERROR) {
1092                         dissect_snmp_parse_error(pd, offset, fd, tree,
1093                             "agent address", ret);
1094                         return;
1095                 }
1096                 length = asn1.pointer - start;
1097                 if (tree) {
1098                         proto_tree_add_text(tree, NullTVB, offset, length,
1099                             "Agent address: %s", ip_to_str(agent_address));
1100                 }
1101                 g_free(agent_address);
1102                 offset += length;
1103                 
1104                 /* generic trap type */
1105                 ret = asn1_uint32_decode (&asn1, &trap_type, &length);
1106                 if (ret != ASN1_ERR_NOERROR) {
1107                         dissect_snmp_parse_error(pd, offset, fd, tree,
1108                             "generic trap type", ret);
1109                         return;
1110                 }
1111                 if (tree) {
1112                         proto_tree_add_text(tree, NullTVB, offset, length,
1113                             "Trap type: %s",
1114                             val_to_str(trap_type, trap_types, "Unknown (%u)"));
1115                 }               
1116                 offset += length;
1117                 
1118                 /* specific trap type */
1119                 ret = asn1_uint32_decode (&asn1, &specific_type, &length);
1120                 if (ret != ASN1_ERR_NOERROR) {
1121                         dissect_snmp_parse_error(pd, offset, fd, tree,
1122                             "specific trap type", ret);
1123                         return;
1124                 }
1125                 if (tree) {
1126                         proto_tree_add_text(tree, NullTVB, offset, length,
1127                             "Specific trap type: %u (%#x)",
1128                             specific_type, specific_type);
1129                 }               
1130                 offset += length;
1131                 
1132                 /* timestamp */
1133                 start = asn1.pointer;
1134                 ret = asn1_header_decode (&asn1, &cls, &con, &tag,
1135                     &def, &timestamp_length);
1136                 if (ret != ASN1_ERR_NOERROR) {
1137                         dissect_snmp_parse_error(pd, offset, fd, tree,
1138                             "timestamp", ret);
1139                         return;
1140                 }
1141                 if (!((cls == ASN1_APL && con == ASN1_PRI && tag == SNMP_TIT) ||
1142                     (cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_INT))) {
1143                         dissect_snmp_parse_error(pd, offset, fd, tree,
1144                             "timestamp", ASN1_ERR_WRONG_TYPE);
1145                         return;
1146                 }
1147                 ret = asn1_uint32_value_decode(&asn1, timestamp_length,
1148                     &timestamp);
1149                 if (ret != ASN1_ERR_NOERROR) {
1150                         dissect_snmp_parse_error(pd, offset, fd, tree,
1151                             "timestamp", ret);
1152                         return;
1153                 }
1154                 length = asn1.pointer - start;
1155                 if (tree) {
1156                         proto_tree_add_text(tree, NullTVB, offset, length,
1157                             "Timestamp: %u", timestamp);
1158                 }               
1159                 offset += length;
1160                 break;
1161         }
1162
1163         /* variable bindings */
1164         /* get header for variable-bindings sequence */
1165         ret = asn1_sequence_decode(&asn1, &variable_bindings_length, &length);
1166         if (ret != ASN1_ERR_NOERROR) {
1167                 dissect_snmp_parse_error(pd, offset, fd, tree,
1168                         "variable bindings header", ret);
1169                 return;
1170         }
1171         offset += length;
1172
1173         /* loop on variable bindings */
1174         vb_index = 0;
1175         while (variable_bindings_length > 0) {
1176                 vb_index++;
1177                 sequence_length = 0;
1178
1179                 /* parse type */
1180                 ret = asn1_sequence_decode(&asn1, &variable_length, &length);
1181                 if (ret != ASN1_ERR_NOERROR) {
1182                         dissect_snmp_parse_error(pd, offset, fd, tree,
1183                                 "variable binding header", ret);
1184                         return;
1185                 }
1186                 sequence_length += length;
1187
1188                 /* parse object identifier */
1189                 ret = asn1_oid_decode (&asn1, &variable_oid,
1190                     &variable_oid_length, &length);
1191                 if (ret != ASN1_ERR_NOERROR) {
1192                         dissect_snmp_parse_error(pd, offset, fd, tree,
1193                             "variable binding OID", ret);
1194                         return;
1195                 }
1196                 sequence_length += length;
1197
1198                 if (tree) {
1199                         oid_string = format_oid(variable_oid,
1200                             variable_oid_length);
1201                         
1202 #if defined(HAVE_UCD_SNMP_SNMP_H) || defined(HAVE_SNMP_SNMP_H)
1203                         sprint_objid(vb_oid_string, variable_oid,
1204                             variable_oid_length);
1205                         proto_tree_add_text(tree, NullTVB, offset, sequence_length,
1206                             "Object identifier %d: %s (%s)", vb_index,
1207                             oid_string, vb_oid_string);
1208 #else
1209                         
1210                         proto_tree_add_text(tree, NullTVB, offset, sequence_length,
1211                             "Object identifier %d: %s", vb_index,
1212                             oid_string);
1213 #endif
1214                         g_free(oid_string);
1215                 }
1216                 offset += sequence_length;
1217                 variable_bindings_length -= sequence_length;
1218                                 
1219                 /* Parse the variable's value */
1220                 ret = snmp_variable_decode(tree, variable_oid,
1221                     variable_oid_length, &asn1, offset, &length);
1222                 if (ret != ASN1_ERR_NOERROR) {
1223                         dissect_snmp_parse_error(pd, offset, fd, tree,
1224                             "variable", ret);
1225                         return;
1226                 }
1227                 offset += length;
1228                 variable_bindings_length -= length;
1229         }
1230 }
1231
1232 void
1233 dissect_snmp_pdu(const u_char *pd, int offset, frame_data *fd,
1234     proto_tree *tree, char *proto_name, int proto, gint ett)
1235 {
1236         ASN1_SCK asn1;
1237         const guchar *start;
1238         gboolean def;
1239         gboolean encrypted;
1240         guint length;
1241         guint message_length;
1242         guint global_length;
1243
1244         guint32 version;
1245         guint32 msgid;
1246         guint32 msgmax;
1247         guint32 msgsec;
1248         guint32 engineboots;
1249         guint32 enginetime;
1250
1251         guchar *msgflags;
1252         guchar *community;
1253         guchar *secparm;
1254         guchar *cengineid;
1255         guchar *cname;
1256         guchar *cryptpdu;
1257         guchar *aengineid;
1258         guchar *username;
1259         guchar *authpar;
1260         guchar *privpar;
1261         int msgflags_length;
1262         int community_length;
1263         int secparm_length;
1264         int cengineid_length;
1265         int cname_length;
1266         int cryptpdu_length;
1267         int aengineid_length;
1268         int username_length;
1269         int authpar_length;
1270         int privpar_length;
1271
1272         guint pdu_type;
1273         guint pdu_length;
1274
1275         proto_tree *snmp_tree = NULL;
1276         proto_tree *global_tree = NULL;
1277         proto_tree *flags_tree = NULL;
1278         proto_tree *secur_tree = NULL;
1279         proto_item *item = NULL;
1280         int ret;
1281         guint cls, con, tag;
1282
1283         if (check_col(fd, COL_PROTOCOL))
1284                 col_add_str(fd, COL_PROTOCOL, proto_name);
1285
1286         if (tree) {
1287                 item = proto_tree_add_item(tree, proto, NullTVB, offset,
1288                     END_OF_FRAME, FALSE);
1289                 snmp_tree = proto_item_add_subtree(item, ett);
1290         }
1291
1292         /* NOTE: we have to parse the message piece by piece, since the
1293          * capture length may be less than the message length: a 'global'
1294          * parsing is likely to fail.
1295          */
1296         /* parse the SNMP header */
1297         asn1_open(&asn1, &pd[offset], END_OF_FRAME);
1298         ret = asn1_sequence_decode(&asn1, &message_length, &length);
1299         if (ret != ASN1_ERR_NOERROR) {
1300                 dissect_snmp_parse_error(pd, offset, fd, tree,
1301                         "message header", ret);
1302                 return;
1303         }
1304         offset += length;
1305
1306         ret = asn1_uint32_decode (&asn1, &version, &length);
1307         if (ret != ASN1_ERR_NOERROR) {
1308                 dissect_snmp_parse_error(pd, offset, fd, tree, "version number",
1309                     ret);
1310                 return;
1311         }
1312         if (snmp_tree) {
1313                 proto_tree_add_text(snmp_tree, NullTVB, offset, length,
1314                     "Version: %s",
1315                     val_to_str(version, versions, "Unknown version %#x"));
1316         }
1317         offset += length;
1318
1319
1320         switch (version) {
1321         case SNMP_VERSION_1:
1322         case SNMP_VERSION_2c:
1323                 ret = asn1_octet_string_decode (&asn1, &community, 
1324                     &community_length, &length);
1325                 if (ret != ASN1_ERR_NOERROR) {
1326                         dissect_snmp_parse_error(pd, offset, fd, tree, 
1327                             "community", ret);
1328                         return;
1329                 }
1330                 if (tree) {
1331                         proto_tree_add_text(snmp_tree, NullTVB, offset, length,
1332                             "Community: %.*s", community_length,
1333                             SAFE_STRING(community));
1334                 }
1335                 g_free(community);
1336                 offset += length;
1337                 break;
1338         case SNMP_VERSION_2u:
1339                 /* FIXME */
1340                 break;
1341         case SNMP_VERSION_3:
1342                 ret = asn1_sequence_decode(&asn1, &global_length, &length);
1343                 if (ret != ASN1_ERR_NOERROR) {
1344                         dissect_snmp_parse_error(pd, offset, fd, tree,
1345                                 "message global header", ret);
1346                         return;
1347                 }
1348                 if (snmp_tree) {
1349                         item = proto_tree_add_text(snmp_tree, NullTVB, offset,
1350                             global_length + length, "Message Global Header");
1351                         global_tree = proto_item_add_subtree(item, ett_global);
1352                         proto_tree_add_text(global_tree, NullTVB, offset,
1353                             length,
1354                             "Message Global Header Length: %d", global_length);
1355                 }
1356                 offset += length;
1357                 ret = asn1_uint32_decode (&asn1, &msgid, &length);
1358                 if (ret != ASN1_ERR_NOERROR) {
1359                         dissect_snmp_parse_error(pd, offset, fd, tree, 
1360                             "message id", ret);
1361                         return;
1362                 }
1363                 if (global_tree) {
1364                         proto_tree_add_text(global_tree, NullTVB, offset,
1365                             length, "Message ID: %d", msgid);
1366                 }
1367                 offset += length;
1368                 ret = asn1_uint32_decode (&asn1, &msgmax, &length);
1369                 if (ret != ASN1_ERR_NOERROR) {
1370                         dissect_snmp_parse_error(pd, offset, fd, tree, 
1371                             "message max size", ret);
1372                         return;
1373                 }
1374                 if (global_tree) {
1375                         proto_tree_add_text(global_tree, NullTVB, offset,
1376                             length, "Message Max Size: %d", msgmax);
1377                 }
1378                 offset += length;
1379                 ret = asn1_octet_string_decode (&asn1, &msgflags, 
1380                     &msgflags_length, &length);
1381                 if (ret != ASN1_ERR_NOERROR) {
1382                         dissect_snmp_parse_error(pd, offset, fd, tree, 
1383                             "message flags", ret);
1384                         return;
1385                 }
1386                 if (msgflags_length != 1) {
1387                         dissect_snmp_parse_error(pd, offset, fd, tree,
1388                             "message flags wrong length", ret);
1389                         g_free(msgflags);
1390                         return;
1391                 }
1392                 if (global_tree) {
1393                         item = proto_tree_add_uint_format(global_tree,
1394                             hf_snmpv3_flags, NullTVB, offset, length,
1395                             msgflags[0], "Flags: 0x%02x", msgflags[0]);
1396                         flags_tree = proto_item_add_subtree(item, ett_flags);
1397                         proto_tree_add_boolean(flags_tree, hf_snmpv3_flags_report,
1398                             NullTVB, offset, length, msgflags[0]);
1399                         proto_tree_add_boolean(flags_tree, hf_snmpv3_flags_crypt,
1400                             NullTVB, offset, length, msgflags[0]);
1401                         proto_tree_add_boolean(flags_tree, hf_snmpv3_flags_auth,
1402                             NullTVB, offset, length, msgflags[0]);
1403                 }
1404                 encrypted = msgflags[0] & TH_CRYPT;
1405                 g_free(msgflags);
1406                 offset += length;
1407                 ret = asn1_uint32_decode (&asn1, &msgsec, &length);
1408                 if (ret != ASN1_ERR_NOERROR) {
1409                         dissect_snmp_parse_error(pd, offset, fd, tree, 
1410                             "message security model", ret);
1411                         return;
1412                 }
1413                 if (global_tree) {
1414                         proto_tree_add_text(global_tree, NullTVB, offset,
1415                             length, "Message Security Model: %s",
1416                             val_to_str(msgsec, sec_models,
1417                             "Unknown model %#x"));
1418                 }
1419                 offset += length;
1420                 switch(msgsec) {
1421                 case SNMP_SEC_USM:
1422                         start = asn1.pointer;
1423                         ret = asn1_header_decode (&asn1, &cls, &con, &tag,
1424                             &def, &secparm_length);
1425                         length = asn1.pointer - start;
1426                         if (cls != ASN1_UNI && con != ASN1_PRI && 
1427                             tag != ASN1_OTS) {
1428                                 dissect_snmp_parse_error(pd, offset, fd, tree, 
1429                                     "Message Security Parameters",
1430                                     ASN1_ERR_WRONG_TYPE);
1431                                 return;
1432                         }
1433                         if (snmp_tree) {
1434                                 item = proto_tree_add_text(snmp_tree, NullTVB,
1435                                     offset, secparm_length + length,
1436                                     "Message Security Parameters");
1437                                 secur_tree = proto_item_add_subtree(item,
1438                                     ett_secur);
1439                                 proto_tree_add_text(secur_tree, NullTVB, offset,
1440                                     length, 
1441                                     "Message Security Parameters Length: %d",
1442                                     secparm_length);
1443                         }
1444                         offset += length;
1445                         ret = asn1_sequence_decode(&asn1, &secparm_length,
1446                             &length);
1447                         if (ret != ASN1_ERR_NOERROR) {
1448                                 dissect_snmp_parse_error(pd, offset, fd, tree,
1449                                     "USM sequence header", ret);
1450                                 return;
1451                         }
1452                         offset += length;
1453                         ret = asn1_octet_string_decode (&asn1, &aengineid, 
1454                             &aengineid_length, &length);
1455                         if (ret != ASN1_ERR_NOERROR) {
1456                                 dissect_snmp_parse_error(pd, offset, fd, tree, 
1457                                     "authoritative engine id", ret);
1458                                 return;
1459                         }
1460                         if (secur_tree) {
1461                                 proto_tree_add_text(secur_tree, NullTVB, offset,
1462                                     length, "Authoritative Engine ID: %s",
1463                                     bytes_to_str(aengineid, aengineid_length));
1464                         }
1465                         g_free(aengineid);
1466                         offset += length;
1467                         ret = asn1_uint32_decode (&asn1, &engineboots, &length);
1468                         if (ret != ASN1_ERR_NOERROR) {
1469                                 dissect_snmp_parse_error(pd, offset, fd, tree, 
1470                                     "engine boots", ret);
1471                                 return;
1472                         }
1473                         if (secur_tree) {
1474                                 proto_tree_add_text(secur_tree, NullTVB,
1475                                     offset, length, "Engine Boots: %d", 
1476                                     engineboots);
1477                         }
1478                         offset += length;
1479                         ret = asn1_uint32_decode (&asn1, &enginetime, &length);
1480                         if (ret != ASN1_ERR_NOERROR) {
1481                                 dissect_snmp_parse_error(pd, offset, fd, tree, 
1482                                     "engine time", ret);
1483                                 return;
1484                         }
1485                         if (secur_tree) {
1486                                 proto_tree_add_text(secur_tree, NullTVB,
1487                                     offset, length, "Engine Time: %d", 
1488                                     enginetime);
1489                         }
1490                         offset += length;
1491                         ret = asn1_octet_string_decode (&asn1, &username, 
1492                             &username_length, &length);
1493                         if (ret != ASN1_ERR_NOERROR) {
1494                                 dissect_snmp_parse_error(pd, offset, fd, tree, 
1495                                     "user name", ret);
1496                                 return;
1497                         }
1498                         if (secur_tree) {
1499                                 proto_tree_add_text(secur_tree, NullTVB, offset,
1500                                     length, "User Name: %.*s", 
1501                                     username_length,
1502                                     SAFE_STRING(username));
1503                         }
1504                         g_free(username);
1505                         offset += length;
1506                         ret = asn1_octet_string_decode (&asn1, &authpar, 
1507                             &authpar_length, &length);
1508                         if (ret != ASN1_ERR_NOERROR) {
1509                                 dissect_snmp_parse_error(pd, offset, fd, tree, 
1510                                     "authentication parameter", ret);
1511                                 return;
1512                         }
1513                         if (secur_tree) {
1514                                 proto_tree_add_text(secur_tree, NullTVB, offset,
1515                                     length, "Authentication Parameter: %s",
1516                                     bytes_to_str(authpar, authpar_length));
1517                         }
1518                         g_free(authpar);
1519                         offset += length;
1520                         ret = asn1_octet_string_decode (&asn1, &privpar, 
1521                             &privpar_length, &length);
1522                         if (ret != ASN1_ERR_NOERROR) {
1523                                 dissect_snmp_parse_error(pd, offset, fd, tree, 
1524                                     "privacy parameter", ret);
1525                                 return;
1526                         }
1527                         if (secur_tree) {
1528                                 proto_tree_add_text(secur_tree, NullTVB, offset,
1529                                     length, "Privacy Parameter: %s",
1530                                     bytes_to_str(privpar, privpar_length));
1531                         }
1532                         g_free(privpar);
1533                         offset += length;
1534                         break;
1535                 default:
1536                         ret = asn1_octet_string_decode (&asn1, 
1537                             &secparm, &secparm_length, &length);
1538                         if (ret != ASN1_ERR_NOERROR) {
1539                                 dissect_snmp_parse_error(pd, offset, fd, tree, 
1540                                     "Message Security Parameters", ret);
1541                                 return;
1542                         }
1543                         if (snmp_tree) {
1544                                 proto_tree_add_text(snmp_tree, NullTVB, offset,
1545                                     length,
1546                                     "Message Security Parameters Data"
1547                                     " (%d bytes)", secparm_length);
1548                         }
1549                         g_free(secparm);
1550                         offset += length;
1551                         break;
1552                 }
1553                 /* PDU starts here */
1554                 if (encrypted) {
1555                         ret = asn1_octet_string_decode (&asn1, &cryptpdu,
1556                             &cryptpdu_length, &length);
1557                         if (ret != ASN1_ERR_NOERROR) {
1558                                 dissect_snmp_parse_error(pd, offset, fd, tree, 
1559                                     "encrypted PDU header", ret);
1560                                 return;
1561                         }
1562                         proto_tree_add_text(snmp_tree, NullTVB, offset, length,
1563                             "Encrypted PDU (%d bytes)", length);
1564                         g_free(cryptpdu);
1565                         if (check_col(fd, COL_INFO))
1566                                 col_set_str(fd, COL_INFO, "Encrypted PDU");
1567                         return;
1568                 }
1569                 ret = asn1_sequence_decode(&asn1, &global_length, &length);
1570                 if (ret != ASN1_ERR_NOERROR) {
1571                         dissect_snmp_parse_error(pd, offset, fd, tree,
1572                                 "PDU header", ret);
1573                         return;
1574                 }
1575                 offset += length;
1576                 ret = asn1_octet_string_decode (&asn1, &cengineid, 
1577                     &cengineid_length, &length);
1578                 if (ret != ASN1_ERR_NOERROR) {
1579                         dissect_snmp_parse_error(pd, offset, fd, tree, 
1580                             "context engine id", ret);
1581                         return;
1582                 }
1583                 if (snmp_tree) {
1584                         proto_tree_add_text(snmp_tree, NullTVB, offset, length,
1585                             "Context Engine ID: %s",
1586                             bytes_to_str(cengineid, cengineid_length));
1587                 }
1588                 g_free(cengineid);
1589                 offset += length;
1590                 ret = asn1_octet_string_decode (&asn1, &cname, 
1591                     &cname_length, &length);
1592                 if (ret != ASN1_ERR_NOERROR) {
1593                         dissect_snmp_parse_error(pd, offset, fd, tree, 
1594                             "context name", ret);
1595                         return;
1596                 }
1597                 if (snmp_tree) {
1598                         proto_tree_add_text(snmp_tree, NullTVB, offset, length,
1599                             "Context Name: %.*s", cname_length,
1600                             SAFE_STRING(cname));
1601                 }
1602                 g_free(cname);
1603                 offset += length;
1604                 break;
1605         default:
1606                 dissect_snmp_error(pd, offset, fd, tree,
1607                     "PDU for unknown version of SNMP");
1608                 return;
1609         }
1610
1611         start = asn1.pointer;
1612         ret = asn1_header_decode (&asn1, &cls, &con, &pdu_type, &def,
1613             &pdu_length);
1614         if (ret != ASN1_ERR_NOERROR) {
1615                 dissect_snmp_parse_error(pd, offset, fd, tree,
1616                     "PDU type", ret);
1617                 return;
1618         }
1619         if (cls != ASN1_CTX || con != ASN1_CON) {
1620                 dissect_snmp_parse_error(pd, offset, fd, tree,
1621                     "PDU type", ASN1_ERR_WRONG_TYPE);
1622                 return;
1623         }
1624         dissect_common_pdu(pd, offset, fd, snmp_tree, asn1, pdu_type, start);
1625 }
1626
1627 static void
1628 dissect_smux_pdu(const u_char *pd, int offset, frame_data *fd,
1629     proto_tree *tree, int proto, gint ett)
1630 {
1631         ASN1_SCK asn1;
1632         const guchar *start;
1633         gboolean def;
1634         guint length;
1635
1636         guint pdu_type;
1637         char *pdu_type_string;
1638         guint pdu_length;
1639
1640         guint32 version;
1641         guint32 cause;
1642         guint32 priority;
1643         guint32 operation;
1644         guint32 commit;
1645
1646         guchar *password;
1647         int password_length;
1648
1649         guchar *application;
1650         int application_length;
1651
1652         subid_t *regid;
1653         guint regid_length;
1654
1655         gchar *oid_string;
1656
1657         proto_tree *smux_tree = NULL;
1658         proto_item *item = NULL;
1659         int ret;
1660         guint cls, con;
1661
1662         if (check_col(fd, COL_PROTOCOL))
1663                 col_set_str(fd, COL_PROTOCOL, "SMUX");
1664
1665         if (tree) {
1666                 item = proto_tree_add_item(tree, proto, NullTVB, offset,
1667                     END_OF_FRAME, FALSE);
1668                 smux_tree = proto_item_add_subtree(item, ett);
1669         }
1670
1671         /* NOTE: we have to parse the message piece by piece, since the
1672          * capture length may be less than the message length: a 'global'
1673          * parsing is likely to fail.
1674          */
1675         /* parse the SNMP header */
1676         asn1_open(&asn1, &pd[offset], END_OF_FRAME);
1677         start = asn1.pointer;
1678         ret = asn1_header_decode (&asn1, &cls, &con, &pdu_type, &def,
1679             &pdu_length);
1680         if (ret != ASN1_ERR_NOERROR) {
1681                 dissect_snmp_parse_error(pd, offset, fd, tree,
1682                     "PDU type", ret);
1683                 return;
1684         }
1685
1686         /* Dissect SMUX here */
1687         if (cls == ASN1_APL && con == ASN1_CON && pdu_type == SMUX_MSG_OPEN) {
1688                 pdu_type_string = val_to_str(pdu_type, smux_types,
1689                     "Unknown PDU type %#x");
1690                 if (check_col(fd, COL_INFO))
1691                         col_add_str(fd, COL_INFO, pdu_type_string);
1692                 length = asn1.pointer - start;
1693                 if (tree) {
1694                         proto_tree_add_text(smux_tree, NullTVB, offset, length,
1695                             "PDU type: %s", pdu_type_string);
1696                 }
1697                 offset += length;
1698                 ret = asn1_uint32_decode (&asn1, &version, &length);
1699                 if (ret != ASN1_ERR_NOERROR) {
1700                         dissect_snmp_parse_error(pd, offset, fd, tree,
1701                             "version", ret);
1702                         return;
1703                 }
1704                 if (tree) {
1705                         proto_tree_add_text(smux_tree, NullTVB, offset, length,
1706                             "Version: %d", version);
1707                 }
1708                 offset += length;
1709
1710                 ret = asn1_oid_decode (&asn1, &regid, &regid_length, &length);
1711                 if (ret != ASN1_ERR_NOERROR) {
1712                         dissect_snmp_parse_error(pd, offset, fd, tree,
1713                             "registration OID", ret);
1714                         return;
1715                 }
1716                 if (tree) {
1717                         oid_string = format_oid(regid, regid_length);
1718                         proto_tree_add_text(smux_tree, NullTVB, offset, length,
1719                             "Registration: %s", oid_string);
1720                         g_free(oid_string);
1721                 }
1722                 g_free(regid);
1723                 offset += length;
1724
1725                 ret = asn1_octet_string_decode (&asn1, &application, 
1726                     &application_length, &length);
1727                 if (ret != ASN1_ERR_NOERROR) {
1728                         dissect_snmp_parse_error(pd, offset, fd, tree, 
1729                             "application", ret);
1730                         return;
1731                 }
1732                 if (tree) {
1733                         proto_tree_add_text(smux_tree, NullTVB, offset, length,
1734                             "Application: %.*s", application_length,
1735                              SAFE_STRING(application));
1736                 }
1737                 g_free(application);
1738                 offset += length;
1739
1740                 ret = asn1_octet_string_decode (&asn1, &password, 
1741                     &password_length, &length);
1742                 if (ret != ASN1_ERR_NOERROR) {
1743                         dissect_snmp_parse_error(pd, offset, fd, tree, 
1744                             "password", ret);
1745                         return;
1746                 }
1747                 if (tree) {
1748                         proto_tree_add_text(smux_tree, NullTVB, offset, length,
1749                             "Password: %.*s", password_length,
1750                             SAFE_STRING(password));
1751                 }
1752                 g_free(password);
1753                 offset += length;
1754                 return;
1755         }
1756         if (cls == ASN1_APL && con == ASN1_PRI && pdu_type == SMUX_MSG_CLOSE) {
1757                 pdu_type_string = val_to_str(pdu_type, smux_types,
1758                     "Unknown PDU type %#x");
1759                 if (check_col(fd, COL_INFO))
1760                         col_add_str(fd, COL_INFO, pdu_type_string);
1761                 length = asn1.pointer - start;
1762                 if (tree) {
1763                         proto_tree_add_text(smux_tree, NullTVB, offset, length,
1764                             "PDU type: %s", pdu_type_string);
1765                 }
1766                 offset += length;
1767                 ret = asn1_uint32_value_decode (&asn1, pdu_length, &cause);
1768                 if (ret != ASN1_ERR_NOERROR) {
1769                         dissect_snmp_parse_error(pd, offset, fd, tree,
1770                             "cause", ret);
1771                         return;
1772                 }
1773                 if (tree) {
1774                         proto_tree_add_text(smux_tree, NullTVB, offset,
1775                             pdu_length, "Cause: %s",
1776                             val_to_str(cause, smux_close, 
1777                                 "Unknown cause %#x"));
1778                 }
1779                 offset += pdu_length;
1780                 return;
1781         }
1782         if (cls == ASN1_APL && con == ASN1_CON && pdu_type == SMUX_MSG_RREQ) {
1783                 pdu_type_string = val_to_str(pdu_type, smux_types,
1784                     "Unknown PDU type %#x");
1785                 if (check_col(fd, COL_INFO))
1786                         col_add_str(fd, COL_INFO, pdu_type_string);
1787                 length = asn1.pointer - start;
1788                 if (tree) {
1789                         proto_tree_add_text(smux_tree, NullTVB, offset, length,
1790                             "PDU type: %s", pdu_type_string);
1791                 }
1792                 offset += length;
1793                 ret = asn1_oid_decode (&asn1, &regid, &regid_length, &length);
1794                 if (ret != ASN1_ERR_NOERROR) {
1795                         dissect_snmp_parse_error(pd, offset, fd, tree,
1796                             "registration subtree", ret);
1797                         return;
1798                 }
1799                 if (tree) {
1800                         oid_string = format_oid(regid, regid_length);
1801                         proto_tree_add_text(smux_tree, NullTVB, offset, length,
1802                             "Registration: %s", oid_string);
1803                         g_free(oid_string);
1804                 }
1805                 g_free(regid);
1806                 offset += length;
1807
1808                 ret = asn1_uint32_decode (&asn1, &priority, &length);
1809                 if (ret != ASN1_ERR_NOERROR) {
1810                         dissect_snmp_parse_error(pd, offset, fd, tree,
1811                             "priority", ret);
1812                         return;
1813                 }
1814                 if (tree) {
1815                         proto_tree_add_text(smux_tree, NullTVB, offset, length,
1816                             "Priority: %d", priority);
1817                 }
1818                 offset += length;
1819
1820                 ret = asn1_uint32_decode (&asn1, &operation, &length);
1821                 if (ret != ASN1_ERR_NOERROR) {
1822                         dissect_snmp_parse_error(pd, offset, fd, tree,
1823                             "operation", ret);
1824                         return;
1825                 }
1826                 if (tree) {
1827                         proto_tree_add_text(smux_tree, NullTVB, offset, length,
1828                             "Operation: %s", 
1829                             val_to_str(operation, smux_rreq, 
1830                                 "Unknown operation %#x"));
1831                 }
1832                 offset += length;
1833                 return;
1834         }
1835         if (cls == ASN1_APL && con == ASN1_PRI && pdu_type == SMUX_MSG_RRSP) {
1836                 pdu_type_string = val_to_str(pdu_type, smux_types,
1837                     "Unknown PDU type %#x");
1838                 if (check_col(fd, COL_INFO))
1839                         col_add_str(fd, COL_INFO, pdu_type_string);
1840                 length = asn1.pointer - start;
1841                 if (tree) {
1842                         proto_tree_add_text(smux_tree, NullTVB, offset, length,
1843                             "PDU type: %s", pdu_type_string);
1844                 }
1845                 offset += length;
1846                 ret = asn1_uint32_value_decode (&asn1, pdu_length, &priority);
1847                 if (ret != ASN1_ERR_NOERROR) {
1848                         dissect_snmp_parse_error(pd, offset, fd, tree,
1849                             "priority", ret);
1850                         return;
1851                 }
1852                 if (tree) {
1853                         proto_tree_add_text(smux_tree, NullTVB, offset,
1854                             pdu_length, "%s",
1855                             val_to_str(priority, smux_prio, 
1856                                 "Priority: %#x"));
1857                 }
1858                 offset += pdu_length;
1859                 return;
1860         }
1861         if (cls == ASN1_APL && con == ASN1_PRI && pdu_type == SMUX_MSG_SOUT) {
1862                 pdu_type_string = val_to_str(pdu_type, smux_types,
1863                     "Unknown PDU type %#x");
1864                 if (check_col(fd, COL_INFO))
1865                         col_add_str(fd, COL_INFO, pdu_type_string);
1866                 length = asn1.pointer - start;
1867                 if (tree) {
1868                         proto_tree_add_text(smux_tree, NullTVB, offset, length,
1869                             "PDU type: %s", pdu_type_string);
1870                 }
1871                 offset += length;
1872                 ret = asn1_uint32_value_decode (&asn1, pdu_length, &commit);
1873                 if (ret != ASN1_ERR_NOERROR) {
1874                         dissect_snmp_parse_error(pd, offset, fd, tree,
1875                             "commit", ret);
1876                         return;
1877                 }
1878                 if (tree) {
1879                         proto_tree_add_text(smux_tree, NullTVB, offset,
1880                             pdu_length, "%s",
1881                             val_to_str(commit, smux_sout, 
1882                                 "Unknown SOUT Value: %#x"));
1883                 }
1884                 offset += pdu_length;
1885                 return;
1886         }
1887         if (cls != ASN1_CTX || con != ASN1_CON) {
1888                 dissect_snmp_parse_error(pd, offset, fd, tree,
1889                     "PDU type", ASN1_ERR_WRONG_TYPE);
1890                 return;
1891         }
1892         dissect_common_pdu(pd, offset, fd, smux_tree, asn1, pdu_type, start);
1893 }
1894
1895 static void
1896 dissect_snmp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) 
1897 {
1898         conversation_t  *conversation;
1899
1900         OLD_CHECK_DISPLAY_AS_DATA(proto_snmp, pd, offset, fd, tree);
1901
1902         /*
1903          * The first SNMP packet goes to the SNMP port; the second one
1904          * may come from some *other* port, but goes back to the same
1905          * IP address and port as the ones from which the first packet
1906          * came; all subsequent packets presumably go between those two
1907          * IP addresses and ports.
1908          *
1909          * If this packet went to the SNMP port, we check to see if
1910          * there's already a conversation with the source IP address
1911          * and port of this packet, the destination IP address of this
1912          * packet, and any destination UDP port.  If not, we create
1913          * one, with a wildcard UDP port, and give it the SNMP dissector
1914          * as a dissector.
1915          */
1916         if (pi.destport == UDP_PORT_SNMP) {
1917           conversation = find_conversation(&pi.src, &pi.dst, PT_UDP,
1918                                            pi.srcport, 0, NO_DST_PORT);
1919           if (conversation == NULL) {
1920             conversation = conversation_new(&pi.src, &pi.dst, PT_UDP,
1921                                             pi.srcport, 0, NULL,
1922                                             NO_DST_PORT);
1923             old_conversation_set_dissector(conversation, dissect_snmp);
1924           }
1925         }
1926
1927         dissect_snmp_pdu(pd, offset, fd, tree, "SNMP", proto_snmp, ett_snmp);
1928 }
1929
1930 static void
1931 dissect_smux(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) 
1932 {
1933         OLD_CHECK_DISPLAY_AS_DATA(proto_smux, pd, offset, fd, tree);
1934         dissect_smux_pdu(pd, offset, fd, tree, proto_smux, ett_smux);
1935 }
1936
1937 void
1938 proto_register_snmp(void)
1939 {
1940 #ifdef linux
1941         void *libsnmp_handle;
1942         int (*snmp_set_suffix_only_p)(int);
1943         int (*ds_set_int_p)(int, int, int);
1944 #endif
1945
1946         static hf_register_info hf[] = {
1947                 { &hf_snmpv3_flags,
1948                 { "SNMPv3 Flags", "snmpv3.flags", FT_UINT8, BASE_HEX, NULL,
1949                     0x0, "" }},
1950                 { &hf_snmpv3_flags_auth,
1951                 { "Authenticated", "snmpv3.flags.auth", FT_BOOLEAN, 8,
1952                     TFS(&flags_set_truth), TH_AUTH, "" }},
1953                 { &hf_snmpv3_flags_crypt,
1954                 { "Encrypted", "snmpv3.flags.crypt", FT_BOOLEAN, 8,
1955                     TFS(&flags_set_truth), TH_CRYPT, "" }},
1956                 { &hf_snmpv3_flags_report,
1957                 { "Reportable", "snmpv3.flags.report", FT_BOOLEAN, 8,
1958                     TFS(&flags_set_truth), TH_REPORT, "" }},
1959         };
1960         static gint *ett[] = {
1961                 &ett_snmp,
1962                 &ett_smux,
1963                 &ett_global,
1964                 &ett_flags,
1965                 &ett_secur,
1966         };
1967
1968 #if defined(HAVE_UCD_SNMP_SNMP_H) || defined(HAVE_SNMP_SNMP_H)
1969         /* UCD or CMU SNMP */
1970         init_mib();
1971 #ifdef HAVE_UCD_SNMP_SNMP_H
1972 #ifdef linux
1973         /* As per the comment near the beginning of the file, UCD SNMP 4.1.1
1974            changed "snmp_set_suffix_only()" from a function to a macro,
1975            removing "snmp_set_suffix_only()" from the library; this means
1976            that binaries that call "snmp_set_suffix_only()" and
1977            that are linked against shared libraries from earlier versions
1978            of the UCD SNMP library won't run with shared libraries from
1979            4.1.1.
1980
1981            This is a problem on Red Hat Linux, as pre-6.2 releases
1982            came with pre-4.1.1 UCD SNMP, while 6.2 comes the 4.1.1.
1983            Versions of Ethereal built on pre-6.2 releases don't run
1984            on 6.2, and the current Ethereal RPMs are built on pre-6.2
1985            releases, causing problems when users running 6.2 download
1986            them and try to use them.
1987
1988            Building the releases on 6.2 isn't necessarily the answer,
1989            as "snmp_set_suffix_only()" expands to a call to "ds_set_int()"
1990            with a second argument not supported by at least some pre-4.1.1
1991            versions of the library - it appears that the 4.0.1 library,
1992            at least, checks for invalid arguments and returns an error
1993            rather than stomping random memory, but that means that you
1994            won't get get OIDs displayed as module-name::sub-OID.
1995
1996            So we use a trick similar to one I've seen mentioned as
1997            used in Windows applications to let you build binaries
1998            that run on many different versions of Windows 9x and
1999            Windows NT, that use features present on later versions
2000            if run on those later versions, but that avoid calling,
2001            when run on older versions, routines not present on those
2002            older versions.
2003
2004            I.e., we load "libsnmp.so.0" with "dlopen()", and call
2005            "dlsym()" to try to find "snmp_set_suffix_only()"; if we
2006            don't find it, we make the appropriate call to
2007            "ds_set_int()" instead.  (We load "libsnmp.so.0" rather
2008            than "libsnmp.so" because, at least on RH 6.2, "libsnmp.so"
2009            exists only if you've loaded the libsnmp development package,
2010            which makes "libsnmp.so" a symlink to "libsnmp.so.0"; we
2011            don't want to force users to install it or to make said
2012            symlink by hand.)
2013
2014            We do this only on Linux, for now, as we've only seen the
2015            problem on Red Hat; it may show up on other OSes that bundle
2016            UCD SNMP, or on OSes where it's not bundled but for which
2017            binary packages are built that link against a shared version
2018            of the UCD SNMP library.  If we run into one of those, we
2019            can do this under those OSes as well, *if* "dlopen()" makes
2020            the run-time linker use the same search rules as it uses when
2021            loading libraries with which the application is linked.
2022
2023            (Perhaps we could use the GLib wrappers for run-time linking,
2024            *if* they're thin enough; however, as this code is currently
2025            used only on Linux, we don't worry about that for now.) */
2026
2027         libsnmp_handle = dlopen("libsnmp.so.0", RTLD_LAZY|RTLD_GLOBAL);
2028         if (libsnmp_handle == NULL) {
2029                 /* We didn't find "libsnmp.so.0".
2030
2031                    This could mean that there is no SNMP shared library
2032                    on this system, in which case we were linked statically,
2033                    in which case whatever call the following line of code
2034                    makes will presumably work, as we have the routine it
2035                    calls wired into our binary.  (If we were linked
2036                    dynamically with "-lsnmp", we would have failed to
2037                    start.)
2038
2039                    It could also mean that there is an SNMP shared library
2040                    on this system, but it's called something other than
2041                    "libsnmp.so.0"; so far, we've seen the problem we're
2042                    working around only on systems where the SNMP shared
2043                    library is called "libsnmp.so.0", so we assume for now
2044                    that systems with shared SNMP libraries named something
2045                    other than "libsnmp.so.0" have an SNMP library that's
2046                    not 4.1.1. */
2047                 snmp_set_suffix_only(2);
2048         } else {
2049                 /* OK, we have it loaded.  Do we have
2050                    "snmp_set_suffix_only()"? */
2051                 snmp_set_suffix_only_p = dlsym(libsnmp_handle,
2052                     "snmp_set_suffix_only");
2053                 if (snmp_set_suffix_only_p != NULL) {
2054                         /* Yes - call it. */
2055                         (*snmp_set_suffix_only_p)(2);
2056                 } else {
2057                         /* No; do we have "ds_set_int()"? */
2058                         ds_set_int_p = dlsym(libsnmp_handle, "ds_set_int");
2059                         if (ds_set_int_p != NULL) {
2060                                 /* Yes - cal it with DS_LIBRARY_ID,
2061                                    DS_LIB_PRINT_SUFFIX_ONLY, and 2 as
2062                                    arguments.
2063
2064                                    We do *not* use DS_LIBRARY_ID or
2065                                    DS_LIB_PRINT_SUFFIX_ONLY by name, so that
2066                                    we don't require that Ethereal be built
2067                                    with versions of UCD SNMP that include
2068                                    that value; instead, we use their values
2069                                    in UCD SNMP 4.1.1, which are 0 and 4,
2070                                    respectively. */
2071                                 (*ds_set_int_p)(0, 4, 2);
2072                         }
2073                 }
2074                 dlclose(libsnmp_handle);
2075         }
2076 #else /* linux */
2077         snmp_set_suffix_only(2);
2078 #endif /* linux */
2079 #endif /* HAVE_UCD_SNMP_SNMP_H */
2080 #endif /* defined(HAVE_UCD_SNMP_SNMP_H) || defined(HAVE_SNMP_SNMP_H) */
2081         proto_snmp = proto_register_protocol("Simple Network Management Protocol", "snmp");
2082         proto_smux = proto_register_protocol("SNMP Multiplex Protocol", "smux");
2083         proto_register_field_array(proto_snmp, hf, array_length(hf));
2084         proto_register_subtree_array(ett, array_length(ett));
2085 }
2086
2087 void
2088 proto_reg_handoff_snmp(void)
2089 {
2090         old_dissector_add("udp.port", UDP_PORT_SNMP, dissect_snmp);
2091         old_dissector_add("udp.port", UDP_PORT_SNMP_TRAP, dissect_snmp);
2092         old_dissector_add("tcp.port", TCP_PORT_SMUX, dissect_smux);
2093         old_dissector_add("ethertype", ETHERTYPE_SNMP, dissect_snmp);
2094         old_dissector_add("ipx.socket", IPX_SOCKET_SNMP_AGENT, dissect_snmp);
2095         old_dissector_add("ipx.socket", IPX_SOCKET_SNMP_SINK, dissect_snmp);
2096 }