From cadd830e5aca1f208541ea6d38da5b4a863db5cc Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Fri, 27 Jan 2006 21:36:35 +0000 Subject: Added textual MIB parsing support. See #45 --- mib/parse.c | 5244 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 5244 insertions(+) create mode 100644 mib/parse.c (limited to 'mib/parse.c') diff --git a/mib/parse.c b/mib/parse.c new file mode 100644 index 0000000..d4987d3 --- /dev/null +++ b/mib/parse.c @@ -0,0 +1,5244 @@ +/* + * Nate Nielsen + * + * Borrowed from net-snmp. Cleaned up a bit (see parse-net-snmp.patch) + * and readied for inclusion in rrdbot. Most of the additional code + * is at the top. + */ +/* + * parse.c + * + * Update: 1998-09-22 + * 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 + * fully qualified OID parsing patch + * + * Update: 1998-10-20 + * merge_anon_children patch + * + * Update: 1998-10-21 + * Merge_parse_objectid associates information with last node in chain. + */ +/* Portions of this file are subject to the following copyrights. See + * the Net-SNMP's COPYING file for more details and other copyrights + * that may apply: + */ +/****************************************************************** + 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. +******************************************************************/ +/* + * Copyright © 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms specified in the COPYING file + * distributed with the Net-SNMP package. + */ + +/* ----------------------------------------------------------------------------- + * ADDITIONAL RRDBOT COMPATIBILITY CODE + */ + +#include +#include +#include +#include +#include +#include + +#include "parse.h" + +/* + * A file with compatibility definitions for making the following + * code work without the net-snmp library and all that. + */ +#include "parse-compat.inc.c" + +/* -------------------------------------------------------------------------- */ + + + +/* + * 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 4096 +struct tc { /* textual conventions */ + int type; + int modid; + char *descriptor; + char *hint; + struct enum_list *enums; + struct range_list *ranges; +} tclist[MAXTC]; + +static int mibLine = 0; +static const char *File = "(none)"; +static int anonymous = 0; + +struct objgroup { + char *name; + int line; + struct objgroup *next; +} *objgroups = NULL, *objects = NULL, *notifs = NULL; + +#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 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 +#ifdef NOACCESS +#undef NOACCESS /* agent 'NOACCESS' token */ +#endif +#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 AGENTCAP 73 +#define MACRO 74 +#define IMPLIED 75 +#define SUPPORTS 76 +#define INCLUDES 77 +#define VARIATION 78 +#define REVISION 79 +#define NOTIMPL 80 +#define OBJECTS 81 +#define NOTIFICATIONS 82 +#define MODULE 83 +#define MINACCESS 84 +#define PRODREL 85 +#define WRSYNTAX 86 +#define CREATEREQ 87 +#define NOTIFGROUP 88 +#define MANDATORYGROUPS 89 +#define GROUP 90 +#define OBJECT 91 +#define IDENTIFIER 92 +#define CHOICE 93 +#define LEFTSQBRACK 95 +#define RIGHTSQBRACK 96 +#define IMPLICIT 97 +#define APPSYNTAX (98 | SYNTAX_MASK) +#define OBJSYNTAX (99 | SYNTAX_MASK) +#define SIMPLESYNTAX (100 | SYNTAX_MASK) +#define OBJNAME (101 | SYNTAX_MASK) +#define NOTIFNAME (102 | SYNTAX_MASK) +#define VARIABLES 103 +#define UNSIGNED32 (104 | SYNTAX_MASK) +#define INTEGER32 (105 | SYNTAX_MASK) +/* + * Beware of reaching SYNTAX_MASK (0x80) + */ + +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} + , + {"IDENTIFIER", sizeof("IDENTIFIER") - 1, IDENTIFIER} + , + {"OBJECT", sizeof("OBJECT") - 1, OBJECT} + , + {"NetworkAddress", sizeof("NetworkAddress") - 1, NETADDR} + , + {"Gauge", sizeof("Gauge") - 1, GAUGE} + , + {"Gauge32", sizeof("Gauge32") - 1, GAUGE} + , + {"Unsigned32", sizeof("Unsigned32") - 1, UNSIGNED32} + , + {"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, NOTIFGROUP} + , + {"DISPLAY-HINT", sizeof("DISPLAY-HINT") - 1, DISPLAYHINT} + , + {"FROM", sizeof("FROM") - 1, FROM} + , + {"AGENT-CAPABILITIES", sizeof("AGENT-CAPABILITIES") - 1, AGENTCAP} + , + {"MACRO", sizeof("MACRO") - 1, MACRO} + , + {"IMPLIED", sizeof("IMPLIED") - 1, IMPLIED} + , + {"SUPPORTS", sizeof("SUPPORTS") - 1, SUPPORTS} + , + {"INCLUDES", sizeof("INCLUDES") - 1, INCLUDES} + , + {"VARIATION", sizeof("VARIATION") - 1, VARIATION} + , + {"REVISION", sizeof("REVISION") - 1, REVISION} + , + {"not-implemented", sizeof("not-implemented") - 1, NOTIMPL} + , + {"OBJECTS", sizeof("OBJECTS") - 1, OBJECTS} + , + {"NOTIFICATIONS", sizeof("NOTIFICATIONS") - 1, NOTIFICATIONS} + , + {"MODULE", sizeof("MODULE") - 1, MODULE} + , + {"MIN-ACCESS", sizeof("MIN-ACCESS") - 1, MINACCESS} + , + {"PRODUCT-RELEASE", sizeof("PRODUCT-RELEASE") - 1, PRODREL} + , + {"WRITE-SYNTAX", sizeof("WRITE-SYNTAX") - 1, WRSYNTAX} + , + {"CREATION-REQUIRES", sizeof("CREATION-REQUIRES") - 1, CREATEREQ} + , + {"MANDATORY-GROUPS", sizeof("MANDATORY-GROUPS") - 1, MANDATORYGROUPS} + , + {"GROUP", sizeof("GROUP") - 1, GROUP} + , + {"CHOICE", sizeof("CHOICE") - 1, CHOICE} + , + {"IMPLICIT", sizeof("IMPLICIT") - 1, IMPLICIT} + , + {"ObjectSyntax", sizeof("ObjectSyntax") - 1, OBJSYNTAX} + , + {"SimpleSyntax", sizeof("SimpleSyntax") - 1, SIMPLESYNTAX} + , + {"ApplicationSyntax", sizeof("ApplicationSyntax") - 1, APPSYNTAX} + , + {"ObjectName", sizeof("ObjectName") - 1, OBJNAME} + , + {"NotificationName", sizeof("NotificationName") - 1, NOTIFNAME} + , + {"VARIABLES", sizeof("VARIABLES") - 1, VARIABLES} + , + {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}, + {"RFC1231-MIB", "TOKENRING-MIB", NULL, 0}, + {"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}, + {"RFC1406-MIB", "DS1-MIB", NULL, 0}, + {"RFC-1213", "RFC1213-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 *, int, + struct objgroup **); +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 struct varbind_list *getVarbinds(FILE * fp, struct varbind_list **); +static void free_indexes(struct index_list **); +static void free_varbinds(struct varbind_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 u_int compute_match(const char *search_base, const char *key); + +void +snmp_mib_toggle_options_usage(const char *lead, FILE * outf) +{ + fprintf(outf, "%su: %sallow the use of underlines in MIB symbols\n", + lead, ((netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_MIB_PARSE_LABEL)) ? + "dis" : "")); + fprintf(outf, "%sc: %sallow the use of \"--\" to terminate comments\n", + lead, ((netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_MIB_COMMENT_TERM)) ? + "" : "dis")); + + fprintf(outf, "%sd: %ssave the DESCRIPTIONs of the MIB objects\n", + lead, ((netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) ? + "do not " : "")); + + fprintf(outf, "%se: disable errors when MIB symbols conflict\n", lead); + + fprintf(outf, "%sw: enable warnings when MIB symbols conflict\n", lead); + + fprintf(outf, "%sW: enable detailed warnings when MIB symbols conflict\n", + lead); + + fprintf(outf, "%sR: replace MIB symbols from latest module\n", lead); +} + +char * +snmp_mib_toggle_options(char *options) +{ + if (options) { + while (*options) { + switch (*options) { + case 'u': + netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_MIB_PARSE_LABEL, + !netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_MIB_PARSE_LABEL)); + break; + + case 'c': + netsnmp_ds_toggle_boolean(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_MIB_COMMENT_TERM); + break; + + case 'e': + netsnmp_ds_toggle_boolean(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_MIB_ERRORS); + break; + + case 'w': + netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_MIB_WARNINGS, 1); + break; + + case 'W': + netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_MIB_WARNINGS, 2); + break; + + case 'd': + netsnmp_ds_toggle_boolean(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_SAVE_MIB_DESCRS); + break; + + case 'R': + netsnmp_ds_toggle_boolean(NETSNMP_DS_LIBRARY_ID, + NETSNMP_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) + return 0; + 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) +{ + struct node *np, *nextp; + 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, mibLine, + File); + else if (token && *token) + snmp_log(LOG_ERR, "%s (%s): At line %d in %s\n", string, token, + mibLine, File); + else + snmp_log(LOG_ERR, "%s: At line %d in %s\n", string, mibLine, 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; + np->filename = strdup(File); + np->lineno = mibLine; + } + 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; + + if (!ntp) { /* this tree has no parent */ + DEBUGMSGTL(("unlink_tree", "Tree node %s has no parent\n", + tp->label)); + } else { + ntp = ntp->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; + } + + if (tree_head == tp) + tree_head = 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); + free_varbinds(&tp->varbinds); + if (!keep_label) + SNMP_FREE(tp->label); + SNMP_FREE(tp->hint); + SNMP_FREE(tp->units); + SNMP_FREE(tp->description); + SNMP_FREE(tp->augments); + SNMP_FREE(tp->defaultValue); +} + +/* + * 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); + free_varbinds(&np->varbinds); + 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->defaultValue) + free(np->defaultValue); + if (np->parent) + free(np->parent); + if (np->augments) + free(np->augments); + if (np->filename) + free(np->filename); + 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 varbind_list *vp; + 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->augments) + fprintf(fp, " Augments: %s\n", np->augments); + if (np->varbinds) { + fprintf(fp, " Varbinds: \n"); + for (vp = np->varbinds; vp; vp = vp->next) { + fprintf(fp, " %s\n", vp->vblabel); + } + } + if (np->hint) + fprintf(fp, " Hint: %s\n", np->hint); + if (np->units) + fprintf(fp, " Units: %s\n", np->units); + if (np->defaultValue) + fprintf(fp, " DefaultValue: %s\n", np->defaultValue); + } +} +#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_NETADDR; + 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 INTEGER32: + translation_table[count] = TYPE_INTEGER32; + break; + case UINTEGER32: + translation_table[count] = TYPE_UINTEGER; + break; + case UNSIGNED32: + translation_table[count] = TYPE_UNSIGNED32; + break; + case TRAPTYPE: + translation_table[count] = TYPE_TRAPTYPE; + break; + case NOTIFTYPE: + translation_table[count] = TYPE_NOTIFTYPE; + break; + case OBJGROUP: + translation_table[count] = TYPE_OBJGROUP; + break; + case MODULEIDENTITY: + translation_table[count] = TYPE_MODID; + break; + case AGENTCAP: + translation_table[count] = TYPE_AGENTCAP; + break; + case COMPLIANCE: + translation_table[count] = TYPE_MODCOMP; + 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 (tp->label && !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 + +static 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); + char *st; + + + entry = strtok_r(newkey, "*", &st); + 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_r(NULL, "*", &st); + } + 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 && tp->label) + 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 (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_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) +{ + struct tree *tp, *anon_tp = NULL; + struct tree *xroot = root; + struct node *np, **headp; + struct node *oldnp = NULL, *child_list = NULL, *childp = NULL; + int hash; + int *int_p; + + while (xroot->next_peer && xroot->next_peer->subid == root->subid) { +#if 0 + printf("xroot: %s.%s => %s\n", xroot->parent->label, xroot->label, + xroot->next_peer->label); +#endif + xroot = xroot->next_peer; + } + + 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) { + struct tree *otp = NULL; + struct tree *xxroot = xroot; + anon_tp = NULL; + tp = xroot->child_list; + + if (np->subid == -1) { + /* + * name ::= { parent } + */ + np->subid = xroot->subid; + tp = xroot; + xxroot = xroot->parent; + } + + while (tp) { + if (tp->subid == np->subid) + break; + else { + otp = tp; + 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 (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, + NETSNMP_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 (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_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 = xxroot; + tp->modid = np->modid; + tp->number_modules = 1; + tp->module_list = &(tp->modid); + tree_from_node(tp, np); + tp->next_peer = otp ? otp->next_peer : xxroot->child_list; + if (otp) + otp->next_peer = tp; + else + xxroot->child_list = tp; + hash = NBUCKET(name_hash(tp->label)); + tp->next = tbuckets[hash]; + tbuckets[hash] = tp; + do_subtree(tp, nodes); + + 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->augments = tp->augments; + anon_tp->varbinds = tp->varbinds; + anon_tp->ranges = tp->ranges; + anon_tp->hint = tp->hint; + anon_tp->units = tp->units; + anon_tp->description = tp->description; + anon_tp->defaultValue = tp->defaultValue; + 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 (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_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, *oldp, *newp; + struct tree *tp; + int i, more; + /* + * 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; + + /* + * quietly move all internal references to the orphan list + */ + oldp = orphan_nodes; + do { + for (i = 0; i < NHASHSIZE; i++) + for (onp = nbuckets[i]; onp; onp = onp->next) { + struct node *op = NULL; + int hash = NBUCKET(name_hash(onp->label)); + np = nbuckets[hash]; + while (np) { + if (label_compare(onp->label, np->parent)) { + op = np; + np = np->next; + } else { + if (op) + op->next = np->next; + else + nbuckets[hash] = np->next; + np->next = orphan_nodes; + orphan_nodes = np; + op = NULL; + np = nbuckets[hash]; + } + } + } + newp = orphan_nodes; + more = 0; + for (onp = orphan_nodes; onp != oldp; onp = onp->next) { + struct node *op = NULL; + int hash = NBUCKET(name_hash(onp->label)); + np = nbuckets[hash]; + while (np) { + if (label_compare(onp->label, np->parent)) { + op = np; + np = np->next; + } else { + if (op) + op->next = np->next; + else + nbuckets[hash] = np->next; + np->next = orphan_nodes; + orphan_nodes = np; + op = NULL; + np = nbuckets[hash]; + more = 1; + } + } + } + oldp = newp; + } while (more); + + /* + * complain about left over nodes + */ + 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) { + snmp_log(LOG_WARNING, + "Unlinked OID in %s: %s ::= { %s %ld }\n", + (mp->name ? mp->name : ""), + (onp->label ? onp->label : ""), + (onp->parent ? onp->parent : ""), + onp->subid); + snmp_log(LOG_WARNING, + "Undefined identifier: %s near line %d of %s\n", + (onp->parent ? onp->parent : ""), + onp->lineno, onp->filename); + 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; + 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 = strtoul(token, NULL, 10); + 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 = strtoul(token, NULL, 10); + } 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); + np->parent = 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 = strtol(token, NULL, 10); + 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 = strtol(nexttoken, NULL, 10); + nexttype = get_token(fp, nexttoken, MAXTOKEN); + if (nexttype == RANGE) { + nexttype = get_token(fp, nexttoken, MAXTOKEN); + high = strtol(nexttoken, NULL, 10); + 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 || type == CHOICE) { + 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 == LEFTSQBRACK) { + int size = 0; + do { + type = get_token(fp, token, MAXTOKEN); + } while (type != ENDOFFILE && type != RIGHTSQBRACK); + if (type != RIGHTSQBRACK) { + print_error("Expected \"]\"", token, type); + return NULL; + } + type = get_token(fp, token, MAXTOKEN); + if (type == IMPLICIT) + type = get_token(fp, token, MAXTOKEN); + *ntype = get_token(fp, ntoken, MAXTOKEN); + if (*ntype == LEFTPAREN) { + switch (type) { + case OCTETSTR: + *ntype = get_token(fp, ntoken, MAXTOKEN); + if (*ntype != SIZE) { + print_error("Expected SIZE", ntoken, *ntype); + return NULL; + } + size = 1; + *ntype = get_token(fp, ntoken, MAXTOKEN); + if (*ntype != LEFTPAREN) { + print_error("Expected \"(\" after SIZE", ntoken, + *ntype); + return NULL; + } + /* + * fall through + */ + case INTEGER: + *ntype = get_token(fp, ntoken, MAXTOKEN); + do { + if (*ntype != NUMBER) + print_error("Expected NUMBER", ntoken, *ntype); + *ntype = get_token(fp, ntoken, MAXTOKEN); + if (*ntype == RANGE) { + *ntype = get_token(fp, ntoken, MAXTOKEN); + if (*ntype != NUMBER) + print_error("Expected NUMBER", ntoken, *ntype); + *ntype = get_token(fp, ntoken, MAXTOKEN); + } + } while (*ntype == BAR); + if (*ntype != RIGHTPAREN) { + print_error("Expected \")\"", ntoken, *ntype); + return NULL; + } + *ntype = get_token(fp, ntoken, MAXTOKEN); + if (size) { + if (*ntype != RIGHTPAREN) { + print_error("Expected \")\" to terminate SIZE", + ntoken, *ntype); + return NULL; + } + *ntype = get_token(fp, ntoken, MAXTOKEN); + } + } + } + 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 == OBJECT) { + type = get_token(fp, token, MAXTOKEN); + if (type != IDENTIFIER) { + print_error("Expected IDENTIFIER", token, type); + SNMP_FREE(hint); + return NULL; + } + type = OBJID; + } + } else if (type == OBJECT) { + type = get_token(fp, token, MAXTOKEN); + if (type != IDENTIFIER) { + print_error("Expected IDENTIFIER", token, type); + return NULL; + } + type = OBJID; + } + + 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 == OBJECT) { + type = get_token(fp, token, MAXTOKEN); + if (type != IDENTIFIER) { + print_error("Expected IDENTIFIER", token, type); + free_node(np); + return NULL; + } + type = OBJID; + } + if (type == LABEL) { + int tmp_index; + tctype = get_tc(token, current_module, &tmp_index, + &np->enums, &np->ranges, &np->hint); + if (tctype == LABEL && + netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_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 INTEGER32: + case UINTEGER32: + case UNSIGNED32: + 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 (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, + NETSNMP_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: + if (np->augments) { + print_error("Cannot have both INDEX and AUGMENTS", token, + type); + free_node(np); + return NULL; + } + np->indexes = getIndexes(fp, &np->indexes); + if (np->indexes == NULL) { + print_error("Bad INDEX list", token, type); + free_node(np); + return NULL; + } + break; + case AUGMENTS: + if (np->indexes) { + print_error("Cannot have both INDEX and AUGMENTS", token, + type); + free_node(np); + return NULL; + } + np->indexes = getIndexes(fp, &np->indexes); + if (np->indexes == NULL) { + print_error("Bad AUGMENTS list", token, type); + free_node(np); + return NULL; + } + np->augments = strdup(np->indexes->ilabel); + free_indexes(&np->indexes); + break; + case DEFVAL: + /* + * Mark's defVal section + */ + type = get_token(fp, quoted_string_buffer, MAXTOKEN); + if (type != LEFTBRACKET) { + print_error("Bad DEFAULTVALUE", quoted_string_buffer, + type); + free_node(np); + return NULL; + } + + { + int level = 1; + char defbuf[512]; + + defbuf[0] = 0; + while (1) { + type = get_token(fp, quoted_string_buffer, MAXTOKEN); + if ((type == RIGHTBRACKET && --level == 0) + || type == ENDOFFILE) + break; + else if (type == LEFTBRACKET) + level++; + if (type == QUOTESTRING) { + if (strlen(defbuf)+2 < sizeof(defbuf)) { + defbuf[ strlen(defbuf)+2 ] = 0; + defbuf[ strlen(defbuf)+1 ] = '"'; + defbuf[ strlen(defbuf) ] = '\\'; + } + defbuf[ sizeof(defbuf)-1 ] = 0; + } + strncat(defbuf, quoted_string_buffer, + sizeof(defbuf)-strlen(defbuf)); + defbuf[ sizeof(defbuf)-1 ] = 0; + if (type == QUOTESTRING) { + if (strlen(defbuf)+2 < sizeof(defbuf)) { + defbuf[ strlen(defbuf)+2 ] = 0; + defbuf[ strlen(defbuf)+1 ] = '"'; + defbuf[ strlen(defbuf) ] = '\\'; + } + defbuf[ sizeof(defbuf)-1 ] = 0; + } + if (strlen(defbuf)+1 < sizeof(defbuf)) { + defbuf[ strlen(defbuf)+1 ] = 0; + defbuf[ strlen(defbuf) ] = ' '; + } + } + + if (type != RIGHTBRACKET) { + print_error("Bad DEFAULTVALUE", quoted_string_buffer, + type); + free_node(np); + return NULL; + } + + defbuf[strlen(defbuf) - 1] = 0; + np->defaultValue = strdup(defbuf); + } + + break; + + 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, int what, struct objgroup **ol) +{ + int type; + char token[MAXTOKEN]; + char quoted_string_buffer[MAXQUOTESTR]; + struct node *np; + + np = alloc_node(current_module); + if (np == NULL) + return (NULL); + type = get_token(fp, token, MAXTOKEN); + if (type == what) { + type = get_token(fp, token, MAXTOKEN); + if (type != LEFTBRACKET) { + print_error("Expected \"{\"", token, type); + goto skip; + } + do { + struct objgroup *o; + type = get_token(fp, token, MAXTOKEN); + if (type != LABEL) { + print_error("Bad identifier", token, type); + goto skip; + } + o = (struct objgroup *) malloc(sizeof(struct objgroup)); + o->line = mibLine; + o->name = strdup(token); + o->next = *ol; + *ol = o; + type = get_token(fp, token, MAXTOKEN); + } while (type == COMMA); + if (type != RIGHTBRACKET) { + print_error("Expected \"}\" after list", token, type); + goto skip; + } + type = get_token(fp, token, type); + } + if (type != STATUS) { + print_error("Expected STATUS", token, type); + goto skip; + } + type = get_token(fp, token, MAXTOKEN); + if (type != CURRENT && type != DEPRECATED && type != OBSOLETE) { + print_error("Bad STATUS value", token, type); + goto skip; + } + type = get_token(fp, token, MAXTOKEN); + if (type != DESCRIPTION) { + print_error("Expected DESCRIPTION", token, type); + goto skip; + } + 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 (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) { + np->description = strdup(quoted_string_buffer); + } + type = get_token(fp, token, MAXTOKEN); + if (type == 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; + } + type = get_token(fp, token, MAXTOKEN); + } + if (type != EQUALS) + print_error("Expected \"::=\"", token, type); + skip: + while (type != EQUALS && type != ENDOFFILE) + 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 (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) { + np->description = strdup(quoted_string_buffer); + } + break; + case OBJECTS: + np->varbinds = getVarbinds(fp, &np->varbinds); + if (!np->varbinds) { + print_error("Bad OBJECTS list", token, 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 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 (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, + NETSNMP_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; + case VARIABLES: + np->varbinds = getVarbinds(fp, &np->varbinds); + if (!np->varbinds) { + print_error("Bad VARIABLES list", token, type); + free_node(np); + return NULL; + } + 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 = strtoul(token, NULL, 10); + 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 int +eat_syntax(FILE * fp, char *token, int maxtoken) +{ + int type, nexttype; + struct node *np = alloc_node(current_module); + char nexttoken[MAXTOKEN]; + + type = get_token(fp, token, maxtoken); + nexttype = get_token(fp, nexttoken, MAXTOKEN); + switch (type) { + case INTEGER: + case INTEGER32: + case UINTEGER32: + case UNSIGNED32: + 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 nexttype; + } + 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 nexttype; + } + free_node(np); + return nexttype; +} + +static int +compliance_lookup(const char *name, int modid) +{ + if (modid == -1) { + struct objgroup *op = + (struct objgroup *) malloc(sizeof(struct objgroup)); + op->next = objgroups; + op->name = strdup(name); + op->line = mibLine; + objgroups = op; + return 1; + } else + return find_tree_node(name, modid) != NULL; +} + +static struct node * +parse_compliance(FILE * fp, char *name) +{ + int type; + char token[MAXTOKEN]; + char quoted_string_buffer[MAXQUOTESTR]; + struct node *np; + + np = alloc_node(current_module); + if (np == NULL) + return (NULL); + type = get_token(fp, token, MAXTOKEN); + if (type != STATUS) { + print_error("Expected STATUS", token, type); + goto skip; + } + type = get_token(fp, token, MAXTOKEN); + if (type != CURRENT && type != DEPRECATED && type != OBSOLETE) { + print_error("Bad STATUS", token, type); + goto skip; + } + type = get_token(fp, token, MAXTOKEN); + if (type != DESCRIPTION) { + print_error("Expected DESCRIPTION", token, type); + goto skip; + } + type = get_token(fp, quoted_string_buffer, MAXQUOTESTR); + if (type != QUOTESTRING) { + print_error("Bad DESCRIPTION", quoted_string_buffer, type); + goto skip; + } + if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) + np->description = strdup(quoted_string_buffer); + type = get_token(fp, token, MAXTOKEN); + if (type == REFERENCE) { + type = get_token(fp, quoted_string_buffer, MAXTOKEN); + if (type != QUOTESTRING) { + print_error("Bad REFERENCE", quoted_string_buffer, type); + goto skip; + } + type = get_token(fp, token, MAXTOKEN); + } + if (type != MODULE) { + print_error("Expected MODULE", token, type); + goto skip; + } + while (type == MODULE) { + int modid = -1; + char modname[MAXTOKEN]; + type = get_token(fp, token, MAXTOKEN); + if (type == LABEL + && strcmp(token, module_name(current_module, modname))) { + modid = read_module_internal(token); + if (modid != MODULE_LOADED_OK + && modid != MODULE_ALREADY_LOADED) { + print_error("Unknown module", token, type); + goto skip; + } + modid = which_module(token); + type = get_token(fp, token, MAXTOKEN); + } + if (type == MANDATORYGROUPS) { + type = get_token(fp, token, MAXTOKEN); + if (type != LEFTBRACKET) { + print_error("Expected \"{\"", token, type); + goto skip; + } + do { + type = get_token(fp, token, MAXTOKEN); + if (type != LABEL) { + print_error("Bad group name", token, type); + goto skip; + } + if (!compliance_lookup(token, modid)) + print_error("Unknown group", token, type); + type = get_token(fp, token, MAXTOKEN); + } while (type == COMMA); + if (type != RIGHTBRACKET) { + print_error("Expected \"}\"", token, type); + goto skip; + } + type = get_token(fp, token, MAXTOKEN); + } + while (type == GROUP || type == OBJECT) { + if (type == GROUP) { + type = get_token(fp, token, MAXTOKEN); + if (type != LABEL) { + print_error("Bad group name", token, type); + goto skip; + } + if (!compliance_lookup(token, modid)) + print_error("Unknown group", token, type); + type = get_token(fp, token, MAXTOKEN); + } else { + type = get_token(fp, token, MAXTOKEN); + if (type != LABEL) { + print_error("Bad object name", token, type); + goto skip; + } + if (!compliance_lookup(token, modid)) + print_error("Unknown group", token, type); + type = get_token(fp, token, MAXTOKEN); + if (type == SYNTAX) + type = eat_syntax(fp, token, MAXTOKEN); + if (type == WRSYNTAX) + type = eat_syntax(fp, token, MAXTOKEN); + if (type == MINACCESS) { + type = get_token(fp, token, MAXTOKEN); + if (type != NOACCESS && type != ACCNOTIFY + && type != READONLY && type != WRITEONLY + && type != READCREATE && type != READWRITE) { + print_error("Bad MIN-ACCESS spec", token, type); + goto skip; + } + type = get_token(fp, token, MAXTOKEN); + } + } + if (type != DESCRIPTION) { + print_error("Expected DESCRIPTION", token, type); + goto skip; + } + type = get_token(fp, token, MAXTOKEN); + if (type != QUOTESTRING) { + print_error("Bad DESCRIPTION", token, type); + goto skip; + } + type = get_token(fp, token, MAXTOKEN); + } + } + skip: + 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) +{ + int type; + char token[MAXTOKEN]; + char quoted_string_buffer[MAXQUOTESTR]; + struct node *np; + + np = alloc_node(current_module); + if (np == NULL) + return (NULL); + type = get_token(fp, token, MAXTOKEN); + if (type != PRODREL) { + print_error("Expected PRODUCT-RELEASE", token, type); + goto skip; + } + type = get_token(fp, token, MAXTOKEN); + if (type != QUOTESTRING) { + print_error("Expected STRING after PRODUCT-RELEASE", token, type); + goto skip; + } + type = get_token(fp, token, MAXTOKEN); + if (type != STATUS) { + print_error("Expected STATUS", token, type); + goto skip; + } + type = get_token(fp, token, MAXTOKEN); + if (type != CURRENT && type != OBSOLETE) { + print_error("STATUS should be current or obsolete", token, type); + goto skip; + } + type = get_token(fp, token, MAXTOKEN); + if (type != DESCRIPTION) { + print_error("Expected DESCRIPTION", token, type); + goto skip; + } + type = get_token(fp, token, MAXTOKEN); + if (type != QUOTESTRING) { + print_error("Bad DESCRIPTION", token, type); + goto skip; + } + if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) { + np->description = strdup(token); + } + type = get_token(fp, token, MAXTOKEN); + if (type == REFERENCE) { + type = get_token(fp, token, MAXTOKEN); + if (type != QUOTESTRING) { + print_error("Bad REFERENCE", token, type); + goto skip; + } + type = get_token(fp, token, type); + } + while (type == SUPPORTS) { + int modid; + struct tree *tp; + + type = get_token(fp, token, MAXTOKEN); + if (type != LABEL) { + print_error("Bad module name", token, type); + goto skip; + } + modid = read_module_internal(token); + if (modid != MODULE_LOADED_OK && modid != MODULE_ALREADY_LOADED) { + print_error("Module not found", token, type); + goto skip; + } + modid = which_module(token); + type = get_token(fp, token, MAXTOKEN); + if (type != INCLUDES) { + print_error("Expected INCLUDES", token, type); + goto skip; + } + type = get_token(fp, token, MAXTOKEN); + if (type != LEFTBRACKET) { + print_error("Expected \"{\"", token, type); + goto skip; + } + do { + type = get_token(fp, token, MAXTOKEN); + if (type != LABEL) { + print_error("Expected group name", token, type); + goto skip; + } + tp = find_tree_node(token, modid); + if (!tp) + print_error("Group not found in module", token, type); + type = get_token(fp, token, MAXTOKEN); + } while (type == COMMA); + if (type != RIGHTBRACKET) { + print_error("Expected \"}\" after group list", token, type); + goto skip; + } + type = get_token(fp, token, MAXTOKEN); + while (type == VARIATION) { + type = get_token(fp, token, MAXTOKEN); + if (type != LABEL) { + print_error("Bad object name", token, type); + goto skip; + } + tp = find_tree_node(token, modid); + if (!tp) + print_error("Object not found in module", token, type); + type = get_token(fp, token, MAXTOKEN); + if (type == SYNTAX) { + type = eat_syntax(fp, token, MAXTOKEN); + } + if (type == WRSYNTAX) { + type = eat_syntax(fp, token, MAXTOKEN); + } + if (type == ACCESS) { + type = get_token(fp, token, MAXTOKEN); + if (type != ACCNOTIFY && type != READONLY + && type != READWRITE && type != READCREATE + && type != WRITEONLY && type != NOTIMPL) { + print_error("Bad ACCESS", token, type); + goto skip; + } + type = get_token(fp, token, MAXTOKEN); + } + if (type == CREATEREQ) { + type = get_token(fp, token, MAXTOKEN); + if (type != LEFTBRACKET) { + print_error("Expected \"{\"", token, type); + goto skip; + } + do { + type = get_token(fp, token, MAXTOKEN); + if (type != LABEL) { + print_error("Bad object name in list", token, + type); + goto skip; + } + type = get_token(fp, token, MAXTOKEN); + } while (type == COMMA); + if (type != RIGHTBRACKET) { + print_error("Expected \"}\" after list", token, type); + goto skip; + } + type = get_token(fp, token, MAXTOKEN); + } + if (type == DEFVAL) { + int level = 1; + type = get_token(fp, token, MAXTOKEN); + if (type != LEFTBRACKET) { + print_error("Expected \"{\" after DEFVAL", token, + type); + goto skip; + } + do { + type = get_token(fp, token, MAXTOKEN); + if (type == LEFTBRACKET) + level++; + else if (type == RIGHTBRACKET) + level--; + } while (type != RIGHTBRACKET && type != ENDOFFILE + && level != 0); + if (type != RIGHTBRACKET) { + print_error("Missing \"}\" after DEFVAL", token, type); + goto skip; + } + type = get_token(fp, token, MAXTOKEN); + } + if (type != DESCRIPTION) { + print_error("Expected DESCRIPTION", token, type); + goto skip; + } + type = get_token(fp, token, MAXTOKEN); + if (type != QUOTESTRING) { + print_error("Bad DESCRIPTION", token, type); + goto skip; + } + type = get_token(fp, token, MAXTOKEN); + } + } + if (type != EQUALS) + print_error("Expected \"::=\"", token, type); + skip: + 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 void +check_utc(const char *utc) +{ + int len, year, month, day, hour, minute; + + len = strlen(utc); + if (utc[len - 1] != 'Z' && utc[len - 1] != 'z') { + print_error("Timestamp should end with Z", utc, QUOTESTRING); + return; + } + if (len == 11) { + len = + sscanf(utc, "%2d%2d%2d%2d%2dZ", &year, &month, &day, &hour, + &minute); + year += 1900; + } else if (len == 13) + len = + sscanf(utc, "%4d%2d%2d%2d%2dZ", &year, &month, &day, &hour, + &minute); + else { + print_error("Bad timestamp format (11 or 13 characters)", + utc, QUOTESTRING); + return; + } + if (len != 5) { + print_error("Bad timestamp format", utc, QUOTESTRING); + return; + } + if (month < 1 || month > 12) + print_error("Bad month in timestamp", utc, QUOTESTRING); + if (day < 1 || day > 31) + print_error("Bad day in timestamp", utc, QUOTESTRING); + if (hour < 0 || hour > 23) + print_error("Bad hour in timestamp", utc, QUOTESTRING); + if (minute < 0 || minute > 59) + print_error("Bad minute in timestamp", utc, QUOTESTRING); +} + +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); + if (type != LASTUPDATED) { + print_error("Expected LAST-UPDATED", token, type); + goto skip; + } + type = get_token(fp, token, MAXTOKEN); + if (type != QUOTESTRING) { + print_error("Need STRING for LAST-UPDATED", token, type); + goto skip; + } + check_utc(token); + type = get_token(fp, token, MAXTOKEN); + if (type != ORGANIZATION) { + print_error("Expected ORGANIZATION", token, type); + goto skip; + } + type = get_token(fp, token, MAXTOKEN); + if (type != QUOTESTRING) { + print_error("Bad ORGANIZATION", token, type); + goto skip; + } + type = get_token(fp, token, MAXTOKEN); + if (type != CONTACTINFO) { + print_error("Expected CONTACT-INFO", token, type); + goto skip; + } + type = get_token(fp, quoted_string_buffer, MAXQUOTESTR); + if (type != QUOTESTRING) { + print_error("Bad CONTACT-INFO", quoted_string_buffer, type); + goto skip; + } + type = get_token(fp, token, MAXTOKEN); + if (type != DESCRIPTION) { + print_error("Expected DESCRIPTION", token, type); + goto skip; + } + type = get_token(fp, quoted_string_buffer, MAXQUOTESTR); + if (type != QUOTESTRING) { + print_error("Bad DESCRIPTION", quoted_string_buffer, type); + goto skip; + } + if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) { + np->description = strdup(quoted_string_buffer); + } + type = get_token(fp, token, MAXTOKEN); + while (type == REVISION) { + type = get_token(fp, token, MAXTOKEN); + if (type != QUOTESTRING) { + print_error("Bad REVISION", token, type); + goto skip; + } + check_utc(token); + type = get_token(fp, token, MAXTOKEN); + if (type != DESCRIPTION) { + print_error("Expected DESCRIPTION", token, type); + goto skip; + } + type = get_token(fp, quoted_string_buffer, MAXQUOTESTR); + if (type != QUOTESTRING) { + print_error("Bad DESCRIPTION", quoted_string_buffer, type); + goto skip; + } + type = get_token(fp, token, MAXTOKEN); + } + if (type != EQUALS) + print_error("Expected \"::=\"", token, type); + skip: + 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 = mibLine; + + 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 (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_MIB_WARNINGS)) { + snmp_log(LOG_WARNING, + "%s MACRO (lines %d..%d parsed and ignored).\n", name, + iLine, mibLine); + } + + 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; + 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; + + /* + * 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]); + } + } + } + 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 (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_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 (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, + NETSNMP_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 (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_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; + + init_mib_internals(); + + for (mp = module_head; mp; mp = mp->next) + if (!label_compare(mp->name, name)) { + const char *oldFile = File; + int oldLine = mibLine; + int oldModule = current_module; + + 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; + mibLine = 1; + current_module = mp->modid; + /* + * Parse the file + */ + np = parse(fp, NULL); + fclose(fp); + File = oldFile; + mibLine = oldLine; + current_module = oldModule; + return MODULE_LOADED_OK; + } + + if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_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 = 1; + + if (!orphan_nodes) + return; + init_node_hash(orphan_nodes); + orphan_nodes = NULL; + + while (adopted) { + 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 do_subtree adopted the entire bucket, stop + */ + if(NULL == nbuckets[i]) + break; + + /* + * do_subtree may modify nbuckets, and if np + * was adopted, np->next probably isn't an orphan + * anymore. if np is still in the bucket (do_subtree + * didn't adopt it) keep on plugging. otherwise + * start over, at the top of the bucket. + */ + for(onp = nbuckets[i]; onp; onp = onp->next) + if(onp == np) + break; + if(NULL == onp) { /* not in the list */ + np = nbuckets[i]; /* start over */ + } + } + } + } + } + + /* + * 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, + "Cannot adopt OID in %s: %s ::= { %s %ld }\n", + module_name(onp->modid, modbuf), + (onp->label ? onp->label : ""), + (onp->parent ? onp->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; +} + +/* + * Prototype definition + */ +void unload_module_by_ID(int modID, struct tree *tree_top); + +void +unload_module_by_ID(int modID, struct tree *tree_top) +{ + struct tree *tp, *next; + int i; + + for (tp = tree_top; tp; tp = next) { + /* + * 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, + * and possible multiple copies of the same module ID. + */ + int nmod = tp->number_modules; + if (nmod > 0) { /* in some module */ + /* + * Remove all copies of this module ID + */ + int cnt = 0, *pi1, *pi2 = tp->module_list; + for (i = 0, pi1 = pi2; i < nmod; i++, pi2++) { + if (*pi2 == modID) + continue; + cnt++; + *pi1++ = *pi2; + } + if (nmod != cnt) { /* in this module */ + /* + * if ( (nmod - cnt) > 1) + * printf("Dup modid %d, %d times, '%s'\n", tp->modid, (nmod-cnt), tp->label); fflush(stdout); ?* XXDEBUG + */ + tp->number_modules = cnt; + switch (cnt) { + case 0: + tp->module_list[0] = -1; /* Mark unused, and FALL THROUGH */ + + case 1: /* save the remaining module */ + if (&(tp->modid) != tp->module_list) { + tp->modid = tp->module_list[0]; + free(tp->module_list); + tp->module_list = &(tp->modid); + } + break; + + default: + break; + } + } /* if tree node is in this module */ + } + /* + * if tree node is in some module + */ + next = tp->next_peer; + + + /* + * 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) { + unlink_tree(tp); + free_tree(tp); + } else { + free_partial_tree(tp, TRUE); + } + } + } +} + +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! */ +} + +/* + * Clear module map, tree nodes, textual convention table. + */ +void +unload_all_mibs() +{ + struct module *mp; + struct module_compatability *mcp; + struct tc *ptc; + int i; + + for (mcp = module_map_head; mcp; mcp = module_map_head) { + if (mcp == module_map) + break; + module_map_head = mcp->next; + if (mcp->tag) free((char *) mcp->tag); + free((char *) mcp->old_module); + free((char *) mcp->new_module); + free(mcp); + } + + for (mp = module_head; mp; mp = module_head) { + struct module_import *mi = mp->imports; + if (mi) { + for (i = 0; i < mp->no_imports; ++i) { + SNMP_FREE((mi + i)->label); + } + mp->no_imports = 0; + if (mi == root_imports) + memset(mi, 0, sizeof(*mi)); + else + free(mi); + } + + unload_module_by_ID(mp->modid, tree_head); + module_head = mp->next; + free(mp->name); + free(mp->file); + free(mp); + } + unload_module_by_ID(-1, tree_head); + /* + * tree nodes are cleared + */ + + for (i = 0, ptc = tclist; i < MAXTC; i++, ptc++) { + if (ptc->type == 0) + continue; + free_enums(&ptc->enums); + free_ranges(&ptc->ranges); + free(ptc->descriptor); + if (ptc->hint) + free(ptc->hint); + } + memset(tclist, 0, MAXTC * sizeof(struct tc)); + + memset(buckets, 0, sizeof(buckets)); + memset(nbuckets, 0, sizeof(nbuckets)); + memset(tbuckets, 0, sizeof(tbuckets)); + + for (i = 0; i < sizeof(root_imports) / sizeof(root_imports[0]); i++) { + SNMP_FREE(root_imports[i].label); + } + + max_module = 0; + current_module = 0; + module_map_head = NULL; + SNMP_FREE(last_err_module); +} + +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 (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_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; +} + + +static void +scan_objlist(struct node *root, struct objgroup *list, const char *error) +{ + int oLine = mibLine; + + while (list) { + struct objgroup *gp = list; + struct node *np; + list = list->next; + np = root; + while (np) + if (label_compare(np->label, gp->name)) + np = np->next; + else + break; + if (!np) { + mibLine = gp->line; + print_error(error, gp->name, QUOTESTRING); + } + free(gp->name); + free(gp); + } + mibLine = oLine; +} + +/* + * 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; + struct objgroup *oldgroups = NULL, *oldobjects = NULL, *oldnotifs = + NULL; + + 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, root); +#endif + scan_objlist(root, objgroups, "Undefined OBJECT-GROUP"); + scan_objlist(root, objects, "Undefined OBJECT"); + scan_objlist(root, notifs, "Undefined NOTIFICATION"); + objgroups = oldgroups; + objects = oldobjects; + notifs = oldnotifs; + 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 (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_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: + case INTEGER: + case INTEGER32: + case UINTEGER32: + case UNSIGNED32: + case COUNTER: + case COUNTER64: + case GAUGE: + case IPADDR: + case NETADDR: + case NSAPADDRESS: + case OBJSYNTAX: + case APPSYNTAX: + case SIMPLESYNTAX: + case OBJNAME: + case NOTIFNAME: + case KW_OPAQUE: + case TIMETICKS: + 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); + oldgroups = objgroups; + objgroups = NULL; + oldobjects = objects; + objects = NULL; + oldnotifs = notifs; + notifs = NULL; + 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, OBJECTS, &objects); + if (nnp == NULL) { + print_error("Bad parse of OBJECT-GROUP", NULL, type); + return NULL; + } + break; + case NOTIFGROUP: + nnp = parse_objectgroup(fp, name, NOTIFICATIONS, ¬ifs); + if (nnp == NULL) { + print_error("Bad parse of NOTIFICATION-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 AGENTCAP: + 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 OBJECT: + type = get_token(fp, token, MAXTOKEN); + if (type != IDENTIFIER) { + print_error("Expected IDENTIFIER", token, type); + return NULL; + } + 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 (nnp->type == TYPE_OTHER) + nnp->type = type; + 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 == '_' && netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, + NETSNMP_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') + mibLine++; + } + 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 LEFTSQBRACK; + case ']': + return RIGHTSQBRACK; + 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 (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, + NETSNMP_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') + mibLine++; + 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') + mibLine++; + 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) +{ + FILE *fp, *ip; + DIR *dir, *dir2; + const char *oldFile = File; + struct dirent *file; + char token[MAXTOKEN], token2[MAXTOKEN]; + char tmpstr[300]; + int count = 0; + + DEBUGMSGTL(("parse-mibs", "Scanning directory %s\n", dirname)); + + if ((dir = opendir(dirname))) { + snprintf(tmpstr, sizeof(tmpstr), "%s/.index", dirname); + tmpstr[ sizeof(tmpstr)-1 ] = 0; + 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] != '.') { + snprintf(tmpstr, sizeof(tmpstr), "%s/%s", dirname, file->d_name); + tmpstr[ sizeof(tmpstr)-1 ] = 0; + 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)); + mibLine = 1; + File = tmpstr; + get_token(fp, token, MAXTOKEN); + /* + * simple test for this being a MIB + */ + if (get_token(fp, token2, MAXTOKEN) == DEFINITIONS) { + new_module(token, tmpstr); + count++; + if (ip) + fprintf(ip, "%s %s\n", token, file->d_name); + } + fclose(fp); + } + } + } + File = oldFile; + closedir(dir); + if (ip) + fclose(ip); + return (count); + } + return (-1); +} + + +/* + * 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; + } + mibLine = 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; + netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_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') { + mibLine++; + } else if (ch == '"') { + *token = '\0'; + if (too_long && netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_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 struct varbind_list * +getVarbinds(FILE * fp, struct varbind_list **retp) +{ + int type; + char token[MAXTOKEN]; + + struct varbind_list *mylist = NULL; + struct varbind_list **mypp = &mylist; + + free_varbinds(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 varbind_list *) calloc(1, + sizeof(struct + varbind_list)); + if (*mypp) { + (*mypp)->vblabel = strdup(token); + mypp = &(*mypp)->next; + } + } + 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_varbinds(struct varbind_list **spp) +{ + if (spp && *spp) { + struct varbind_list *pp, *npp; + + pp = *spp; + *spp = NULL; + + while (pp) { + npp = pp->next; + if (pp->vblabel) + free(pp->vblabel); + 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); +} + +/* + * 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 == TYPE_OTHER || tp->type > TYPE_SIMPLE_LAST) { + fprintf(f, "%s--%s(%ld)\n", leave_indent, tp->label, tp->subid); + if (tp->indexes) { + struct index_list *xp = tp->indexes; + int first = 1, cpos = 0, len, cmax = + width - strlen(leave_indent) - 12; + *ip = last_ipch; + fprintf(f, "%s | Index: ", leave_indent); + while (xp) { + if (first) + first = 0; + else + fprintf(f, ", "); + cpos += (len = strlen(xp->ilabel) + 2); + if (cpos > cmax) { + fprintf(f, "\n"); + fprintf(f, "%s | ", leave_indent); + cpos = len; + } + fprintf(f, "%s", xp->ilabel); + xp = xp->next; + } + fprintf(f, "\n"); + *ip = '+'; + } + } 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_UNSIGNED32: + typ = "Unsigned "; + break; + case TYPE_UINTEGER: + typ = "UInteger "; + break; + case TYPE_INTEGER32: + typ = "Integer32"; + 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, ", "); + snprintf(buf, sizeof(buf), "%s(%d)", ep->label, ep->value); + buf[ sizeof(buf)-1 ] = 0; + 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 != TYPE_OTHER; + + { + 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; + /* + * printf("merge defval --> %s\n",np->defaultValue); + */ + 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; + if (nnp->filename != NULL) { + free(nnp->filename); + } + 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->augments = np->augments; + np->augments = NULL; + tp->varbinds = np->varbinds; + np->varbinds = NULL; + tp->hint = np->hint; + np->hint = NULL; + tp->units = np->units; + np->units = NULL; + tp->description = np->description; + np->description = NULL; + tp->defaultValue = np->defaultValue; + np->defaultValue = 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); +} -- cgit v1.2.3