MSWSP: add two more Property Sets
[wireshark-wip.git] / epan / oids.c
blobb34f9593bb6a62c9b3bb6fa498a44a958977ed7f
1 /* oids.c
2 * Object IDentifier Support
4 * (c) 2007, Luis E. Garcia Ontanon <luis@ontanon.org>
6 * $Id$
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1998 Gerald Combs
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 #include "config.h"
29 #include <glib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <ctype.h>
34 #include <wsutil/report_err.h>
36 #include "emem.h"
37 #include "wmem/wmem.h"
38 #include "uat.h"
39 #include "prefs.h"
40 #include "proto.h"
41 #include "packet.h"
42 #include "filesystem.h"
43 #include "dissectors/packet-ber.h"
45 #ifdef HAVE_LIBSMI
46 #include <smi.h>
48 static gboolean oids_init_done = FALSE;
49 static gboolean load_smi_modules = FALSE;
50 static gboolean suppress_smi_errors = FALSE;
51 #endif
53 #define D(level,args) do if (debuglevel >= level) { printf args; printf("\n"); fflush(stdout); } while(0)
55 #include "oids.h"
57 static int debuglevel = 0;
60 * From SNMPv2-SMI and X.690
62 * Counter32 ::= [APPLICATION 1] IMPLICIT INTEGER (0..4294967295)
63 * Gauge32 ::= [APPLICATION 2] IMPLICIT INTEGER (0..4294967295)
64 * Unsigned32 ::= [APPLICATION 2] IMPLICIT INTEGER (0..4294967295) (alias of Gauge32)
65 * TimeTicks ::= [APPLICATION 3] IMPLICIT INTEGER (0..4294967295)
67 * If the BER encoding should not have the top bit set as to not become a negative number
68 * the BER encoding may take 5 octets to encode.
71 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};
72 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};
73 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};
74 static const oid_value_type_t ipv4_type = { FT_IPv4, BASE_NONE, BER_CLASS_APP, 0, 4, 4, OID_KEY_TYPE_IPADDR, 4};
75 static const oid_value_type_t counter32_type = { FT_UINT64, BASE_DEC, BER_CLASS_APP, 1, 1, 5, OID_KEY_TYPE_INTEGER, 1};
76 static const oid_value_type_t unsigned32_type = { FT_UINT64, BASE_DEC, BER_CLASS_APP, 2, 1, 5, OID_KEY_TYPE_INTEGER, 1};
77 static const oid_value_type_t timeticks_type = { FT_UINT64, BASE_DEC, BER_CLASS_APP, 3, 1, 5, OID_KEY_TYPE_INTEGER, 1};
78 #if 0
79 static const oid_value_type_t opaque_type = { FT_BYTES, BASE_NONE, BER_CLASS_APP, 4, 1, 4, OID_KEY_TYPE_BYTES, 0};
80 #endif
81 static const oid_value_type_t nsap_type = { FT_BYTES, BASE_NONE, BER_CLASS_APP, 5, 0, -1, OID_KEY_TYPE_NSAP, 0};
82 static const oid_value_type_t counter64_type = { FT_UINT64, BASE_DEC, BER_CLASS_APP, 6, 1, 8, OID_KEY_TYPE_INTEGER, 1};
83 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};
84 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};
85 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};
86 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};
87 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};
88 static const oid_value_type_t date_and_time_type = { FT_STRING, BASE_NONE, BER_CLASS_UNI, BER_UNI_TAG_OCTETSTRING, 8, 11, OID_KEY_TYPE_DATE_AND_TIME, 0};
89 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};
91 static oid_info_t oid_root = { 0, NULL, OID_KIND_UNKNOWN, NULL, &unknown_type, -2, NULL, NULL, NULL};
93 static void prepopulate_oids(void) {
94 if (!oid_root.children) {
95 char* debug_env = getenv("WIRESHARK_DEBUG_MIBS");
96 guint32 subid;
98 debuglevel = debug_env ? (int)strtoul(debug_env,NULL,10) : 0;
100 oid_root.children = wmem_tree_new(wmem_epan_scope());
103 * make sure we got strings at least in the three root-children oids
104 * that way oid_resolved() will always have a string to print
106 subid = 0; oid_add("itu-t",1,&subid);
107 subid = 1; oid_add("iso",1,&subid);
108 subid = 2; oid_add("joint-iso-itu-t",1,&subid);
114 static oid_info_t* add_oid(const char* name, oid_kind_t kind, const oid_value_type_t* type, oid_key_t* key, guint oid_len, guint32 *subids) {
115 guint i = 0;
116 oid_info_t* c = &oid_root;
118 prepopulate_oids();
119 oid_len--;
121 do {
122 oid_info_t* n = (oid_info_t *)wmem_tree_lookup32(c->children,subids[i]);
124 if(n) {
125 if (i == oid_len) {
126 if (n->name) {
127 if (!g_str_equal(n->name,name)) {
128 D(2,("Renaming Oid from: %s -> %s, this means the same oid is registered more than once",n->name,name));
130 wmem_free(wmem_epan_scope(), n->name);
133 n->name = wmem_strdup(wmem_epan_scope(), name);
135 if (! n->value_type) {
136 n->value_type = type;
139 return n;
141 } else {
142 n = wmem_new(wmem_epan_scope(), oid_info_t);
143 n->subid = subids[i];
144 n->kind = kind;
145 n->children = wmem_tree_new(wmem_epan_scope());
146 n->value_hfid = -2;
147 n->key = key;
148 n->parent = c;
149 n->bits = NULL;
151 wmem_tree_insert32(c->children,n->subid,n);
153 if (i == oid_len) {
154 n->name = wmem_strdup(wmem_epan_scope(), name);
155 n->value_type = type;
156 n->kind = kind;
157 return n;
158 } else {
159 n->name = NULL;
160 n->value_type = NULL;
161 n->kind = OID_KIND_UNKNOWN;
164 c = n;
165 } while(++i);
167 g_assert_not_reached();
168 return NULL;
171 void oid_add(const char* name, guint oid_len, guint32 *subids) {
172 g_assert(subids && *subids <= 2);
173 if (oid_len) {
174 D(3,("\tOid (from subids): %s %s ",name?name:"NULL", oid_subid2string(subids,oid_len)));
175 add_oid(name,OID_KIND_UNKNOWN,NULL,NULL,oid_len,subids);
176 } else {
177 D(1,("Failed to add Oid: %s (from subids)",name?name:"NULL"));
181 void oid_add_from_string(const char* name, const gchar *oid_str) {
182 guint32* subids;
183 guint oid_len = oid_string2subid(oid_str, &subids);
185 if (oid_len) {
186 D(3,("\tOid (from string): %s %s ",name?name:"NULL", oid_subid2string(subids,oid_len)));
187 add_oid(name,OID_KIND_UNKNOWN,NULL,NULL,oid_len,subids);
188 } else {
189 D(1,("Failed to add Oid: %s %s ",name?name:"NULL", oid_str?oid_str:NULL));
193 extern void oid_add_from_encoded(const char* name, const guint8 *oid, gint oid_len) {
194 guint32* subids;
195 guint subids_len = oid_encoded2subid(oid, oid_len, &subids);
197 if (subids_len) {
198 D(3,("\tOid (from encoded): %s %s ",name, oid_subid2string(subids,subids_len)));
199 add_oid(name,OID_KIND_UNKNOWN,NULL,NULL,subids_len,subids);
200 } else {
201 D(1,("Failed to add Oid: %s [%d]%s ",name?name:"NULL", oid_len,bytestring_to_str(oid, oid_len, ':')));
205 #ifdef HAVE_LIBSMI
206 /* de-allocate storage mallocated by libsmi */
207 /* */
208 /* XXX: libsmi provides access to smiFree as of libsmi v 0.4.8. */
209 /* On Windows: Wireshark 1.01 and later is built and distributed */
210 /* with libsmi 0.4.8 (or newer). */
211 /* On non-Windows systems, free() should be OK for libsmi */
212 /* versions older than 0.4.8. */
214 static void smi_free(void *ptr) {
216 #if (SMI_VERSION_MAJOR >= 0) && (SMI_VERSION_MINOR >= 4) && (SMI_VERSION_PATCHLEVEL >= 8)
217 smiFree(ptr);
218 #else
219 #ifdef _WIN32
220 #error Invalid Windows libsmi version ?? !!
221 #endif
222 #define xx_free free /* hack so checkAPIs.pl doesn't complain */
223 xx_free(ptr);
224 #endif
228 typedef struct smi_module_t {
229 char* name;
230 } smi_module_t;
232 static smi_module_t* smi_paths = NULL;
233 static guint num_smi_paths = 0;
234 static uat_t* smi_paths_uat = NULL;
236 static smi_module_t* smi_modules = NULL;
237 static guint num_smi_modules = 0;
238 static uat_t* smi_modules_uat = NULL;
240 static GString* smi_errors;
242 UAT_DIRECTORYNAME_CB_DEF(smi_mod,name,smi_module_t)
244 static void smi_error_handler(char *path, int line, int severity, char *msg, char *tag) {
245 g_string_append_printf(smi_errors,"%s:%d %d %s %s\n",
246 path ? path : "-",
247 line, severity,
248 tag ? tag : "-",
249 msg ? msg : "");
253 static void* smi_mod_copy_cb(void* dest, const void* orig, size_t len _U_) {
254 const smi_module_t* m = (const smi_module_t*)orig;
255 smi_module_t* d = (smi_module_t*)dest;
257 d->name = g_strdup(m->name);
259 return d;
262 static void smi_mod_free_cb(void* p) {
263 smi_module_t* m = (smi_module_t*)p;
264 g_free(m->name);
268 static char* alnumerize(const char* name) {
269 char* s = g_strdup(name);
270 char* r = s;
271 char* w = r;
272 char c;
274 for (;(c = *r); r++) {
275 if (isalnum(c) || c == '_' || c == '-' || c == '.') {
276 *(w++) = c;
277 } else if (c == ':' && r[1] == ':') {
278 *(w++) = '.';
282 *w = '\0';
284 return s;
287 static const oid_value_type_t* get_typedata(SmiType* smiType) {
289 * There has to be a better way to know if a given
290 * OCTETSTRING type is actually human readable text,
291 * an address of some type or some moe specific FT_
292 * Until that is found, this is the mappping between
293 * SNMP Types and our FT_s
295 static const struct _type_mapping_t {
296 const char* name;
297 SmiBasetype base;
298 const oid_value_type_t* type;
299 } types[] = {
300 {"IpAddress", SMI_BASETYPE_UNKNOWN, &ipv4_type},
301 {"InetAddressIPv4",SMI_BASETYPE_UNKNOWN,&ipv4_type},
302 {"InetAddressIPv6",SMI_BASETYPE_UNKNOWN,&ipv6_type},
303 {"NetworkAddress",SMI_BASETYPE_UNKNOWN,&ipv4_type},
304 {"MacAddress",SMI_BASETYPE_UNKNOWN,&ether_type},
305 {"TimeTicks",SMI_BASETYPE_UNKNOWN,&timeticks_type},
306 {"Ipv6Address",SMI_BASETYPE_UNKNOWN,&ipv6_type},
307 {"TimeStamp",SMI_BASETYPE_UNKNOWN,&timeticks_type},
308 {"DisplayString",SMI_BASETYPE_UNKNOWN,&string_type},
309 {"SnmpAdminString",SMI_BASETYPE_UNKNOWN,&string_type},
310 {"DateAndTime",SMI_BASETYPE_UNKNOWN,&date_and_time_type},
311 {"Counter",SMI_BASETYPE_UNKNOWN,&counter32_type},
312 {"Counter32",SMI_BASETYPE_UNKNOWN,&counter32_type},
313 {"Unsigned32",SMI_BASETYPE_UNKNOWN,&unsigned32_type},
314 {"Gauge",SMI_BASETYPE_UNKNOWN,&unsigned32_type},
315 {"Gauge32",SMI_BASETYPE_UNKNOWN,&unsigned32_type},
316 {"NsapAddress",SMI_BASETYPE_UNKNOWN,&nsap_type},
317 {"i32",SMI_BASETYPE_INTEGER32,&integer_type},
318 {"octets",SMI_BASETYPE_OCTETSTRING,&bytes_type},
319 {"oid",SMI_BASETYPE_OBJECTIDENTIFIER,&oid_type},
320 {"u32",SMI_BASETYPE_UNSIGNED32,&unsigned32_type},
321 {"u64",SMI_BASETYPE_UNSIGNED64,&counter64_type},
322 {"f32",SMI_BASETYPE_FLOAT32,&float_type},
323 {"f64",SMI_BASETYPE_FLOAT64,&double_type},
324 {"f128",SMI_BASETYPE_FLOAT128,&bytes_type},
325 {"enum",SMI_BASETYPE_ENUM,&integer_type},
326 {"bits",SMI_BASETYPE_BITS,&bytes_type},
327 {"unk",SMI_BASETYPE_UNKNOWN,&unknown_type},
328 {NULL,SMI_BASETYPE_UNKNOWN,NULL} /* SMI_BASETYPE_UNKNOWN = 0 */
330 const struct _type_mapping_t* t;
331 SmiType* sT = smiType;
333 if (!smiType) return NULL;
335 do {
336 for (t = types; t->type ; t++ ) {
337 char* name = smiRenderType(sT, SMI_RENDER_NAME);
338 if (name && t->name && g_str_equal(name, t->name )) {
339 smi_free(name);
340 return t->type;
342 if (name) {
343 smi_free (name);
346 } while(( sT = smiGetParentType(sT) ));
348 for (t = types; t->type ; t++ ) {
349 if(smiType->basetype == t->base) {
350 return t->type;
354 return &unknown_type;
357 static guint get_non_implicit_size(SmiType* sT) {
358 SmiRange *sR;
359 guint size = 0xffffffff;
361 switch (sT->basetype) {
362 case SMI_BASETYPE_OCTETSTRING:
363 case SMI_BASETYPE_OBJECTIDENTIFIER:
364 break;
365 default:
366 return 0;
369 for ( ; sT; sT = smiGetParentType(sT) ) {
370 for (sR = smiGetFirstRange(sT); sR ; sR = smiGetNextRange(sR)) {
371 if (size == 0xffffffff) {
372 if (sR->minValue.value.unsigned32 == sR->maxValue.value.unsigned32) {
373 size = (guint32)sR->minValue.value.unsigned32;
374 } else {
375 return 0;
377 } else {
378 if (sR->minValue.value.unsigned32 != size || sR->maxValue.value.unsigned32 != size) {
379 return 0;
385 return size == 0xffffffff ? 0 : size;
389 static inline oid_kind_t smikind(SmiNode* sN, oid_key_t** key_p) {
390 *key_p = NULL;
392 switch(sN->nodekind) {
393 case SMI_NODEKIND_ROW: {
394 SmiElement* sE;
395 oid_key_t* kl = NULL;
396 const oid_value_type_t* typedata = NULL;
397 gboolean implied;
399 switch (sN->indexkind) {
400 case SMI_INDEX_INDEX:
401 break;
402 case SMI_INDEX_AUGMENT:
403 case SMI_INDEX_REORDER:
404 case SMI_INDEX_SPARSE:
405 case SMI_INDEX_EXPAND:
406 sN = smiGetRelatedNode(sN);
407 break;
408 case SMI_INDEX_UNKNOWN:
409 return OID_KIND_UNKNOWN;
412 implied = sN->implied;
414 for (sE = smiGetFirstElement(sN); sE; sE = smiGetNextElement(sE)) {
415 SmiNode* elNode = smiGetElementNode(sE) ;
416 SmiType* elType = smiGetNodeType(elNode);
417 oid_key_t* k;
418 guint non_implicit_size = 0;
419 char *oid1, *oid2;
421 if (elType) {
422 non_implicit_size = get_non_implicit_size(elType);
425 typedata = get_typedata(elType);
427 k = g_new(oid_key_t,1);
429 oid1 = smiRenderOID(sN->oidlen, sN->oid, SMI_RENDER_QUALIFIED);
430 oid2 = smiRenderOID(elNode->oidlen, elNode->oid, SMI_RENDER_NAME);
431 k->name = g_strdup_printf("%s.%s", oid1, oid2);
432 smi_free (oid1);
433 smi_free (oid2);
435 k->hfid = -2;
436 k->ft_type = typedata ? typedata->ft_type : FT_BYTES;
437 k->display = typedata ? typedata->display : BASE_NONE;
438 k->next = NULL;
441 if (typedata) {
442 k->key_type = typedata->keytype;
443 k->num_subids = typedata->keysize;
444 } else {
445 if (elType) {
446 switch (elType->basetype) {
447 case SMI_BASETYPE_BITS:
448 case SMI_BASETYPE_OCTETSTRING: {
449 k->key_type = OID_KEY_TYPE_BYTES;
450 k->num_subids = non_implicit_size;
451 break;
453 case SMI_BASETYPE_ENUM:
454 case SMI_BASETYPE_OBJECTIDENTIFIER:
455 case SMI_BASETYPE_INTEGER32:
456 case SMI_BASETYPE_UNSIGNED32:
457 case SMI_BASETYPE_INTEGER64:
458 case SMI_BASETYPE_UNSIGNED64:
459 k->key_type = OID_KEY_TYPE_INTEGER;
460 k->num_subids = 1;
461 break;
462 default:
463 k->key_type = OID_KEY_TYPE_WRONG;
464 k->num_subids = 0;
465 break;
467 } else {
468 k->key_type = OID_KEY_TYPE_WRONG;
469 k->num_subids = 0;
470 break;
474 if (!*key_p) *key_p = k;
475 if (kl) kl->next = k;
477 kl = k;
480 if (implied && kl) {
481 switch (kl->key_type) {
482 case OID_KEY_TYPE_BYTES: kl->key_type = OID_KEY_TYPE_IMPLIED_BYTES; break;
483 case OID_KEY_TYPE_STRING: kl->key_type = OID_KEY_TYPE_IMPLIED_STRING; break;
484 case OID_KEY_TYPE_OID: kl->key_type = OID_KEY_TYPE_IMPLIED_OID; break;
485 default: break;
489 return OID_KIND_ROW;
491 case SMI_NODEKIND_NODE: return OID_KIND_NODE;
492 case SMI_NODEKIND_SCALAR: return OID_KIND_SCALAR;
493 case SMI_NODEKIND_TABLE: return OID_KIND_TABLE;
494 case SMI_NODEKIND_COLUMN: return OID_KIND_COLUMN;
495 case SMI_NODEKIND_NOTIFICATION: return OID_KIND_NOTIFICATION;
496 case SMI_NODEKIND_GROUP: return OID_KIND_GROUP;
497 case SMI_NODEKIND_COMPLIANCE: return OID_KIND_COMPLIANCE;
498 case SMI_NODEKIND_CAPABILITIES: return OID_KIND_CAPABILITIES;
499 default: return OID_KIND_UNKNOWN;
503 #define IS_ENUMABLE(ft) ( (ft == FT_UINT8) || (ft == FT_UINT16) || (ft == FT_UINT24) || (ft == FT_UINT32) \
504 || (ft == FT_INT8) || (ft == FT_INT16) || (ft == FT_INT24) || (ft == FT_INT32) \
505 || (ft == FT_UINT64) || (ft == FT_INT64) )
507 static void unregister_mibs(void) {
508 /* TODO: Unregister "MIBs" proto and clean up field array and subtree array.
509 * Wireshark does not support that yet. :-( */
511 /* smiExit(); */
514 static void restart_needed_warning(void) {
515 if (oids_init_done)
516 report_failure("Wireshark needs to be restarted for these changes to take effect");
519 static void register_mibs(void) {
520 SmiModule *smiModule;
521 SmiNode *smiNode;
522 guint i;
523 int proto_mibs = -1;
524 wmem_array_t* hfa;
525 GArray* etta;
526 gchar* path_str;
528 if (!load_smi_modules) {
529 D(1,("OID resolution not enabled"));
530 return;
533 /* TODO: Remove this workaround when unregistration of "MIBs" proto is solved.
534 * Wireshark does not support that yet. :-( */
535 if (oids_init_done) {
536 D(1,("Exiting register_mibs() to avoid double registration of MIBs proto."));
537 return;
538 } else {
539 oids_init_done = TRUE;
542 hfa = wmem_array_new(wmem_epan_scope(), sizeof(hf_register_info));
543 etta = g_array_new(FALSE,TRUE,sizeof(gint*));
545 smiInit(NULL);
547 smi_errors = g_string_new("");
548 smiSetErrorHandler(smi_error_handler);
550 path_str = oid_get_default_mib_path();
551 D(1,("SMI Path: '%s'",path_str));
553 smiSetPath(path_str);
555 for(i=0;i<num_smi_modules;i++) {
556 if (!smi_modules[i].name) continue;
558 if (smiIsLoaded(smi_modules[i].name)) {
559 continue;
560 } else {
561 char* mod_name = smiLoadModule(smi_modules[i].name);
562 if (mod_name)
563 D(2,("Loaded: '%s'[%d] as %s",smi_modules[i].name,i,mod_name ));
564 else
565 D(1,("Failed to load: '%s'[%d]",smi_modules[i].name,i));
569 if (smi_errors->len) {
570 if (!suppress_smi_errors) {
571 report_failure("The following errors were found while loading the MIBS:\n%s\n\n"
572 "The Current Path is: %s\n\nYou can avoid this error message "
573 "by removing the missing MIB modules at Edit -> Preferences"
574 " -> Name Resolution -> SMI (MIB and PIB) modules or by "
575 "installing them.\n" , smi_errors->str , path_str);
577 D(1,("Errors while loading:\n%s\n",smi_errors->str));
580 g_free(path_str);
581 g_string_free(smi_errors,TRUE);
583 for (smiModule = smiGetFirstModule();
584 smiModule;
585 smiModule = smiGetNextModule(smiModule)) {
587 D(3,("\tModule: %s", smiModule->name));
589 /* TODO: Check libsmi version at compile time and disable this
590 * workaround for libsmi versions where this problem is fixed.
591 * Currently there is no such version. :-(
593 if (smiModule->conformance == 1) {
594 if (!suppress_smi_errors) {
595 report_failure("Stopped processing module %s due to "
596 "error(s) to prevent potential crash in libsmi.\n"
597 "Module's conformance level: %d.\n"
598 "See details at: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=560325\n",
599 smiModule->name, smiModule->conformance);
601 continue;
603 for (smiNode = smiGetFirstNode(smiModule, SMI_NODEKIND_ANY);
604 smiNode;
605 smiNode = smiGetNextNode(smiNode, SMI_NODEKIND_ANY)) {
607 SmiType* smiType = smiGetNodeType(smiNode);
608 const oid_value_type_t* typedata = get_typedata(smiType);
609 oid_key_t* key;
610 oid_kind_t kind = smikind(smiNode,&key);
611 char *oid = smiRenderOID(smiNode->oidlen, smiNode->oid, SMI_RENDER_QUALIFIED);
612 oid_info_t* oid_data = add_oid(oid,
613 kind,
614 typedata,
615 key,
616 smiNode->oidlen,
617 smiNode->oid);
618 smi_free (oid);
620 D(4,("\t\tNode: kind=%d oid=%s name=%s ",
621 oid_data->kind, oid_subid2string(smiNode->oid, smiNode->oidlen), oid_data->name ));
623 if ( typedata && oid_data->value_hfid == -2 ) {
624 SmiNamedNumber* smiEnum;
625 hf_register_info hf = { &(oid_data->value_hfid), {
626 oid_data->name,
627 alnumerize(oid_data->name),
628 typedata->ft_type,
629 typedata->display,
630 NULL,
632 smiRenderOID(smiNode->oidlen, smiNode->oid, SMI_RENDER_ALL),
633 HFILL }};
635 /* Don't allow duplicate blurb/name */
636 if (strcmp(hf.hfinfo.blurb, hf.hfinfo.name) == 0) {
637 smi_free((void *) hf.hfinfo.blurb);
638 hf.hfinfo.blurb = NULL;
641 oid_data->value_hfid = -1;
643 if ( IS_ENUMABLE(hf.hfinfo.type) && (smiEnum = smiGetFirstNamedNumber(smiType))) {
644 GArray* vals = g_array_new(TRUE,TRUE,sizeof(value_string));
646 for(;smiEnum; smiEnum = smiGetNextNamedNumber(smiEnum)) {
647 if (smiEnum->name) {
648 value_string val = {(guint32)smiEnum->value.value.integer32,g_strdup(smiEnum->name)};
649 g_array_append_val(vals,val);
653 hf.hfinfo.strings = vals->data;
654 g_array_free(vals,FALSE);
656 #if 0 /* packet-snmp does not handle bits yet */
657 } else if (smiType->basetype == SMI_BASETYPE_BITS && ( smiEnum = smiGetFirstNamedNumber(smiType) )) {
658 guint n = 0;
659 oid_bits_info_t* bits = g_malloc(sizeof(oid_bits_info_t));
660 gint* ettp = &(bits->ett);
662 bits->num = 0;
663 bits->ett = -1;
665 g_array_append_val(etta,ettp);
667 for(;smiEnum; smiEnum = smiGetNextNamedNumber(smiEnum), bits->num++);
669 bits->data = g_malloc(sizeof(struct _oid_bit_t)*bits->num);
671 for(smiEnum = smiGetFirstNamedNumber(smiType),n=0;
672 smiEnum;
673 smiEnum = smiGetNextNamedNumber(smiEnum),n++) {
674 guint mask = 1 << (smiEnum->value.value.integer32 % 8);
675 char* base = alnumerize(oid_data->name);
676 char* ext = alnumerize(smiEnum->name);
677 hf_register_info hf2 = { &(bits->data[n].hfid), { NULL, NULL, FT_UINT8, BASE_HEX, NULL, mask, NULL, HFILL }};
679 bits->data[n].hfid = -1;
680 bits->data[n].offset = smiEnum->value.value.integer32 / 8;
682 hf2.hfinfo.name = g_strdup_printf("%s:%s",oid_data->name,smiEnum->name);
683 hf2.hfinfo.abbrev = g_strdup_printf("%s.%s",base,ext);
685 g_free(base);
686 g_free(ext);
687 g_array_append_val(hfa,hf2);
689 #endif /* packet-snmp does not use this yet */
690 wmem_array_append_one(hfa,hf);
693 if ((key = oid_data->key)) {
694 for(; key; key = key->next) {
695 hf_register_info hf = { &(key->hfid), {
696 key->name,
697 alnumerize(key->name),
698 key->ft_type,
699 key->display,
700 NULL,
702 NULL,
703 HFILL }};
705 D(5,("\t\t\tIndex: name=%s subids=%d key_type=%d",
706 key->name, key->num_subids, key->key_type ));
708 if (key->hfid == -2) {
709 wmem_array_append_one(hfa,hf);
710 key->hfid = -1;
711 } else {
712 g_free((void*)hf.hfinfo.abbrev);
719 proto_mibs = proto_register_protocol("MIBs", "MIBS", "mibs");
721 proto_register_field_array(proto_mibs, (hf_register_info*)wmem_array_get_raw(hfa), wmem_array_get_count(hfa));
723 proto_register_subtree_array((gint**)(void*)etta->data, etta->len);
725 g_array_free(etta,TRUE);
727 #endif
729 void oid_pref_init(module_t *nameres)
731 #ifdef HAVE_LIBSMI
732 static uat_field_t smi_fields[] = {
733 UAT_FLD_CSTRING(smi_mod,name,"Module name","The module's name"),
734 UAT_END_FIELDS
736 static uat_field_t smi_paths_fields[] = {
737 UAT_FLD_DIRECTORYNAME(smi_mod,name,"Directory path","The directory name"),
738 UAT_END_FIELDS
741 prefs_register_bool_preference(nameres, "load_smi_modules",
742 "Enable OID resolution",
743 "You must restart Wireshark for this change to take effect",
744 &load_smi_modules);
746 prefs_register_bool_preference(nameres, "suppress_smi_errors",
747 "Suppress SMI errors",
748 "Some errors can be ignored. If unsure, set to false.",
749 &suppress_smi_errors);
751 smi_paths_uat = uat_new("SMI Paths",
752 sizeof(smi_module_t),
753 "smi_paths",
754 FALSE,
755 (void**)&smi_paths,
756 &num_smi_paths,
757 /* affects dissection of packets (as the MIBs and PIBs affect the
758 interpretation of e.g. SNMP variable bindings), but not set of
759 named fields
761 XXX - if named fields are generated from the MIBs and PIBs
762 for particular variable bindings, this *does* affect the set
763 of named fields! */
764 UAT_AFFECTS_DISSECTION,
765 "ChSNMPSMIPaths",
766 smi_mod_copy_cb,
767 NULL,
768 smi_mod_free_cb,
769 restart_needed_warning,
770 smi_paths_fields);
772 prefs_register_uat_preference(nameres,
773 "smi_paths",
774 "SMI (MIB and PIB) paths",
775 "Search paths for SMI (MIB and PIB) modules. You must\n"
776 "restart Wireshark for these changes to take effect.",
777 smi_paths_uat);
779 smi_modules_uat = uat_new("SMI Modules",
780 sizeof(smi_module_t),
781 "smi_modules",
782 FALSE,
783 (void**)&smi_modules,
784 &num_smi_modules,
785 /* affects dissection of packets (as the MIBs and PIBs affect the
786 interpretation of e.g. SNMP variable bindings), but not set of
787 named fields
789 XXX - if named fields are generated from the MIBs and PIBs
790 for particular variable bindings, would this affect the set
791 of named fields? */
792 UAT_AFFECTS_DISSECTION,
793 "ChSNMPSMIModules",
794 smi_mod_copy_cb,
795 NULL,
796 smi_mod_free_cb,
797 restart_needed_warning,
798 smi_fields);
800 prefs_register_uat_preference(nameres,
801 "smi_modules",
802 "SMI (MIB and PIB) modules",
803 "List of enabled SMI (MIB and PIB) modules. You must\n"
804 "restart Wireshark for these changes to take effect.",
805 smi_modules_uat);
807 #else
808 prefs_register_static_text_preference(nameres, "load_smi_modules_static",
809 "Enable OID resolution: N/A",
810 "Support for OID resolution was not compiled into this version of Wireshark");
812 prefs_register_static_text_preference(nameres, "suppress_smi_errors_static",
813 "Suppress SMI errors: N/A",
814 "Support for OID resolution was not compiled into this version of Wireshark");
816 prefs_register_static_text_preference(nameres, "smi_module_path",
817 "SMI (MIB and PIB) modules and paths: N/A",
818 "Support for OID resolution was not compiled into this version of Wireshark");
819 #endif
822 void oids_init(void) {
823 prepopulate_oids();
824 #ifdef HAVE_LIBSMI
825 register_mibs();
826 #else
827 D(1,("libsmi disabled oid resolution not enabled"));
828 #endif
831 void oids_cleanup(void) {
832 #ifdef HAVE_LIBSMI
833 unregister_mibs();
834 #else
835 D(1,("libsmi disabled oid resolution not enabled"));
836 #endif
839 const char* oid_subid2string(guint32* subids, guint len) {
840 return rel_oid_subid2string(subids, len, TRUE);
842 const char* rel_oid_subid2string(guint32* subids, guint len, gboolean is_absolute) {
843 char *s, *w;
845 if(!subids || len == 0)
846 return "*** Empty OID ***";
848 s = (char *)ep_alloc0(((len)*11)+2);
849 w = s;
851 if (!is_absolute)
852 *w++ = '.';
854 do {
855 w += g_snprintf(w,12,"%u.",*subids++);
856 } while(--len);
858 if (w!=s) *(w-1) = '\0'; else *(s) = '\0';
860 return s;
863 static guint check_num_oid(const char* str) {
864 const char* r = str;
865 char c = '.';
866 guint n = 0;
868 D(8,("check_num_oid: '%s'",str));
869 if (!r) return 0;
871 do {
872 D(9,("\tcheck_num_oid: '%c' %d",*r,n));
873 switch(*r) {
874 case '.': case '\0':
875 n++;
876 if (c == '.') return 0;
877 break;
878 case '1' : case '2' : case '3' : case '4' : case '5' :
879 case '6' : case '7' : case '8' : case '9' : case '0' :
880 continue;
881 default:
882 return 0;
884 } while((c = *r++));
886 return n;
889 guint oid_string2subid(const char* str, guint32** subids_p) {
890 const char* r = str;
891 guint32* subids;
892 guint32* subids_overflow;
893 guint n = check_num_oid(str);
895 * we cannot handle sub-ids greater than 32bytes
896 * keep a pilot subid of 64 bytes to check the limit
898 guint64 subid = 0;
900 D(6,("oid_string2subid: str='%s'",str));
902 if (!n) {
903 *subids_p = NULL;
904 return 0;
907 *subids_p = subids = (guint32 *)ep_alloc0(sizeof(guint32)*n);
908 subids_overflow = subids + n;
909 do switch(*r) {
910 case '.':
911 subid = 0;
912 subids++;
913 continue;
914 case '1' : case '2' : case '3' : case '4' : case '5' :
915 case '6' : case '7' : case '8' : case '9' : case '0' :
916 subid *= 10;
917 subid += *r - '0';
919 if( subids >= subids_overflow || subid > 0xffffffff) {
920 *subids_p=NULL;
921 return 0;
924 *(subids) *= 10;
925 *(subids) += *r - '0';
926 continue;
927 case '\0':
928 break;
929 default:
930 return 0;
931 } while(*r++);
933 return n;
937 guint oid_encoded2subid(const guint8 *oid_bytes, gint oid_len, guint32** subids_p) {
938 return oid_encoded2subid_sub(oid_bytes, oid_len, subids_p, TRUE);
940 guint oid_encoded2subid_sub(const guint8 *oid_bytes, gint oid_len, guint32** subids_p,
941 gboolean is_first) {
942 gint i;
943 guint n = is_first ? 1 : 0;
944 guint32* subids;
945 guint32* subid_overflow;
947 * we cannot handle sub-ids greater than 32bytes
948 * have the subid in 64 bytes to be able to check the limit
950 guint64 subid = 0;
952 for (i=0; i<oid_len; i++) { if (! (oid_bytes[i] & 0x80 )) n++; }
954 *subids_p = subids = (guint32 *)ep_alloc(sizeof(guint32)*n);
955 subid_overflow = subids+n;
957 /* If n is 0 or 1 (depending on how it was initialized) then we found
958 * no bytes in the OID with first bit cleared, so initialize our one
959 * byte (if any) to zero and return. This *seems* to be the right thing
960 * to do in this situation, and at the very least it avoids
961 * uninitialized memory errors that would otherwise occur. */
962 if (is_first && n == 1) {
963 *subids = 0;
964 return n;
966 else if (!is_first && n == 0) {
967 return n;
970 for (i=0; i<oid_len; i++){
971 guint8 byte = oid_bytes[i];
973 subid <<= 7;
974 subid |= byte & 0x7F;
976 if (byte & 0x80) {
977 continue;
980 if (is_first) {
981 guint32 subid0 = 0;
983 if (subid >= 40) { subid0++; subid-=40; }
984 if (subid >= 40) { subid0++; subid-=40; }
986 *subids++ = subid0;
988 is_first = FALSE;
991 if( subids >= subid_overflow || subid > 0xffffffff) {
992 *subids_p=NULL;
993 return 0;
996 *subids++ = (guint32)subid;
997 subid = 0;
1000 g_assert(subids == subid_overflow);
1002 return n;
1005 oid_info_t* oid_get(guint len, guint32* subids, guint* matched, guint* left) {
1006 oid_info_t* curr_oid = &oid_root;
1007 guint i;
1009 if(!(subids && *subids <= 2)) {
1010 *matched = 0;
1011 *left = len;
1012 return curr_oid;
1015 for( i=0; i < len; i++) {
1016 oid_info_t* next_oid = (oid_info_t *)wmem_tree_lookup32(curr_oid->children,subids[i]);
1017 if (next_oid) {
1018 curr_oid = next_oid;
1019 } else {
1020 goto done;
1023 done:
1024 *matched = i;
1025 *left = len - i;
1026 return curr_oid;
1030 oid_info_t* oid_get_from_encoded(const guint8 *bytes, gint byteslen, guint32** subids_p, guint* matched_p, guint* left_p) {
1031 guint subids_len = oid_encoded2subid(bytes, byteslen, subids_p);
1032 return oid_get(subids_len, *subids_p, matched_p, left_p);
1035 oid_info_t* oid_get_from_string(const gchar *oid_str, guint32** subids_p, guint* matched, guint* left) {
1036 guint subids_len = oid_string2subid(oid_str, subids_p);
1037 return oid_get(subids_len, *subids_p, matched, left);
1040 const gchar *oid_resolved_from_encoded(const guint8 *oid, gint oid_len) {
1041 guint32 *subid_oid;
1042 guint subid_oid_length = oid_encoded2subid(oid, oid_len, &subid_oid);
1044 return oid_resolved(subid_oid_length, subid_oid);
1047 const gchar *rel_oid_resolved_from_encoded(const guint8 *oid, gint oid_len) {
1048 guint32 *subid_oid;
1049 guint subid_oid_length = oid_encoded2subid_sub(oid, oid_len, &subid_oid, FALSE);
1051 return rel_oid_subid2string(subid_oid, subid_oid_length, FALSE);
1055 guint oid_subid2encoded(guint subids_len, guint32* subids, guint8** bytes_p) {
1056 guint bytelen = 0;
1057 guint i;
1058 guint32 subid;
1059 guint8* b;
1061 if ( !subids || subids_len <= 1) {
1062 *bytes_p = NULL;
1063 return 0;
1066 for (subid=subids[0] * 40, i = 1; i<subids_len; i++, subid=0) {
1067 subid += subids[i];
1068 if (subid <= 0x0000007F) {
1069 bytelen += 1;
1070 } else if (subid <= 0x00003FFF ) {
1071 bytelen += 2;
1072 } else if (subid <= 0x001FFFFF ) {
1073 bytelen += 3;
1074 } else if (subid <= 0x0FFFFFFF ) {
1075 bytelen += 4;
1076 } else {
1077 bytelen += 5;
1081 *bytes_p = b = (guint8 *)ep_alloc(bytelen);
1083 for (subid=subids[0] * 40, i = 1; i<subids_len; i++, subid=0) {
1084 guint len;
1086 subid += subids[i];
1087 if ((subid <= 0x0000007F )) len = 1;
1088 else if ((subid <= 0x00003FFF )) len = 2;
1089 else if ((subid <= 0x001FFFFF )) len = 3;
1090 else if ((subid <= 0x0FFFFFFF )) len = 4;
1091 else len = 5;
1093 switch(len) {
1094 default: *bytes_p=NULL; return 0;
1095 case 5: *(b++) = ((subid & 0xF0000000) >> 28) | 0x80;
1096 case 4: *(b++) = ((subid & 0x0FE00000) >> 21) | 0x80;
1097 case 3: *(b++) = ((subid & 0x001FC000) >> 14) | 0x80;
1098 case 2: *(b++) = ((subid & 0x00003F80) >> 7) | 0x80;
1099 case 1: *(b++) = subid & 0x0000007F ; break;
1103 return bytelen;
1106 const gchar* oid_encoded2string(const guint8* encoded, guint len) {
1107 guint32* subids;
1108 guint subids_len = oid_encoded2subid(encoded, len, &subids);
1110 if (subids_len) {
1111 return oid_subid2string(subids,subids_len);
1112 } else {
1113 return "";
1117 const gchar* rel_oid_encoded2string(const guint8* encoded, guint len) {
1118 guint32* subids;
1119 guint subids_len = oid_encoded2subid_sub(encoded, len, &subids, FALSE);
1121 if (subids_len) {
1122 return rel_oid_subid2string(subids,subids_len, FALSE);
1123 } else {
1124 return "";
1128 guint oid_string2encoded(const char *oid_str, guint8 **bytes) {
1129 guint32* subids;
1130 guint32 subids_len;
1131 guint byteslen;
1133 if ( ( subids_len = oid_string2subid(oid_str, &subids) )
1135 ( byteslen = oid_subid2encoded(subids_len, subids, bytes) ) ) {
1136 return byteslen;
1138 return 0;
1141 const gchar *oid_resolved_from_string(const gchar *oid_str) {
1142 guint32 *subid_oid;
1143 guint subid_oid_length = oid_string2subid(oid_str, &subid_oid);
1145 return oid_resolved(subid_oid_length, subid_oid);
1148 const gchar *oid_resolved(guint32 num_subids, guint32* subids) {
1149 guint matched;
1150 guint left;
1151 oid_info_t* oid;
1153 if(! (subids && *subids <= 2 ))
1154 return "*** Malformed OID ***";
1156 oid = oid_get(num_subids, subids, &matched, &left);
1158 while (! oid->name ) {
1159 if (!(oid = oid->parent)) {
1160 return oid_subid2string(subids,num_subids);
1162 left++;
1163 matched--;
1166 if (left) {
1167 return ep_strdup_printf("%s.%s",
1168 oid->name ? oid->name : oid_subid2string(subids,matched),
1169 oid_subid2string(&(subids[matched]),left));
1170 } else {
1171 return oid->name ? oid->name : oid_subid2string(subids,matched);
1175 extern void oid_both(guint oid_len, guint32 *subids, char** resolved_p, char** numeric_p) {
1176 *resolved_p = (char *)oid_resolved(oid_len,subids);
1177 *numeric_p = (char *)oid_subid2string(subids,oid_len);
1180 extern void oid_both_from_encoded(const guint8 *oid, gint oid_len, char** resolved_p, char** numeric_p) {
1181 guint32* subids;
1182 guint subids_len = oid_encoded2subid(oid, oid_len, &subids);
1183 *resolved_p = (char *)oid_resolved(subids_len,subids);
1184 *numeric_p = (char *)oid_subid2string(subids,subids_len);
1187 extern void oid_both_from_string(const gchar *oid_str, char** resolved_p, char** numeric_p) {
1188 guint32* subids;
1189 guint subids_len = oid_string2subid(oid_str, &subids);
1190 *resolved_p = (char *)oid_resolved(subids_len,subids);
1191 *numeric_p = (char *)oid_subid2string(subids,subids_len);
1195 * Fetch the default OID path.
1197 extern gchar *
1198 oid_get_default_mib_path(void) {
1199 #ifdef HAVE_LIBSMI
1200 GString* path_str;
1201 gchar *path_ret;
1202 char *path;
1203 guint i;
1205 path_str = g_string_new("");
1207 if (!load_smi_modules) {
1208 D(1,("OID resolution not enabled"));
1209 return path_str->str;
1211 #ifdef _WIN32
1212 #define PATH_SEPARATOR ";"
1213 path = get_datafile_path("snmp\\mibs");
1214 g_string_append_printf(path_str, "%s;", path);
1215 g_free (path);
1217 path = get_persconffile_path("snmp\\mibs", FALSE);
1218 g_string_append_printf(path_str, "%s", path);
1219 g_free (path);
1220 #else
1221 #define PATH_SEPARATOR ":"
1222 path = smiGetPath();
1223 g_string_append(path_str, "/usr/share/snmp/mibs");
1224 if (strlen(path) > 0 ) {
1225 g_string_append(path_str, PATH_SEPARATOR);
1227 g_string_append_printf(path_str, "%s", path);
1228 free (path);
1229 #endif
1231 for(i=0;i<num_smi_paths;i++) {
1232 if (!( smi_paths[i].name && *smi_paths[i].name))
1233 continue;
1235 g_string_append_printf(path_str,PATH_SEPARATOR "%s",smi_paths[i].name);
1238 path_ret = path_str->str;
1239 g_string_free(path_str, FALSE);
1240 return path_ret;
1241 #else /* HAVE_LIBSMI */
1242 return g_strdup("");
1243 #endif
1246 #ifdef DEBUG_OIDS
1247 char* oid_test_a2b(guint32 num_subids, guint32* subids) {
1248 guint8* sub2enc;
1249 guint8* str2enc;
1250 guint32* enc2sub;
1251 guint32* str2sub;
1252 const char* sub2str = oid_subid2string(subids, num_subids);
1253 guint sub2enc_len = oid_subid2encoded(num_subids, subids,&sub2enc);
1254 guint enc2sub_len = oid_encoded2subid(sub2enc, sub2enc_len, &enc2sub);
1255 const char* enc2str = oid_encoded2string(sub2enc, sub2enc_len);
1256 guint str2enc_len = oid_string2encoded(sub2str,&str2enc);
1257 guint str2sub_len = oid_string2subid(sub2str,&str2sub);
1259 return ep_strdup_printf(
1260 "oid_subid2string=%s \n"
1261 "oid_subid2encoded=[%d]%s \n"
1262 "oid_encoded2subid=%s \n "
1263 "oid_encoded2string=%s \n"
1264 "oid_string2encoded=[%d]%s \n"
1265 "oid_string2subid=%s \n "
1266 ,sub2str
1267 ,sub2enc_len,bytestring_to_str(sub2enc, sub2enc_len, ':')
1268 ,enc2sub ? oid_subid2string(enc2sub,enc2sub_len) : "-"
1269 ,enc2str
1270 ,str2enc_len,bytestring_to_str(str2enc, str2enc_len, ':')
1271 ,str2sub ? oid_subid2string(str2sub,str2sub_len) : "-"
1275 void add_oid_debug_subtree(oid_info_t* oid_info, proto_tree *tree) {
1276 static const char* oid_kinds[] = { "Unknown", "Node", "Scalar", "Table", "Row", "Column", "Notification", "Group", "Compliance", "Capabilities"};
1277 static const char* key_types[] = {"OID_KEY_TYPE_WRONG","OID_KEY_TYPE_INTEGER",
1278 "OID_KEY_TYPE_FIXED_STRING","OID_KEY_TYPE_FIXED_BYTES","OID_KEY_TYPE_STRING",
1279 "OID_KEY_TYPE_BYTES","OID_KEY_TYPE_NSAP","OID_KEY_TYPE_OID","OID_KEY_TYPE_IPADDR"};
1280 proto_item* pi = proto_tree_add_text(tree,NULL,0,0,
1281 "OidInfo: Name='%s' sub-id=%u kind=%s hfid=%d",
1282 oid_info->name ? oid_info->name : "",
1283 oid_info->subid,
1284 oid_info->kind <= OID_KIND_CAPABILITIES ? oid_kinds[oid_info->kind] : "BROKEN",
1285 oid_info->value_hfid);
1286 proto_tree* pt = proto_item_add_subtree(pi,0);
1287 oid_key_t* key;
1289 for(key = oid_info->key; key; key = key->next) {
1290 proto_tree_add_text(pt,NULL,0,0,
1291 "Key: name='%s' num_subids=%d type=%s",
1292 key->name,
1293 key->key_type <= OID_KEY_TYPE_IPADDR ? key_types[key->key_type] : "BROKEN"
1297 if (oid_info->parent) {
1298 pi = proto_tree_add_text(pt,NULL,0,0,"Parent:");
1299 pt = proto_item_add_subtree(pi,0);
1300 add_oid_debug_subtree(oid_info->parent, pt);
1303 #endif
1306 * Editor modelines
1308 * Local Variables:
1309 * c-basic-offset: 8
1310 * tab-width: 8
1311 * indent-tabs-mode: t
1312 * End:
1314 * ex: set shiftwidth=8 tabstop=8 noexpandtab:
1315 * :indentSize=8:tabSize=8:noTabs=false: