added decode support for 'poll inactive station' ipx message
[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.15 1999/12/05 02:32:38 guy Exp $
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@unicom.net>
9  * Copyright 1998 Didier Jorand
10  *
11  * 
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  * 
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  * 
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #if defined(HAVE_UCD_SNMP_SNMP_H)
32 # define WITH_SNMP_UCD 1
33 #elif defined(HAVE_SNMP_SNMP_H)
34 # define WITH_SNMP_CMU 1
35 #endif
36
37 #include <stdio.h>
38 #include <string.h>
39 #include <ctype.h>
40
41 #ifdef HAVE_SYS_TYPES_H
42 # include <sys/types.h>
43 #endif
44
45 #ifdef HAVE_NETINET_IN_H
46 # include <netinet/in.h>
47 #endif
48
49 #include <glib.h>
50 #include "packet.h"
51
52 #include "packet-snmp.h"
53
54 static int proto_snmp = -1;
55
56 static gint ett_snmp = -1;
57
58 #if defined(WITH_SNMP_CMU) || defined(WITH_SNMP_UCD)
59
60 #define in_addr_t u_int
61
62 #ifdef WITH_SNMP_UCD
63 /* should be defined only if supported in ucd-snmp */
64 #define OPAQUE_SPECIAL_TYPES 1
65 #include <ucd-snmp/asn1.h>
66 #include <ucd-snmp/snmp.h>
67 #include <ucd-snmp/snmp_api.h>
68 #include <ucd-snmp/snmp_impl.h>
69 #include <ucd-snmp/mib.h>
70
71 typedef long SNMP_INT;
72 typedef unsigned  long SNMP_UINT;
73 #define OID_FORMAT_STRING "%ld"
74 #define OID_FORMAT_STRING1 ".%ld"
75 #endif
76
77 #ifdef WITH_SNMP_CMU
78 #include <snmp/snmp.h>
79 #include <snmp/snmp_impl.h>
80
81 #ifndef MAX_NAME_LEN
82 #define MAX_NAME_LEN SNMP_MAX_LEN
83 #endif
84
85 #define SNMP_MSG_GET GET_REQ_MSG
86 #define SNMP_MSG_GETNEXT GETNEXT_REQ_MSG
87 #define SNMP_MSG_RESPONSE GET_RSP_MSG
88 #define SNMP_MSG_SET SET_REQ_MSG   
89 #define SNMP_MSG_TRAP TRP_REQ_MSG
90
91 #ifdef GETBULK_REQ_MSG
92 #define SNMP_MSG_GETBULK GETBULK_REQ_MSG
93 #else
94 #define SNMP_MSG_GETBULK SNMP_PDU_GETBULK
95 #endif
96
97 #ifdef INFORM_REQ_MSG
98 #define SNMP_MSG_INFORM INFORM_REQ_MSG
99 #else
100 #define SNMP_MSG_INFORM SNMP_PDU_INFORM
101 #endif
102
103 #ifdef TRP2_REQ_MSG
104 #define SNMP_MSG_TRAP2 TRP2_REQ_MSG
105 #else
106 #define SNMP_MSG_TRAP2 SNMP_PDU_V2TRAP
107 #endif
108
109 #ifdef REPORT_MSG
110 #define SNMP_MSG_REPORT REPORT_MSG
111 #else
112 #define SNMP_MSG_REPORT SNMP_PDU_REPORT
113 #endif
114
115 #ifndef SNMP_VERSION_2c
116 #define SNMP_VERSION_2c 1
117 #endif
118 #ifndef SNMP_VERSION_2u
119 #define SNMP_VERSION_2u 2
120 #endif
121 #ifndef SNMP_VERSION_3
122 #define SNMP_VERSION_3 3
123 #endif
124
125 #ifdef SNMP_TRAP_AUTHENTICATIONFAILURE
126 #define SNMP_TRAP_AUTHFAIL SNMP_TRAP_AUTHENTICATIONFAILURE
127 #endif
128
129 #ifndef COMMUNITY_MAX_LEN
130 #define COMMUNITY_MAX_LEN 256
131 #endif
132
133 #ifndef ASN_INTEGER
134 #define ASN_INTEGER SMI_INTEGER
135 #endif
136 #ifndef ASN_OCTET_STR
137 #define ASN_OCTET_STR SMI_STRING
138 #endif
139 #ifndef ASN_OBJECT_ID
140 #define ASN_OBJECT_ID SMI_OBJID
141 #endif
142 #ifndef ASN_NULL
143 #define ASN_NULL SMI_NULLOBJ
144 #endif
145
146 #ifndef ASN_IPADDRESS
147         #ifdef IPADDRESS
148         #define ASN_IPADDRESS IPADDRESS
149         #else
150         #define ASN_IPADDRESS SMI_IPADDRESS
151         #endif
152 #endif
153
154 #ifndef ASN_COUNTER
155         #ifdef COUNTER
156         #define ASN_COUNTER COUNTER
157         #else
158         #define ASN_COUNTER SMI_COUNTER32
159         #endif
160 #endif
161
162 #ifndef ASN_GAUGE
163         #ifdef GAUGE
164         #define ASN_GAUGE GAUGE
165         #else
166         #define ASN_GAUGE SMI_GAUGE32
167         #endif
168 #endif
169
170 #ifndef ASN_TIMETICKS
171         #ifdef TIMETICKS
172         #define ASN_TIMETICKS TIMETICKS
173         #else
174         #define ASN_TIMETICKS SMI_TIMETICKS
175         #endif
176 #endif
177
178 #ifndef ASN_OPAQUE
179         #ifdef OPAQUE
180         #define ASN_OPAQUE OPAQUE
181         #else
182         #define ASN_OPAQUE SMI_OPAQUE
183         #endif
184 #endif
185
186 #ifndef ASN_COUNTER64
187         #ifdef COUNTER64
188         #define ASN_COUNTER64 COUNTER64
189         #else
190         #define ASN_COUNTER64 SMI_COUNTER64
191         #endif
192 #endif
193
194 #ifndef ASN_UINTEGER
195 /* historic: should not be used! */
196 #define ASN_UINTEGER (ASN_APPLICATION | 7)
197 #endif
198 #ifndef ASN_NSAP
199 /* historic: should not be used! */
200 #define ASN_NSAP (ASN_APPLICATION | 5)
201 #endif
202 #ifndef SNMP_NOSUCHOBJECT
203 #define SNMP_NOSUCHOBJECT SMI_NOSUCHOBJECT
204 #endif
205 #ifndef SNMP_NOSUCHINSTANCE
206 #define SNMP_NOSUCHINSTANCE SMI_NOSUCHINSTANCE
207 #endif
208 #ifndef SNMP_ENDOFMIBVIEW
209 #define SNMP_ENDOFMIBVIEW SMI_ENDOFMIBVIEW
210 #endif
211
212 typedef int SNMP_INT;
213 typedef unsigned int SNMP_UINT;
214 #define OID_FORMAT_STRING "%d"
215 #define OID_FORMAT_STRING1 ".%d"
216
217 #endif /* WITH_SNMP_CMU */
218
219 static const value_string versions[] = {
220         { SNMP_VERSION_1,       "VERSION 1" },
221         { SNMP_VERSION_2c,      "VERSION 2C" },
222         { SNMP_VERSION_2u,      "VERSION 2U" },
223         { SNMP_VERSION_3,       "VERSION 3" },
224         { 0,                    NULL },
225 };
226
227 static const value_string pdu_types[] = {
228         { SNMP_MSG_GET,         "GET" },
229         { SNMP_MSG_GETNEXT,     "GET-NEXT" },
230         { SNMP_MSG_SET,         "SET" },
231         { SNMP_MSG_RESPONSE,    "RESPONSE" },
232         { SNMP_MSG_TRAP,        "TRAP-V1" },
233         { SNMP_MSG_GETBULK,     "GETBULK" },
234         { SNMP_MSG_INFORM,      "INFORM" },
235         { SNMP_MSG_TRAP2,       "TRAP-V2" },
236         { SNMP_MSG_REPORT,      "REPORT" },
237         { 0,                    NULL }
238 };
239
240 static const value_string error_statuses[] = {
241         { SNMP_ERR_NOERROR,             "NO ERROR" },
242         { SNMP_ERR_TOOBIG,              "ERROR: TOOBIG" },
243         { SNMP_ERR_NOSUCHNAME,          "ERROR: NO SUCH NAME" },
244         { SNMP_ERR_BADVALUE,            "ERROR: BAD VALUE" },
245         { SNMP_ERR_READONLY,            "ERROR: READ ONLY" },
246         { SNMP_ERR_GENERR,              "ERROR: GENERIC ERROR" },
247         { SNMP_ERR_NOACCESS,            "ERROR: NO ACCESS" },
248         { SNMP_ERR_WRONGTYPE,           "ERROR: WRONG TYPE" },
249         { SNMP_ERR_WRONGLENGTH,         "ERROR: WRONG LENGTH" },
250         { SNMP_ERR_WRONGENCODING,       "ERROR: WRONG ENCODING" },
251         { SNMP_ERR_WRONGVALUE,          "ERROR: WRONG VALUE" },
252         { SNMP_ERR_NOCREATION,          "ERROR: NO CREATION" },
253         { SNMP_ERR_INCONSISTENTVALUE,   "ERROR: INCONSISTENT VALUE" },
254         { SNMP_ERR_RESOURCEUNAVAILABLE, "ERROR: RESOURCE UNAVAILABLE" },
255         { SNMP_ERR_COMMITFAILED,        "ERROR: COMMIT FAILED" },
256         { SNMP_ERR_UNDOFAILED,          "ERROR: UNDO FAILED" },
257         { SNMP_ERR_AUTHORIZATIONERROR,  "ERROR: AUTHORIZATION ERROR" },
258         { SNMP_ERR_NOTWRITABLE,         "ERROR: NOT WRITABLE" },
259         { SNMP_ERR_INCONSISTENTNAME,    "ERROR: INCONSISTENT NAME" },
260         { 0,                            NULL }
261 };
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 static void
275 dissect_snmp_error(const u_char *pd, int offset, frame_data *fd,
276                    proto_tree *tree, const char *message)
277 {
278         if (check_col(fd, COL_INFO))
279                 col_add_str(fd, COL_INFO, message);
280
281         dissect_data(pd, offset, fd, tree);
282 }
283
284 void
285 dissect_snmp_pdu(const u_char *pd, int offset, frame_data *fd,
286     proto_tree *tree, char *proto_name, int proto, gint ett)
287 {
288         int length=fd->pkt_len-offset;
289         u_char *data, *tmp_data;
290
291         int all_length, header_length;
292         u_char type, pdu_type;
293         int pdu_type_length;
294         SNMP_INT request_id, error_status, error_index;
295         int request_id_length, error_status_length, error_index_length;
296         
297         SNMP_INT version;
298         u_char community[COMMUNITY_MAX_LEN];
299         int community_length = COMMUNITY_MAX_LEN;
300
301         oid enterprise[MAX_NAME_LEN];
302         int enterprise_length;
303         SNMP_INT trap_type, specific_type;
304         SNMP_UINT timestamp;
305         
306         int tmp_length;
307         oid vb_name[MAX_NAME_LEN];
308         int vb_name_length;
309         int vb_index;
310         u_char vb_type;
311         char vb_string[MAX_NAME_LEN*6]; /* TBC */
312         char vb_string2[2048]; /* TBC */
313         char tmp_string[12];
314         SNMP_INT vb_integer_value;
315         SNMP_UINT vb_unsigned_value;
316 #ifdef WITH_SNMP_UCD    
317         struct counter64 vb_counter64_value;
318 #endif  
319         oid vb_oid_value[MAX_NAME_LEN];
320         int vb_oid_value_length;
321         unsigned char vb_string_value[128];
322         int vb_string_value_length;
323 #ifdef WITH_SNMP_UCD    
324         float vb_float_value;
325         double vb_double_value;
326 #endif
327         
328         int i;
329
330         char *pdu_type_string;
331
332         proto_tree *snmp_tree=NULL;
333         proto_item *item=NULL;
334
335         if (check_col(fd, COL_PROTOCOL))
336                 col_add_str(fd, COL_PROTOCOL, proto_name);
337
338         /* NOTE: we have to parse the message piece by piece, since the
339          * capture length may be less than the message length: a 'global'
340          * parsing is likely to fail.
341          */
342         
343 #ifdef WITH_SNMP_UCD    
344         /* parse the SNMP header */
345         if(NULL == asn_parse_header((u_char*)&pd[offset], &length, &type)) {
346                 dissect_snmp_error(pd, offset, fd, tree,
347                         "Couldn't parse SNMP header");
348                 return;
349         }
350         
351         if (type != (ASN_SEQUENCE | ASN_CONSTRUCTOR)) {
352                 dissect_snmp_error(pd, offset, fd, tree, "Not an SNMP PDU");
353                 return;
354         }
355         
356         /* authenticates message */
357         length=fd->pkt_len-offset;
358         header_length=length;
359         data = snmp_comstr_parse((u_char*)&pd[offset], &length, community, &community_length, (long*)&version);
360         if(NULL == data) {
361                 dissect_snmp_error(pd, offset, fd, tree,
362                     "Couldn't parse authentication");
363                 return;
364         }
365 #endif /* WITH_SNMP_UCD */
366 #ifdef WITH_SNMP_CMU
367         /* initialize length variables */
368         /* length=fd->pkt_len-offset; */
369         header_length=length;   
370
371         /* parse the SNMP header */
372         data = asn_parse_header((u_char*)&pd[offset], &length, &type);
373         if(NULL == data) {
374                 dissect_snmp_error(pd, offset, fd, tree,
375                         "Couldn't parse SNMP header");
376                 return;
377         }
378         
379         if (type != (ASN_SEQUENCE | ASN_CONSTRUCTOR)) {
380                 dissect_snmp_error(pd, offset, fd, tree, "Not an SNMP PDU");
381                 return;
382         }
383
384         data = asn_parse_int(data, &length, &type, &version, sizeof(SNMP_INT));
385         if(NULL == data) {
386                 dissect_snmp_error(pd, offset, fd, tree,
387                     "Couldn't parse SNMP version number");
388                 return;
389         }
390         data = asn_parse_string(data, &length, &type, community, &community_length);
391         if(NULL == data) {
392                 dissect_snmp_error(pd, offset, fd, tree,
393                     "Couldn't parse SNMP community");
394                 return;
395         }
396         community[community_length] = '\0';     
397 #endif /* WITH_SNMP_CMU */
398
399         header_length-=length;
400         /* printf("Community is %s, version is %d (header length is %d)\n", community, version, header_length); */
401         if(version != SNMP_VERSION_1) {
402                 dissect_snmp_error(pd, offset, fd, tree,
403                     "Non-version-1 SNMP PDU");
404                 return;
405         }
406
407         pdu_type_length=length;
408         data = asn_parse_header(data, &length, &pdu_type);
409         if (data == NULL) {
410                 dissect_snmp_error(pd, offset, fd, tree,
411                     "Couldn't parse PDU type");
412                 return;
413         }
414         pdu_type_length-=length;
415         /* printf("pdu type is %#x (length is %d)\n", type, pdu_type_length); */
416         
417         /* get the fields in the PDU preceeding the variable-bindings sequence */
418         if (pdu_type != SNMP_MSG_TRAP) {
419
420         /* request id */
421                 request_id_length=length;
422                 data = asn_parse_int(data, &length, &type, &request_id, sizeof(request_id));
423                 if (data == NULL) {
424                         dissect_snmp_error(pd, offset, fd, tree,
425                                 "Couldn't parse request ID");
426                         return;
427                 }
428                 request_id_length-=length;
429                 /* printf("request id is %#lx (length is %d)\n", request_id, request_id_length); */
430                 
431         /* error status (getbulk non-repeaters) */
432                 error_status_length=length;
433                 data = asn_parse_int(data, &length, &type, &error_status, sizeof(error_status));
434                 if (data == NULL) {
435                         dissect_snmp_error(pd, offset, fd, tree,
436                                 "Couldn't parse error status");
437                         return;
438                 }
439                 error_status_length-=length;
440
441         /* error index (getbulk max-repetitions) */
442                 error_index_length=length;
443                 data = asn_parse_int(data, &length, &type, &error_index, sizeof(error_index));
444                 if (data == NULL) {
445                         dissect_snmp_error(pd, offset, fd, tree,
446                                 "Couldn't parse error index");
447                         return;
448                 }
449                 error_index_length-=length;
450
451                 pdu_type_string = val_to_str(pdu_type, pdu_types,
452                     "Unknown PDU type %#x");
453                 if (check_col(fd, COL_INFO))
454                         col_add_str(fd, COL_INFO, pdu_type_string);
455                 if (tree) {
456                         /* all_length=header_length+pdu_type_length+request_id_length+error_status_length+error_index_length; */
457                         all_length=fd->pkt_len-offset;
458                         item = proto_tree_add_item(tree, proto, offset, all_length, NULL);
459                         snmp_tree = proto_item_add_subtree(item, ett);
460                         proto_tree_add_text(snmp_tree, offset, header_length, "Community: \"%s\", Version: %s", community, val_to_str(version, versions, "Unknown version %#x"));
461                         offset+=header_length;
462                         proto_tree_add_text(snmp_tree, offset, pdu_type_length, "%s", pdu_type_string);
463                         offset+=pdu_type_length;
464                         proto_tree_add_text(snmp_tree, offset, request_id_length, "Request Id.: %#x", (unsigned int)request_id);
465                         offset+=request_id_length;
466                         proto_tree_add_text(snmp_tree, offset, error_status_length, "Error Status: %s", val_to_str(error_status, error_statuses, "Unknown (%d)"));
467                         offset+=error_status_length;
468                         proto_tree_add_text(snmp_tree, offset, error_index_length, "Error Index: %d", (int)error_index);
469                         offset+=error_index_length;
470                 } else {
471                         offset+=header_length;
472                         offset+=pdu_type_length;
473                         offset+=request_id_length;
474                         offset+=error_status_length;
475                         offset+=error_index_length;             
476                 }
477                 
478         } else {
479                 /* an SNMPv1 trap PDU */
480                 pdu_type_string = val_to_str(pdu_type, pdu_types,
481                     "Unknown PDU type %#x");
482                 if (check_col(fd, COL_INFO))
483                         col_add_str(fd, COL_INFO, pdu_type_string);
484                 if(tree) {
485                         all_length=fd->pkt_len-offset;
486                         item = proto_tree_add_item(tree, proto, offset, all_length, NULL);
487                         snmp_tree = proto_item_add_subtree(item, ett);
488                         proto_tree_add_text(snmp_tree, offset, header_length, "Community: \"%s\", Version: %s", community, val_to_str(version, versions, "Unknown version %#x"));
489                         offset+=header_length;
490                         proto_tree_add_text(snmp_tree, offset, pdu_type_length, "Pdu type: %s", pdu_type_string);
491                         offset+=pdu_type_length;
492                 } else {
493                         offset+=header_length;
494                         offset+=pdu_type_length;
495                 }
496                 
497         /* enterprise */
498                 enterprise_length = MAX_NAME_LEN;
499                 tmp_length=length;
500                 data = asn_parse_objid(data, &length, &type, enterprise,  &enterprise_length);
501                 if (data == NULL) {
502                         dissect_snmp_error(pd, offset, fd, tree,
503                                 "Couldn't parse enterprise OID");
504                         return;
505                 }
506                 tmp_length-=length;
507
508                 sprintf(vb_string, OID_FORMAT_STRING, enterprise[0]);
509                 for(i=1; i<enterprise_length;i++) {
510                         sprintf(tmp_string, OID_FORMAT_STRING1, enterprise[i]);
511                         strcat(vb_string,tmp_string);
512                 }
513                 if(tree) {
514                         proto_tree_add_text(snmp_tree, offset, tmp_length, "Enterprise: %s", vb_string);
515                 }
516                 offset+=tmp_length;
517
518         /* agent address */
519                 vb_string_value_length = 4;
520                 tmp_length=length;
521                 data = asn_parse_string(data, &length, &type, vb_string_value, &vb_string_value_length);
522                 if (data == NULL) {
523                         dissect_snmp_error(pd, offset, fd, tree,
524                                 "Couldn't parse agent address");
525                         return;
526                 }
527                 tmp_length-=length;
528                 if(tree) {
529                         proto_tree_add_text(snmp_tree, offset, tmp_length, "Agent address: %d.%d.%d.%d",
530                                                          vb_string_value[0],vb_string_value[1],vb_string_value[2],vb_string_value[3]);
531                 }
532                 offset+=tmp_length;
533                 
534         /* generic trap */
535                 tmp_length=length;
536                 data = asn_parse_int(data, &length, &type, &trap_type, sizeof(trap_type));
537                 if (data == NULL) {
538                         dissect_snmp_error(pd, offset, fd, tree,
539                                 "Couldn't parse trap type");
540                         return;
541                 }
542                 tmp_length-=length;
543                 if(tree) {
544                         proto_tree_add_text(snmp_tree, offset, tmp_length, "Trap type: %s", val_to_str(trap_type, trap_types, "Unknown (%d)"));
545                 }               
546                 offset+=tmp_length;
547                 
548         /* specific trap */
549                 tmp_length=length;
550                 data = asn_parse_int(data, &length, &type, &specific_type, sizeof(specific_type));
551                 if (data == NULL) {
552                         dissect_snmp_error(pd, offset, fd, tree,
553                                 "Couldn't parse specific trap type");
554                         return;
555                 }
556                 tmp_length-=length;
557                 if(tree) {
558                         proto_tree_add_text(snmp_tree, offset, tmp_length, "Specific trap type: %ld (%#lx)", (long)specific_type, (long)specific_type);
559                 }               
560                 offset+=tmp_length;
561                 
562         /* timestamp  */
563                 tmp_length=length;
564                 data = asn_parse_unsigned_int(data, &length, &type, &timestamp, sizeof(timestamp));
565                 if (data == NULL) {
566                         dissect_snmp_error(pd, offset, fd, tree,
567                                 "Couldn't parse time stamp");
568                         return;
569                 }
570                 tmp_length-=length;
571                 if(tree) {
572                         proto_tree_add_text(snmp_tree, offset, tmp_length, "Timestamp: %lu", (unsigned long)timestamp);
573                 }               
574                 offset+=tmp_length;
575         }
576         
577         /* variable bindings */
578     /* get header for variable-bindings sequence */
579         tmp_length=length;
580         data = asn_parse_header(data, &length, &type);
581         if (data == NULL) {
582                 dissect_snmp_error(pd, offset, fd, tree,
583                         "Couldn't variable-bindings header");
584                 return;
585         }
586         tmp_length-=length;
587         if (type != (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR)) {
588                 dissect_snmp_error(pd, offset, fd, tree,
589                         "Bad type for variable-bindings header");
590                 return;
591         }
592         offset+=tmp_length;
593         /* printf("VB header: offset is %d; length is %d.\n", offset, tmp_length); */
594
595         /* loop on variable bindings */
596         vb_index=0;
597         while(length>0) {
598                 vb_index++;
599                 /* printf("VB index is %d (offset=%d; length=%d).\n", vb_index, offset, length); */
600                 /* parse type */
601                 tmp_length=length;
602                 tmp_data=data;
603                 data = asn_parse_header(data, &tmp_length, &type);
604                 if (data == NULL) {
605                         dissect_snmp_error(pd, offset, fd, tree,
606                                 "Couldn't parse variable-binding header");
607                         return;
608                 }
609                 if (type != (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR)) {
610                         dissect_snmp_error(pd, offset, fd, tree,
611                                 "Bad type for variable-binding header");
612                         return;
613                 }
614                 tmp_length=(int)(data-tmp_data);
615                 length-=tmp_length;
616                 offset+=tmp_length;
617                 
618                 /* parse object identifier */
619                 vb_name_length=MAX_NAME_LEN;
620                 tmp_length=length;
621                 data = asn_parse_objid(data, &length, &type, vb_name, &vb_name_length);
622                 if (data == NULL) {
623                         dissect_snmp_error(pd, offset, fd, tree,
624                                 "No object-identifier for variable-binding");
625                         return;
626                 }
627
628                 if (type != (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID)) {
629                         dissect_snmp_error(pd, offset, fd, tree,
630                                 "Bad type for variable-binding");
631                         return;
632                 }
633                 tmp_length-=length;
634
635                 if(tree) {
636                         sprintf(vb_string, OID_FORMAT_STRING, vb_name[0]);
637                         for(i=1; i<vb_name_length;i++) {
638                                 sprintf(tmp_string, OID_FORMAT_STRING1, vb_name[i]);
639                                 strcat(vb_string,tmp_string);
640                         }
641                         
642                         sprint_objid(vb_string2, vb_name, vb_name_length);
643                         
644                         proto_tree_add_text(snmp_tree, offset, tmp_length, "Object identifier %d: %s (%s)", vb_index, vb_string, vb_string2);
645                 }
646                 offset+=tmp_length;
647                                 
648                 /* parse the type of the object */
649                 tmp_length=length;
650                 if (NULL == asn_parse_header(data, &tmp_length, &vb_type)){
651                         dissect_snmp_error(pd, offset, fd, tree,
652                                 "Bad type for variable-binding value");
653                         return;
654                 }
655
656                 /* parse the value */
657                 switch(vb_type) {
658                 case ASN_NULL:
659                         tmp_length=length;
660                         data=asn_parse_null(data, &length, &type);
661                         tmp_length-=length;
662                         if (data == NULL){
663                                 dissect_snmp_error(pd, offset, fd, tree,
664                                         "Couldn't parse null value");
665                                 return;
666                         }
667                         if(tree) {
668                                 proto_tree_add_text(snmp_tree, offset, tmp_length, "Value: NULL");
669                         }
670                         offset+=tmp_length;
671                         break;
672                         
673                 case ASN_INTEGER:
674                         tmp_length=length;
675                         data=asn_parse_int(data,  &length, &type, &vb_integer_value, sizeof(vb_integer_value));
676                         tmp_length-=length;
677                         if (data == NULL){
678                                 dissect_snmp_error(pd, offset, fd, tree,
679                                         "Couldn't parse integer value");
680                                 return;
681                         }
682                         if(tree) {
683                                 proto_tree_add_text(snmp_tree, offset, tmp_length, "Value: <i> %ld (%#lx)", (long)vb_integer_value, (long)vb_integer_value);
684                         }
685                         offset+=tmp_length;
686                         break;
687
688                 case ASN_COUNTER:
689                 case ASN_GAUGE:
690                 case ASN_TIMETICKS:
691                 case ASN_UINTEGER:
692                         tmp_length=length;
693                         data=asn_parse_unsigned_int(data, &length, &type, &vb_unsigned_value, sizeof(vb_unsigned_value));
694                         tmp_length-=length;
695                         if (data == NULL){
696                                 dissect_snmp_error(pd, offset, fd, tree,
697                                         "Couldn't parse unsigned value");
698                                 return;
699                         }
700                         if(tree) {
701                                 proto_tree_add_text(snmp_tree, offset, tmp_length, "Value: <u> %lu (%#lx)", (unsigned long)vb_unsigned_value, (unsigned long)vb_unsigned_value);
702                         }
703                         offset+=tmp_length;
704                         break;
705
706 #ifdef WITH_SNMP_UCD
707                         /* only ucd support 64bits types */
708                 case ASN_COUNTER64:
709 #ifdef OPAQUE_SPECIAL_TYPES
710                 case ASN_OPAQUE_COUNTER64:
711                 case ASN_OPAQUE_U64:
712 #endif /* OPAQUE_SPECIAL_TYPES */
713                         tmp_length=length;
714                         data=asn_parse_unsigned_int64(data, &length, &type, &vb_counter64_value, sizeof(vb_counter64_value));
715                         tmp_length-=length;
716                         if (data == NULL){
717                                 dissect_snmp_error(pd, offset, fd, tree,
718                                         "Couldn't parse counter64 value");
719                                 return;
720                         }
721                         if(tree) {
722                                 proto_tree_add_text(snmp_tree, offset, tmp_length, "Value: <i64> %lu:%lu (%#lx:%lx)",
723                                                                  vb_counter64_value.high,
724                                                                  vb_counter64_value.low,
725                                                                  vb_counter64_value.high,
726                                                                  vb_counter64_value.low);
727                         }
728                         offset+=tmp_length;
729                         break;
730 #endif /* WITH_SNMP_UCD */
731                         
732                 case ASN_OBJECT_ID:
733                         vb_oid_value_length = MAX_NAME_LEN;
734                         tmp_length=length;
735                         data=asn_parse_objid(data, &length, &type, vb_oid_value, &vb_oid_value_length);
736                         tmp_length-=length;
737                         if (data == NULL){
738                                 dissect_snmp_error(pd, offset, fd, tree,
739                                         "Couldn't parse OID value");
740                                 return;
741                         }
742                         if(tree) {
743                                 sprintf(vb_string, OID_FORMAT_STRING, vb_oid_value[0]);
744                                 for(i=1; i<vb_oid_value_length;i++) {
745                                         sprintf(tmp_string, OID_FORMAT_STRING1, vb_oid_value[i]);
746                                         strcat(vb_string,tmp_string);
747                                 }
748                                 proto_tree_add_text(snmp_tree, offset, tmp_length, "Value: <oid> %s", vb_string);
749                         }                       
750                         offset+=tmp_length;
751                         break;
752                 case ASN_OCTET_STR:
753                 case ASN_IPADDRESS:
754                 case ASN_OPAQUE:
755                 case ASN_NSAP:
756                         vb_string_value_length=128;
757                         tmp_length=length;
758                         data=asn_parse_string(data, &length, &type, vb_string_value, &vb_string_value_length);
759                         tmp_length-=length;
760                         if (data == NULL){
761                                 dissect_snmp_error(pd, offset, fd, tree,
762                                         "Couldn't parse octet string value");
763                                 return;
764                         }
765                         if(tree) {
766                                 vb_string_value[vb_string_value_length]=0;
767                                 /* if some characters are not printable, display the string as
768                                  * bytes */
769                                 for(i=0; i<vb_string_value_length; i++) {
770                                         if(!(isprint(vb_string_value[i]) || isspace(vb_string_value[i]))) break;
771                                 }
772                                 if(i<vb_string_value_length) {
773                                         sprintf(vb_string, "%03d", (int)vb_string_value[0]);
774                                         for(i=1; i<vb_string_value_length; i++) {
775                                                 sprintf(tmp_string, ".%03d", (int)vb_string_value[i]);
776                                                 strcat(vb_string,tmp_string);
777                                         }
778                                         proto_tree_add_text(snmp_tree, offset, tmp_length, "Value: <str> %s", vb_string);
779                                 }else {
780                                         proto_tree_add_text(snmp_tree, offset, tmp_length, "Value: <str> %s", vb_string_value);
781                                 }
782                         }
783                         offset+=tmp_length;
784                         break;                  
785
786 #ifdef OPAQUE_SPECIAL_TYPES
787                 case ASN_OPAQUE_I64:
788                         tmp_length=length;
789                         data=asn_parse_signed_int64(data, &length, &type, &vb_counter64_value, sizeof(vb_counter64_value));
790                         tmp_length-=length;
791                         if (data == NULL){
792                                 dissect_snmp_error(pd, offset, fd, tree,
793                                         "Couldn't parse integer64 value");
794                                 return;
795                         }
796                         if(tree) {
797                                 proto_tree_add_text(snmp_tree, offset, tmp_length, "Value: <i64> %ld:%lu (%#lx:%lx)",
798                                                                  vb_counter64_value.high,
799                                                                  vb_counter64_value.low,
800                                                                  vb_counter64_value.high,
801                                                                  vb_counter64_value.low);
802                         }
803                         offset+=tmp_length;
804                         break;
805                         break;
806
807                 case ASN_OPAQUE_FLOAT:
808                         tmp_length=length;
809                         data=asn_parse_float(data, &length, &type,&vb_float_value, sizeof(vb_float_value));
810                         tmp_length-=length;
811                         if (data == NULL){
812                                 dissect_snmp_error(pd, offset, fd, tree,
813                                         "Couldn't parse float value");
814                                 return;
815                         }
816                         if(tree) {
817                                 proto_tree_add_text(snmp_tree, offset, tmp_length, "Value: <f> %f", (double)vb_float_value);
818                         }
819                         offset+=tmp_length;
820                         break;
821                         
822                 case ASN_OPAQUE_DOUBLE:
823                         tmp_length=length;
824                         data=asn_parse_double(data, &length, &type,&vb_double_value, sizeof(vb_double_value));
825                         tmp_length-=length;
826                         if (data == NULL){
827                                 dissect_snmp_error(pd, offset, fd, tree,
828                                         "Couldn't parse double value");
829                                 return;
830                         }
831                         if(tree) {
832                                 proto_tree_add_text(snmp_tree, offset, tmp_length, "Value: <d> %f", vb_double_value);
833                         }
834                         offset+=tmp_length;
835                         break;
836 #endif /* OPAQUE_SPECIAL_TYPES */
837                         
838                 case SNMP_NOSUCHOBJECT:
839                         if(tree) {
840                                 proto_tree_add_text(snmp_tree, offset, tmp_length, "Value: <err> no such object");
841                         }                       
842                         break;
843                 case SNMP_NOSUCHINSTANCE:
844                         if(tree) {
845                                 proto_tree_add_text(snmp_tree, offset, tmp_length, "Value: <err> no such instance");
846                         }                       
847                         break;
848                 case SNMP_ENDOFMIBVIEW:
849                         if(tree) {
850                                 proto_tree_add_text(snmp_tree, offset, tmp_length, "Value: <err> end of mib view");
851                         }                       
852                         break;
853                         
854                 default:
855                         dissect_snmp_error(pd, offset, fd, tree,
856                                 "Unsupported type for variable-binding value");
857                         return;
858                 }                       
859         }
860 }
861
862 #else /* WITH_SNMP: CMU or UCD */
863
864 /* Stub dissector, for use when there's no SNMP library. */
865 void
866 dissect_snmp_pdu(const u_char *pd, int offset, frame_data *fd,
867     proto_tree *tree, char *proto_name, int proto, gint ett)
868 {
869         proto_item *item;
870         proto_tree *snmp_tree;
871
872         if (check_col(fd, COL_PROTOCOL))
873                 col_add_str(fd, COL_PROTOCOL, proto_name);
874         if (check_col(fd, COL_INFO))
875                 col_add_str(fd, COL_INFO, "SNMP request or reply");
876         if (tree) {
877                 item = proto_tree_add_item(tree, proto, offset, END_OF_FRAME,
878                     NULL);
879                 snmp_tree = proto_item_add_subtree(item, ett);
880                 dissect_data(pd, offset, fd, snmp_tree);
881         }
882 }
883
884 #endif /* WITH_SNMP: CMU or UCD */
885
886 void
887 dissect_snmp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) 
888 {
889         dissect_snmp_pdu(pd, offset, fd, tree, "SNMP", proto_snmp, ett_snmp);
890 }
891
892 /*
893  * Allow the stuff that builds "register.c" to scan "packet-snmp.c" even
894  * if we're not enabling SNMP decoding (for "canned" source distributions
895  * such as BSD ports, it may be a pain to arrange that it be scanned only
896  * if SNMP is being used), by having "proto_register_snmp()" always be
897  * defined, but not do anything if SNMP decoding isn't enabled.
898  */
899 void
900 proto_register_snmp(void)
901 {
902 #if defined(WITH_SNMP_CMU) || defined(WITH_SNMP_UCD)
903 /*        static hf_register_info hf[] = {
904                 { &variable,
905                 { "Name",           "snmp.abbreviation", TYPE, VALS_POINTER }},
906         };*/
907 #endif /* WITH_SNMP: CMU or UCD */
908         static gint *ett[] = {
909                 &ett_snmp,
910         };
911
912 #if defined(WITH_SNMP_CMU) || defined(WITH_SNMP_UCD)
913         init_mib();
914 #endif /* WITH_SNMP: CMU or UCD */
915         proto_snmp = proto_register_protocol("Simple Network Management Protocol", "snmp");
916 #if defined(WITH_SNMP_CMU) || defined(WITH_SNMP_UCD)
917  /*       proto_register_field_array(proto_snmp, hf, array_length(hf));*/
918 #endif /* WITH_SNMP: CMU or UCD */
919         proto_register_subtree_array(ett, array_length(ett));
920 }