/******************************************************************************* * This file is part of OpenNMS(R). * * Copyright (C) 2006-2011 The OpenNMS Group, Inc. * OpenNMS(R) is Copyright (C) 1999-2011 The OpenNMS Group, Inc. * * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc. * * OpenNMS(R) is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * OpenNMS(R) is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenNMS(R). If not, see: * http://www.gnu.org/licenses/ * * For more information contact: * OpenNMS(R) Licensing <license@opennms.org> * http://www.opennms.org/ * http://www.opennms.com/ *******************************************************************************/ package org.opennms.netmgt.config; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Serializable; import java.io.StringWriter; import java.io.Writer; import java.net.InetAddress; import java.net.UnknownHostException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Enumeration; import java.util.Set; import java.util.TreeSet; import org.apache.regexp.RE; import org.exolab.castor.xml.MarshalException; import org.exolab.castor.xml.Marshaller; import org.exolab.castor.xml.ValidationException; import org.opennms.core.utils.ConfigFileConstants; import org.opennms.core.utils.DBUtils; import org.opennms.core.utils.InetAddressUtils; import org.opennms.core.utils.LogUtils; import org.opennms.core.utils.ThreadCategory; import org.opennms.core.xml.CastorUtils; import org.opennms.netmgt.config.vulnscand.Excludes; import org.opennms.netmgt.config.vulnscand.Range; import org.opennms.netmgt.config.vulnscand.ScanLevel; import org.opennms.netmgt.config.vulnscand.VulnscandConfiguration; import org.springframework.core.io.FileSystemResource; /** * This is the singleton class used to load the configuration for the OpenNMS * Vulnscand service from the vulnscand-configuration xml file. * * <strong>Note: </strong>Users of this class should make sure the * <em>init()</em> is called before calling any other method to ensure the * config is loaded before accessing other convenience methods. * * @author <a href="mailto:seth@opennms.org">Seth Leger </a> * @author <a href="mailto:mike@opennms.org">Mike Davidson </a> * @author <a href="mailto:weave@oculan.com">Weave </a> */ public final class VulnscandConfigFactory { /** * This member is set to true if the configuration file has been loaded. */ private static boolean m_loaded = false; /** * The singleton instance of this factory */ private static VulnscandConfigFactory m_singleton = null; /** * The config class loaded from the config file */ private static VulnscandConfiguration m_config; /** * Cached value of the plugin lists for each scan level */ private static String[] m_pluginLists = null; /** * Cached value of the "safe checks" values for each scan level */ private static boolean[] m_safeChecks = null; /** * Cached set of the excluded IP addresses */ private static Set<Serializable> m_excludes = null; /** * Whitespace regex */ private static RE m_space = null; /** * The SQL statement used to determine if an IP address is already in the * ipInterface table and there is known. */ private static final String RETRIEVE_IPADDR_SQL = "SELECT ipaddr FROM ipinterface WHERE ipaddr=? AND ismanaged!='D'"; /** * The SQL statement used to determine if an IP address is already in the * ipInterface table and if so what its parent nodeid is. */ private static final String RETRIEVE_IPADDR_NODEID_SQL = "SELECT nodeid FROM ipinterface WHERE ipaddr=? AND ismanaged!='D'"; /** * Constructs a new VulnscandConfigFactory object for access to the * Vulnscand configuration information. * * @param configFile * The configuration file to load. * @exception java.io.IOException * Thrown if the specified config file cannot be read * @exception org.exolab.castor.xml.MarshalException * Thrown if the file does not conform to the schema. * @exception org.exolab.castor.xml.ValidationException * Thrown if the contents do not match the required schema. */ private VulnscandConfigFactory(String configFile) throws IOException, MarshalException, ValidationException { m_config = CastorUtils.unmarshal(VulnscandConfiguration.class, new FileSystemResource(configFile)); } /** * Load the config from the default config file and create the singleton * instance of this factory. * * @exception java.io.IOException * Thrown if the specified config file cannot be read * @exception org.exolab.castor.xml.MarshalException * Thrown if the file does not conform to the schema. * @exception org.exolab.castor.xml.ValidationException * Thrown if the contents do not match the required schema. * @throws java.io.IOException if any. * @throws org.exolab.castor.xml.MarshalException if any. * @throws org.exolab.castor.xml.ValidationException if any. */ public static synchronized void init() throws IOException, MarshalException, ValidationException { if (m_loaded) { // init already called - return // to reload, reload() will need to be called return; } File cfgFile = ConfigFileConstants.getFile(ConfigFileConstants.VULNSCAND_CONFIG_FILE_NAME); ThreadCategory.getInstance(VulnscandConfigFactory.class).debug("init: config file path: " + cfgFile.getPath()); m_singleton = new VulnscandConfigFactory(cfgFile.getPath()); try { m_space = new RE("[:space:]+"); } catch (org.apache.regexp.RESyntaxException ex) { ThreadCategory.getInstance(VulnscandConfigFactory.class).error("UNEXPECTED CONDITION: Regex in config factory is incorrect. Check the code.", ex); } m_loaded = true; } /** * Reload the config from the default config file * * @exception java.io.IOException * Thrown if the specified config file cannot be read/loaded * @exception org.exolab.castor.xml.MarshalException * Thrown if the file does not conform to the schema. * @exception org.exolab.castor.xml.ValidationException * Thrown if the contents do not match the required schema. * @throws java.io.IOException if any. * @throws org.exolab.castor.xml.MarshalException if any. * @throws org.exolab.castor.xml.ValidationException if any. */ public static synchronized void reload() throws IOException, MarshalException, ValidationException { m_singleton = null; m_loaded = false; // Destroy all cached values m_pluginLists = null; m_safeChecks = null; m_excludes = null; init(); } /** * Saves the current settings to disk * * @throws java.lang.Exception if any. */ public static synchronized void saveCurrent() throws Exception { // Marshal to a string first, then write the string to the file. This // way the original config // isn't lost if the XML from the marshal is hosed. StringWriter stringWriter = new StringWriter(); Marshaller.marshal(m_config, stringWriter); if (stringWriter.toString() != null) { Writer fileWriter = new OutputStreamWriter(new FileOutputStream(ConfigFileConstants.getFile(ConfigFileConstants.VULNSCAND_CONFIG_FILE_NAME)), "UTF-8"); fileWriter.write(stringWriter.toString()); fileWriter.flush(); fileWriter.close(); } reload(); } /** * Return the singleton instance of this factory. * * @return The current factory instance. * @throws java.lang.IllegalStateException * Thrown if the factory has not yet been initialized. */ public static synchronized VulnscandConfigFactory getInstance() { if (!m_loaded) throw new IllegalStateException("The factory has not been initialized"); return m_singleton; } /** * Return the Vulnscand configuration object. * * @return a {@link org.opennms.netmgt.config.vulnscand.VulnscandConfiguration} object. */ public static VulnscandConfiguration getConfiguration() { return m_config; } /** * This method is used to convert the passed IP address to a * <code>long</code> value. The address is converted in network byte order * (big endian). This is compatible with the number format of the JVM, and * thus the return longs can be compared with other converted IP Addresses * to determine inclusion. * * @param addr * The IP address to convert. * @return The converted IP address. * @deprecated See * org.opennms.core.utils.InetAddressCollection.toLong(InetAddress * addr) */ public static long toLong(InetAddress addr) { byte[] baddr = addr.getAddress(); return ((((long) baddr[0] & 0xffL) << 24) | (((long) baddr[1] & 0xffL) << 16) | (((long) baddr[2] & 0xffL) << 8) | ((long) baddr[3] & 0xffL)); } /** * <P> * Converts a 64-bit unsigned quantity to a IPv4 dotted decimal string * address. * </P> * * @param address * The 64-bit quantity to convert. * @return The dotted decimal IPv4 address string. * @throws java.net.UnknownHostException if any. */ public static InetAddress toInetAddress(long address) throws UnknownHostException { StringBuffer buf = new StringBuffer(); buf.append((int) ((address >>> 24) & 0xff)).append('.'); buf.append((int) ((address >>> 16) & 0xff)).append('.'); buf.append((int) ((address >>> 8) & 0xff)).append('.'); buf.append((int) (address & 0xff)); return InetAddressUtils.addr(buf.toString()); } /** * <p>isInterfaceInDB</p> * * @param dbConn a {@link java.sql.Connection} object. * @param ifAddress a {@link java.net.InetAddress} object. * @return a boolean. * @throws java.sql.SQLException if any. */ public static boolean isInterfaceInDB(Connection dbConn, InetAddress ifAddress) throws SQLException { boolean result = false; final String hostAddress = InetAddressUtils.str(ifAddress); LogUtils.debugf(VulnscandConfigFactory.class, "isInterfaceInDB: attempting to lookup interface %s in the database.", hostAddress); DBUtils d = new DBUtils(VulnscandConfigFactory.class); try { PreparedStatement s = dbConn.prepareStatement(RETRIEVE_IPADDR_SQL); d.watch(s); s.setString(1, hostAddress); ResultSet rs = s.executeQuery(); d.watch(rs); if (rs.next()) result = true; } finally { d.cleanUp(); } return result; } /** * <p>getInterfaceDbNodeId</p> * * @param dbConn a {@link java.sql.Connection} object. * @param ifAddress a {@link java.net.InetAddress} object. * @return a int. * @throws java.sql.SQLException if any. */ public static int getInterfaceDbNodeId(Connection dbConn, InetAddress ifAddress) throws SQLException { final String hostAddress = InetAddressUtils.str(ifAddress); LogUtils.debugf(VulnscandConfigFactory.class, "getInterfaceDbNodeId: attempting to lookup interface %s in the database.", hostAddress); // Set connection as read-only // // dbConn.setReadOnly(true); int nodeid = -1; DBUtils d = new DBUtils(VulnscandConfigFactory.class); try { PreparedStatement s = dbConn.prepareStatement(RETRIEVE_IPADDR_NODEID_SQL); d.watch(s); s.setString(1, hostAddress); ResultSet rs = s.executeQuery(); d.watch(rs); if (rs.next()) { nodeid = rs.getInt(1); } } finally { d.cleanUp(); } return nodeid; } private static ScanLevel getScanLevel(int level) { Enumeration<ScanLevel> scanLevels = m_config.enumerateScanLevel(); while (scanLevels.hasMoreElements()) { ScanLevel scanLevel = scanLevels.nextElement(); if (level == scanLevel.getLevel()) { return scanLevel; } } throw new ArrayIndexOutOfBoundsException("No scan level with that index could be located in the configuration file, index = " + level); } /** * <p>addSpecific</p> * * @param level a int. * @param specific a {@link java.net.InetAddress} object. */ public void addSpecific(int level, InetAddress specific) { addSpecific(getScanLevel(level), specific); } /** * <p>addSpecific</p> * * @param level a {@link org.opennms.netmgt.config.vulnscand.ScanLevel} object. * @param specific a {@link java.net.InetAddress} object. */ public void addSpecific(ScanLevel level, InetAddress specific) { level.addSpecific(InetAddressUtils.str(specific)); } /** * <p>addRange</p> * * @param level a int. * @param begin a {@link java.net.InetAddress} object. * @param end a {@link java.net.InetAddress} object. */ public void addRange(int level, InetAddress begin, InetAddress end) { addRange(getScanLevel(level), begin, end); } /** * <p>addRange</p> * * @param level a {@link org.opennms.netmgt.config.vulnscand.ScanLevel} object. * @param begin a {@link java.net.InetAddress} object. * @param end a {@link java.net.InetAddress} object. */ public void addRange(ScanLevel level, InetAddress begin, InetAddress end) { Range addMe = new Range(); addMe.setBegin(InetAddressUtils.str(begin)); addMe.setEnd(InetAddressUtils.str(end)); level.addRange(addMe); } /** * <p>removeSpecific</p> * * @param level a int. * @param specific a {@link java.net.InetAddress} object. */ public void removeSpecific(int level, InetAddress specific) { removeSpecific(getScanLevel(level), specific); } /** * <p>removeSpecific</p> * * @param level a {@link org.opennms.netmgt.config.vulnscand.ScanLevel} object. * @param specific a {@link java.net.InetAddress} object. */ public void removeSpecific(ScanLevel level, InetAddress specific) { level.removeSpecific(InetAddressUtils.str(specific)); } /** * <p>removeRange</p> * * @param level a int. * @param begin a {@link java.net.InetAddress} object. * @param end a {@link java.net.InetAddress} object. */ public void removeRange(int level, InetAddress begin, InetAddress end) { removeRange(getScanLevel(level), begin, end); } /** * <p>removeRange</p> * * @param level a {@link org.opennms.netmgt.config.vulnscand.ScanLevel} object. * @param begin a {@link java.net.InetAddress} object. * @param end a {@link java.net.InetAddress} object. */ public void removeRange(ScanLevel level, InetAddress begin, InetAddress end) { Range removeMe = new Range(); removeMe.setBegin(InetAddressUtils.str(begin)); removeMe.setEnd(InetAddressUtils.str(end)); level.removeRange(removeMe); } /** * <p>getAllIpAddresses</p> * * @param level a int. * @return a {@link java.util.Set} object. */ public Set<InetAddress> getAllIpAddresses(int level) { return getAllIpAddresses(getScanLevel(level)); } /** * <p>getAllIpAddresses</p> * * @param level a {@link org.opennms.netmgt.config.vulnscand.ScanLevel} object. * @return a {@link java.util.Set} object. */ public Set<InetAddress> getAllIpAddresses(ScanLevel level) { Set<InetAddress> retval = new TreeSet<InetAddress>(); Enumeration<Range> e = level.enumerateRange(); while (e.hasMoreElements()) { Range ir = e.nextElement(); try { for (long i = Long.parseLong(ir.getBegin()); i <= Long.parseLong(ir.getEnd()); i++) { retval.add(toInetAddress(i)); } } catch (NumberFormatException wanker) { ThreadCategory.getInstance(getClass()).warn("Failed to convert address range (" + ir.getBegin() + ", " + ir.getEnd() + ")", wanker); } catch (UnknownHostException uhE) { ThreadCategory.getInstance(getClass()).warn("Failed to convert address range (" + ir.getBegin() + ", " + ir.getEnd() + ")", uhE); } } Enumeration<String> specifics = level.enumerateSpecific(); while (specifics.hasMoreElements()) { String current = specifics.nextElement(); final InetAddress addr = InetAddressUtils.addr(current); if (addr == null) { ThreadCategory.getInstance().warn("Failed to convert address: " + current); } retval.add(addr); } return retval; } /** * <p>getAllExcludes</p> * * @return a {@link java.util.Set} object. */ public Set<Serializable> getAllExcludes() { ThreadCategory log = ThreadCategory.getInstance(VulnscandConfigFactory.class); if (m_excludes == null) { m_excludes = new TreeSet<Serializable>(); Excludes excludes = m_config.getExcludes(); if (excludes != null) { if (excludes.getRangeCount() > 0) { Enumeration<Range> e = excludes.enumerateRange(); while (e.hasMoreElements()) { Range ir = e.nextElement(); try { for (long i = Long.parseLong(ir.getBegin()); i <= Long.parseLong(ir.getEnd()); i++) { m_excludes.add(toInetAddress(i)); } } catch (UnknownHostException uhE) { ThreadCategory.getInstance(getClass()).warn("Failed to convert address range (" + ir.getBegin() + ", " + ir.getEnd() + ")", uhE); } } } if (excludes.getSpecificCount() > 0) { Enumeration<String> e = excludes.enumerateSpecific(); while (e.hasMoreElements()) { String current = e.nextElement(); log.debug("excludes: Specific: " + current); // try { //m_excludes.add(InetAddressUtils.addr(current)); //JOHAN - The Scheduler expects a String m_excludes.add(current); //} catch (UnknownHostException uhE) { // ThreadCategory.getInstance().warn("Failed to convert address: " + current, uhE); //} } } } } return m_excludes; } /** * <p>removeExcludeRange</p> * * @param begin a {@link java.net.InetAddress} object. * @param end a {@link java.net.InetAddress} object. */ public void removeExcludeRange(InetAddress begin, InetAddress end) { Range removeMe = new Range(); removeMe.setBegin(InetAddressUtils.str(begin)); removeMe.setEnd(InetAddressUtils.str(end)); m_config.getExcludes().removeRange(removeMe); } /** * <p>removeExcludeSpecific</p> * * @param specific a {@link java.net.InetAddress} object. */ public void removeExcludeSpecific(InetAddress specific) { m_config.getExcludes().removeSpecific(InetAddressUtils.str(specific)); } /** * <p>getRescanFrequency</p> * * @return a long. */ public long getRescanFrequency() { long frequency = -1; if (m_config.hasRescanFrequency()) frequency = m_config.getRescanFrequency(); else { ThreadCategory.getInstance(VulnscandConfigFactory.class).warn("Vulnscand configuration file is missing rescan interval, defaulting to 24 hour interval."); frequency = 86400000; // default is 24 hours } return frequency; } /** * <p>getInitialSleepTime</p> * * @return a long. */ public long getInitialSleepTime() { long sleep = -1; if (m_config.hasInitialSleepTime()) sleep = m_config.getInitialSleepTime(); else { ThreadCategory.getInstance(VulnscandConfigFactory.class).warn("Vulnscand configuration file is missing initial pause time, defaulting to 5 minutes."); sleep = 300000; // default is 5 minutes } return sleep; } /** * <p>getServerAddress</p> * * @return a {@link java.net.InetAddress} object. */ public InetAddress getServerAddress() { return (InetAddressUtils.addr(m_config.getServerAddress())); } /** * Gets the cached value of the plugin lists in the config file * * @return an array of {@link java.lang.String} objects. */ public String[] getPluginLists() { if (m_pluginLists == null) { m_pluginLists = new String[5]; // Dummy value m_pluginLists[0] = ""; try { Enumeration<?> scanLevels = m_config.enumerateScanLevel(); while (scanLevels.hasMoreElements()) { ScanLevel scanLevel = (ScanLevel) scanLevels.nextElement(); String levelPluginList = scanLevel.getPluginList(); // Get rid of all of the carriage returns, tabs, and spaces levelPluginList = levelPluginList.replace('\n', ' '); levelPluginList = levelPluginList.replace('\t', ' '); levelPluginList = m_space.subst(levelPluginList, ""); m_pluginLists[scanLevel.getLevel()] = levelPluginList; } } catch (ArrayIndexOutOfBoundsException ex) { ThreadCategory.getInstance(getClass()).error("Error while loading plugin lists from config file", ex); } } return m_pluginLists; } /** * Gets the cached value of the safe checks settings in the config file * * @return an array of boolean. */ public boolean[] getSafeChecks() { if (m_safeChecks == null) { m_safeChecks = new boolean[5]; // Dummy value m_safeChecks[0] = true; try { Enumeration<?> scanLevels = m_config.enumerateScanLevel(); while (scanLevels.hasMoreElements()) { ScanLevel scanLevel = (ScanLevel) scanLevels.nextElement(); m_safeChecks[scanLevel.getLevel()] = scanLevel.getSafeChecks(); } } catch (ArrayIndexOutOfBoundsException ex) { ThreadCategory.getInstance(getClass()).error("Error while loading safe checks settings from config file", ex); } } return m_safeChecks; } /** * <p>getServerPort</p> * * @return a int. */ public int getServerPort() { return m_config.getServerPort(); } /** * <p>getServerUsername</p> * * @return a {@link java.lang.String} object. */ public String getServerUsername() { return m_config.getServerUsername(); } /** * <p>getServerPassword</p> * * @return a {@link java.lang.String} object. */ public String getServerPassword() { return m_config.getServerPassword(); } /** * <p>getStatus</p> * * @return a boolean. */ public boolean getStatus() { return m_config.getStatus(); } /** * <p>getManagedInterfacesStatus</p> * * @return a boolean. */ public boolean getManagedInterfacesStatus() { return m_config.getManagedInterfaces().getStatus(); } /** * <p>getManagedInterfacesScanLevel</p> * * @return a int. */ public int getManagedInterfacesScanLevel() { return m_config.getManagedInterfaces().getScanLevel(); } /** * <p>getMaxSuspectThreadPoolSize</p> * * @return a int. */ public int getMaxSuspectThreadPoolSize() { return m_config.getMaxSuspectThreadPoolSize(); } /** * <p>getMaxRescanThreadPoolSize</p> * * @return a int. */ public int getMaxRescanThreadPoolSize() { return m_config.getMaxRescanThreadPoolSize(); } }