// // This file is part of the OpenNMS(R) Application. // // OpenNMS(R) is Copyright (C) 2002-2010 The OpenNMS Group, Inc. All rights reserved. // OpenNMS(R) is a derivative work, containing both original code, included code and modified // code that was published under the GNU General Public License. Copyrights for modified // and included code are below. // // OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc. // // Modifications: // // 2010 Feb 16: Make fatal error codes configurable (bug 3564) - jeffg@opennms.org // 2003 Jul 18: Enabled retries for monitors. // 2003 Jun 11: Added a "catch" for RRD update errors. Bug #748. // 2003 Jan 31: Added the ability to imbed RRA information in poller packages. // 2003 Jan 29: Added response times to certain monitors. // 2002 Nov 12: Display DNS response time data in webUI. // // Original code base Copyright (C) 1999-2001 Oculan Corp. All rights reserved. // // This program 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 2 of the License, or // (at your option) any later version. // // This program 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 this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // // For more information contact: // OpenNMS Licensing <license@opennms.org> // http://www.opennms.org/ // http://www.opennms.com/ // // Tab Size = 8 // package org.infosec.ismp.poller.monitor; import java.io.IOException; import java.io.InterruptedIOException; import java.lang.reflect.UndeclaredThrowableException; import java.net.ConnectException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.NoRouteToHostException; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.log4j.Level; import org.infosec.ismp.model.poller.MonitoredService; import org.infosec.ismp.model.poller.NetworkInterface; import org.infosec.ismp.model.poller.NetworkInterfaceNotSupportedException; import org.infosec.ismp.model.poller.PollStatus; import org.infosec.ismp.model.poller.monitors.IPv4Monitor; import org.infosec.ismp.protocols.dns.DNSAddressRequest; import org.infosec.ismp.util.ParameterMap; import org.infosec.ismp.util.TimeoutTracker; /** * <P> * This class is designed to be used by the service poller framework to test the * availability of the DNS service on remote interfaces. The class implements * the ServiceMonitor interface that allows it to be used along with other * plug-ins by the service poller framework. * </P> * * @author <A HREF="mailto:tarus@opennms.org">Tarus Balog </A> * @author <A HREF="http://www.opennms.org/">OpenNMS </A> * */ final public class DnsMonitor extends IPv4Monitor { /** * Default DNS port. */ private static final int DEFAULT_PORT = 53; /** * Default retries. */ private static final int DEFAULT_RETRY = 0; /** * Default timeout. Specifies how long (in milliseconds) to block waiting * for data from the monitored interface. */ private static final int DEFAULT_TIMEOUT = 5000; /** * Default list of fatal response codes. Original behavior was hard-coded * so that only a ServFail(2) was fatal, so make that the configurable * default even though it makes little sense. */ private static final int[] DEFAULT_FATAL_RESP_CODES = { 2 }; /** * <P> * Poll the specified address for DNS service availability. * </P> * * <P> * During the poll an DNS address request query packet is generated for * hostname 'localhost'. The query is sent via UDP socket to the interface * at the specified port (by default UDP port 53). If a response is * received, it is parsed and validated. If the DNS lookup was successful * the service status is set to SERVICE_AVAILABLE and the method returns. * </P> * @param parameters * The package parameters (timeout, retry, etc...) to be used for * this poll. * @param iface * The network interface to test the service on. * @return The availibility of the interface and if a transition event * should be supressed. * */ public PollStatus poll(MonitoredService svc, Map<String, Object> parameters) { NetworkInterface iface = svc.getNetInterface(); // // Get interface address from NetworkInterface // if (iface.getType() != NetworkInterface.TYPE_IPV4) throw new NetworkInterfaceNotSupportedException("Unsupported interface type, only TYPE_IPV4 currently supported"); // get the parameters // TimeoutTracker timeoutTracker = new TimeoutTracker(parameters, DEFAULT_RETRY, DEFAULT_TIMEOUT); int port = ParameterMap.getKeyedInteger(parameters, "port", DEFAULT_PORT); // Host to lookup? // String lookup = ParameterMap.getKeyedString(parameters, "lookup", null); if (lookup == null || lookup.length() == 0) { // Get hostname of local machine for future DNS lookups // try { lookup = InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException ukE) { // Recast the exception as a Service Monitor Exception // ukE.fillInStackTrace(); throw new UndeclaredThrowableException(ukE); } } // get the address and DNS address request // InetAddress ipv4Addr = (InetAddress) iface.getAddress(); DNSAddressRequest request = new DNSAddressRequest(lookup); // List of fatal response codes? // int[] fatalCodes = ParameterMap.getKeyedIntegerArray(parameters, "fatal-response-codes", DEFAULT_FATAL_RESP_CODES); if (fatalCodes != DEFAULT_FATAL_RESP_CODES) { List<Integer> codeList = new ArrayList<Integer>(); for (int code : fatalCodes) { codeList.add(code); } request.setFatalResponseCodes(codeList); } PollStatus serviceStatus = PollStatus.unavailable(); DatagramSocket socket = null; try { socket = new DatagramSocket(); socket.setSoTimeout(timeoutTracker.getSoTimeout()); // will force the InterruptedIOException for (timeoutTracker.reset(); timeoutTracker.shouldRetry() && !serviceStatus.isAvailable(); timeoutTracker.nextAttempt()) { try { // Send DNS request // byte[] data = request.buildRequest(); DatagramPacket outgoing = new DatagramPacket(data, data.length, ipv4Addr, port); byte[] buffer = new byte[512]; DatagramPacket incoming = new DatagramPacket(buffer, buffer.length); timeoutTracker.startAttempt(); socket.send(outgoing); // Get DNS Response socket.receive(incoming); double responseTime = timeoutTracker.elapsedTimeInMillis(); // Validate DNS Response // IOException thrown if packet does not decode as expected. request.verifyResponse(incoming.getData(), incoming.getLength()); serviceStatus = logUp(Level.DEBUG, responseTime, "valid DNS request received, responseTime= " + responseTime + "ms"); } catch (InterruptedIOException ex) { // Ignore, no response received. } catch (NoRouteToHostException e) { serviceStatus = logDown(Level.DEBUG, "No route to host exception for address: " + ipv4Addr, e); } catch (ConnectException e) { serviceStatus = logDown(Level.DEBUG, "Connection exception for address: " + ipv4Addr, e); } catch (IOException ex) { serviceStatus = logDown(Level.DEBUG, "IOException while polling address: " + ipv4Addr, ex); } } } catch (IOException ex) { serviceStatus = logDown(Level.DEBUG, "Failed to create Datagram Socket for : " + ipv4Addr, ex); } finally { if (socket != null) socket.close(); } // // // return the status of the service // return serviceStatus; } }