| /* |
| * parse.c |
| * |
| */ |
| /* 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. |
| */ |
| #include <net-snmp/net-snmp-config.h> |
| #include <net-snmp/net-snmp-features.h> |
| |
| #ifndef NETSNMP_DISABLE_MIB_LOADING |
| |
| #if HAVE_LIMITS_H |
| #include <limits.h> |
| #endif |
| #include <stdio.h> |
| #if HAVE_STDLIB_H |
| #include <stdlib.h> |
| #endif |
| #if HAVE_STRING_H |
| #include <string.h> |
| #else |
| #include <strings.h> |
| #endif |
| #include <ctype.h> |
| #include <sys/types.h> |
| #if HAVE_SYS_STAT_H |
| #include <sys/stat.h> |
| #endif |
| |
| /* |
| * Wow. This is ugly. -- Wes |
| */ |
| #if HAVE_DIRENT_H |
| # include <dirent.h> |
| # define NAMLEN(dirent) strlen((dirent)->d_name) |
| #else |
| # define dirent direct |
| # define NAMLEN(dirent) (dirent)->d_namlen |
| # if HAVE_SYS_NDIR_H |
| # include <sys/ndir.h> |
| # endif |
| # if HAVE_SYS_DIR_H |
| # include <sys/dir.h> |
| # endif |
| # if HAVE_NDIR_H |
| # include <ndir.h> |
| # endif |
| #endif |
| #if TIME_WITH_SYS_TIME |
| # include <sys/time.h> |
| # include <time.h> |
| #else |
| # if HAVE_SYS_TIME_H |
| # include <sys/time.h> |
| # else |
| # include <time.h> |
| # endif |
| #endif |
| #if HAVE_NETINET_IN_H |
| #include <netinet/in.h> |
| #endif |
| #if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP) |
| #include <regex.h> |
| #endif |
| #if HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| #if HAVE_DMALLOC_H |
| #include <dmalloc.h> |
| #endif |
| |
| #include <errno.h> |
| |
| #include <net-snmp/types.h> |
| #include <net-snmp/output_api.h> |
| #include <net-snmp/config_api.h> |
| #include <net-snmp/utilities.h> |
| |
| #include <net-snmp/library/parse.h> |
| #include <net-snmp/library/mib.h> |
| #include <net-snmp/library/snmp_api.h> |
| #include <net-snmp/library/tools.h> |
| |
| netsnmp_feature_child_of(find_module, mib_api) |
| netsnmp_feature_child_of(get_tc_description, mib_api) |
| |
| /* |
| * A linked list of nodes. |
| */ |
| struct node { |
| struct node *next; |
| char *label; /* This node's (unique) textual name */ |
| u_long subid; /* This node's integer subidentifier */ |
| int modid; /* The module containing this node */ |
| char *parent; /* The parent's textual name */ |
| int tc_index; /* index into tclist (-1 if NA) */ |
| int type; /* The type of object this represents */ |
| int access; |
| int status; |
| struct enum_list *enums; /* (optional) list of enumerated integers */ |
| struct range_list *ranges; |
| struct index_list *indexes; |
| char *augments; |
| struct varbind_list *varbinds; |
| char *hint; |
| char *units; |
| char *description; /* description (a quoted string) */ |
| char *reference; /* references (a quoted string) */ |
| char *defaultValue; |
| char *filename; |
| int lineno; |
| }; |
| |
| /* |
| * 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; |
| char *description; |
| } tclist[MAXTC]; |
| |
| int mibLine = 0; |
| 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) |
| #define OBJIDENTITY 106 |
| /* |
| * 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, OBJIDENTITY} |
| , |
| {"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 MODULE_SYNTAX_ERROR 4 |
| |
| int gMibError = 0,gLoop = 0; |
| char *gpMibErrorString = NULL; |
| char gMibNames[STRINGMAX]; |
| |
| #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; |
| |
| static struct node *orphan_nodes = NULL; |
| NETSNMP_IMPORT struct tree *tree_head; |
| 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 int first_err_module = 1; |
| static char *last_err_module = NULL; /* 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 int read_module_replacements(const char *); |
| static int 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((unsigned char)(*cp)); |
| return (hash); |
| } |
| |
| void |
| netsnmp_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 |
| */ |
| } |
| |
| #ifndef NETSNMP_NO_LEGACY_DEFINITIONS |
| void |
| init_mib_internals(void) |
| { |
| netsnmp_init_mib_internals(); |
| } |
| #endif |
| |
| 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; |
| |
| netsnmp_feature_child_of(parse_get_error_count, netsnmp_unused) |
| #ifndef NETSNMP_FEATURE_REMOVE_PARSE_GET_ERROR_COUNT |
| int |
| get_mib_parse_error_count(void) |
| { |
| return erroneousMibs; |
| } |
| #endif /* NETSNMP_FEATURE_REMOVE_PARSE_GET_ERROR_COUNT */ |
| |
| |
| static void |
| print_error(const char *str, 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", str, mibLine, |
| File); |
| else if (token && *token) |
| snmp_log(LOG_ERR, "%s (%s): At line %d in %s\n", str, token, |
| mibLine, File); |
| else |
| snmp_log(LOG_ERR, "%s: At line %d in %s\n", str, mibLine, File); |
| } |
| |
| static void |
| print_module_not_found(const char *cp) |
| { |
| if (first_err_module) { |
| snmp_log(LOG_ERR, "MIB search path: %s\n", |
| netsnmp_get_mib_directory()); |
| first_err_module = 0; |
| } |
| 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->reference); |
| 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->module_list != &Tree->modid) |
| free(Tree->module_list); |
| free(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->reference) |
| free(np->reference); |
| 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); |
| } |
| |
| static void |
| print_range_value(FILE * fp, int type, struct range_list * rp) |
| { |
| switch (type) { |
| case TYPE_INTEGER: |
| case TYPE_INTEGER32: |
| if (rp->low == rp->high) |
| fprintf(fp, "%d", rp->low); |
| else |
| fprintf(fp, "%d..%d", rp->low, rp->high); |
| break; |
| case TYPE_UNSIGNED32: |
| case TYPE_OCTETSTR: |
| case TYPE_GAUGE: |
| case TYPE_UINTEGER: |
| if (rp->low == rp->high) |
| fprintf(fp, "%u", (unsigned)rp->low); |
| else |
| fprintf(fp, "%u..%u", (unsigned)rp->low, (unsigned)rp->high); |
| break; |
| default: |
| /* No other range types allowed */ |
| break; |
| } |
| } |
| |
| #ifdef TEST |
| static void |
| print_nodes(FILE * fp, struct node *root) |
| { |
| struct enum_list *ep; |
| struct index_list *ip; |
| 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) { |
| struct range_list *rp; |
| fprintf(fp, " Ranges: "); |
| for (rp = np->ranges; rp; rp = rp->next) { |
| fprintf(fp, "\n "); |
| print_range_value(fp, np->type, rp); |
| } |
| fprintf(fp, "\n"); |
| } |
| 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(void) |
| { |
| 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 NOTIFGROUP: |
| translation_table[count] = TYPE_NOTIFGROUP; |
| break; |
| case OBJGROUP: |
| translation_table[count] = TYPE_OBJGROUP; |
| break; |
| case MODULEIDENTITY: |
| translation_table[count] = TYPE_MODID; |
| break; |
| case OBJIDENTITY: |
| translation_table[count] = TYPE_OBJIDENTITY; |
| 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(void) |
| { |
| 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 = 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->module_list != &tp->modid) |
| free(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->reference = tp->reference; |
| 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) { |
| 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 : "<no module>"), |
| (onp->label ? onp->label : "<no label>"), |
| (onp->parent ? onp->parent : "<no parent>"), |
| onp->subid); |
| snmp_log(LOG_WARNING, |
| "Undefined identifier: %s near line %d of %s\n", |
| (onp->parent ? onp->parent : "<no 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) { |
| if (length == 1) { |
| print_error("Attempt to define a root oid", name, OBJECT); |
| return NULL; |
| } |
| 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); |
| if (np->label == NULL) { |
| SNMP_FREE(np->parent); |
| SNMP_FREE(np); |
| return (NULL); |
| } |
| } else { |
| if (!nop->label) { |
| nop->label = (char *) malloc(20 + ANON_LEN); |
| if (nop->label == NULL) { |
| SNMP_FREE(np->parent); |
| SNMP_FREE(np); |
| 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); |
| } |
| |
| #ifndef NETSNMP_FEATURE_REMOVE_GET_TC_DESCRIPTION |
| /* used in the perl module */ |
| const char * |
| get_tc_description(int tc_index) |
| { |
| if (tc_index < 0 || tc_index >= MAXTC) |
| return NULL; |
| return (tclist[tc_index].description); |
| } |
| #endif /* NETSNMP_FEATURE_REMOVE_GET_TC_DESCRIPTION */ |
| |
| |
| /* |
| * 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; |
| /* some enums use "deprecated" to indicate a no longer value label */ |
| /* (EG: IP-MIB's IpAddressStatusTC) */ |
| if (type == LABEL || type == DEPRECATED) { |
| /* |
| * 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 = strtoul(nexttoken, NULL, 10); |
| nexttype = get_token(fp, nexttoken, MAXTOKEN); |
| if (nexttype == RANGE) { |
| nexttype = get_token(fp, nexttoken, MAXTOKEN); |
| errno = 0; |
| high = strtoul(nexttoken, NULL, 10); |
| if ( errno == ERANGE ) { |
| if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_MIB_WARNINGS)) |
| snmp_log(LOG_WARNING, |
| "Warning: Upper bound not handled correctly (%s != %d): At line %d in %s\n", |
| nexttoken, high, mibLine, File); |
| } |
| 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; |
| char *descr = 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 if (type == DESCRIPTION && |
| netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) { |
| type = get_token(fp, quoted_string_buffer, MAXQUOTESTR); |
| if (type != QUOTESTRING) |
| print_error("DESCRIPTION must be string", token, |
| type); |
| else |
| descr = strdup(quoted_string_buffer); |
| } 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->description = descr; |
| 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; |
| } |
| np->reference = strdup(quoted_string_buffer); |
| 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) |
| strlcat(defbuf, "\\\"", sizeof(defbuf)); |
| strlcat(defbuf, quoted_string_buffer, sizeof(defbuf)); |
| if (type == QUOTESTRING) |
| strlcat(defbuf, "\\\"", sizeof(defbuf)); |
| strlcat(defbuf, " ", sizeof(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)); |
| if (!o) { |
| print_error("Resource failure", token, type); |
| goto skip; |
| } |
| 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; |
| } |
| np->reference = strdup(quoted_string_buffer); |
| 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 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; |
| } |
| np->reference = 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); |
| } |
| |
| /* |
|