5452 lines
160 KiB
C
Executable File
5452 lines
160 KiB
C
Executable File
/*
|
|
* 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>
|
|
|
|
#ifndef 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
|
|
# ifdef WIN32
|
|
# include <sys/timeb.h>
|
|
# else
|
|
# include <sys/time.h>
|
|
# endif
|
|
# include <time.h>
|
|
#else
|
|
# if HAVE_SYS_TIME_H
|
|
# include <sys/time.h>
|
|
# else
|
|
# include <time.h>
|
|
# endif
|
|
#endif
|
|
#if HAVE_WINSOCK_H
|
|
#include <winsock.h>
|
|
#endif
|
|
#if HAVE_NETINET_IN_H
|
|
#include <netinet/in.h>
|
|
#endif
|
|
#if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP)
|
|
#include <regex.h>
|
|
#endif
|
|
#if HAVE_DMALLOC_H
|
|
#include <dmalloc.h>
|
|
#endif
|
|
|
|
#include <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>
|
|
|
|
/*
|
|
* 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)
|
|
/*
|
|
* 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 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(*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 *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 (!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->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->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);
|
|
}
|
|
|
|
#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->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);
|
|
} 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);
|
|
}
|
|
|
|
const char *
|
|
get_tc_description(int tc_index)
|
|
{
|
|
if (tc_index < 0 || tc_index >= MAXTC)
|
|
return NULL;
|
|
return (tclist[tc_index].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;
|
|
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);
|
|
errno = 0;
|
|
high = strtol(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) {
|
|
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));
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* 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 REFERENCE:
|
|
/* I'm not sure REFERENCEs are legal in smiv1 traps??? */
|
|
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 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));
|
|
if (!op)
|
|
return 0;
|
|
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;
|
|
}
|
|
np->reference = strdup(quoted_string_buffer);
|
|
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;
|
|
}
|
|
np->reference = strdup(token);
|
|
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) {
|
|
int found = 0;
|
|
for (; old_i < import_count; ++old_i) {
|
|
found += read_import_replacements(token, &import_list[old_i]);
|
|
}
|
|
if (!found)
|
|
print_module_not_found(token);
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
|
|
if (modid != -1) 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 int
|
|
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) netsnmp_read_module(mcp->new_module);
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
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) netsnmp_read_module(mcp->new_module);
|
|
identifier->modid = which_module(mcp->new_module);
|
|
return 1; /* finished! */
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If no exact match, load everything relevant
|
|
*/
|
|
return 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;
|
|
}
|
|
|
|
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 : "<no label>"),
|
|
(onp->parent ? onp->parent : "<no parent>"),
|
|
onp->subid);
|
|
|
|
np = onp;
|
|
onp = onp->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifndef NETSNMP_CLEAN_NAMESPACE
|
|
struct tree *
|
|
read_module(const char *name)
|
|
{
|
|
return netsnmp_read_module(name);
|
|
}
|
|
#endif
|
|
|
|
struct tree *
|
|
netsnmp_read_module(const char *name)
|
|
{
|
|
if (read_module_internal(name) == MODULE_NOT_FOUND)
|
|
if (!read_module_replacements(name))
|
|
print_module_not_found(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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifndef NETSNMP_CLEAN_NAMESPACE
|
|
int
|
|
unload_module(const char *name)
|
|
{
|
|
return netsnmp_unload_module(name);
|
|
}
|
|
#endif
|
|
|
|
int
|
|
netsnmp_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)) {
|
|
DEBUGMSGTL(("parse-mibs", " %s is now in %s\n",
|
|
name, 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 (np)
|
|
np->next = nnp;
|
|
else
|
|
np = root = nnp;
|
|
while (np->next)
|
|
np = np->next;
|
|
if (np->type == TYPE_OTHER)
|
|
np->type = type;
|
|
}
|
|
}
|
|
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_mibfile(const char* tmpstr, const char* d_name, FILE *ip )
|
|
{
|
|
FILE *fp;
|
|
char token[MAXTOKEN], token2[MAXTOKEN];
|
|
|
|
/*
|
|
* which module is this
|
|
*/
|
|
if ((fp = fopen(tmpstr, "r")) == NULL) {
|
|
snmp_log_perror(tmpstr);
|
|
return 1;
|
|
}
|
|
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);
|
|
if (ip)
|
|
fprintf(ip, "%s %s\n", token, d_name);
|
|
fclose(fp);
|
|
return 0;
|
|
} else {
|
|
fclose(fp);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* For Win32 platforms, the directory does not maintain a last modification
|
|
* date that we can compare with the modification date of the .index file.
|
|
* Therefore there is no way to know whether any .index file is valid.
|
|
* This is the reason for the #if !(defined(WIN32) || defined(cygwin))
|
|
* in the add_mibdir function
|
|
*/
|
|
int
|
|
add_mibdir(const char *dirname)
|
|
{
|
|
FILE *ip;
|
|
DIR *dir, *dir2;
|
|
const char *oldFile = File;
|
|
struct dirent *file;
|
|
char tmpstr[300];
|
|
int count = 0;
|
|
int fname_len = 0;
|
|
#if !(defined(WIN32) || defined(cygwin))
|
|
char token[MAXTOKEN];
|
|
char space;
|
|
char newline;
|
|
struct stat dir_stat, idx_stat;
|
|
char tmpstr1[300];
|
|
#endif
|
|
|
|
DEBUGMSGTL(("parse-mibs", "Scanning directory %s\n", dirname));
|
|
#if !(defined(WIN32) || defined(cygwin))
|
|
snprintf(token, sizeof(token), "%s/%s", dirname, ".index");
|
|
token[ sizeof(token)-1 ] = 0;
|
|
if (stat(token, &idx_stat) == 0 && stat(dirname, &dir_stat) == 0) {
|
|
if (dir_stat.st_mtime < idx_stat.st_mtime) {
|
|
DEBUGMSGTL(("parse-mibs", "The index is good\n"));
|
|
if ((ip = fopen(token, "r")) != NULL) {
|
|
while (fscanf(ip, "%127s%c%299s%c", token, &space, tmpstr,
|
|
&newline) == 4) {
|
|
|
|
/*
|
|
* If an overflow of the token or tmpstr buffers has been
|
|
* found log a message and break out of the while loop,
|
|
* thus the rest of the file tokens will be ignored.
|
|
*/
|
|
if (space != ' ' || newline != '\n') {
|
|
snmp_log(LOG_ERR,
|
|
"add_mibdir: strings scanned in from %s/%s " \
|
|
"are too large. count = %d\n ", dirname,
|
|
".index", count);
|
|
break;
|
|
}
|
|
|
|
snprintf(tmpstr1, sizeof(tmpstr1), "%s/%s", dirname, tmpstr);
|
|
tmpstr1[ sizeof(tmpstr1)-1 ] = 0;
|
|
new_module(token, tmpstr1);
|
|
count++;
|
|
}
|
|
fclose(ip);
|
|
return count;
|
|
} else
|
|
DEBUGMSGTL(("parse-mibs", "Can't read index\n"));
|
|
} else
|
|
DEBUGMSGTL(("parse-mibs", "Index outdated\n"));
|
|
} else
|
|
DEBUGMSGTL(("parse-mibs", "No index\n"));
|
|
#endif
|
|
|
|
if ((dir = opendir(dirname))) {
|
|
snprintf(tmpstr, sizeof(tmpstr), "%s/.index", dirname);
|
|
tmpstr[ sizeof(tmpstr)-1 ] = 0;
|
|
ip = fopen(tmpstr, "w");
|
|
while ((file = readdir(dir))) {
|
|
/*
|
|
* Only parse file names that don't begin with a '.'
|
|
* Also skip files ending in '~', or starting/ending
|
|
* with '#' which are typically editor backup files.
|
|
*/
|
|
if (file->d_name != NULL) {
|
|
fname_len = strlen( file->d_name );
|
|
if (fname_len > 0 && file->d_name[0] != '.'
|
|
&& file->d_name[0] != '#'
|
|
&& file->d_name[fname_len-1] != '#'
|
|
&& file->d_name[fname_len-1] != '~') {
|
|
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 {
|
|
if ( add_mibfile( tmpstr, file->d_name, ip ))
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
File = oldFile;
|
|
closedir(dir);
|
|
if (ip)
|
|
fclose(ip);
|
|
return (count);
|
|
}
|
|
else
|
|
DEBUGMSGTL(("parse-mibs","cannot open MIB directory %s\n", dirname));
|
|
|
|
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) netsnmp_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)
|
|
netsnmp_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;
|
|
}
|
|
|
|
/* Find node in any MIB module
|
|
Used by Perl modules */
|
|
struct tree *
|
|
find_node(const char *name, struct tree *subtree)
|
|
{ /* Unused */
|
|
return (find_tree_node(name, -1));
|
|
}
|
|
|
|
/* Find node in specific MIB module
|
|
Used by Perl modules */
|
|
struct tree *
|
|
find_node2(const char *name, const char *module)
|
|
{
|
|
int modid = -1;
|
|
if (module) {
|
|
modid = which_module(module);
|
|
}
|
|
if (modid == -1)
|
|
{
|
|
return (NULL);
|
|
}
|
|
return (find_tree_node(name, modid));
|
|
}
|
|
|
|
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->reference = np->reference;
|
|
np->reference = 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);
|
|
}
|
|
|
|
#endif /* DISABLE_MIB_LOADING */
|