/* * Mibble MIB Parser (www.mibble.org) * * See LICENSE.txt for licensing information. * * Copyright (c) 2004-2017 Per Cederberg. All rights reserved. */ package net.percederberg.mibble; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import net.percederberg.mibble.value.NumberValue; import net.percederberg.mibble.value.ObjectIdentifierValue; /** * An SNMP MIB module. This class contains all the information * from a single MIB module, including all defined types and values. * Note that a single MIB file may contain several such modules, * although that is not very common. MIB files are loaded through a * {@link MibLoader MIB loader}. * * @author Per Cederberg * @version 2.10 * @since 2.0 * * @see <a href="http://www.ietf.org/rfc/rfc3411.txt">RFC 3411 - An * Architecture for Describing SNMP Management Frameworks</a> */ public class Mib implements MibContext { /** * The loader used for this MIB. */ private MibLoader loader; /** * The loader log used for loading this MIB. */ private MibLoaderLog log; /** * The MIB file reference. */ private MibFileRef fileRef = null; /** * The explicitly loaded flag. This flag is set when a MIB is * loaded by a direct call to the MibLoader, in contrast to when * it is loaded as the result of an import. */ private boolean loaded = false; /** * The MIB name. */ private String name = null; /** * The SMI version. */ private int smiVersion = 1; /** * The MIB file header comment. */ private String headerComment = null; /** * The MIB file footer comment. */ private String footerComment = null; /** * The MIB source text (split into lines). */ private ArrayList<String> text = new ArrayList<>(); /** * The references to imported MIB files. */ private ArrayList<MibImport> imports = new ArrayList<>(); /** * The MIB symbol list. This list contains the MIB symbol objects * in the order they were added (i.e. present in the file). */ private ArrayList<MibSymbol> symbolList = new ArrayList<>(); /** * The MIB symbol name map. This maps the symbol names to their * respective MIB symbol objects. */ private HashMap<String,MibSymbol> symbolNameMap = new HashMap<>(); /** * The MIB symbol value map. This maps the symbol values to their * respective MIB symbol objects. Only the value symbols with * either a number or an object identifier value is present in * this map. */ private HashMap<String,MibSymbol> symbolValueMap = new HashMap<>(); /** * Creates a new MIB module. This will NOT read the actual MIB * file, but only creates an empty container. The symbols are * then added during the first analysis pass (of two), leaving * symbols in the MIB possibly containing unresolved references. * A separate call to initialize() must be made once all * referenced MIB modules have also been loaded. * * @param loader the MIB loader to use for imports * @param log the MIB log to use for errors * * @see #initialize() */ Mib(MibLoader loader, MibLoaderLog log) { this.loader = loader; this.log = log; } /** * Initializes the MIB file. This will resolve all imported MIB * file references. Note that this method shouldn't be called * until all referenced MIB files (and their respective * references) have been loaded. * * @throws MibLoaderException if the MIB file couldn't be * analyzed correctly * * @see #validate() */ void initialize() throws MibLoaderException { // Resolve imported MIB files int errors = log.errorCount(); for (MibImport imp : imports) { imp.initialize(log); if (loaded) { imp.validateSmiVersion(log, this); } } // Check for errors if (errors != log.errorCount()) { throw new MibLoaderException(log); } } /** * Validates the MIB file. This will resolve all type and value * references in the MIB symbols, while also validating them for * consistency. Note that this method shouldn't be called until * all referenced MIB files (and their respective references) * have been initialized. * * @throws MibLoaderException if the MIB file couldn't be * analyzed correctly * * @see #initialize() */ void validate() throws MibLoaderException { // Validate all symbols int errors = log.errorCount(); for (MibSymbol symbol : new ArrayList<>(symbolList)) { try { symbol.initialize(log); } catch (MibException e) { log.addError(e); } if (symbol instanceof MibValueSymbol) { MibValue value = ((MibValueSymbol) symbol).getValue(); boolean isNumber = value instanceof NumberValue; boolean isOID = value instanceof ObjectIdentifierValue; if (isNumber || isOID) { if (symbolValueMap.containsKey(value.toString())) { String msg = "duplicate definition of " + value.toString() + " in MIB"; log.addWarning(symbol.getFileRef(), msg); } symbolValueMap.put(value.toString(), symbol); } } } // Check for errors if (errors != log.errorCount()) { throw new MibLoaderException(log); } } /** * Clears and prepares this MIB for garbage collection. This method * will recursively clear all associated symbols, making sure that * no data structures references symbols from this MIB. Obviously, * this method shouldn't be called unless all dependent MIBs have * been cleared first. */ void clear() { loader = null; log = null; if (imports != null) { imports.clear(); } imports = null; if (symbolList != null) { for (MibSymbol symbol : new ArrayList<>(symbolList)) { symbol.clear(); } symbolList.clear(); } symbolList = null; if (symbolNameMap != null) { symbolNameMap.clear(); } symbolNameMap = null; if (symbolValueMap != null) { symbolValueMap.clear(); } symbolValueMap = null; } /** * Compares this MIB to another object. This method will return * true if the object is a string containing the MIB name, a file * containing the MIB file, or a Mib having the same name. * * @param obj the object to compare with * * @return true if the objects are equal, or * false otherwise */ public boolean equals(Object obj) { if (obj instanceof String) { return name.equals(obj); } else if (fileRef.file != null && obj instanceof File) { return fileRef.file.equals(obj); } else if (obj instanceof Mib) { return obj.equals(name); } else { return false; } } /** * Returns the hash code value for the object. This method is * reimplemented to fulfil the contract of returning the same * hash code for objects that are considered equal. * * @return the hash code value for the object * * @since 2.6 */ public int hashCode() { return name.hashCode(); } /** * Checks if this MIB module has been explicitly loaded. A MIB * module is considered explicitly loaded if the file or resource * containing the MIB definition was loaded by a direct call to * the MIB loader. Implictly loaded MIB modules are loaded as a * result of import statements in explicitly loaded MIBs. * * @return true if this MIB module was explicitly loaded, or * false otherwise * * @since 2.7 */ public boolean isLoaded() { return loaded; } /** * Sets the the explicitly loaded flag. * * @param loaded the new flag value */ void setLoaded(boolean loaded) { this.loaded = loaded; } /** * Returns the MIB name. This is sometimes also referred to as * the MIB module name. * * @return the MIB name */ public String getName() { return name; } /** * Changes the MIB name. This method should only be called by * the MIB analysis classes. * * @param name the MIB name */ void setName(String name) { this.name = name; } /** * Returns the MIB file. * * @return the MIB file */ public File getFile() { return fileRef.getFile(); } /** * Sets the MIB file reference. This method should only be called * by the MIB analysis classes. * * @param fileRef the MIB file reference */ void setFileRef(MibFileRef fileRef) { this.fileRef = fileRef; if (fileRef.file == null) { fileRef.file = new File(name); } } /** * Returns the MIB loader used when loading this MIB. * * @return the loader used */ public MibLoader getLoader() { return loader; } /** * Returns the loader log used when loading this MIB. * * @return the loader log used */ public MibLoaderLog getLog() { return log; } /** * Returns the SMI version used for defining this MIB. This * number can be either 1 (for SMIv1) or 2 (for SMIv2). It is set * based on which macros are used in the MIB file. * * @return the SMI version used for defining the MIB * * @since 2.6 */ public int getSmiVersion() { return smiVersion; } /** * Sets the SMI version used for defining this MIB. This method * should only be called by the MIB analysis classes. * * @param version the new SMI version * * @since 2.6 */ void setSmiVersion(int version) { this.smiVersion = version; } /** * Returns the unparsed input MIB text. * * @return the raw MIB file text * * @since 2.10 */ public String getText() { StringBuilder buffer = new StringBuilder(); for (String line : text) { buffer.append(line); buffer.append('\n'); } return buffer.toString(); } /** * Returns the unparsed input MIB text for a reference. * * @param ref the MIB file reference * * @return the raw MIB text for the reference * * @since 2.10 */ String getText(MibFileRef ref) { int from = ref.lineCommentStart - this.fileRef.lineCommentStart; int to = ref.lineEnd - this.fileRef.lineCommentStart; StringBuilder buffer = new StringBuilder(); for (int i = from; i <= to; i++) { buffer.append(text.get(i)); buffer.append('\n'); } return buffer.toString(); } /** * Sets the unparsed input MIB text. This method should only be * called by the MIB analysis classes. * * @param text the raw MIB file text * * @since 2.10 */ void setText(String text) { this.text.clear(); String[] lines = text.split("[ \\t\\r]*\\n"); for (String line : lines) { this.text.add(line); } } /** * Returns the MIB file header comment. * * @return the MIB file header comment, or * null if no comment was present * * @since 2.6 */ public String getHeaderComment() { return headerComment; } /** * Sets the MIB file header comment. * * @param comment the MIB header comment * * @since 2.6 */ void setHeaderComment(String comment) { this.headerComment = comment; } /** * Returns the MIB file footer comment. * * @return the MIB file footer comment, or * null if no comment was present * * @since 2.6 */ public String getFooterComment() { return footerComment; } /** * Sets the MIB file footer comment. * * @param comment the MIB footer comment * * @since 2.6 */ void setFooterComment(String comment) { this.footerComment = comment; } /** * Returns all MIB import references. * * @return a list of all imports * * @see MibImport * * @since 2.6 */ public List<MibImport> getAllImports() { ArrayList<MibImport> res = new ArrayList<>(); for (MibImport imp : imports) { if (imp.hasSymbols()) { res.add(imp); } } return res; } /** * Returns a MIB import reference. * * @param name the imported MIB name * * @return the MIB import reference, or * null if not found */ MibImport getImport(String name) { for (MibImport imp : imports) { if (imp.getName().equals(name)) { return imp; } } return null; } /** * Adds a reference to an imported MIB file. * * @param ref the reference to add */ void addImport(MibImport ref) { imports.add(ref); } /** * Finds all MIB:s that are dependant on this one. The search * will iterate through all loaded MIB:s and return those that * import this one. * * @return the array of MIB:s importing this one * * @see MibLoader * * @since 2.7 */ public Mib[] getImportingMibs() { ArrayList<Mib> res = new ArrayList<>(); for (Mib mib : loader.getAllMibs()) { if (mib != this && mib.getImport(name) != null) { res.add(mib); } } return res.toArray(new Mib[res.size()]); } /** * Returns all symbols in this MIB. * * @return a list of the MIB symbols * * @see MibSymbol */ public List<MibSymbol> getAllSymbols() { return symbolList; } /** * Returns a symbol from this MIB. * * @param name the symbol name * * @return the MIB symbol, or null if not found */ public MibSymbol getSymbol(String name) { return symbolNameMap.get(name); } /** * Returns a value symbol from this MIB. * * @param value the symbol value * * @return the MIB value symbol, or null if not found */ public MibValueSymbol getSymbolByValue(String value) { if (value.startsWith(".")) { value = value.substring(1); } return (MibValueSymbol) symbolValueMap.get(value); } /** * Returns a value symbol from this MIB. * * @param value the symbol value * * @return the MIB value symbol, or null if not found */ public MibValueSymbol getSymbolByValue(MibValue value) { return (MibValueSymbol) symbolValueMap.get(value.toString()); } /** * Returns a value symbol from this MIB. The search is performed * by using the strictly numerical OID value specified. Differing * from the getSymbolByValue() methods, this method may return a * symbol with only a partial OID match. If an exact match for * the OID is present in the MIB, this method will always return * the same result as getSymbolByValue(). Otherwise, the symbol * with the longest matching OID will be returned, making it * possible to identify a MIB symbol from an OID containing table * row indices or similar. * * @param oid the numeric OID value * * @return the MIB value symbol, or null if not found * * @since 2.5 */ public MibValueSymbol getSymbolByOid(String oid) { if (oid.startsWith(".")) { oid = oid.substring(1); } int pos = 0; do { MibValueSymbol sym = getSymbolByValue(oid); if (sym != null) { return sym; } pos = oid.lastIndexOf("."); if (pos > 0) { oid = oid.substring(0, pos); } } while (pos > 0); return null; } /** * Returns the root MIB value symbol. This value symbol is * normally the module identifier (in SMIv2), but may also be * just the base object identifier in the MIB. * * @return the root MIB value symbol * * @since 2.6 */ public MibValueSymbol getRootSymbol() { MibValueSymbol root = null; for (MibSymbol symbol : symbolList) { if (symbol instanceof MibValueSymbol) { root = (MibValueSymbol) symbol; break; } } MibValueSymbol parent = null; while (root != null && (parent = root.getParent()) != null) { if (!root.getMib().equals(parent.getMib())) { break; } root = parent; } return root; } /** * Adds a symbol to this MIB. * * @param symbol the symbol to add */ void addSymbol(MibSymbol symbol) { symbolList.add(symbol); symbolNameMap.put(symbol.getName(), symbol); } /** * Searches for a named MIB symbol. This method is required to * implement the MibContext interface but returns the same results * as getSymbol(String).<p> * * <strong>NOTE:</strong> This is an internal method that should * only be called by the MIB loader. * * @param name the symbol name * @param expanded the expanded scope flag * * @return the MIB symbol, or null if not found * * @since 2.4 */ public MibSymbol findSymbol(String name, boolean expanded) { return getSymbol(name); } /** * Returns a string representation of this object. * * @return a string representation of this object */ public String toString() { return getName(); } }