Mercurial > flash_v2
diff packages/net/snmp/lib/current/src/parse.c @ 103:95f3e12a6327 ecos-sw-2000-06-23
Merge from eCos master repository on 2000-06-23-16:41:10-BST
| author | jlarmour |
|---|---|
| date | Fri, 23 Jun 2000 17:06:31 +0000 |
| parents | |
| children | e0c0827131d1 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/packages/net/snmp/lib/current/src/parse.c @@ -0,0 +1,3854 @@ +//========================================================================== +// +// ./lib/current/src/parse.c +// +// +//========================================================================== +//####COPYRIGHTBEGIN#### +// +// ------------------------------------------- +// The contents of this file are subject to the Red Hat eCos Public License +// Version 1.1 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://www.redhat.com/ +// +// Software distributed under the License is distributed on an "AS IS" +// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +// License for the specific language governing rights and limitations under +// the License. +// +// The Original Code is eCos - Embedded Configurable Operating System, +// released September 30, 1998. +// +// The Initial Developer of the Original Code is Red Hat. +// Portions created by Red Hat are +// Copyright (C) 1998, 1999, 2000 Red Hat, Inc. +// All Rights Reserved. +// ------------------------------------------- +// +//####COPYRIGHTEND#### +//####UCDSNMPCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from the UCD-SNMP +// project, <http://ucd-snmp.ucdavis.edu/> from the University of +// California at Davis, which was originally based on the Carnegie Mellon +// University SNMP implementation. Portions of this software are therefore +// covered by the appropriate copyright disclaimers included herein. +// +// The release used was version 4.1.2 of May 2000. "ucd-snmp-4.1.2" +// ------------------------------------------- +// +//####UCDSNMPCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): hmt +// Contributors: hmt +// Date: 2000-05-30 +// Purpose: Port of UCD-SNMP distribution to eCos. +// Description: +// +// +//####DESCRIPTIONEND#### +// +//========================================================================== +/******************************************************************** + Copyright 1989, 1991, 1992 by Carnegie Mellon University + + Derivative Work - +Copyright 1996, 1998, 1999, 2000 The Regents of the University of California + + All Rights Reserved + +Permission to use, copy, modify and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appears in all copies and +that both that copyright notice and this permission notice appear in +supporting documentation, and that the name of CMU and The Regents of +the University of California not be used in advertising or publicity +pertaining to distribution of the software without specific written +permission. + +CMU AND THE REGENTS OF THE UNIVERSITY OF CALIFORNIA DISCLAIM ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL CMU OR +THE REGENTS OF THE UNIVERSITY OF CALIFORNIA BE LIABLE FOR ANY SPECIAL, +INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +FROM THE LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF +CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*********************************************************************/ +/* + * parse.c + * + * Update: 1998-09-22 <mslifcak@iss.net> + * Clear nbuckets in init_node_hash. + * New method xcalloc returns zeroed data structures. + * New method alloc_node encapsulates common node creation. + * New method to configure terminate comment at end of line. + * New method to configure accept underscore in labels. + * + * Update: 1998-10-10 <daves@csc.liv.ac.uk> + * fully qualified OID parsing patch + * + * Update: 1998-10-20 <daves@csc.liv.ac.uk> + * merge_anon_children patch + * + * Update: 1998-10-21 <mslifcak@iss.net> + * Merge_parse_objectid associates information with last node in chain. + */ +/****************************************************************** + Copyright 1989, 1991, 1992 by Carnegie Mellon University + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of CMU not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. +******************************************************************/ +#include <config.h> +#include <snmp_logging.h> +#include <stdio.h> +#if HAVE_STDLIB_H +#include <stdlib.h> +#endif +#if HAVE_STRING_H +#include <string.h> +#else +#include <strings.h> +#endif +#include <ctype.h> +#include <sys/types.h> +#ifndef __ECOS +#include <sys/stat.h> +#endif + +/* Wow. This is ugly. -- Wes */ +#if HAVE_DIRENT_H +# include <dirent.h> +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# if HAVE_SYS_NDIR_H +# include <sys/ndir.h> +# endif +# if HAVE_SYS_DIR_H +# include <sys/dir.h> +# endif +# if HAVE_NDIR_H +# include <ndir.h> +# endif +#endif +#if HAVE_WINSOCK_H +#include <winsock.h> +#endif +#if HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP) +#include <regex.h> +#endif +#if HAVE_DMALLOC_H +#include <dmalloc.h> +#endif + +#include "system.h" +#include "parse.h" +#include "asn1.h" +#include "mib.h" +#include "snmp_api.h" +#include "snmp_debug.h" +#include "snmp_logging.h" +#include "default_store.h" +#include "tools.h" + +/* + * This is one element of an object identifier with either an integer + * subidentifier, or a textual string label, or both. + * The subid is -1 if not present, and label is NULL if not present. + */ +struct subid_s { + int subid; + int modid; + char *label; +}; + +#define MAXTC 1024 +struct tc { /* textual conventions */ + int type; + int modid; + char *descriptor; + char *hint; + struct enum_list *enums; + struct range_list *ranges; +} tclist[MAXTC]; + +int Line = 0; +const char *File = "(none)"; +static int anonymous = 0; + +#define SYNTAX_MASK 0x80 +/* types of tokens + Tokens wiht the SYNTAX_MASK bit set are syntax tokens */ +#define CONTINUE -1 +#define ENDOFFILE 0 +#define LABEL 1 +#define SUBTREE 2 +#define SYNTAX 3 +#define OBJID (4 | SYNTAX_MASK) +#define OCTETSTR (5 | SYNTAX_MASK) +#define INTEGER (6 | SYNTAX_MASK) +#define INTEGER32 INTEGER +#define NETADDR (7 | SYNTAX_MASK) +#define IPADDR (8 | SYNTAX_MASK) +#define COUNTER (9 | SYNTAX_MASK) +#define GAUGE (10 | SYNTAX_MASK) +#define TIMETICKS (11 | SYNTAX_MASK) +#define KW_OPAQUE (12 | SYNTAX_MASK) +#define NUL (13 | SYNTAX_MASK) +#define SEQUENCE 14 +#define OF 15 /* SEQUENCE OF */ +#define OBJTYPE 16 +#define ACCESS 17 +#define READONLY 18 +#define READWRITE 19 +#define WRITEONLY 20 +#define NOACCESS 21 +#define STATUS 22 +#define MANDATORY 23 +#define KW_OPTIONAL 24 +#define OBSOLETE 25 +/* #define RECOMMENDED 26 */ +#define PUNCT 27 +#define EQUALS 28 +#define NUMBER 29 +#define LEFTBRACKET 30 +#define RIGHTBRACKET 31 +#define LEFTPAREN 32 +#define RIGHTPAREN 33 +#define COMMA 34 +#define DESCRIPTION 35 +#define QUOTESTRING 36 +#define INDEX 37 +#define DEFVAL 38 +#define DEPRECATED 39 +#define SIZE 40 +#define BITSTRING (41 | SYNTAX_MASK) +#define NSAPADDRESS (42 | SYNTAX_MASK) +#define COUNTER64 (43 | SYNTAX_MASK) +#define OBJGROUP 44 +#define NOTIFTYPE 45 +#define AUGMENTS 46 +#define COMPLIANCE 47 +#define READCREATE 48 +#define UNITS 49 +#define REFERENCE 50 +#define NUM_ENTRIES 51 +#define MODULEIDENTITY 52 +#define LASTUPDATED 53 +#define ORGANIZATION 54 +#define CONTACTINFO 55 +#define UINTEGER32 (56 | SYNTAX_MASK) +#define CURRENT 57 +#define DEFINITIONS 58 +#define END 59 +#define SEMI 60 +#define TRAPTYPE 61 +#define ENTERPRISE 62 +/* #define DISPLAYSTR (63 | SYNTAX_MASK) */ +#define BEGIN 64 +#define IMPORTS 65 +#define EXPORTS 66 +#define ACCNOTIFY 67 +#define BAR 68 +#define RANGE 69 +#define CONVENTION 70 +#define DISPLAYHINT 71 +#define FROM 72 +#define CAPABILITIES 73 +#define MACRO 74 +#define IMPLIED 75 + +struct tok { + const char *name; /* token name */ + int len; /* length not counting nul */ + int token; /* value */ + int hash; /* hash of name */ + struct tok *next; /* pointer to next in hash table */ +}; + + +static struct tok tokens[] = { + { "obsolete", sizeof ("obsolete")-1, OBSOLETE }, + { "Opaque", sizeof ("Opaque")-1, KW_OPAQUE }, + { "optional", sizeof ("optional")-1, KW_OPTIONAL }, + { "LAST-UPDATED", sizeof ("LAST-UPDATED")-1, LASTUPDATED }, + { "ORGANIZATION", sizeof ("ORGANIZATION")-1, ORGANIZATION }, + { "CONTACT-INFO", sizeof ("CONTACT-INFO")-1, CONTACTINFO }, + { "MODULE-IDENTITY", sizeof ("MODULE-IDENTITY")-1, MODULEIDENTITY }, + { "MODULE-COMPLIANCE", sizeof ("MODULE-COMPLIANCE")-1, COMPLIANCE }, + { "DEFINITIONS", sizeof("DEFINITIONS")-1, DEFINITIONS}, + { "END", sizeof("END")-1, END}, + { "AUGMENTS", sizeof ("AUGMENTS")-1, AUGMENTS }, + { "not-accessible", sizeof ("not-accessible")-1, NOACCESS }, + { "write-only", sizeof ("write-only")-1, WRITEONLY }, + { "NsapAddress", sizeof("NsapAddress")-1, NSAPADDRESS}, + { "UNITS", sizeof("Units")-1, UNITS}, + { "REFERENCE", sizeof("REFERENCE")-1, REFERENCE}, + { "NUM-ENTRIES", sizeof("NUM-ENTRIES")-1, NUM_ENTRIES}, + { "BITSTRING", sizeof("BITSTRING")-1, BITSTRING}, + { "BIT", sizeof("BIT")-1, CONTINUE}, + { "BITS", sizeof("BITS")-1, BITSTRING}, + { "Counter64", sizeof("Counter64")-1, COUNTER64}, + { "TimeTicks", sizeof ("TimeTicks")-1, TIMETICKS }, + { "NOTIFICATION-TYPE", sizeof ("NOTIFICATION-TYPE")-1, NOTIFTYPE }, + { "OBJECT-GROUP", sizeof ("OBJECT-GROUP")-1, OBJGROUP }, + { "OBJECT-IDENTITY", sizeof ("OBJECT-IDENTITY")-1, OBJGROUP }, + { "OBJECTIDENTIFIER", sizeof ("OBJECTIDENTIFIER")-1, OBJID }, + { "OBJECT", sizeof ("OBJECT")-1, CONTINUE }, + { "NetworkAddress", sizeof ("NetworkAddress")-1, NETADDR }, + { "Gauge", sizeof ("Gauge")-1, GAUGE }, + { "Gauge32", sizeof ("Gauge32")-1, GAUGE }, + { "Unsigned32", sizeof ("Unsigned32")-1, GAUGE }, + { "read-write", sizeof ("read-write")-1, READWRITE }, + { "read-create", sizeof ("read-create")-1, READCREATE }, + { "OCTETSTRING", sizeof ("OCTETSTRING")-1, OCTETSTR }, + { "OCTET", sizeof ("OCTET")-1, CONTINUE }, + { "OF", sizeof ("OF")-1, OF }, + { "SEQUENCE", sizeof ("SEQUENCE")-1, SEQUENCE }, + { "NULL", sizeof ("NULL")-1, NUL }, + { "IpAddress", sizeof ("IpAddress")-1, IPADDR }, + { "UInteger32", sizeof ("UInteger32")-1, UINTEGER32 }, + { "INTEGER", sizeof ("INTEGER")-1, INTEGER }, + { "Integer32", sizeof ("Integer32")-1, INTEGER32 }, + { "Counter", sizeof ("Counter")-1, COUNTER }, + { "Counter32", sizeof ("Counter32")-1, COUNTER }, + { "read-only", sizeof ("read-only")-1, READONLY }, + { "DESCRIPTION", sizeof ("DESCRIPTION")-1, DESCRIPTION }, + { "INDEX", sizeof ("INDEX")-1, INDEX }, + { "DEFVAL", sizeof ("DEFVAL")-1, DEFVAL }, + { "deprecated", sizeof ("deprecated")-1, DEPRECATED }, + { "SIZE", sizeof ("SIZE")-1, SIZE }, + { "MAX-ACCESS", sizeof ("MAX-ACCESS")-1, ACCESS }, + { "ACCESS", sizeof ("ACCESS")-1, ACCESS }, + { "mandatory", sizeof ("mandatory")-1, MANDATORY }, + { "current", sizeof ("current")-1, CURRENT }, + { "STATUS", sizeof ("STATUS")-1, STATUS }, + { "SYNTAX", sizeof ("SYNTAX")-1, SYNTAX }, + { "OBJECT-TYPE", sizeof ("OBJECT-TYPE")-1, OBJTYPE }, + { "TRAP-TYPE", sizeof ("TRAP-TYPE")-1, TRAPTYPE }, + { "ENTERPRISE", sizeof ("ENTERPRISE")-1, ENTERPRISE }, + { "BEGIN", sizeof ("BEGIN")-1, BEGIN }, + { "IMPORTS", sizeof ("IMPORTS")-1, IMPORTS }, + { "EXPORTS", sizeof ("EXPORTS")-1, EXPORTS }, + { "accessible-for-notify", sizeof ("accessible-for-notify")-1, ACCNOTIFY }, + { "TEXTUAL-CONVENTION", sizeof ("TEXTUAL-CONVENTION")-1, CONVENTION }, + { "NOTIFICATION-GROUP", sizeof ("NOTIFICATION-GROUP")-1, NOTIFTYPE }, + { "DISPLAY-HINT", sizeof ("DISPLAY-HINT")-1, DISPLAYHINT }, + { "FROM", sizeof ("FROM")-1, FROM }, + { "AGENT-CAPABILITIES", sizeof ("AGENT-CAPABILITIES")-1, CAPABILITIES }, + { "MACRO", sizeof ("MACRO")-1, MACRO }, + { "IMPLIED", sizeof ("IMPLIED")-1, IMPLIED }, + { NULL } +}; + +static struct module_compatability *module_map_head; +static struct module_compatability module_map[] = { + { "RFC1065-SMI", "RFC1155-SMI", NULL, 0}, + { "RFC1066-MIB", "RFC1156-MIB", NULL, 0}, + /* 'mib' -> 'mib-2' */ + { "RFC1156-MIB", "RFC1158-MIB", NULL, 0}, + /* 'snmpEnableAuthTraps' -> 'snmpEnableAuthenTraps' */ + { "RFC1158-MIB", "RFC1213-MIB", NULL, 0}, + /* 'nullOID' -> 'zeroDotZero' */ + { "RFC1155-SMI", "SNMPv2-SMI", NULL, 0}, + { "RFC1213-MIB", "SNMPv2-SMI", "mib-2", 0}, + { "RFC1213-MIB", "SNMPv2-MIB", "sys", 3}, + { "RFC1213-MIB", "IF-MIB", "if", 2}, + { "RFC1213-MIB", "IP-MIB", "ip", 2}, + { "RFC1213-MIB", "IP-MIB", "icmp", 4}, + { "RFC1213-MIB", "TCP-MIB", "tcp", 3}, + { "RFC1213-MIB", "UDP-MIB", "udp", 3}, + { "RFC1213-MIB", "SNMPv2-SMI", "transmission", 0}, + { "RFC1213-MIB", "SNMPv2-MIB", "snmp", 4}, + { "RFC1271-MIB", "RMON-MIB", NULL, 0}, + { "RFC1286-MIB", "SOURCE-ROUTING-MIB", "dot1dSr", 7}, + { "RFC1286-MIB", "BRIDGE-MIB", NULL, 0}, + { "RFC1315-MIB", "FRAME-RELAY-DTE-MIB", NULL, 0}, + { "RFC1316-MIB", "CHARACTER-MIB", NULL, 0}, +}; +#define MODULE_NOT_FOUND 0 +#define MODULE_LOADED_OK 1 +#define MODULE_ALREADY_LOADED 2 +/* #define MODULE_LOAD_FAILED 3 */ +#define MODULE_LOAD_FAILED MODULE_NOT_FOUND + + +#define HASHSIZE 32 +#define BUCKET(x) (x & (HASHSIZE-1)) + +#define NHASHSIZE 128 +#define NBUCKET(x) (x & (NHASHSIZE-1)) + +static struct tok *buckets[HASHSIZE]; + +static struct node *nbuckets[NHASHSIZE]; +static struct tree *tbuckets[NHASHSIZE]; +static struct module *module_head = NULL; + +struct node *orphan_nodes = NULL; +struct tree *tree_head = NULL; + +#define NUMBER_OF_ROOT_NODES 3 +static struct module_import root_imports[NUMBER_OF_ROOT_NODES]; + +static int current_module = 0; +static int max_module = 0; +static char *last_err_module = 0; /* no repeats on "Cannot find module..." */ + +static void tree_from_node(struct tree *tp, struct node *np); +static void do_subtree (struct tree *, struct node **); +static void do_linkup (struct module *, struct node *); +static void dump_module_list (void); +static int get_token (FILE *, char *, int); +static int parseQuoteString (FILE *, char *, int); +static int tossObjectIdentifier (FILE *); +static int name_hash (const char *); +static void init_node_hash (struct node *); +static void print_error (const char *, const char *, int); +static void free_tree (struct tree *); +static void free_partial_tree (struct tree *, int); +static void free_node (struct node *); +static void build_translation_table (void); +static void init_tree_roots (void); +static void merge_anon_children (struct tree *, struct tree *); +static void unlink_tbucket(struct tree *); +static void unlink_tree(struct tree *); +static int getoid (FILE *, struct subid_s *, int); +static struct node *parse_objectid (FILE *, char *); +static int get_tc (const char *, int, int *, struct enum_list **, struct range_list **, char **); +static int get_tc_index (const char *, int); +static struct enum_list *parse_enumlist (FILE *, struct enum_list **); +static struct range_list *parse_ranges(FILE *fp, struct range_list **); +static struct node *parse_asntype (FILE *, char *, int *, char *); +static struct node *parse_objecttype (FILE *, char *); +static struct node *parse_objectgroup (FILE *, char *); +static struct node *parse_notificationDefinition (FILE *, char *); +static struct node *parse_trapDefinition (FILE *, char *); +static struct node *parse_compliance (FILE *, char *); +static struct node *parse_capabilities(FILE *, char *); +static struct node *parse_moduleIdentity (FILE *, char *); +static struct node *parse_macro(FILE *, char *); +static void parse_imports (FILE *); +static struct node *parse (FILE *, struct node *); + +static int read_module_internal (const char *); +static void read_module_replacements (const char *); +static void read_import_replacements (const char *, struct module_import *); + +static void new_module (const char *, const char *); + +static struct node *merge_parse_objectid (struct node *, FILE *, char *); +static struct index_list *getIndexes(FILE *fp, struct index_list **); +static void free_indexes(struct index_list **); +static void free_ranges(struct range_list **); +static void free_enums(struct enum_list **); +static struct range_list * copy_ranges(struct range_list *); +static struct enum_list * copy_enums(struct enum_list *); +static struct index_list * copy_indexes(struct index_list *); + +/* backwards compatibility wrappers */ +void snmp_set_mib_errors(int err) +{ + ds_set_boolean(DS_LIBRARY_ID, DS_LIB_MIB_ERRORS, err); +} + +void snmp_set_mib_warnings(int warn) +{ + ds_set_int(DS_LIBRARY_ID, DS_LIB_MIB_WARNINGS, warn); +} + +void snmp_set_save_descriptions(int save) +{ + ds_set_boolean(DS_LIBRARY_ID, DS_LIB_SAVE_MIB_DESCRS, save); +} + +void snmp_set_mib_comment_term(int save) +{ + /* 0=strict, 1=EOL terminated */ + ds_set_boolean(DS_LIBRARY_ID, DS_LIB_MIB_COMMENT_TERM, save); +} + +void snmp_set_mib_parse_label(int save) +{ + /* 0=strict, 1=underscore OK in label */ + ds_set_boolean(DS_LIBRARY_ID, DS_LIB_MIB_PARSE_LABEL, save); +} + +/* end wrappers */ + +void snmp_mib_toggle_options_usage(const char *lead, FILE *outf) { + fprintf(outf, "%sMIBOPTS values:\n", lead); + fprintf(outf, "%s u: %sallow the usage of underlines in mib symbols.\n", + lead, ((ds_get_boolean(DS_LIBRARY_ID, DS_LIB_MIB_PARSE_LABEL))?"dis":"")); + fprintf(outf, "%s c: %sallow the usage of \"--\" to terminate comments.\n", + lead, ((ds_get_boolean(DS_LIBRARY_ID, DS_LIB_MIB_COMMENT_TERM))?"":"dis")); + fprintf(outf, "%s d: %ssave the descriptions of the mib objects.\n", + lead, ((ds_get_boolean(DS_LIBRARY_ID, DS_LIB_SAVE_MIB_DESCRS))?"don't ":"")); + fprintf(outf, "%s e: Disable mib errors of MIB symbols conflicts\n", + lead); + fprintf(outf, "%s w: Enable mib warnings of MIB symbols conflicts\n", + lead); + fprintf(outf, "%s W: Enable detailed warnings of MIB symbols conflicts\n", + lead); + fprintf(outf, "%s R: Replace MIB symbols from latest module\n", + lead); +} + +char *snmp_mib_toggle_options(char *options) { + if (options) { + while(*options) { + switch(*options) { + case 'u': + ds_set_boolean(DS_LIBRARY_ID, DS_LIB_MIB_PARSE_LABEL, !ds_get_boolean(DS_LIBRARY_ID, DS_LIB_MIB_PARSE_LABEL)); + break; + + case 'c': + ds_toggle_boolean(DS_LIBRARY_ID, DS_LIB_MIB_COMMENT_TERM); + break; + + case 'e': + ds_toggle_boolean(DS_LIBRARY_ID, DS_LIB_MIB_ERRORS); + break; + + case 'w': + ds_set_int(DS_LIBRARY_ID, DS_LIB_MIB_WARNINGS, 1); + break; + + case 'W': + ds_set_int(DS_LIBRARY_ID, DS_LIB_MIB_WARNINGS, 2); + break; + + case 'd': + ds_toggle_boolean(DS_LIBRARY_ID, DS_LIB_SAVE_MIB_DESCRS); + break; + + case 'R': + ds_toggle_boolean(DS_LIBRARY_ID, DS_LIB_MIB_REPLACE); + break; + + default: + /* return at the unknown option */ + return options; + } + options++; + } + } + return NULL; +} + +static int +name_hash(const char* name) +{ + int hash = 0; + const char *cp; + + if (name) { + for(cp = name; *cp; cp++) { + hash += tolower(*cp); + } + } + return(hash); +} + +void +init_mib_internals (void) +{ + register struct tok *tp; + register int b, i; + int max_modc; + + if (tree_head) + return; + + /* + * Set up hash list of pre-defined tokens + */ + memset(buckets, 0, sizeof(buckets)); + for (tp = tokens; tp->name; tp++) { + tp->hash = name_hash( tp->name ); + b = BUCKET(tp->hash); + if (buckets[b]) + tp->next = buckets[b]; /* BUG ??? */ + buckets[b] = tp; + } + + /* + * Initialise other internal structures + */ + + max_modc = sizeof(module_map)/sizeof(module_map[0])-1; + for ( i = 0; i < max_modc; ++i ) + module_map[i].next = &(module_map[i+1]); + module_map[max_modc].next = NULL; + module_map_head = module_map; + + memset(nbuckets, 0, sizeof(nbuckets)); + memset(tbuckets, 0, sizeof(tbuckets)); + memset(tclist, 0, MAXTC * sizeof(struct tc)); + build_translation_table(); + init_tree_roots(); /* Set up initial roots */ + /* Relies on 'add_mibdir' having set up the modules */ +} + +static void +init_node_hash(struct node *nodes) +{ + register struct node *np, *nextp; + register int hash; + + memset(nbuckets, 0, sizeof(nbuckets)); + for(np = nodes; np;){ + nextp = np->next; + hash = NBUCKET(name_hash(np->parent)); + np->next = nbuckets[hash]; + nbuckets[hash] = np; + np = nextp; + } +} + +static int erroneousMibs = 0; + +int get_mib_parse_error_count(void) +{ + return erroneousMibs; +} + + +static void +print_error(const char *string, + const char *token, + int type) +{ + erroneousMibs++; + DEBUGMSGTL(("parse-mibs", "\n")); + if (type == ENDOFFILE) + snmp_log(LOG_ERR, "%s (EOF): At line %d in %s\n", string, Line, + File); + else if (token && *token) + snmp_log(LOG_ERR, "%s (%s): At line %d in %s\n", string, token, + Line, File); + else + snmp_log(LOG_ERR, "%s: At line %d in %s\n", string, Line, File); +} + +static void +print_module_not_found(const char *cp) +{ + if (!last_err_module || strcmp(cp, last_err_module)) + print_error("Cannot find module", cp, CONTINUE); + if (last_err_module) free(last_err_module); + last_err_module = strdup(cp); +} + +static struct node * +alloc_node(int modid) +{ + struct node *np; + np = (struct node *) calloc(1, sizeof(struct node)); + if (np) { + np->tc_index = -1; + np->modid = modid; + } + return np; +} + +static void unlink_tbucket(struct tree *tp) +{ + int hash = NBUCKET(name_hash(tp->label)); + struct tree *otp = NULL, *ntp = tbuckets[hash]; + + while (ntp && ntp != tp) { + otp = ntp; ntp = ntp->next; + } + if (!ntp) snmp_log(LOG_EMERG, "Can't find %s in tbuckets\n", tp->label); + else if (otp) otp->next = ntp->next; + else tbuckets[hash] = tp->next; +} + +static void unlink_tree(struct tree *tp) +{ + struct tree *otp = NULL, *ntp = tp->parent->child_list; + + while (ntp && ntp != tp) { + otp = ntp; ntp = ntp->next_peer; + } + if (!ntp) snmp_log(LOG_EMERG, "Can't find %s in %s's children\n", + tp->label, tp->parent->label); + else if (otp) otp->next_peer = ntp->next_peer; + else tp->parent->child_list = tp->next_peer; +} + +static void +free_partial_tree(struct tree *tp, int keep_label) +{ + if ( !tp) + return; + + /* remove the data from this tree node */ + free_enums(&tp->enums); + free_ranges(&tp->ranges); + free_indexes(&tp->indexes); + if (!keep_label) + SNMP_FREE(tp->label); + SNMP_FREE(tp->hint); + SNMP_FREE(tp->units); + SNMP_FREE(tp->description); +} + +/* + * free a tree node. Note: the node must already have been unlinked + * from the tree when calling this routine + */ +static void +free_tree(struct tree *Tree) +{ + if (!Tree) + return; + + unlink_tbucket(Tree); + free_partial_tree (Tree, FALSE); + if (Tree->number_modules > 1 ) + free((char*)Tree->module_list); + free ((char*)Tree); +} + +static void +free_node(struct node *np) +{ + if ( !np) return; + + free_enums(&np->enums); + free_ranges(&np->ranges); + free_indexes(&np->indexes); + if (np->label) free(np->label); + if (np->hint) free(np->hint); + if (np->units) free(np->units); + if (np->description) free(np->description); + if (np->parent) free(np->parent); + free((char*)np); +} + +#ifdef TEST +static void +print_nodes(FILE *fp, + struct node *root) +{ +extern void xmalloc_stats (FILE *); + struct enum_list *ep; + struct index_list *ip; + struct range_list *rp; + struct node *np; + + for(np = root; np; np = np->next){ + fprintf(fp, "%s ::= { %s %ld } (%d)\n", np->label, np->parent, + np->subid, np->type); + if (np->tc_index >= 0) + fprintf(fp, " TC = %s\n", tclist[np->tc_index].descriptor); + if (np->enums){ + fprintf(fp, " Enums: \n"); + for(ep = np->enums; ep; ep = ep->next){ + fprintf(fp, " %s(%d)\n", ep->label, ep->value); + } + } + if (np->ranges){ + fprintf(fp, " Ranges: \n"); + for(rp = np->ranges; rp; rp = rp->next){ + fprintf(fp, " %d..%d\n", rp->low, rp->high); + } + } + if (np->indexes){ + fprintf(fp, " Indexes: \n"); + for(ip = np->indexes; ip; ip = ip->next){ + fprintf(fp, " %s\n", ip->ilabel); + } + } + if (np->hint) + fprintf(fp, " Hint: %s\n", np->hint); + if (np->units) + fprintf(fp, " Units: %s\n", np->units); + } +} +#endif + +void +print_subtree(FILE *f, + struct tree *tree, + int count) +{ + struct tree *tp; + int i; + char modbuf[256]; + + for(i = 0; i < count; i++) + fprintf(f, " "); + fprintf(f, "Children of %s(%ld):\n", tree->label, tree->subid); + count++; + for(tp = tree->child_list; tp; tp = tp->next_peer){ + for(i = 0; i < count; i++) + fprintf(f, " "); + fprintf(f, "%s:%s(%ld) type=%d", + module_name(tp->module_list[0], modbuf), + tp->label, tp->subid, tp->type); + if (tp->tc_index != -1) fprintf(f, " tc=%d", tp->tc_index); + if (tp->hint) fprintf(f, " hint=%s", tp->hint); + if (tp->units) fprintf(f, " units=%s", tp->units); + if (tp->number_modules > 1) { + fprintf(f, " modules:"); + for (i = 1; i < tp->number_modules; i++) + fprintf(f, " %s", module_name(tp->module_list[i], modbuf)); + } + fprintf(f, "\n"); + } + for(tp = tree->child_list; tp; tp = tp->next_peer){ + if (tp->child_list) + print_subtree(f, tp, count); + } +} + +void +print_ascii_dump_tree(FILE *f, + struct tree *tree, + int count) +{ + struct tree *tp; + + count++; + for(tp = tree->child_list; tp; tp = tp->next_peer){ + fprintf(f, "%s OBJECT IDENTIFIER ::= { %s %ld }\n", tp->label, tree->label, tp->subid); + } + for(tp = tree->child_list; tp; tp = tp->next_peer){ + if (tp->child_list) + print_ascii_dump_tree(f, tp, count); + } +} + +static int translation_table[256]; + +static void +build_translation_table() +{ + int count; + + for(count = 0; count < 256; count++){ + switch(count){ + case OBJID: + translation_table[count] = TYPE_OBJID; + break; + case OCTETSTR: + translation_table[count] = TYPE_OCTETSTR; + break; + case INTEGER: + translation_table[count] = TYPE_INTEGER; + break; + case NETADDR: + translation_table[count] = TYPE_IPADDR; + break; + case IPADDR: + translation_table[count] = TYPE_IPADDR; + break; + case COUNTER: + translation_table[count] = TYPE_COUNTER; + break; + case GAUGE: + translation_table[count] = TYPE_GAUGE; + break; + case TIMETICKS: + translation_table[count] = TYPE_TIMETICKS; + break; + case KW_OPAQUE: + translation_table[count] = TYPE_OPAQUE; + break; + case NUL: + translation_table[count] = TYPE_NULL; + break; + case COUNTER64: + translation_table[count] = TYPE_COUNTER64; + break; + case BITSTRING: + translation_table[count] = TYPE_BITSTRING; + break; + case NSAPADDRESS: + translation_table[count] = TYPE_NSAPADDRESS; + break; + case UINTEGER32: + translation_table[count] = TYPE_UINTEGER; + break; + default: + translation_table[count] = TYPE_OTHER; + break; + } + } +} + +static void +init_tree_roots() +{ + struct tree *tp, *lasttp; + int base_modid; + int hash; + + base_modid = which_module("SNMPv2-SMI"); + if (base_modid == -1 ) + base_modid = which_module("RFC1155-SMI"); + if (base_modid == -1 ) + base_modid = which_module("RFC1213-MIB"); + + /* build root node */ + tp = (struct tree *) calloc(1, sizeof(struct tree)); + if (tp == NULL) return; + tp->label = strdup("joint-iso-ccitt"); + tp->modid = base_modid; + tp->number_modules = 1; + tp->module_list = &(tp->modid); + tp->subid = 2; + tp->tc_index = -1; + set_function(tp); /* from mib.c */ + hash = NBUCKET(name_hash(tp->label)); + tp->next = tbuckets[hash]; + tbuckets[hash] = tp; + lasttp = tp; + root_imports[0].label = strdup( tp->label ); + root_imports[0].modid = base_modid; + + /* build root node */ + tp = (struct tree *) calloc(1, sizeof(struct tree)); + if (tp == NULL) return; + tp->next_peer = lasttp; + tp->label = strdup("ccitt"); + tp->modid = base_modid; + tp->number_modules = 1; + tp->module_list = &(tp->modid); + tp->subid = 0; + tp->tc_index = -1; + set_function(tp); /* from mib.c */ + hash = NBUCKET(name_hash(tp->label)); + tp->next = tbuckets[hash]; + tbuckets[hash] = tp; + lasttp = tp; + root_imports[1].label = strdup( tp->label ); + root_imports[1].modid = base_modid; + + /* build root node */ + tp = (struct tree *) calloc(1, sizeof(struct tree)); + if (tp == NULL) return; + tp->next_peer = lasttp; + tp->label = strdup("iso"); + tp->modid = base_modid; + tp->number_modules = 1; + tp->module_list = &(tp->modid); + tp->subid = 1; + tp->tc_index = -1; + set_function(tp); /* from mib.c */ + hash = NBUCKET(name_hash(tp->label)); + tp->next = tbuckets[hash]; + tbuckets[hash] = tp; + lasttp = tp; + root_imports[2].label = strdup( tp->label ); + root_imports[2].modid = base_modid; + + tree_head = tp; +} + +#ifdef STRICT_MIB_PARSEING +#define label_compare strcasecmp +#else +#define label_compare strcmp +#endif + + +struct tree * +find_tree_node(const char *name, + int modid) +{ + struct tree *tp, *headtp; + int count, *int_p; + + if (!name || !*name) + return(NULL); + + headtp = tbuckets[NBUCKET(name_hash(name))]; + for ( tp = headtp ; tp ; tp=tp->next ) { + if ( !label_compare(tp->label, name) ) { + + if ( modid == -1 ) /* Any module */ + return(tp); + + for (int_p = tp->module_list, count=0 ; + count < tp->number_modules ; + ++count, ++int_p ) + if ( *int_p == modid ) + return(tp); + } + } + + return(NULL); +} + +/* computes a value which represents how close name1 is to name2. + * high scores mean a worse match. + * (yes, the algorithm sucks!) + */ +#define MAX_BAD 0xffffff + +u_int +compute_match(const char *search_base, const char *key) { +#if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP) + int rc; + regex_t parsetree; + regmatch_t pmatch; + + rc=regcomp(&parsetree, key, REG_ICASE | REG_EXTENDED); + if (rc == 0) + rc=regexec(&parsetree, search_base, 1, &pmatch, 0); + regfree(&parsetree); + if (rc == 0) { + /* found */ + return pmatch.rm_so; + } +#else /* use our own wildcard matcher */ + /* first find the longest matching substring (ick) */ + char *first = NULL, *result = NULL, *entry; + const char *position; + char *newkey = strdup(key); + + + entry = strtok( newkey, "*" ); + position = search_base; + while ( entry ) { + result = strcasestr(position, entry); + + if (result == NULL) { + free(newkey); + return MAX_BAD; + } + + if (first == NULL) + first = result; + + position = result + strlen(entry); + entry = strtok( NULL, "*" ); + } + free(newkey); + if (result) + return(first-search_base); +#endif + + /* not found */ + return MAX_BAD; +} + +/* + * Find the tree node that best matches the pattern string. + * Use the "reported" flag such that only one match + * is attempted for every node. + * + * Warning! This function may recurse. + * + * Caller _must_ invoke clear_tree_flags before first call + * to this function. This function may be called multiple times + * to ensure that the entire tree is traversed. + */ + +struct tree * +find_best_tree_node(const char *pattrn, struct tree *tree_top, u_int *match) +{ + struct tree *tp, *best_so_far = NULL, *retptr; + u_int old_match=MAX_BAD, new_match=MAX_BAD; + + if (!pattrn || !*pattrn) + return(NULL); + + if (!tree_top) + tree_top = get_tree_head(); + + for ( tp = tree_top ; tp ; tp=tp->next_peer ) { + if (!tp->reported) + new_match = compute_match(tp->label, pattrn); + tp->reported = 1; + + if (new_match < old_match) { + best_so_far = tp; + old_match = new_match; + } + if (new_match == 0) + break; /* this is the best result we can get */ + if (tp->child_list) { + retptr = find_best_tree_node(pattrn, tp->child_list, &new_match); + if (new_match < old_match) { + best_so_far = retptr; + old_match = new_match; + } + if (new_match == 0) + break; /* this is the best result we can get */ + } + } + + if (match) + *match = old_match; + return(best_so_far); +} + + +static void +merge_anon_children(struct tree *tp1, + struct tree *tp2) + /* NB: tp1 is the 'anonymous' node */ +{ + struct tree *child1, *child2, *previous; + + for ( child1 = tp1->child_list ; child1 ; ) { + + for ( child2 = tp2->child_list, previous = NULL ; + child2 ; previous = child2, child2 = child2->next_peer ) { + + if ( child1->subid == child2->subid ) { + /* + * Found 'matching' children, + * so merge them + */ + if ( !strncmp( child1->label, ANON, ANON_LEN)) { + merge_anon_children( child1, child2 ); + + child1->child_list = NULL; + previous = child1; /* Finished with 'child1' */ + child1 = child1->next_peer; + free_tree( previous ); + goto next; + } + + else if ( !strncmp( child2->label, ANON, ANON_LEN)) { + merge_anon_children( child2, child1 ); + + if ( previous ) + previous->next_peer = child2->next_peer; + else + tp2->child_list = child2->next_peer; + free_tree(child2); + + previous = child1; /* Move 'child1' to 'tp2' */ + child1 = child1->next_peer; + previous->next_peer = tp2->child_list; + tp2->child_list = previous; + for ( previous = tp2->child_list ; + previous ; + previous = previous->next_peer ) + previous->parent = tp2; + goto next; + } + else if ( !label_compare( child1->label, child2->label) ) { + if (ds_get_int(DS_LIBRARY_ID, DS_LIB_MIB_WARNINGS)) + snmp_log(LOG_WARNING, "Warning: %s.%ld is both %s and %s (%s)\n", + tp2->label, child1->subid, + child1->label, child2->label, File); + continue; + } + else { + /* + * Two copies of the same node. + * 'child2' adopts the children of 'child1' + */ + + if ( child2->child_list ) { + for ( previous = child2->child_list ; + previous->next_peer ; + previous = previous->next_peer ) + ; /* Find the end of the list */ + previous->next_peer = child1->child_list; + } + else + child2->child_list = child1->child_list; + for ( previous = child1->child_list ; + previous ; + previous = previous->next_peer ) + previous->parent = child2; + child1->child_list = NULL; + + previous = child1; /* Finished with 'child1' */ + child1 = child1->next_peer; + free_tree( previous ); + goto next; + } + } + } + /* + * If no match, move 'child1' to 'tp2' child_list + */ + if ( child1 ) { + previous = child1; + child1 = child1->next_peer; + previous->parent = tp2; + previous->next_peer = tp2->child_list; + tp2->child_list = previous; + } + next:; + } +} + + +/* + * Find all the children of root in the list of nodes. Link them into the + * tree and out of the nodes list. + */ +static void +do_subtree(struct tree *root, + struct node **nodes) +{ + register struct tree *tp, *anon_tp=NULL; + register struct node *np, **headp; + struct node *oldnp = NULL, *child_list = NULL, *childp = NULL; + int hash; + int *int_p; + + tp = root; + headp = &nbuckets[NBUCKET(name_hash(tp->label))]; + /* + * Search each of the nodes for one whose parent is root, and + * move each into a separate list. + */ + for(np = *headp; np; np = np->next){ + if ( !label_compare(tp->label, np->parent)){ + /* take this node out of the node list */ + if (oldnp == NULL){ + *headp = np->next; /* fix root of node list */ + } else { + oldnp->next = np->next; /* link around this node */ + } + if (child_list) childp->next = np; + else child_list = np; + childp = np; + } + else { + oldnp = np; + } + + } + if (childp) childp->next = NULL; + /* + * Take each element in the child list and place it into the tree. + */ + for(np = child_list; np; np = np->next){ + anon_tp = NULL; + tp = root->child_list; + while (tp) + if (tp->subid == np->subid) break; + else tp = tp->next_peer; + if (tp) { + if (!label_compare (tp->label, np->label)) { + /* Update list of modules */ + int_p = (int *) malloc((tp->number_modules+1) * sizeof(int)); + if (int_p == NULL) return; + memcpy(int_p, tp->module_list, tp->number_modules*sizeof(int)); + int_p[tp->number_modules] = np->modid; + if (tp->number_modules > 1 ) + free((char*)tp->module_list); + ++tp->number_modules; + tp->module_list = int_p; + + if ( ds_get_boolean(DS_LIBRARY_ID, DS_LIB_MIB_REPLACE) ) { + /* Replace from node */ + tree_from_node(tp,np); + } + /* Handle children */ + do_subtree(tp, nodes); + continue; + } + if (!strncmp( np->label, ANON, ANON_LEN) || + !strncmp( tp->label, ANON, ANON_LEN)) { + anon_tp = tp; /* Need to merge these two trees later */ + } + else if (ds_get_int(DS_LIBRARY_ID, DS_LIB_MIB_WARNINGS)) + snmp_log(LOG_WARNING, "Warning: %s.%ld is both %s and %s (%s)\n", + root->label, np->subid, tp->label, np->label, File); + } + + tp = (struct tree *) calloc(1, sizeof(struct tree)); + if (tp == NULL) return; + tp->parent = root; + tp->modid = np->modid; + tp->number_modules = 1; + tp->module_list = &(tp->modid); + tree_from_node(tp, np); + tp->next_peer = root->child_list; + root->child_list = tp; + hash = NBUCKET(name_hash(tp->label)); + tp->next = tbuckets[hash]; + tbuckets[hash] = tp; +/* if (tp->type == TYPE_OTHER) */ + do_subtree(tp, nodes); /* recurse on this child if it isn't + an end node */ + if ( anon_tp ) { + if (!strncmp( tp->label, ANON, ANON_LEN)) { + /* + * The new node is anonymous, + * so merge it with the existing one. + */ + merge_anon_children( tp, anon_tp ); + + /* unlink and destroy tp */ + unlink_tree(tp); + free_tree(tp); + } + else if (!strncmp( anon_tp->label, ANON, ANON_LEN)) { + struct tree *ntp; + /* + * The old node was anonymous, + * so merge it with the existing one, + * and fill in the full information. + */ + merge_anon_children( anon_tp, tp ); + + /* unlink anon_tp from the hash */ + unlink_tbucket(anon_tp); + + /* get rid of old contents of anon_tp */ + free_partial_tree(anon_tp, FALSE); + + /* put in the current information */ + anon_tp->label = tp->label; + anon_tp->child_list = tp->child_list; + anon_tp->modid = tp->modid; + anon_tp->tc_index = tp->tc_index; + anon_tp->type = tp->type; + anon_tp->enums = tp->enums; + anon_tp->indexes = tp->indexes; + anon_tp->ranges = tp->ranges; + anon_tp->hint = tp->hint; + anon_tp->units = tp->units; + anon_tp->description = tp->description; + anon_tp->parent = tp->parent; + set_function(anon_tp); + + /* update parent pointer in moved children */ + ntp = anon_tp->child_list; + while (ntp) { + ntp->parent = anon_tp; + ntp = ntp->next_peer; + } + + /* hash in anon_tp in its new place */ + hash = NBUCKET(name_hash(anon_tp->label)); + anon_tp->next = tbuckets[hash]; + tbuckets[hash] = anon_tp; + + /* unlink and destroy tp */ + unlink_tbucket(tp); + unlink_tree(tp); + free(tp); + } + else { + /* Uh? One of these two should have been anonymous! */ + if (ds_get_int(DS_LIBRARY_ID, DS_LIB_MIB_WARNINGS)) + snmp_log(LOG_WARNING, + "Warning: expected anonymous node (either %s or %s) in %s\n", + tp->label, anon_tp->label, File); + } + anon_tp = NULL; + } + } + /* free all nodes that were copied into tree */ + oldnp = NULL; + for(np = child_list; np; np = np->next){ + if (oldnp) + free_node(oldnp); + oldnp = np; + } + if (oldnp) + free_node(oldnp); +} + +static void do_linkup(struct module *mp, + struct node *np) +{ + struct module_import *mip; + struct node *onp; + struct tree *tp; + int i; + /* + * All modules implicitly import + * the roots of the tree + */ + if (snmp_get_do_debugging() > 1) dump_module_list(); + DEBUGMSGTL(("parse-mibs", "Processing IMPORTS for module %d %s\n", mp->modid, mp->name)); + if ( mp->no_imports == 0 ) { + mp->no_imports = NUMBER_OF_ROOT_NODES; + mp->imports = root_imports; + } + + /* + * Build the tree + */ + init_node_hash( np ); + for ( i=0, mip=mp->imports ; i < mp->no_imports ; ++i, ++mip ) { + char modbuf[256]; + DEBUGMSGTL(("parse-mibs", " Processing import: %s\n", mip->label)); + if (get_tc_index( mip->label, mip->modid ) != -1) + continue; + tp = find_tree_node( mip->label, mip->modid ); + if (!tp) { + if (mip->modid != -1) + snmp_log(LOG_WARNING, "Did not find '%s' in module %s (%s)\n", + mip->label, module_name(mip->modid, modbuf), File); + continue; + } + do_subtree( tp, &np ); + } + + /* + * If any nodes left over, + * check that they're not the result of a "fully qualified" + * name, and then add them to the list of orphans + */ + + if (!np) return; + for ( tp = tree_head ; tp ; tp=tp->next_peer ) + do_subtree( tp, &np ); + if (!np) return; + for ( np = orphan_nodes ; np && np->next ; np = np->next ) + ; /* find the end of the orphan list */ + for (i = 0; i < NHASHSIZE; i++) + if ( nbuckets[i] ) { + if ( orphan_nodes ) + onp = np->next = nbuckets[i]; + else + onp = orphan_nodes = nbuckets[i]; + nbuckets[i] = NULL; + while (onp) { + if (ds_get_int(DS_LIBRARY_ID, DS_LIB_MIB_WARNINGS)) + snmp_log(LOG_WARNING, + "Unlinked OID in %s: %s ::= { %s %ld }\n", + (mp->name ? mp->name : "<no module>"), + (onp->label ? onp->label : "<no label>"), + (onp->parent ? onp->parent : "<no parent>"), + onp->subid); + np = onp; + onp = onp->next; + } + } + + return; +} + + +/* + * Takes a list of the form: + * { iso org(3) dod(6) 1 } + * and creates several nodes, one for each parent-child pair. + * Returns 0 on error. + */ +static int +getoid(FILE *fp, + struct subid_s *id, /* an array of subids */ + int length) /* the length of the array */ +{ + register int count; + int type; + char token[MAXTOKEN]; + + if ((type = get_token(fp, token, MAXTOKEN)) != LEFTBRACKET){ + print_error("Expected \"{\"", token, type); + return 0; + } + type = get_token(fp, token, MAXTOKEN); + for(count = 0; count < length; count++, id++){ + id->label = NULL; + id->modid = current_module; + id->subid = -1; + if (type == RIGHTBRACKET){ + return count; + } else if (type != LABEL && type != NUMBER){ + print_error("Not valid for object identifier", token, type); + return 0; + } + if (type == LABEL){ + /* this entry has a label */ + id->label = strdup(token); + type = get_token(fp, token, MAXTOKEN); + if (type == LEFTPAREN){ + type = get_token(fp, token, MAXTOKEN); + if (type == NUMBER){ + id->subid = atoi(token); + if ((type = get_token(fp, token, MAXTOKEN)) != RIGHTPAREN){ + print_error("Expected a closing parenthesis", + token, type); + return 0; + } + } else { + print_error("Expected a number", token, type); + return 0; + } + } else { + continue; + } + } else if (type == NUMBER) { + /* this entry has just an integer sub-identifier */ + id->subid = atoi(token); + } + else { + print_error("Expected label or number", token, type); + return 0; + } + type = get_token(fp, token, MAXTOKEN); + } + print_error ("Too long OID", token, type); + return 0; +} + +/* + * Parse a sequence of object subidentifiers for the given name. + * The "label OBJECT IDENTIFIER ::=" portion has already been parsed. + * + * The majority of cases take this form : + * label OBJECT IDENTIFIER ::= { parent 2 } + * where a parent label and a child subidentifier number are specified. + * + * Variations on the theme include cases where a number appears with + * the parent, or intermediate subidentifiers are specified by label, + * by number, or both. + * + * Here are some representative samples : + * internet OBJECT IDENTIFIER ::= { iso org(3) dod(6) 1 } + * mgmt OBJECT IDENTIFIER ::= { internet 2 } + * rptrInfoHealth OBJECT IDENTIFIER ::= { snmpDot3RptrMgt 0 4 } + * + * Here is a very rare form : + * iso OBJECT IDENTIFIER ::= { 1 } + * + * Returns NULL on error. When this happens, memory may be leaked. + */ +static struct node * +parse_objectid(FILE *fp, + char *name) +{ + register int count; + register struct subid_s *op, *nop; + int length; + struct subid_s loid[32]; + struct node *np, *root = NULL, *oldnp = NULL; + struct tree *tp; + + if ((length = getoid(fp, loid, 32)) == 0){ + print_error("Bad object identifier", NULL, CONTINUE); + return NULL; + } + + /* + * Handle numeric-only object identifiers, + * by labelling the first sub-identifier + */ + op = loid; + if ( !op->label ) + for ( tp = tree_head ; tp ; tp=tp->next_peer ) + if ( (int)tp->subid == op->subid ) { + op->label = strdup(tp->label); + break; + } + + /* + * Handle "label OBJECT-IDENTIFIER ::= { subid }" + */ + if (length == 1) { + op = loid; + np = alloc_node(op->modid); + if (np == NULL) return(NULL); + np->subid = op->subid; + np->label = strdup(name); + if (op->label) free(op->label); + return np; + } + + /* + * For each parent-child subid pair in the subid array, + * create a node and link it into the node list. + */ + for(count = 0, op = loid, nop=loid+1; count < (length - 1); + count++, op++, nop++){ + /* every node must have parent's name and child's name or number */ +/* XX the next statement is always true -- does it matter ?? */ + if (op->label && (nop->label || (nop->subid != -1))){ + np = alloc_node(nop->modid); + if (np == NULL) return(NULL); + if (root == NULL) root = np; + + np->parent = strdup (op->label); + if (count == (length - 2)) { + /* The name for this node is the label for this entry */ + np->label = strdup (name); + } + else { + if (!nop->label) { + nop->label = (char *) malloc(20 + ANON_LEN); + if (nop->label == NULL) return(NULL); + sprintf(nop->label, "%s%d", ANON, anonymous++); + } + np->label = strdup (nop->label); + } + if (nop->subid != -1) + np->subid = nop->subid; + else + print_error("Warning: This entry is pretty silly", + np->label, CONTINUE); + + /* set up next entry */ + if (oldnp) oldnp->next = np; + oldnp = np; + } /* end if(op->label... */ + } + + /* free the loid array */ + for(count = 0, op = loid; count < length; count++, op++){ + if (op->label) + free(op->label); + } + + return root; +} + +static int +get_tc(const char *descriptor, + int modid, + int *tc_index, + struct enum_list **ep, + struct range_list **rp, + char **hint) +{ + int i; + struct tc *tcp; + + i = get_tc_index(descriptor, modid); + if (tc_index) *tc_index = i; + if (i != -1) + { + tcp = &tclist[i]; + if (ep) { + free_enums(ep); + *ep = copy_enums(tcp->enums); + } + if (rp) { + free_ranges(rp); + *rp = copy_ranges(tcp->ranges); + } + if (hint) { + if (*hint) free(*hint); + *hint = (tcp->hint ? strdup(tcp->hint) : NULL); + } + return tcp->type; + } + return LABEL; +} + +/* return index into tclist of given TC descriptor + return -1 if not found + */ +static int +get_tc_index(const char *descriptor, + int modid) +{ + int i; + struct tc *tcp; + struct module *mp; + struct module_import *mip; + + /* + * Check that the descriptor isn't imported + * by searching the import list + */ + + for ( mp = module_head ; mp ; mp = mp->next ) + if ( mp->modid == modid ) + break; + if ( mp ) + for ( i=0, mip=mp->imports ; i < mp->no_imports ; ++i, ++mip ) { + if ( !label_compare( mip->label, descriptor )) { + /* Found it - so amend the module ID */ + modid = mip->modid; + break; + } + } + + + for(i=0, tcp=tclist; i < MAXTC; i++, tcp++){ + if (tcp->type == 0) + break; + if (!label_compare(descriptor, tcp->descriptor) && + ((modid == tcp->modid) || (modid == -1))){ + return i; + } + } + return -1; +} + +/* translate integer tc_index to string identifier from tclist + * + * Returns pointer to string in table (should not be modified) or NULL + */ +const char * +get_tc_descriptor(int tc_index) +{ + if (tc_index < 0 || tc_index >= MAXTC) return NULL; + return (tclist[tc_index].descriptor); +} + + +/* + * Parses an enumeration list of the form: + * { label(value) label(value) ... } + * The initial { has already been parsed. + * Returns NULL on error. + */ + +static struct enum_list * +parse_enumlist(FILE *fp, struct enum_list **retp) +{ + register int type; + char token [MAXTOKEN]; + struct enum_list *ep = NULL, **epp = &ep; + + free_enums(retp); + + while((type = get_token(fp, token, MAXTOKEN)) != ENDOFFILE){ + if (type == RIGHTBRACKET) + break; + if (type == LABEL){ + /* this is an enumerated label */ + *epp = (struct enum_list *) calloc(1, sizeof(struct enum_list)); + if (*epp == NULL) return(NULL); + /* a reasonable approximation for the length */ + (*epp)->label = strdup(token); + type = get_token(fp, token, MAXTOKEN); + if (type != LEFTPAREN) { + print_error("Expected \"(\"", token, type); + return NULL; + } + type = get_token(fp, token, MAXTOKEN); + if (type != NUMBER) { + print_error("Expected integer", token, type); + return NULL; + } + (*epp)->value = atoi(token); + type = get_token(fp, token, MAXTOKEN); + if (type != RIGHTPAREN) { + print_error("Expected \")\"", token, type); + return NULL; + } + epp = &(*epp)->next; + } + } + if (type == ENDOFFILE){ + print_error("Expected \"}\"", token, type); + return NULL; + } + *retp = ep; + return ep; +} + +static struct range_list *parse_ranges(FILE *fp, struct range_list **retp) +{ int low, high; + char nexttoken[MAXTOKEN]; + int nexttype; + struct range_list *rp = NULL, **rpp = &rp; + int size = 0, taken = 1; + + free_ranges(retp); + + nexttype = get_token(fp, nexttoken, MAXTOKEN); + if (nexttype == SIZE) { + size = 1; + taken = 0; + nexttype = get_token(fp, nexttoken, MAXTOKEN); + if (nexttype != LEFTPAREN) + print_error("Expected \"(\" after SIZE", nexttoken, nexttype); + } + + do { + if (!taken) nexttype = get_token(fp, nexttoken, MAXTOKEN); + else taken = 0; + high = low = atol(nexttoken); + nexttype = get_token(fp, nexttoken, MAXTOKEN); + if (nexttype == RANGE) { + nexttype = get_token(fp, nexttoken, MAXTOKEN); + high = atol(nexttoken); + nexttype = get_token(fp, nexttoken, MAXTOKEN); + } + *rpp = (struct range_list *)calloc (1, sizeof(struct range_list)); + if (*rpp == NULL) break; + (*rpp)->low = low; + (*rpp)->high = high; + rpp = &(*rpp)->next; + + } while (nexttype == BAR); + if (size) { + if (nexttype != RIGHTPAREN) + print_error ("Expected \")\" after SIZE", nexttoken, nexttype); + nexttype = get_token(fp, nexttoken, nexttype); + } + if (nexttype != RIGHTPAREN) + print_error ("Expected \")\"", nexttoken, nexttype); + + *retp = rp; + return rp; +} + +/* + * Parses an asn type. Structures are ignored by this parser. + * Returns NULL on error. + */ +static struct node * +parse_asntype(FILE *fp, + char *name, + int *ntype, + char *ntoken) +{ + int type, i; + char token[MAXTOKEN]; + char quoted_string_buffer[MAXQUOTESTR]; + char *hint = NULL; + struct tc *tcp; + int level; + + type = get_token(fp, token, MAXTOKEN); + if (type == SEQUENCE){ + level = 0; + while((type = get_token(fp, token, MAXTOKEN)) != ENDOFFILE){ + if (type == LEFTBRACKET){ + level++; + } + else if (type == RIGHTBRACKET && --level == 0){ + *ntype = get_token(fp, ntoken, MAXTOKEN); + return NULL; + } + } + print_error("Expected \"}\"", token, type); + return NULL; + } else if (type == LEFTBRACKET) { + struct node *np; + int ch_next = '{'; + ungetc(ch_next, fp); + np = parse_objectid (fp, name); + if (np != NULL) { + *ntype = get_token(fp, ntoken, MAXTOKEN); + return np; + } + return NULL; + } else { + if (type == CONVENTION) { + while (type != SYNTAX && type != ENDOFFILE) { + if (type == DISPLAYHINT) { + type = get_token(fp, token, MAXTOKEN); + if (type != QUOTESTRING) print_error("DISPLAY-HINT must be string", token, type); + else hint = strdup (token); + } + else + type = get_token(fp, quoted_string_buffer, MAXQUOTESTR); + } + type = get_token(fp, token, MAXTOKEN); + } + + if (type == LABEL) + { + type = get_tc(token, current_module, NULL, NULL, NULL, NULL); + } + + /* textual convention */ + for(i = 0; i < MAXTC; i++){ + if (tclist[i].type == 0) + break; + } + + if (i == MAXTC){ + print_error("Too many textual conventions", token, type); + SNMP_FREE(hint); + return NULL; + } + if (!(type & SYNTAX_MASK)){ + print_error("Textual convention doesn't map to real type", token, + type); + SNMP_FREE(hint); + return NULL; + } + tcp = &tclist[i]; + tcp->modid = current_module; + tcp->descriptor = strdup(name); + tcp->hint = hint; + tcp->type = type; + *ntype = get_token(fp, ntoken, MAXTOKEN); + if (*ntype == LEFTPAREN){ + tcp->ranges = parse_ranges(fp, &tcp->ranges); + *ntype = get_token(fp, ntoken, MAXTOKEN); + } else if (*ntype == LEFTBRACKET) { + /* if there is an enumeration list, parse it */ + tcp->enums = parse_enumlist(fp, &tcp->enums); + *ntype = get_token(fp, ntoken, MAXTOKEN); + } + return NULL; + } +} + + +/* + * Parses an OBJECT TYPE macro. + * Returns 0 on error. + */ +static struct node * +parse_objecttype(FILE *fp, + char *name) +{ + register int type; + char token[MAXTOKEN]; + char nexttoken[MAXTOKEN]; + char quoted_string_buffer[MAXQUOTESTR]; + int nexttype, tctype; + register struct node *np; + + type = get_token(fp, token, MAXTOKEN); + if (type != SYNTAX){ + print_error("Bad format for OBJECT-TYPE", token, type); + return NULL; + } + np = alloc_node(current_module); + if (np == NULL) return(NULL); + type = get_token(fp, token, MAXTOKEN); + if (type == LABEL){ + int tmp_index; + tctype = get_tc(token, current_module, &tmp_index, + &np->enums, &np->ranges, &np->hint); + if (tctype == LABEL && + ds_get_int(DS_LIBRARY_ID, DS_LIB_MIB_WARNINGS) > 1){ + print_error("Warning: No known translation for type", token, type); + } + type = tctype; + np->tc_index = tmp_index; /* store TC for later reference */ + } + np->type = type; + nexttype = get_token(fp, nexttoken, MAXTOKEN); + switch(type){ + case SEQUENCE: + if (nexttype == OF){ + nexttype = get_token(fp, nexttoken, MAXTOKEN); + nexttype = get_token(fp, nexttoken, MAXTOKEN); + } + break; + case INTEGER: + case UINTEGER32: + case COUNTER: + case GAUGE: + case BITSTRING: + case LABEL: + if (nexttype == LEFTBRACKET) { + /* if there is an enumeration list, parse it */ + np->enums = parse_enumlist(fp, &np->enums); + nexttype = get_token(fp, nexttoken, MAXTOKEN); + } else if (nexttype == LEFTPAREN){ + /* if there is a range list, parse it */ + np->ranges = parse_ranges(fp, &np->ranges); + nexttype = get_token(fp, nexttoken, MAXTOKEN); + } + break; + case OCTETSTR: + case KW_OPAQUE: + /* parse any SIZE specification */ + if (nexttype == LEFTPAREN) { + nexttype = get_token(fp, nexttoken, MAXTOKEN); + if (nexttype == SIZE) { + nexttype = get_token(fp, nexttoken, MAXTOKEN); + if (nexttype == LEFTPAREN) { + np->ranges = parse_ranges(fp, &np->ranges); + nexttype = get_token(fp, nexttoken, MAXTOKEN); /* ) */ + if (nexttype == RIGHTPAREN) + { + nexttype = get_token(fp, nexttoken, MAXTOKEN); + break; + } + } + } + print_error("Bad SIZE syntax", token, type); + free_node(np); + return NULL; + } + break; + case OBJID: + case NETADDR: + case IPADDR: + case TIMETICKS: + case NUL: + case NSAPADDRESS: + case COUNTER64: + break; + default: + print_error("Bad syntax", token, type); + free_node(np); + return NULL; + } + if (nexttype == UNITS){ + type = get_token(fp, quoted_string_buffer, MAXQUOTESTR); + if (type != QUOTESTRING) { + print_error("Bad UNITS", quoted_string_buffer, type); + free_node(np); + return NULL; + } + np->units = strdup (quoted_string_buffer); + nexttype = get_token(fp, nexttoken, MAXTOKEN); + } + if (nexttype != ACCESS){ + print_error("Should be ACCESS", nexttoken, nexttype); + free_node(np); + return NULL; + } + type = get_token(fp, token, MAXTOKEN); + if (type != READONLY && type != READWRITE && type != WRITEONLY + && type != NOACCESS && type != READCREATE && type != ACCNOTIFY){ + print_error("Bad ACCESS type", token, type); + free_node(np); + return NULL; + } + np->access = type; + type = get_token(fp, token, MAXTOKEN); + if (type != STATUS){ + print_error("Should be STATUS", token, type); + free_node(np); + return NULL; + } + type = get_token(fp, token, MAXTOKEN); + if (type != MANDATORY && type != CURRENT && type != KW_OPTIONAL && + type != OBSOLETE && type != DEPRECATED){ + print_error("Bad STATUS", token, type); + free_node(np); + return NULL; + } + np->status = type; + /* + * Optional parts of the OBJECT-TYPE macro + */ + type = get_token(fp, token, MAXTOKEN); + while (type != EQUALS && type != ENDOFFILE) { + switch (type) { + case DESCRIPTION: + type = get_token(fp, quoted_string_buffer, MAXQUOTESTR); + if (type != QUOTESTRING) { + print_error("Bad DESCRIPTION", quoted_string_buffer, type); + free_node(np); + return NULL; + } + if (ds_get_boolean(DS_LIBRARY_ID, DS_LIB_SAVE_MIB_DESCRS)) { + np->description = strdup (quoted_string_buffer); + } + break; + + case REFERENCE: + type = get_token(fp, quoted_string_buffer, MAXQUOTESTR); + if (type != QUOTESTRING) { + print_error("Bad REFERENCE", quoted_string_buffer, type); + free_node(np); + return NULL; + } + break; + case INDEX: + np->indexes = getIndexes(fp, &np->indexes); + if (np->indexes == NULL) { + print_error("Bad Index List",token,type); + free_node(np); + return NULL; + } + break; + + case DEFVAL: + case AUGMENTS: + case NUM_ENTRIES: + if (tossObjectIdentifier(fp) != OBJID) { + print_error("Bad Object Identifier", token, type); + free_node(np); + return NULL; + } + break; + + default: + print_error("Bad format of optional clauses", token, type); + free_node(np); + return NULL; + + } + type = get_token(fp, token, MAXTOKEN); + } + if (type != EQUALS){ + print_error("Bad format", token, type); + free_node(np); + return NULL; + } + return merge_parse_objectid(np, fp, name); +} + +/* + * Parses an OBJECT GROUP macro. + * Returns 0 on error. + * + * Also parses object-identity, since they are similar (ignore STATUS). + * - WJH 10/96 + */ +static struct node * +parse_objectgroup(FILE *fp, + char *name) +{ + register int type; + char token[MAXTOKEN]; + char quoted_string_buffer[MAXQUOTESTR]; + register struct node *np; + + np = alloc_node(current_module); + if (np == NULL) return(NULL); + type = get_token(fp, token, MAXTOKEN); + while (type != EQUALS && type != ENDOFFILE) { + switch (type) { + case DESCRIPTION: + type = get_token(fp, quoted_string_buffer, MAXQUOTESTR); + if (type != QUOTESTRING) { + print_error("Bad DESCRIPTION", quoted_string_buffer, type); + free_node(np); + return NULL; + } + if (ds_get_boolean(DS_LIBRARY_ID, DS_LIB_SAVE_MIB_DESCRS)) { + np->description = strdup (quoted_string_buffer); + } + break; + + case REFERENCE: + type = get_token(fp, quoted_string_buffer, MAXQUOTESTR); + if (type != QUOTESTRING) { + print_error("Bad REFERENCE", quoted_string_buffer, type); + free_node(np); + return NULL; + } + break; + + default: + /* NOTHING */ + break; + } + type = get_token(fp, token, MAXTOKEN); + } + return merge_parse_objectid(np, fp, name); +} + +/* + * Parses a NOTIFICATION-TYPE macro. + * Returns 0 on error. + */ +static struct node * +parse_notificationDefinition(FILE *fp, + char *name) +{ + register int type; + char token[MAXTOKEN]; + char quoted_string_buffer[MAXQUOTESTR]; + register struct node *np; + + np = alloc_node(current_module); + if (np == NULL) return(NULL); + type = get_token(fp, token, MAXTOKEN); + while (type != EQUALS && type != ENDOFFILE) { + switch (type) { + case DESCRIPTION: + type = get_token(fp, quoted_string_buffer, MAXQUOTESTR); + if (type != QUOTESTRING) { + print_error("Bad DESCRIPTION", quoted_string_buffer, type); + free_node(np); + return NULL; + } + if (ds_get_boolean(DS_LIBRARY_ID, DS_LIB_SAVE_MIB_DESCRS)) { + np->description = strdup (quoted_string_buffer); + } + break; + + default: + /* NOTHING */ + break; + } + type = get_token(fp, token, MAXTOKEN); + } + return merge_parse_objectid(np, fp, name); +} + +/* + * Parses a TRAP-TYPE macro. + * Returns 0 on error. + */ +static struct node * +parse_trapDefinition(FILE *fp, + char *name) +{ + register int type; + char token[MAXTOKEN]; + char quoted_string_buffer[MAXQUOTESTR]; + register struct node *np; + + np = alloc_node(current_module); + if (np == NULL) return(NULL); + type = get_token(fp, token, MAXTOKEN); + while (type != EQUALS && type != ENDOFFILE) { + switch (type) { + case DESCRIPTION: + type = get_token(fp, quoted_string_buffer, MAXQUOTESTR); + if (type != QUOTESTRING) { + print_error("Bad DESCRIPTION", quoted_string_buffer, type); + free_node(np); + return NULL; + } + if (ds_get_boolean(DS_LIBRARY_ID, DS_LIB_SAVE_MIB_DESCRS)) { + np->description = strdup (quoted_string_buffer); + } + break; + case ENTERPRISE: + type = get_token(fp, token, MAXTOKEN); + if (type == LEFTBRACKET) { + type = get_token(fp, token, MAXTOKEN); + if (type != LABEL) { + print_error("Bad Trap Format", token, type); + free_node(np); + return NULL; + } + np->parent = strdup(token); + /* Get right bracket */ + type = get_token(fp, token, MAXTOKEN); + } + else if (type == LABEL) + np->parent = strdup(token); + break; + default: + /* NOTHING */ + break; + } + type = get_token(fp, token, MAXTOKEN); + } + type = get_token(fp, token, MAXTOKEN); + + np->label = strdup(name); + + if (type != NUMBER) { + print_error("Expected a Number", token, type); + free_node(np); + return NULL; + } + np->subid = atoi(token); + np->next = alloc_node(current_module); + if (np->next == NULL) { + free_node(np); + return(NULL); + } + np->next->parent = np->parent; + np->parent = (char *)malloc(strlen(np->parent)+2); + if (np->parent == NULL) { + free_node(np->next); free_node(np); + return(NULL); + } + strcpy(np->parent, np->next->parent); + strcat(np->parent, "#"); + np->next->label = strdup(np->parent); + return np; +} + + +/* + * Parses a compliance macro + * Returns 0 on error. + */ +static struct node * +parse_compliance(FILE *fp, + char *name) +{ + register int type; + char token[MAXTOKEN]; + char quoted_string_buffer[MAXQUOTESTR]; + register struct node *np; + + np = alloc_node(current_module); + if (np == NULL) return(NULL); + type = get_token(fp, token, MAXTOKEN); + while (type != EQUALS && type != ENDOFFILE) { + type = get_token(fp, quoted_string_buffer, MAXQUOTESTR); + } + return merge_parse_objectid(np, fp, name); +} + + +/* + * Parses a capabilities macro + * Returns 0 on error. + */ +static struct node * +parse_capabilities(FILE *fp, + char *name) +{ + register int type; + char token[MAXTOKEN]; + char quoted_string_buffer[MAXQUOTESTR]; + register struct node *np; + + np = alloc_node(current_module); + if (np == NULL) return(NULL); + type = get_token(fp, token, MAXTOKEN); + while (type != EQUALS && type != ENDOFFILE) { + type = get_token(fp, quoted_string_buffer, MAXQUOTESTR); + } + return merge_parse_objectid(np, fp, name); +} + +/* + * Parses a module identity macro + * Returns 0 on error. + */ +static struct node * +parse_moduleIdentity(FILE *fp, + char *name) +{ + register int type; + char token[MAXTOKEN]; + char quoted_string_buffer[MAXQUOTESTR]; + register struct node *np; + + np = alloc_node(current_module); + if (np == NULL) return(NULL); + type = get_token(fp, token, MAXTOKEN); + while (type != EQUALS && type != ENDOFFILE) { + type = get_token(fp, quoted_string_buffer, MAXQUOTESTR); + } + return merge_parse_objectid(np, fp, name); +} + + +/* + * Parses a MACRO definition + * Expect BEGIN, discard everything to end. + * Returns 0 on error. + */ +static struct node * +parse_macro(FILE *fp, + char *name) +{ + register int type; + char token[MAXTOKEN]; + struct node *np; + int iLine = Line; + + np = alloc_node(current_module); + if (np == NULL) return(NULL); + type = get_token(fp, token, sizeof(token)); + while (type != EQUALS && type != ENDOFFILE) { + type = get_token(fp, token, sizeof(token)); + } + if (type != EQUALS) return NULL; + while (type != BEGIN && type != ENDOFFILE) { + type = get_token(fp, token, sizeof(token)); + } + if (type != BEGIN) return NULL; + while (type != END && type != ENDOFFILE) { + type = get_token(fp, token, sizeof(token)); + } + if (type != END) return NULL; + + if (ds_get_int(DS_LIBRARY_ID, DS_LIB_MIB_WARNINGS)) + snmp_log(LOG_WARNING, + "%s MACRO (lines %d..%d parsed and ignored).\n", name, iLine, Line); + + return np; +} + +/* + * Parses a module import clause + * loading any modules referenced + */ +static void +parse_imports(FILE *fp) +{ + register int type; + char token[MAXTOKEN]; + char modbuf[256]; +#define MAX_IMPORTS 256 + struct module_import import_list[MAX_IMPORTS]; + int this_module, old_current_module; + const char *old_File; + int old_line; + struct module *mp; + + int import_count=0; /* Total number of imported descriptors */ + int i=0, old_i; /* index of first import from each module */ + + type = get_token(fp, token, MAXTOKEN); + + /* + * Parse the IMPORTS clause + */ + while (type != SEMI && type != ENDOFFILE) { + if (type == LABEL ) { + if (import_count == MAX_IMPORTS ) { + print_error("Too many imported symbols", token, type); + do { + type = get_token(fp, token, MAXTOKEN); + } while (type != SEMI && type != ENDOFFILE); + return; + } + import_list[import_count++].label = strdup(token); + } + else if ( type == FROM ) { + type = get_token(fp, token, MAXTOKEN); + if ( import_count == i ) { /* All imports are handled internally */ + type = get_token(fp, token, MAXTOKEN); + continue; + } + this_module = which_module(token); + + for ( old_i=i ; i<import_count ; ++i) + import_list[i].modid = this_module; + + old_current_module = current_module; /* Save state */ + old_File = File; + old_line = Line; + current_module = this_module; + + /* + * Recursively read any pre-requisite modules + */ + if (read_module_internal(token) == MODULE_NOT_FOUND ) { + for ( ; old_i<import_count ; ++old_i ) { + read_import_replacements( token, &import_list[old_i]); + } + } + + current_module = old_current_module; /* Restore state */ + File = old_File; + Line = old_line; + } + type = get_token(fp, token, MAXTOKEN); + } + + /* + * Save the import information + * in the global module table + */ + for ( mp=module_head ; mp ; mp=mp->next ) + if ( mp->modid == current_module) { + if ( import_count == 0) + return; + if (mp->imports && (mp->imports != root_imports)) + { + /* this can happen if all modules are in one source file. */ + for ( i=0 ; i<mp->no_imports; ++i ) { + DEBUGMSGTL(("parse-mibs", "#### freeing Module %d '%s' %d\n", + mp->modid, mp->imports[i].label, mp->imports[i].modid)); + free((char *)mp->imports[i].label); + } + free((char*)mp->imports); + } + mp->imports = (struct module_import *) + calloc(import_count, sizeof(struct module_import)); + if (mp->imports == NULL) return; + for ( i=0 ; i<import_count ; ++i ) { + mp->imports[i].label = import_list[i].label; + mp->imports[i].modid = import_list[i].modid; + DEBUGMSGTL(("parse-mibs", "#### adding Module %d '%s' %d\n", + mp->modid, mp->imports[i].label, mp->imports[i].modid)); + } + mp->no_imports = import_count; + return; + } + + /* + * Shouldn't get this far + */ + print_module_not_found(module_name(current_module,modbuf)); + return; +} + + + +/* + * MIB module handling routines + */ + +static void dump_module_list (void) +{ + struct module *mp = module_head; + + DEBUGMSGTL(("parse-mibs", "Module list:\n")); + while (mp) { + DEBUGMSGTL(("parse-mibs", " %s %d %s %d\n", mp->name, mp->modid, mp->file, mp->no_imports)); + mp = mp->next; + } +} + +int +which_module(const char *name) +{ + struct module *mp; + + for ( mp=module_head ; mp ; mp=mp->next ) + if ( !label_compare(mp->name, name)) + return(mp->modid); + + DEBUGMSGTL(("parse-mibs", "Module %s not found\n", name)); + return(-1); +} + +/* + * module_name - copy module name to user buffer, return ptr to same. + */ +char * +module_name (int modid, + char *cp) +{ + struct module *mp; + + for ( mp=module_head ; mp ; mp=mp->next ) + if ( mp->modid == modid ) + { + strcpy(cp, mp->name); + return(cp); + } + + DEBUGMSGTL(("parse-mibs", "Module %d not found\n", modid)); + sprintf(cp, "#%d", modid); + return(cp); +} + +/* + * Backwards compatability + * Read newer modules that replace the one specified:- + * either all of them (read_module_replacements), + * or those relating to a specified identifier (read_import_replacements) + * plus an interface to add new replacement requirements + */ +void +add_module_replacement(const char *old_module, + const char *new_module_name, + const char *tag, + int len) +{ + struct module_compatability *mcp; + + mcp = (struct module_compatability *) + calloc(1, sizeof( struct module_compatability)); + if (mcp == NULL) return; + + mcp->old_module = strdup( old_module ); + mcp->new_module = strdup( new_module_name ); + if (tag) + mcp->tag = strdup( tag ); + mcp->tag_len = len; + + mcp->next = module_map_head; + module_map_head = mcp; +} + +static void +read_module_replacements(const char *name) +{ + struct module_compatability *mcp; + + for ( mcp=module_map_head ; mcp; mcp=mcp->next ) { + if ( !label_compare( mcp->old_module, name )) { + if (ds_get_int(DS_LIBRARY_ID, DS_LIB_MIB_WARNINGS)) + snmp_log(LOG_WARNING, + "Loading replacement module %s for %s (%s)\n", + mcp->new_module, name, File); + (void)read_module( mcp->new_module ); + return; + } + } + if (!ds_get_boolean(DS_LIBRARY_ID, DS_LIB_MIB_ERRORS)) + print_module_not_found(name); + +} + +static void +read_import_replacements(const char *old_module_name, + struct module_import *identifier) +{ + struct module_compatability *mcp; + + /* + * Look for matches first + */ + for ( mcp=module_map_head ; mcp; mcp=mcp->next ) { + if ( !label_compare( mcp->old_module, old_module_name )) { + + if ( /* exact match */ + ( mcp->tag_len==0 && + (mcp->tag == NULL || + !label_compare( mcp->tag, identifier->label ))) || + /* prefix match */ + ( mcp->tag_len!=0 && + !strncmp( mcp->tag, identifier->label, mcp->tag_len )) + ) { + + if (ds_get_int(DS_LIBRARY_ID, DS_LIB_MIB_WARNINGS)) + snmp_log(LOG_WARNING, + "Importing %s from replacement module %s instead of %s (%s)\n", + identifier->label, mcp->new_module, old_module_name, File); + (void)read_module( mcp->new_module ); + identifier->modid = which_module(mcp->new_module); + return; /* finished! */ + } + } + } + + /* + * If no exact match, load everything relevant + */ + read_module_replacements( old_module_name ); +} + + +/* + * Read in the named module + * Returns the root of the whole tree + * (by analogy with 'read_mib') + */ +static int +read_module_internal (const char *name) +{ + struct module *mp; + FILE *fp; + struct node *np; + + if ( tree_head == NULL ) + init_mib_internals(); /* was init_mib */ + + for ( mp=module_head ; mp ; mp=mp->next ) + if ( !label_compare(mp->name, name)) { + const char *oldFile = File; + int oldLine = Line; + + if ( mp->no_imports != -1 ) { + DEBUGMSGTL(("parse-mibs", "Module %s already loaded\n", name)); + return MODULE_ALREADY_LOADED; + } + if ((fp = fopen(mp->file, "r")) == NULL) { + snmp_log_perror(mp->file); + return MODULE_LOAD_FAILED; + } + mp->no_imports=0; /* Note that we've read the file */ + File = mp->file; + Line = 1; + /* + * Parse the file + */ + np = parse( fp, NULL ); + fclose(fp); + File = oldFile; + Line = oldLine; + return MODULE_LOADED_OK; + } + + if (ds_get_int(DS_LIBRARY_ID, DS_LIB_MIB_WARNINGS) > 1) + snmp_log(LOG_WARNING, "Module %s not found\n", name); + return MODULE_NOT_FOUND; +} + +void +adopt_orphans (void) +{ + struct node *np, *onp; + struct tree *tp; + int i, adopted; + + if ( !orphan_nodes ) + return; + init_node_hash(orphan_nodes); + orphan_nodes = NULL; + + while (1) { + adopted = 0; + for ( i = 0; i < NHASHSIZE; i++) + if ( nbuckets[i] ) { + for ( np = nbuckets[i] ; np!= NULL ; np=np->next ) + tp = find_tree_node( np->parent, -1 ); + if ( tp ) { + do_subtree( tp, &np ); + adopted = 1; + } + } + if ( adopted == 0 ) + break; + } + + /* + * Report on outstanding orphans + * and link them back into the orphan list + */ + for (i = 0; i < NHASHSIZE; i++) + if ( nbuckets[i] ) { + if ( orphan_nodes ) + onp = np->next = nbuckets[i]; + else + onp = orphan_nodes = nbuckets[i]; + nbuckets[i] = NULL; + while (onp) { + char modbuf[256]; + snmp_log (LOG_WARNING, + "Unlinked OID in %s: %s ::= { %s %ld }\n", + module_name(onp->modid, modbuf), + (onp->label ? onp->label : "<no label>"), + (onp->parent ? onp->parent : "<no parent>"), + onp->subid); + + np = onp; + onp = onp->next; + } + } +} + +struct tree * +read_module(const char *name) +{ + if ( read_module_internal(name) == MODULE_NOT_FOUND ) + read_module_replacements( name ); + return tree_head; +} + +void +unload_module_by_ID( int modID, struct tree *tree_top ) +{ + struct tree *tp, *prev, *next; + int i; + + prev = NULL; + for ( tp=tree_top ; tp ; tp=next ) { + next = tp->next_peer; + /* + * This next section looks rather complex. + * Essentially, this is equivalent to the code fragment: + * if (tp->modID = modID) + * tp->number_modules--; + * but handles one tree node being part of several modules. + */ + for ( i=0 ; i<tp->number_modules ; i++ ) { + if ( tp->module_list[i] == modID ) { + tp->number_modules--; + switch ( tp->number_modules ) { + + case 0: /* That was the only module */ + tp->modid = -1; /* Mark as unused */ + break; + + case 1: /* We did have a list of two, but this is no + longer needed. Transfer the other entry + ( i.e. module_list[1-i] - think about it! ) + to the 'single' slot tp->modid, and discard + the list. + */ + tp->modid = tp->module_list[1-i]; + free(tp->module_list); + tp->module_list = NULL; /* let's be tidy */ + break; + + default: /* We still need the list, so shuffle down + all following entries to close up the gap */ + while ( i < tp->number_modules ) { + tp->module_list[i] = tp->module_list[i+1]; + i++; + } + break; + } + break; /* Don't need to look through the rest of the list */ + } + } + + /* + * OK - that's dealt with *this* node. + * Now let's look at the children. + * (Isn't recursion wonderful!) + */ + if ( tp->child_list ) + unload_module_by_ID( modID, tp->child_list ); + + + if ( tp->number_modules == 0 ) { + /* This node isn't needed any more (except perhaps + for the sake of the children) */ + if ( tp->child_list == NULL ) { + if ( prev ) + prev->next_peer = tp->next_peer; + else + tp->parent->child_list = tp->next_peer; + free_tree( tp ); + } + else + free_partial_tree( tp, TRUE ); + } + else + prev = tp; + } +} + +int +unload_module(const char *name) +{ + struct module *mp; + int modID = -1; + + for ( mp=module_head ; mp ; mp=mp->next ) + if ( !label_compare(mp->name, name)) { + modID = mp->modid; + break; + } + + if ( modID == -1 ) { + DEBUGMSGTL(("unload-mib", "Module %s not found to unload\n", name)); + return MODULE_NOT_FOUND; + } + unload_module_by_ID( modID, tree_head ); + mp->no_imports = -1; /* mark as unloaded */ + return MODULE_LOADED_OK; /* Well, you know what I mean! */ +} + +static void +new_module (const char *name, + const char *file) +{ + struct module *mp; + + for ( mp=module_head ; mp ; mp=mp->next ) + if ( !label_compare(mp->name, name)) { + DEBUGMSGTL(("parse-mibs", "Module %s already noted\n", name)); + /* Not the same file */ + if (label_compare(mp->file, file)) { + if (ds_get_int(DS_LIBRARY_ID, DS_LIB_MIB_WARNINGS)) + snmp_log(LOG_WARNING, + "Warning: Module %s was in %s now is %s\n", + name, mp->file, file); + + /* Use the new one in preference */ + free(mp->file); + mp->file = strdup(file); + } + return; + } + + /* Add this module to the list */ + DEBUGMSGTL(("parse-mibs", " Module %d %s is in %s\n", max_module, name, file)); + mp = (struct module *) calloc(1, sizeof(struct module)); + if (mp == NULL) return; + mp->name = strdup(name); + mp->file = strdup(file); + mp->imports = NULL; + mp->no_imports = -1; /* Not yet loaded */ + mp->modid = max_module; + ++max_module; + + mp->next = module_head; /* Or add to the *end* of the list? */ + module_head = mp; +} + + + + +/* + * Parses a mib file and returns a linked list of nodes found in the file. + * Returns NULL on error. + */ +static struct node * +parse(FILE *fp, + struct node *root) +{ + char token[MAXTOKEN]; + char name[MAXTOKEN]; + int type = LABEL; + int lasttype = LABEL; + +#define BETWEEN_MIBS 1 +#define IN_MIB 2 + int state = BETWEEN_MIBS; + struct node *np, *nnp; + + DEBUGMSGTL(("parse-file", "Parsing file: %s...\n", File)); + + if (last_err_module) free(last_err_module); last_err_module = 0; + + np = root; + if (np != NULL) { + /* now find end of chain */ + while(np->next) + np = np->next; + } + + while (type != ENDOFFILE){ + if (lasttype == CONTINUE) lasttype = type; + else type = lasttype = get_token(fp, token, MAXTOKEN); + + switch (type) { + case END: + if (state != IN_MIB){ + print_error("Error, END before start of MIB", NULL, type); + return NULL; + } + else { + struct module *mp; +#ifdef TEST + printf("\nNodes for Module %s:\n", name); + print_nodes( stdout, np ); +#endif + for (mp = module_head; mp; mp = mp->next) + if (mp->modid == current_module) break; + do_linkup(mp, root); + np = root = NULL; + } + state = BETWEEN_MIBS; +#ifdef TEST + if (ds_get_int(DS_LIBRARY_ID, DS_LIB_MIB_WARNINGS)) + xmalloc_stats (stderr); +#endif + continue; + case IMPORTS: + parse_imports( fp ); + continue; + case EXPORTS: + while (type != SEMI && type != ENDOFFILE) + type = get_token(fp, token, MAXTOKEN); + continue; + case LABEL: + break; + case ENDOFFILE: + continue; + default: + strcpy(name, token); + type = get_token(fp, token, MAXTOKEN); + nnp = NULL; + if (type == MACRO) { + nnp = parse_macro(fp, name); + if (nnp == NULL){ + print_error("Bad parse of MACRO", NULL, type); + /*return NULL;*/ + } + free_node(nnp); /* IGNORE */ + nnp = NULL; + } + else + print_error(name, "is a reserved word", lasttype); + continue; /* see if we can parse the rest of the file */ + } + strcpy(name, token); + type = get_token(fp, token, MAXTOKEN); + nnp = NULL; + + /* Handle obsolete method to assign an object identifier to a + module*/ + if (lasttype == LABEL && type == LEFTBRACKET) { + while (type != RIGHTBRACKET && type != ENDOFFILE) + type = get_token(fp, token, MAXTOKEN); + if (type == ENDOFFILE){ + print_error("Expected \"}\"", token, type); + return NULL; + } + type = get_token(fp, token, MAXTOKEN); + } + + switch (type) { + case DEFINITIONS: + if (state != BETWEEN_MIBS){ + print_error("Error, nested MIBS", NULL, type); + return NULL; + } + state = IN_MIB; + current_module = which_module( name ); + if ( current_module == -1 ) { + new_module(name, File); + current_module = which_module(name); + } + DEBUGMSGTL(("parse-mibs", "Parsing MIB: %d %s\n", current_module, name)); + while ((type = get_token (fp, token, MAXTOKEN)) != ENDOFFILE) + if (type == BEGIN) break; + break; + case OBJTYPE: + nnp = parse_objecttype(fp, name); + if (nnp == NULL){ + print_error("Bad parse of OBJECT-TYPE", NULL, type); + return NULL; + } + break; + case OBJGROUP: + nnp = parse_objectgroup(fp, name); + if (nnp == NULL){ + print_error("Bad parse of OBJECT-GROUP", NULL, type); + return NULL; + } + break; + case TRAPTYPE: + nnp = parse_trapDefinition(fp, name); + if (nnp == NULL){ + print_error("Bad parse of TRAP-TYPE", NULL, type); + return NULL; + } + break; + case NOTIFTYPE: + nnp = parse_notificationDefinition(fp, name); + if (nnp == NULL){ + print_error("Bad parse of NOTIFICATION-TYPE", NULL, type); + return NULL; + } + break; + case COMPLIANCE: + nnp = parse_compliance(fp, name); + if (nnp == NULL){ + print_error("Bad parse of MODULE-COMPLIANCE", NULL, type); + return NULL; + } + break; + case CAPABILITIES: + nnp = parse_capabilities(fp, name); + if (nnp == NULL){ + print_error("Bad parse of AGENT-CAPABILITIES", NULL, type); + return NULL; + } + break; + case MACRO: + nnp = parse_macro(fp, name); + if (nnp == NULL){ + print_error("Bad parse of MACRO", NULL, type); + /*return NULL;*/ + } + free_node(nnp); /* IGNORE */ + nnp = NULL; + break; + case MODULEIDENTITY: + nnp = parse_moduleIdentity(fp, name); + if (nnp == NULL){ + print_error("Bad parse of MODULE-IDENTITY", NULL, type); + return NULL; + } + break; + case OBJID: + type = get_token(fp, token, MAXTOKEN); + if (type != EQUALS){ + print_error("Expected \"::=\"", token, type); + return NULL; + } + nnp = parse_objectid(fp, name); + if (nnp == NULL){ + print_error("Bad parse of OBJECT IDENTIFIER", NULL, type); + return NULL; + } + break; + case EQUALS: + nnp = parse_asntype(fp, name, &type, token); + lasttype = CONTINUE; + break; + case ENDOFFILE: + break; + default: + print_error("Bad operator", token, type); + return NULL; + } + if (nnp) { + if (np) np->next = nnp; + else np = root = nnp; + while (np->next) np = np->next; + } + } + DEBUGMSGTL(("parse-file", "End of file (%s)\n", File)); + return root; +} + +/* return zero if character is not a label character. */ +static int +is_labelchar (int ich) +{ + if ((isalnum(ich)) || (ich == '-')) + return 1; + if (ich == '_' && ds_get_boolean(DS_LIBRARY_ID, DS_LIB_MIB_PARSE_LABEL)) + return 1; + + return 0; +} + +/* + * Parses a token from the file. The type of the token parsed is returned, + * and the text is placed in the string pointed to by token. + * Warning: this method may recurse. + */ +static int +get_token(FILE *fp, + char *token, + int maxtlen) +{ + register int ch, ch_next; + register char *cp = token; + register int hash = 0; + register struct tok *tp; + int too_long = 0; + + /* skip all white space */ + do { + ch = getc(fp); + if (ch == '\n') + Line++; + } + while(isspace(ch) && ch != EOF); + *cp++ = ch; *cp = '\0'; + switch (ch) { + case EOF: + return ENDOFFILE; + case '"': + return parseQuoteString(fp, token, maxtlen); + case '\'': /* binary or hex constant */ + while ((ch = getc(fp)) != EOF && ch != '\'' && cp-token < maxtlen-2) + *cp++ = ch; + if (ch == '\'') { + unsigned long val = 0; + *cp++ = '\''; + *cp++ = ch = getc(fp); + *cp = 0; + cp = token+1; + switch (ch) { + case EOF: + return ENDOFFILE; + case 'b': + case 'B': + while ((ch = *cp++) != '\'') + if (ch != '0' && ch != '1') return LABEL; + else val = val * 2 + ch - '0'; + break; + case 'h': + case 'H': + while ((ch = *cp++) != '\'') + if ('0' <= ch && ch <= '9') val = val*16+ch-'0'; + else if ('a' <= ch && ch <= 'f') val = val*16+ch-'a'+10; + else if ('A' <= ch && ch <= 'F') val = val*16+ch-'A'+10; + else return LABEL; + break; + default: + return LABEL; + } + sprintf(token, "%ld", val); + return NUMBER; + } + else return LABEL; + case '(': + return LEFTPAREN; + case ')': + return RIGHTPAREN; + case '{': + return LEFTBRACKET; + case '}': + return RIGHTBRACKET; + case ';': + return SEMI; + case ',': + return COMMA; + case '|': + return BAR; + case '.': + ch_next = getc(fp); + if (ch_next == '.') return RANGE; + ungetc(ch_next, fp); + return LABEL; + case ':': + ch_next = getc(fp); + if (ch_next != ':') { + ungetc(ch_next, fp); + return LABEL; + } + ch_next = getc(fp); + if (ch_next != '=') { + ungetc(ch_next, fp); + return LABEL; + } + return EQUALS; + case '-': + ch_next = getc(fp); + if (ch_next == '-') { + if (ds_get_boolean(DS_LIBRARY_ID, DS_LIB_MIB_COMMENT_TERM)) { + /* Treat the rest of this line as a comment. */ + while ((ch_next != EOF) && (ch_next != '\n')) + ch_next = getc(fp); + } else { + /* Treat the rest of the line or until another '--' as a comment */ + /* (this is the "technically" correct way to parse comments) */ + ch = ' '; + ch_next = getc(fp); + while (ch_next != EOF && ch_next != '\n' && + (ch != '-' || ch_next != '-')) { + ch = ch_next; ch_next = getc(fp); + } + } + if (ch_next == EOF) return ENDOFFILE; + if (ch_next == '\n') Line++; + return get_token (fp, token, maxtlen); + } + ungetc(ch_next, fp); + default: + /* + * Accumulate characters until end of token is found. Then attempt to + * match this token as a reserved word. If a match is found, return the + * type. Else it is a label. + */ + if (!is_labelchar(ch)) return LABEL; + hash += tolower(ch); + more: + while (is_labelchar(ch_next = getc(fp))) { + hash += tolower(ch_next); + if (cp - token < maxtlen - 1) *cp++ = ch_next; + else too_long = 1; + } + ungetc(ch_next, fp); + *cp = '\0'; + + if (too_long) + print_error("Warning: token too long", token, CONTINUE); + for (tp = buckets[BUCKET(hash)]; tp; tp = tp->next) { + if ((tp->hash == hash) && (!label_compare(tp->name, token))) + break; + } + if (tp) { + if (tp->token != CONTINUE) return (tp->token); + while (isspace((ch_next = getc(fp)))) + if (ch_next == '\n') Line++; + if (ch_next == EOF) return ENDOFFILE; + if (isalnum(ch_next)) { + *cp++ = ch_next; + hash += tolower(ch_next); + goto more; + } + } + if (token[0] == '-' || isdigit(token[0])) { + for(cp = token+1; *cp; cp++) + if (!isdigit(*cp)) + return LABEL; + return NUMBER; + } + return LABEL; + } +} + +int +snmp_get_token(FILE *fp, + char *token, + int maxtlen) +{ + return get_token(fp, token, maxtlen); +} + +int +add_mibdir(const char *dirname) +{ +#ifndef ECOSFIXME_NEEDFILESYSTEM + FILE *fp, *ip; + DIR *dir, *dir2; + const char *oldFile = File; + struct dirent *file; + char token[MAXTOKEN]; + char tmpstr[300]; + int count = 0; +#ifndef WIN32 + struct stat dir_stat, idx_stat; + char tmpstr1[300]; +#endif + + DEBUGMSGTL(("parse-mibs", "Scanning directory %s\n", dirname)); +#ifndef WIN32 + sprintf(token, "%s/%s", dirname, ".index"); + if (stat(token, &idx_stat) == 0 && stat(dirname, &dir_stat) == 0) { + if (dir_stat.st_mtime < idx_stat.st_mtime) { + DEBUGMSGTL(("parse-mibs", "The index is good\n")); + if ((ip = fopen(token, "r")) != NULL) { + while (fscanf(ip, "%s %s\n", token, tmpstr) == 2) { + sprintf(tmpstr1, "%s/%s", dirname, tmpstr); + new_module(token, tmpstr1); + count++; + } + fclose(ip); + return count; + } + else DEBUGMSGTL(("parse-mibs", "Can't read index\n")); + } + else DEBUGMSGTL(("parse-mibs", "Index outdated\n")); + } + else DEBUGMSGTL(("parse-mibs", "No index\n")); +#endif + + if ((dir = opendir(dirname))) { + sprintf(tmpstr, "%s/.index", dirname); + ip = fopen(tmpstr, "w"); + while ((file = readdir(dir))) { + /* Only parse file names not beginning with a '.' */ + if (file->d_name != NULL && file->d_name[0] != '.') { + sprintf(tmpstr, "%s/%s", dirname, file->d_name); + if ((dir2 = opendir(tmpstr))) { + /* file is a directory, don't read it */ + closedir(dir2); + } else { + /* which module is this */ + if ((fp = fopen(tmpstr, "r")) == NULL) { + snmp_log_perror(tmpstr); + continue; + } + DEBUGMSGTL(("parse-mibs", "Checking file: %s...\n", tmpstr)); + Line = 1; + File = tmpstr; + get_token( fp, token, MAXTOKEN); + new_module(token, tmpstr); + count++; + fclose (fp); + if (ip) fprintf(ip, "%s %s\n", token, file->d_name); + } + } + } + File = oldFile; + closedir(dir); + if (ip) fclose(ip); + return(count); + } + return(-1); + +#else + // __ECOS + +#if 0 // MIBS not needed for agent use of library. + int count = 0; + int i; + + static const char *snmp_miblist[] = { + "EtherLike-MIB", + "IANAifType-MIB", + "IF-MIB", + "IP-MIB", + "RFC-1215", + "SNMPv2-CONF", + "SNMPv2-MIB", + "SNMPv2-SMI", + "SNMPv2-TC", + "SNMPv2-TM", + "TCP-MIB", + "UDP-MIB", + }; + + for ( i = 0; + i < sizeof(snmp_miblist)/sizeof(snmp_miblist[0]); + i++ ) { + + new_module(snmp_miblist[i],snmp_miblist[i]); + count++; + } + return(count); +#else + return 0; +#endif + +#endif +} + + +/* + * Returns the root of the whole tree + * (for backwards compatability) + */ +struct tree * +read_mib(const char *filename) +{ + FILE *fp; + char token[MAXTOKEN]; + + fp = fopen(filename, "r"); + if (fp == NULL) { + snmp_log_perror(filename); + return NULL; + } + Line = 1; + File = filename; + DEBUGMSGTL(("parse-mibs", "Parsing file: %s...\n", filename)); + get_token( fp, token, MAXTOKEN); + fclose(fp); + new_module(token, filename); + (void) read_module(token); + + return tree_head; +} + + +struct tree * +read_all_mibs() +{ + struct module *mp; + + for ( mp=module_head ; mp ; mp=mp->next ) + if ( mp->no_imports == -1 ) + read_module( mp->name ); + adopt_orphans(); + + return tree_head; +} + + +#ifdef TEST +main(int argc, char *argv[]) +{ + int i; + struct tree *tp; + ds_set_int(DS_LIBRARY_ID, DS_LIB_MIB_WARNINGS, 2); + + init_mib(); + + if ( argc == 1 ) + (void) read_all_mibs(); + else + for ( i=1 ; i<argc ; i++ ) + read_mib( argv[i] ); + + for ( tp = tree_head ; tp ; tp=tp->next_peer ) + print_subtree( stdout, tp, 0 ); + free_tree( tree_head ); + + return 0; +} +#endif /* TEST */ + +static int +parseQuoteString(FILE *fp, + char *token, + int maxtlen) +{ + register int ch; + int count = 0; + int too_long = 0; + char *token_start = token; + + for (ch = getc(fp); ch != EOF; ch = getc(fp)) { + if (ch == '\r') continue; + if (ch == '\n') { + Line++; + } + else if (ch == '"') { + *token = '\0'; + if (too_long && + ds_get_int(DS_LIBRARY_ID, DS_LIB_MIB_WARNINGS) > 1) + { + /* show short form for brevity sake */ + char ch_save = *(token_start + 50); + *(token_start + 50) = '\0'; + print_error ("Warning: string too long", + token_start, QUOTESTRING); + *(token_start + 50) = ch_save; + } + return QUOTESTRING; + } + /* maximum description length check. If greater, keep parsing + but truncate the string */ + if (++count < maxtlen) + *token++ = ch; + else too_long = 1; + } + + return 0; +} + +/* + * struct index_list * + * getIndexes(FILE *fp): + * This routine parses a string like { blah blah blah } and returns a + * list of the strings enclosed within it. + * + */ +static struct index_list * +getIndexes(FILE *fp, struct index_list **retp) { + int type; + char token[MAXTOKEN]; + char nextIsImplied = 0; + + struct index_list *mylist = NULL; + struct index_list **mypp = &mylist; + + free_indexes(retp); + + type = get_token(fp, token, MAXTOKEN); + + if (type != LEFTBRACKET) { + return NULL; + } + + type = get_token(fp, token, MAXTOKEN); + while (type != RIGHTBRACKET && type != ENDOFFILE) { + if ((type == LABEL) || (type & SYNTAX_MASK)) { + *mypp = (struct index_list *) calloc(1, sizeof(struct index_list)); + if (*mypp) { + (*mypp)->ilabel = strdup(token); + (*mypp)->isimplied = nextIsImplied; + mypp = &(*mypp)->next; + nextIsImplied = 0; + } + } else if (type == IMPLIED) { + nextIsImplied = 1; + } + type = get_token(fp, token, MAXTOKEN); + } + + *retp = mylist; + return mylist; +} + +static void +free_indexes(struct index_list **spp) { + if (spp && *spp) { + struct index_list *pp, *npp; + + pp = *spp; *spp = NULL; + + while(pp) { + npp = pp->next; + if (pp->ilabel) free(pp->ilabel); + free(pp); + pp = npp; + } + } +} + +static void +free_ranges(struct range_list **spp) { + if (spp && *spp) { + struct range_list *pp, *npp; + + pp = *spp; *spp = NULL; + + while(pp) { + npp = pp->next; + free(pp); + pp = npp; + } + } +} + +static void +free_enums(struct enum_list **spp) +{ + if (spp && *spp) { + struct enum_list *pp, *npp; + + pp = *spp; *spp = NULL; + + while(pp) + { + npp = pp->next; + if (pp->label) free(pp->label); + free(pp); + pp = npp; + } + } +} + +static struct enum_list * +copy_enums (struct enum_list *sp) +{ + struct enum_list *xp = NULL, **spp = &xp; + + while (sp) { + *spp = (struct enum_list *) calloc(1, sizeof(struct enum_list)); + if (!*spp) break; + (*spp)->label = strdup(sp->label); + (*spp)->value = sp->value; + spp = &(*spp)->next; + sp = sp->next; + } + return (xp); +} + +static struct range_list * +copy_ranges (struct range_list *sp) +{ + struct range_list *xp = NULL, **spp = &xp; + + while (sp) { + *spp = (struct range_list *) calloc(1, sizeof(struct range_list)); + if (!*spp) break; + (*spp)->low = sp->low; + (*spp)->high = sp->high; + spp = &(*spp)->next; + sp = sp->next; + } + return (xp); +} + +static struct index_list * +copy_indexes (struct index_list *sp) +{ + struct index_list *xp = NULL, **spp = &xp; + + while (sp) { + *spp = (struct index_list *) calloc(1, sizeof(struct index_list)); + if (!*spp) break; + (*spp)->ilabel = strdup(sp->ilabel); + spp = &(*spp)->next; + sp = sp->next; + } + return (xp); +} + +/* + * This routine parses a string like { blah blah blah } and returns OBJID if + * it is well formed, and NULL if not. + */ +static int +tossObjectIdentifier(FILE *fp) +{ + int type; + char token[MAXTOKEN]; + int bracketcount = 1; + + type = get_token(fp, token, MAXTOKEN); + + if (type != LEFTBRACKET) + return 0; + while ((type != RIGHTBRACKET || bracketcount > 0) && type != ENDOFFILE ) + { + type = get_token(fp, token, MAXTOKEN); + if (type == LEFTBRACKET) + bracketcount++; + else if (type == RIGHTBRACKET) + bracketcount--; + } + + if (type == RIGHTBRACKET) + return OBJID; + else + return 0; +} + +struct tree * +find_node(const char *name, + struct tree *subtree) /* Unused */ +{ + return( find_tree_node( name, -1 )); +} + +struct module * +find_module(int mid) +{ + struct module *mp; + + for(mp=module_head; mp!=NULL; mp = mp->next) { + if (mp->modid == mid) + break; + } + if (mp != 0) + return mp; + return NULL; +} + + +static char leave_indent[256]; +static int leave_was_simple; + +static void print_mib_leaves(FILE *f, struct tree *tp, int width) +{ struct tree *ntp; + char *ip = leave_indent+strlen(leave_indent)-1; + char last_ipch = *ip; + + *ip = '+'; + if (tp->type == 0) + fprintf(f, "%s--%s(%ld)\n", leave_indent, tp->label, tp->subid); + else { + const char *acc, *typ; + int size = 0; + switch (tp->access) { + case MIB_ACCESS_NOACCESS: acc = "----"; break; + case MIB_ACCESS_READONLY: acc = "-R--"; break; + case MIB_ACCESS_WRITEONLY: acc = "--W-"; break; + case MIB_ACCESS_READWRITE: acc = "-RW-"; break; + case MIB_ACCESS_NOTIFY: acc = "---N"; break; + case MIB_ACCESS_CREATE: acc = "CR--"; break; + default: acc = " "; break; + } + switch (tp->type) { + case TYPE_OBJID: typ = "ObjID "; break; + case TYPE_OCTETSTR: typ = "String "; size = 1; break; + case TYPE_INTEGER: + if (tp->enums) typ = "EnumVal "; + else typ = "Integer "; break; + case TYPE_NETADDR: typ = "NetAddr "; break; + case TYPE_IPADDR: typ = "IpAddr "; break; + case TYPE_COUNTER: typ = "Counter "; break; + case TYPE_GAUGE: typ = "Gauge "; break; + case TYPE_TIMETICKS: typ = "TimeTicks"; break; + case TYPE_OPAQUE: typ = "Opaque "; size = 1; break; + case TYPE_NULL: typ = "Null "; break; + case TYPE_COUNTER64: typ = "Counter64"; break; + case TYPE_BITSTRING: typ = "BitString"; break; + case TYPE_NSAPADDRESS: typ = "NsapAddr "; break; + case TYPE_UINTEGER: typ = "UInteger "; break; + default: typ = " "; break; + } + fprintf(f, "%s-- %s %s %s(%ld)\n", leave_indent, acc, typ, tp->label, tp->subid); + *ip = last_ipch; + if (tp->tc_index >= 0) + fprintf(f, "%s Textual Convention: %s\n", leave_indent, + tclist[tp->tc_index].descriptor); + if (tp->enums) { + struct enum_list *ep = tp->enums; + int cpos = 0, cmax = width - strlen(leave_indent) - 16; + fprintf(f, "%s Values: ", leave_indent); + while (ep) { + char buf[80]; + int bufw; + if (ep != tp->enums) fprintf(f, ", "); + sprintf(buf, "%s(%d)", ep->label, ep->value); + cpos += (bufw = strlen(buf) + 2); + if (cpos >= cmax) { + fprintf(f, "\n%s ", leave_indent); + cpos = bufw; + } + fprintf(f, "%s", buf); + ep = ep->next; + } + fprintf(f, "\n"); + } + if (tp->ranges) { + struct range_list *rp = tp->ranges; + if (size) fprintf(f, "%s Size: ", leave_indent); + else fprintf(f, "%s Range: ", leave_indent); + while (rp) { + if (rp != tp->ranges) fprintf(f, " | "); + if (rp->low == rp->high) fprintf(f, "%d", rp->low); + else fprintf(f, "%d..%d", rp->low, rp->high); + rp = rp->next; + } + fprintf(f, "\n"); + } + } + *ip = last_ipch; + strcat(leave_indent, " |"); + leave_was_simple = tp->type != 0; + + { int i, j, count = 0; + struct leave { + oid id; + struct tree *tp; + } *leaves, *lp; + + for (ntp = tp->child_list; ntp; ntp = ntp->next_peer) count++; + if (count) { + leaves = (struct leave *)calloc(count, sizeof(struct leave)); + if (!leaves) return; + for (ntp = tp->child_list, count = 0; ntp; ntp = ntp->next_peer) { + for (i = 0, lp = leaves; i < count; i++, lp++) + if (lp->id >= ntp->subid) break; + for (j = count; j > i; j--) leaves[j] = leaves[j-1]; + lp->id = ntp->subid; + lp->tp = ntp; + count++; + } + for (i = 1, lp = leaves; i <= count; i++, lp++) { + if (!leave_was_simple || lp->tp->type == 0) + fprintf(f, "%s\n", leave_indent); + if (i == count) ip[3] = ' '; + print_mib_leaves(f, lp->tp, width); + } + free(leaves); + leave_was_simple = 0; + } + } + ip[1] = 0; +} + +void print_mib_tree(FILE *f, struct tree *tp, int width) +{ + leave_indent[0] = ' '; + leave_indent[1] = 0; + leave_was_simple = 1; + print_mib_leaves(f, tp, width); +} + + +/* + * Merge the parsed object identifier with the existing node. + * If there is a problem with the identifier, release the existing node. + */ +static struct node * +merge_parse_objectid(struct node *np, + FILE *fp, + char *name) +{ + struct node *nnp; + + nnp = parse_objectid(fp, name); + if (nnp) { + /* apply last OID sub-identifier data to the information */ + /* already collected for this node. */ + struct node *headp, *nextp; + int ncount = 0; + nextp = headp = nnp; + while (nnp->next) { + nextp = nnp; + ncount++; + nnp = nnp->next; + } + + np->label = nnp->label; + np->subid = nnp->subid; + np->modid = nnp->modid; + np->parent = nnp->parent; + free(nnp); + + if (ncount) { + nextp->next = np; + np = headp; + } + } + else { + free_node(np); np = NULL; + } + + return np; +} + +/* + * transfer data to tree from node + * + * move pointers for alloc'd data from np to tp. + * this prevents them from being freed when np is released. + * parent member is not moved. + * + * CAUTION: nodes may be repeats of existing tree nodes. + * This can happen especially when resolving IMPORT clauses. + * + */ +static void +tree_from_node(struct tree *tp, struct node *np) +{ + free_partial_tree(tp, FALSE); + + tp->label = np->label; np->label = NULL; + tp->enums = np->enums; np->enums = NULL; + tp->ranges = np->ranges; np->ranges = NULL; + tp->indexes = np->indexes; np->indexes = NULL; + tp->hint = np->hint; np->hint = NULL; + tp->units = np->units; np->units = NULL; + tp->description = np->description; np->description = NULL; + + tp->subid = np->subid; + tp->tc_index = np->tc_index; + tp->type = translation_table[np->type]; + tp->access = np->access; + tp->status = np->status; + set_function(tp); +} +
