From Tyson Key:
[obnox/wireshark/wip.git] / epan / oids.c
index 17b65983c745f413e44a1f8cb73fb56787701a27..2f3f94aea5a5b1664358cf149258851931cc569a 100644 (file)
@@ -1,7 +1,7 @@
 /* oids.c
  * Object IDentifier Support
  *
- * (c) 2007, Luis E. Garcia Ontanon <luis.ontanon@gmail.com>
+ * (c) 2007, Luis E. Garcia Ontanon <luis@ontanon.org>
  *
  * $Id$
  *
@@ -44,6 +44,8 @@
 
 #ifdef HAVE_LIBSMI
 #include <smi.h>
+
+static gboolean oids_init_done = FALSE;
 #endif
 
 #define D(level,args) do if (debuglevel >= level) { printf args; printf("\n"); fflush(stdout); } while(0)
 
 static int debuglevel = 0;
 
+/*
+ * From SNMPv2-SMI and X.690
+ *
+ * Counter32  ::= [APPLICATION 1] IMPLICIT INTEGER (0..4294967295)
+ * Gauge32    ::= [APPLICATION 2] IMPLICIT INTEGER (0..4294967295)
+ * Unsigned32 ::= [APPLICATION 2] IMPLICIT INTEGER (0..4294967295) (alias of Gauge32)
+ * TimeTicks  ::= [APPLICATION 3] IMPLICIT INTEGER (0..4294967295)
+ *
+ * If the BER encoding should not have the top bit set as to not become a negative number
+ * the BER encoding may take 5 octets to encode.
+ */
+
 static const oid_value_type_t integer_type =    { FT_INT32,  BASE_DEC,  BER_CLASS_UNI, BER_UNI_TAG_INTEGER,     1,   4, OID_KEY_TYPE_INTEGER, 1};
 static const oid_value_type_t bytes_type =      { FT_BYTES,  BASE_NONE, BER_CLASS_UNI, BER_UNI_TAG_OCTETSTRING, 0,  -1, OID_KEY_TYPE_BYTES,   0};
 static const oid_value_type_t oid_type =        { FT_OID,    BASE_NONE, BER_CLASS_UNI, BER_UNI_TAG_OID,         1,  -1, OID_KEY_TYPE_OID,     0};
 static const oid_value_type_t ipv4_type =       { FT_IPv4,   BASE_NONE, BER_CLASS_APP, 0,                       4,   4, OID_KEY_TYPE_IPADDR,  4};
 static const oid_value_type_t counter32_type =  { FT_UINT64, BASE_DEC,  BER_CLASS_APP, 1,                       1,   5, OID_KEY_TYPE_INTEGER, 1};
-static const oid_value_type_t unsigned32_type = { FT_UINT32, BASE_DEC,  BER_CLASS_APP, 2,                       1,   4, OID_KEY_TYPE_INTEGER, 1};
-/*static const oid_value_type_t timeticks_type =  { FT_UINT32, BASE_DEC,  BER_CLASS_APP, 3,                       1,   4, OID_KEY_TYPE_INTEGER, 1};
- * TimeTicks ::= [APPLICATION 3] IMPLICIT INTEGER (0..4294967295)
- * If the BER encoding should not have the top bit set as to not become a negative number
- * the ber encoding may take 5 octets to encode.
- */
+static const oid_value_type_t unsigned32_type = { FT_UINT64, BASE_DEC,  BER_CLASS_APP, 2,                       1,   5, OID_KEY_TYPE_INTEGER, 1};
 static const oid_value_type_t timeticks_type =  { FT_UINT64, BASE_DEC,  BER_CLASS_APP, 3,                       1,   5, OID_KEY_TYPE_INTEGER, 1};
 static const oid_value_type_t opaque_type =     { FT_BYTES,  BASE_NONE, BER_CLASS_APP, 4,                       1,   4, OID_KEY_TYPE_BYTES,   0};
 static const oid_value_type_t nsap_type =       { FT_BYTES,  BASE_NONE, BER_CLASS_APP, 5,                       0,  -1, OID_KEY_TYPE_NSAP,    0};
@@ -70,7 +79,7 @@ static const oid_value_type_t counter64_type =  { FT_UINT64, BASE_DEC,  BER_CLAS
 static const oid_value_type_t ipv6_type =       { FT_IPv6,   BASE_NONE, BER_CLASS_UNI, BER_UNI_TAG_OCTETSTRING, 16, 16, OID_KEY_TYPE_BYTES,   16};
 static const oid_value_type_t float_type =      { FT_FLOAT,  BASE_DEC,  BER_CLASS_UNI, BER_UNI_TAG_OCTETSTRING, 4,   4, OID_KEY_TYPE_WRONG,   0};
 static const oid_value_type_t double_type =     { FT_DOUBLE, BASE_DEC,  BER_CLASS_UNI, BER_UNI_TAG_OCTETSTRING, 8,   8, OID_KEY_TYPE_WRONG,   0};
-static const oid_value_type_t ether_type =      { FT_ETHER,  BASE_NONE, BER_CLASS_UNI, BER_UNI_TAG_OCTETSTRING, 6,   6, OID_KEY_TYPE_BYTES,   6};
+static const oid_value_type_t ether_type =      { FT_ETHER,  BASE_NONE, BER_CLASS_UNI, BER_UNI_TAG_OCTETSTRING, 6,   6, OID_KEY_TYPE_ETHER,   6};
 static const oid_value_type_t string_type =     { FT_STRING, BASE_NONE, BER_CLASS_UNI, BER_UNI_TAG_OCTETSTRING, 0,  -1, OID_KEY_TYPE_STRING,  0};
 static const oid_value_type_t unknown_type =    { FT_BYTES,  BASE_NONE, BER_CLASS_ANY, BER_TAG_ANY,             0,  -1, OID_KEY_TYPE_WRONG,   0};
 
@@ -187,22 +196,22 @@ extern void oid_add_from_encoded(const char* name, const guint8 *oid, gint oid_l
 #ifdef HAVE_LIBSMI
 /* de-allocate storage mallocated by libsmi                            */
 /*                                                                     */
-/* XXX: On Windows we can use free() only if the Windows libsmi.dll    */
-/*      being used is linked with the same CRTL as wireshark.          */
-/*      As a temporary hack we'll assume on Windows that the Wireshark */
-/*      build of libsmi.dll was done with VC6/msvcrt.dll (as is        */
-/*      currently the case).  If wireshark is being built with         */
-/*      vc6 (which is also currently the case for the standard         */
-/*      wireshark release). we can use free().                         */
-/*      If vc6 is not being used here, then we'll just have to live    */
-/*      with a memory leak for now.                                    */
-/*      Note: A permanent fix would probably be for libsmi to include  */
-/*       its' existing smiFree function as part of the libsmi API so   */
-/*       wireshark can call it to free storage mallocated by libsmi.   */
+/* XXX: libsmi provides access to smiFree as of libsmi v 0.4.8.        */
+/*      On Windows: Wireshark 1.01 and later is built and distributed  */
+/*      with libsmi 0.4.8 (or newer).                                  */
+/*      On non-Windows systems, free() should be OK for libsmi         */
+/*       versions older than 0.4.8.                                    */
 
 static void smi_free(void *ptr) {
-#if !defined _WIN32 || (_MSC_VER == 1200)
-       free(ptr);
+
+#if (SMI_VERSION_MAJOR >= 0) && (SMI_VERSION_MINOR >= 4) && (SMI_VERSION_PATCHLEVEL >= 8)
+       smiFree(ptr);
+#else
+ #ifdef _WIN32
+ #error Invalid Windows libsmi version ?? !!
+ #endif
+#define xx_free free  /* hack so checkAPIs.pl doesn't complain */
+       xx_free(ptr);
 #endif
 }
 
@@ -221,10 +230,10 @@ static uat_t* smi_modules_uat = NULL;
 
 static GString* smi_errors;
 
-UAT_CSTRING_CB_DEF(smi_mod,name,smi_module_t)
+UAT_DIRECTORYNAME_CB_DEF(smi_mod,name,smi_module_t)
 
 static void smi_error_handler(char *path, int line, int severity, char *msg, char *tag) {
-               g_string_sprintfa(smi_errors,"%s:%d %d %s %s\n",
+               g_string_append_printf(smi_errors,"%s:%d %d %s %s\n",
                                                  path ? path : "-",
                                                  line, severity,
                                                  tag ? tag : "-",
@@ -232,7 +241,7 @@ static void smi_error_handler(char *path, int line, int severity, char *msg, cha
 }
 
 
-static void* smi_mod_copy_cb(void* dest, const void* orig, unsigned len _U_) {
+static void* smi_mod_copy_cb(void* dest, const void* orig, size_t len _U_) {
        const smi_module_t* m = orig;
        smi_module_t* d = dest;
 
@@ -243,7 +252,7 @@ static void* smi_mod_copy_cb(void* dest, const void* orig, unsigned len _U_) {
 
 static void smi_mod_free_cb(void* p) {
        smi_module_t* m = p;
-       if (m->name) g_free(m->name);
+       g_free(m->name);
 }
 
 
@@ -266,7 +275,7 @@ static char* alnumerize(const char* name) {
        return s;
 }
 
-const oid_value_type_t* get_typedata(SmiType* smiType) {
+static const oid_value_type_t* get_typedata(SmiType* smiType) {
        /*
         * There has to be a better way to know if a given
         * OCTETSTRING type is actually human readable text,
@@ -275,7 +284,7 @@ const oid_value_type_t* get_typedata(SmiType* smiType) {
         * SNMP Types and our FT_s
         */
        static const struct _type_mapping_t {
-               char* name;
+               const char* name;
                SmiBasetype base;
                const oid_value_type_t* type;
        } types[] =  {
@@ -459,7 +468,7 @@ static inline oid_kind_t smikind(SmiNode* sN, oid_key_t** key_p) {
                                kl = k;
                        }
 
-                       if (implied) {
+                       if (implied && kl) {
                                switch (kl->key_type) {
                                        case OID_KEY_TYPE_BYTES:  kl->key_type = OID_KEY_TYPE_IMPLIED_BYTES; break;
                                        case OID_KEY_TYPE_STRING: kl->key_type = OID_KEY_TYPE_IMPLIED_STRING; break;
@@ -486,7 +495,19 @@ static inline oid_kind_t smikind(SmiNode* sN, oid_key_t** key_p) {
                                                   || (ft == FT_INT8) || (ft == FT_INT16) || (ft == FT_INT24) || (ft == FT_INT32) \
                                                   || (ft == FT_UINT64) || (ft == FT_INT64) )
 
-void register_mibs(void) {
+static void unregister_mibs(void) {
+       /* TODO: Unregister "MIBs" proto and clean up field array and subtree array.
+        * Wireshark does not support that yet. :-( */
+
+       /* smiExit(); */
+}
+
+static void restart_needed_warning(void) {
+       if (oids_init_done)
+               report_failure("Wireshark needs to be restarted for these changes to take effect");
+}
+
+static void register_mibs(void) {
        SmiModule *smiModule;
        SmiNode *smiNode;
        guint i;
@@ -494,11 +515,11 @@ void register_mibs(void) {
        GArray* hfa = g_array_new(FALSE,TRUE,sizeof(hf_register_info));
        GArray* etta = g_array_new(FALSE,TRUE,sizeof(gint*));
        static uat_field_t smi_fields[] = {
-               UAT_FLD_CSTRING(smi_mod,name,"The module's name"),
+               UAT_FLD_CSTRING(smi_mod,name,"Module name","The module's name"),
                UAT_END_FIELDS
        };
        static uat_field_t smi_paths_fields[] = {
-               UAT_FLD_CSTRING(smi_mod,name,"The directory name"),
+               UAT_FLD_DIRECTORYNAME(smi_mod,name,"Directory path","The directory name"),
                UAT_END_FIELDS
        };
        char* smi_load_error = NULL;
@@ -515,6 +536,7 @@ void register_mibs(void) {
                                                          smi_mod_copy_cb,
                                                          NULL,
                                                          smi_mod_free_cb,
+                                                         restart_needed_warning,
                                                          smi_fields);
 
        smi_paths_uat = uat_new("SMI Paths",
@@ -528,11 +550,10 @@ void register_mibs(void) {
                                                          smi_mod_copy_cb,
                                                          NULL,
                                                          smi_mod_free_cb,
+                                                         restart_needed_warning,
                                                          smi_paths_fields);
 
 
-       smiInit(NULL);
-
        uat_load(smi_modules_uat, &smi_load_error);
 
        if (smi_load_error) {
@@ -547,20 +568,33 @@ void register_mibs(void) {
                return;
        }
 
-       path_str = oid_get_default_mib_path();
-       D(1,("SMI Path: '%s'",path_str));
-
-       smiSetPath(path_str);
+       if (!prefs.load_smi_modules) {
+               D(1,("OID resolution not enabled"));
+               return;
+       }
 
+       /* TODO: Remove this workaround when unregistration of "MIBs" proto is solved.
+        * Wireshark does not support that yet. :-( */
+       if (oids_init_done) {
+               D(1,("Exiting register_mibs() to avoid double registration of MIBs proto."));
+               return;
+       } else {
+               oids_init_done = TRUE;
+       }
 
+       smiInit(NULL);
 
        smi_errors = g_string_new("");
        smiSetErrorHandler(smi_error_handler);
 
+       path_str = oid_get_default_mib_path();
+       D(1,("SMI Path: '%s'",path_str));
+
+       smiSetPath(path_str);
+
        for(i=0;i<num_smi_modules;i++) {
                if (!smi_modules[i].name) continue;
 
-
                if (smiIsLoaded(smi_modules[i].name)) {
                        continue;
                } else {
@@ -573,8 +607,13 @@ void register_mibs(void) {
        }
 
        if (smi_errors->len) {
-               report_failure("The following errors were found while loading the MIBS:\n%s\n\n"
-                                          "The Current Path is: %s\n" , smi_errors->str , path_str);
+               if (!prefs.suppress_smi_errors) {
+                       report_failure("The following errors were found while loading the MIBS:\n%s\n\n"
+                                          "The Current Path is: %s\n\nYou can avoid this error message "
+                                          "by removing the missing MIB modules at Edit -> Preferences"
+                                          " -> Name Resolution -> SMI (MIB and PIB) modules or by "
+                                          "installing them.\n" , smi_errors->str , path_str);
+               }
                D(1,("Errors while loading:\n%s\n",smi_errors->str));
        }
 
@@ -587,6 +626,20 @@ void register_mibs(void) {
 
                D(3,("\tModule: %s", smiModule->name));
 
+               /* TODO: Check libsmi version at compile time and disable this
+                * workaround for libsmi versions where this problem is fixed.
+                * Currently there is no such version. :-(
+                */
+               if (smiModule->conformance == 1) {
+                       if (!prefs.suppress_smi_errors) {
+                               report_failure("Stopped processing module %s due to "
+                                       "error(s) to prevent potential crash in libsmi.\n"
+                                       "Module's conformance level: %d.\n"
+                                       "See details at: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=560325\n",
+                                        smiModule->name, smiModule->conformance);
+                       }
+                       continue;
+               }
                for (smiNode = smiGetFirstNode(smiModule, SMI_NODEKIND_ANY);
                         smiNode;
                         smiNode = smiGetNextNode(smiNode, SMI_NODEKIND_ANY)) {
@@ -616,13 +669,15 @@ void register_mibs(void) {
                                        typedata->display,
                                        NULL,
                                        0,
-#if !defined _WIN32 || (_MSC_VER == 1200)
                                        smiRenderOID(smiNode->oidlen, smiNode->oid, SMI_RENDER_ALL),
-#else
-                                       g_strdup (smiRenderOID(smiNode->oidlen, smiNode->oid, SMI_RENDER_ALL)),
-#endif
                                        HFILL }};
 
+                               /* Don't allow duplicate blurb/name */
+                               if (strcmp(hf.hfinfo.blurb, hf.hfinfo.name) == 0) {
+                                       smi_free((void *) hf.hfinfo.blurb);
+                                       hf.hfinfo.blurb = NULL;
+                               }
+
                                oid_data->value_hfid = -1;
 
                                if ( IS_ENUMABLE(hf.hfinfo.type) && (smiEnum = smiGetFirstNamedNumber(smiType))) {
@@ -635,7 +690,7 @@ void register_mibs(void) {
                                                }
                                        }
 
-                                       hf.hfinfo.strings = VALS(vals->data);
+                                       hf.hfinfo.strings = vals->data;
                                        g_array_free(vals,FALSE);
                                }
 #if 0 /* packet-snmp does not handle bits yet */
@@ -659,7 +714,7 @@ void register_mibs(void) {
                                        guint mask = 1 << (smiEnum->value.value.integer32 % 8);
                                        char* base = alnumerize(oid_data->name);
                                        char* ext = alnumerize(smiEnum->name);
-                                       hf_register_info hf2 = { &(bits->data[n].hfid), { NULL, NULL, FT_UINT8, BASE_HEX, NULL, mask, "", HFILL }};
+                                       hf_register_info hf2 = { &(bits->data[n].hfid), { NULL, NULL, FT_UINT8, BASE_HEX, NULL, mask, NULL, HFILL }};
 
                                        bits->data[n].hfid = -1;
                                        bits->data[n].offset = smiEnum->value.value.integer32 / 8;
@@ -684,7 +739,7 @@ void register_mibs(void) {
                                                key->display,
                                                NULL,
                                                0,
-                                               "",
+                                               NULL,
                                                HFILL }};
 
                                        D(5,("\t\t\tIndex: name=%s subids=%d key_type=%d",
@@ -703,9 +758,9 @@ void register_mibs(void) {
 
        proto_mibs = proto_register_protocol("MIBs", "MIBS", "mibs");
 
-       proto_register_field_array(proto_mibs, (hf_register_info*)hfa->data, hfa->len);
+       proto_register_field_array(proto_mibs, (hf_register_info*)(void*)hfa->data, hfa->len);
 
-       proto_register_subtree_array((gint**)etta->data, etta->len);
+       proto_register_subtree_array((gint**)(void*)etta->data, etta->len);
 
 
        g_array_free(etta,TRUE);
@@ -722,6 +777,14 @@ void oids_init(void) {
 #endif
 }
 
+void oids_cleanup(void) {
+#ifdef HAVE_LIBSMI
+       unregister_mibs();
+#else
+       D(1,("libsmi disabled oid resolution not enabled"));
+#endif
+}
+
 const char* oid_subid2string(guint32* subids, guint len) {
        char* s = ep_alloc0(((len)*11)+1);
        char* w = s;
@@ -730,7 +793,7 @@ const char* oid_subid2string(guint32* subids, guint len) {
                return "*** Empty OID ***";
 
        do {
-               w += sprintf(w,"%u.",*subids++);
+               w += g_snprintf(w,12,"%u.",*subids++);
        } while(--len);
 
        if (w!=s) *(w-1) = '\0'; else *(s) = '\0';
@@ -738,13 +801,13 @@ const char* oid_subid2string(guint32* subids, guint len) {
        return s;
 }
 
-guint check_num_oid(const char* str) {
+static guint check_num_oid(const char* str) {
        const char* r = str;
        char c = '\0';
        guint n = 0;
 
        D(8,("check_num_oid: '%s'",str));
-       if (*r == '.' || *r == '\0') return 0;
+       if (!r || *r == '.' || *r == '\0') return 0;
 
        do {
                D(9,("\tcheck_num_oid: '%c' %d",*r,n));
@@ -913,7 +976,6 @@ guint oid_subid2encoded(guint subids_len, guint32* subids, guint8** bytes_p) {
        guint bytelen = 0;
        guint i;
        guint32 subid;
-       guint8* bytes;
        guint8* b;
 
        if ( !subids || subids_len <= 0) {
@@ -940,7 +1002,7 @@ guint oid_subid2encoded(guint subids_len, guint32* subids, guint8** bytes_p) {
                        subid = subids[i];
        } while ( i++ < subids_len );
 
-       *bytes_p = b = bytes = ep_alloc(bytelen);
+       *bytes_p = b = ep_alloc(bytelen);
 
        subid = (subids[0] * 40) + subids[1];
        i = 2;
@@ -956,11 +1018,11 @@ guint oid_subid2encoded(guint subids_len, guint32* subids, guint8** bytes_p) {
 
                switch(len) {
                        default: *bytes_p=NULL; return 0;
-                       case 5: *(b++) = ((subid & 0xF0000000) << 28) | 0x80;
-                       case 4: *(b++) = ((subid & 0x0FE00000 ) >> 21)  | 0x80;
-                       case 3: *(b++) = ((subid & 0x001FC000 ) >> 14)  | 0x80;
-                       case 2: *(b++) = ((subid & 0x00003F10 ) >> 7)  | 0x80;
-                       case 1: *(b++) =  subid & 0x0000007F ; break;
+                       case 5: *(b++) = ((subid & 0xF0000000) >> 28) | 0x80;
+                       case 4: *(b++) = ((subid & 0x0FE00000) >> 21) | 0x80;
+                       case 3: *(b++) = ((subid & 0x001FC000) >> 14) | 0x80;
+                       case 2: *(b++) = ((subid & 0x00003F10) >> 7)  | 0x80;
+                       case 1: *(b++) =   subid & 0x0000007F ; break;
                }
 
                subid = subids[i];
@@ -995,14 +1057,6 @@ guint oid_string2encoded(const char *oid_str, guint8 **bytes) {
        return 0;
 }
 
-char* oid2str(oid_info_t* oid, guint32* subids, guint len, guint left) {
-       if (left == 0) {
-               return oid->name;
-       } else {
-               return ep_strdup_printf("%s.%s",oid->name,oid_subid2string(subids+(len-left),left));
-       }
-}
-
 const gchar *oid_resolved_from_string(const gchar *oid_str) {
        guint32 *subid_oid;
        guint subid_oid_length = oid_string2subid(oid_str, &subid_oid);
@@ -1068,19 +1122,28 @@ oid_get_default_mib_path(void) {
        guint i;
 
        path_str = g_string_new("");
+       
+       if (!prefs.load_smi_modules) {
+               D(1,("OID resolution not enabled"));
+               return path_str->str;
+       }
 #ifdef _WIN32
 #define PATH_SEPARATOR ";"
        path = get_datafile_path("snmp\\mibs");
-       g_string_sprintfa(path_str, "%s;", path);
+       g_string_append_printf(path_str, "%s;", path);
        g_free (path);
 
        path = get_persconffile_path("snmp\\mibs", FALSE, FALSE);
-       g_string_sprintfa(path_str, "%s", path);
+       g_string_append_printf(path_str, "%s", path);
        g_free (path);
 #else
 #define PATH_SEPARATOR ":"
        path = smiGetPath();
-       g_string_sprintfa(path_str, "%s", path);
+       g_string_append(path_str, "/usr/share/snmp/mibs");
+       if (strlen(path) > 0 ) {
+               g_string_append(path_str, PATH_SEPARATOR);
+       }
+       g_string_append_printf(path_str, "%s", path);
        free (path);
 #endif
 
@@ -1088,7 +1151,7 @@ oid_get_default_mib_path(void) {
                if (!( smi_paths[i].name && *smi_paths[i].name))
                        continue;
 
-               g_string_sprintfa(path_str,PATH_SEPARATOR "%s",smi_paths[i].name);
+               g_string_append_printf(path_str,PATH_SEPARATOR "%s",smi_paths[i].name);
        }
 
        path_ret = path_str->str;
@@ -1164,10 +1227,10 @@ void add_oid_debug_subtree(oid_info_t* oid_info, proto_tree *tree) {
  * Local Variables:
  * c-basic-offset: 8
  * tab-width: 8
- * indent-tabs-mode: tabs
+ * indent-tabs-mode: t
  * End:
  *
- * ex: set shiftwidth=8 tabstop=8 noexpandtab
+ * ex: set shiftwidth=8 tabstop=8 noexpandtab:
  * :indentSize=8:tabSize=8:noTabs=false:
  */