/******************************************************************************* * 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.poller.monitors; import java.io.IOException; import java.io.InterruptedIOException; import java.net.ConnectException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.NoRouteToHostException; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.log4j.Level; import org.opennms.core.utils.InetAddressUtils; import org.opennms.core.utils.LogUtils; import org.opennms.core.utils.ParameterMap; import org.opennms.core.utils.TimeoutTracker; import org.opennms.netmgt.model.PollStatus; import org.opennms.netmgt.poller.Distributable; import org.opennms.netmgt.poller.MonitoredService; import org.opennms.netmgt.poller.NetworkInterface; import org.opennms.netmgt.poller.NetworkInterfaceNotSupportedException; import org.xbill.DNS.DClass; import org.xbill.DNS.Message; import org.xbill.DNS.Name; import org.xbill.DNS.Record; import org.xbill.DNS.SimpleResolver; import org.xbill.DNS.Type; /** * <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> */ @Distributable final public class DnsMonitor extends AbstractServiceMonitor { /** * 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 }; /** * {@inheritDoc} * * <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> */ public PollStatus poll(MonitoredService svc, Map<String, Object> parameters) { NetworkInterface<InetAddress> iface = svc.getNetInterface(); // // Get interface address from NetworkInterface // if (iface.getType() != NetworkInterface.TYPE_INET) throw new NetworkInterfaceNotSupportedException("Unsupported interface type, only TYPE_INET 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 lookup = InetAddressUtils.getLocalHostAddressAsString(); if (lookup == null) { throw new UnsupportedOperationException("Unable to look up local host address."); } } // What do we consider fatal? // final List<Integer> fatalCodes = new ArrayList<Integer>(); for (final int code : ParameterMap.getKeyedIntegerArray(parameters, "fatal-response-codes", DEFAULT_FATAL_RESP_CODES)) { fatalCodes.add(code); } // get the address and DNS address request // final InetAddress addr = iface.getAddress(); PollStatus serviceStatus = null; serviceStatus = pollDNS(timeoutTracker, port, addr, lookup, fatalCodes); if (serviceStatus == null) { serviceStatus = logDown(Level.DEBUG, "Never received valid DNS response for address: " + addr); } // // // return the status of the service // return serviceStatus; } private PollStatus pollDNS(final TimeoutTracker timeoutTracker, final int port, final InetAddress address, final String lookup, final List<Integer> fatalCodes) { final String addr = InetAddressUtils.str(address); for (timeoutTracker.reset(); timeoutTracker.shouldRetry(); timeoutTracker.nextAttempt()) { try { final Name name = Name.fromString(lookup, Name.root); final SimpleResolver resolver = new SimpleResolver(); resolver.setAddress(new InetSocketAddress(addr, port)); resolver.setLocalAddress((InetSocketAddress)null); double timeout = timeoutTracker.getSoTimeout()/1000; resolver.setTimeout((timeout < 1 ? 1 : (int) timeout)); final Record question = Record.newRecord(name, Type.A, DClass.IN); final Message query = Message.newQuery(question); timeoutTracker.startAttempt(); final Message response = resolver.send(query); double responseTime = timeoutTracker.elapsedTimeInMillis(); final Integer rcode = response.getHeader().getRcode(); LogUtils.debugf(this, "received response code: %s", rcode); if (fatalCodes.contains(rcode)) { return logDown(Level.DEBUG, "Received an invalid DNS response for address: " + addr); } else { return logUp(Level.DEBUG, responseTime, "valid DNS request received, responseTime= " + responseTime + "ms"); } } catch (final InterruptedIOException e) { // No response received, retry without marking the poll failed. If we get this condition over and over until // the retries are exhausted, it will leave serviceStatus null and we'll get the log message at the bottom } catch (final NoRouteToHostException e) { return logDown(Level.WARN, "No route to host exception for address: " + addr, e); } catch (final ConnectException e) { return logDown(Level.WARN, "Connection exception for address: " + addr, e); } catch (final IOException e) { return logDown(Level.WARN, "IOException while polling address: " + addr + " " + e.getMessage(), e); } } return logDown(Level.DEBUG, "Never received valid DNS response for address: " + addr); } }