/******************************************************************************* * This file is part of OpenNMS(R). * * Copyright (C) 2007-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.protocols.ssh; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.ConnectException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.util.regex.Pattern; import org.opennms.core.utils.ThreadCategory; import org.opennms.core.utils.TimeoutTracker; import org.opennms.netmgt.model.PollStatus; import org.opennms.netmgt.protocols.InsufficientParametersException; /** * <p>Ssh class.</p> * * @author <a href="mailto:ranger@opennms.org">Benjamin Reed</a> * @version $Id: $ */ public class Ssh extends org.opennms.netmgt.protocols.AbstractPoll { private static final Pattern SSH_IOEXCEPTION_PATTERN = Pattern.compile("^.*java.io.IOException.*$"); private static final Pattern SSH_AUTHENTICATION_PATTERN = Pattern.compile("^.*Authentication:.*$"); private static final Pattern SSH_NOROUTETOHOST_PATTERN = Pattern.compile("^.*java.net.NoRouteToHostException.*$"); private static final Pattern SSH_SOCKETERROR_PATTERN = Pattern.compile("^.*(timeout: socket is not established|java.io.InterruptedIOException|java.net.SocketTimeoutException).*$"); private static final Pattern SSH_CONNECTIONERROR_PATTERN = Pattern.compile("^.*(connection is closed by foreign host|java.net.ConnectException).*$"); private static final Pattern SSH_NUMBERFORMAT_PATTERN = Pattern.compile("^.*NumberFormatException.*$"); // SSH port is 22 /** Constant <code>DEFAULT_PORT=22</code> */ public static final int DEFAULT_PORT = 22; // Default to 1.99 (v1 + v2 support) /** Constant <code>DEFAULT_CLIENT_BANNER="SSH-1.99-OpenNMS_1.5"</code> */ public static final String DEFAULT_CLIENT_BANNER = "SSH-1.99-OpenNMS_1.5"; protected int m_port = DEFAULT_PORT; protected String m_username; protected String m_password; protected String m_banner = DEFAULT_CLIENT_BANNER; protected String m_serverBanner = ""; protected InetAddress m_address; protected Throwable m_error; private Socket m_socket = null; private BufferedReader m_reader = null; private OutputStream m_writer = null; /** * <p>Constructor for Ssh.</p> */ public Ssh() { } /** * <p>Constructor for Ssh.</p> * * @param address a {@link java.net.InetAddress} object. */ public Ssh(final InetAddress address) { setAddress(address); } /** * <p>Constructor for Ssh.</p> * * @param address a {@link java.net.InetAddress} object. * @param port a int. */ public Ssh(final InetAddress address, final int port) { setAddress(address); setPort(port); } /** * <p>Constructor for Ssh.</p> * * @param address a {@link java.net.InetAddress} object. * @param port a int. * @param timeout a int. */ public Ssh(final InetAddress address, final int port, final int timeout) { setAddress(address); setPort(port); setTimeout(timeout); } /** * Set the address to connect to. * * @param address the address */ public void setAddress(final InetAddress address) { m_address = address; } /** * Get the address to connect to. * * @return the address */ public InetAddress getAddress() { return m_address; } /** * Set the port to connect to. * * @param port the port */ public void setPort(final int port) { m_port = port; } /** * Get the port to connect to. * * @return the port */ public int getPort() { if (m_port == 0) { return 22; } return m_port; } /** * Set the username to connect as. * * @param username the username */ public void setUsername(final String username) { m_username = username; } /** * Get the username to connect as. * * @return the username */ public String getUsername() { return m_username; } /** * Set the password to connect with. * * @param password the password */ public void setPassword(final String password) { m_password = password; } /** * Get the password to connect with. * * @return the password */ public String getPassword() { return m_password; } /** * Set the banner string to use when connecting * * @param banner the banner */ public void setClientBanner(final String banner) { m_banner = banner; } /** * Get the banner string used when connecting * * @return the banner */ public String getClientBanner() { return m_banner; } /** * Get the SSH server version banner. * * @return the version string */ public String getServerBanner() { return m_serverBanner; } /** * <p>setError</p> * * @param t a {@link java.lang.Throwable} object. */ protected void setError(final Throwable t) { m_error = t; } /** * <p>getError</p> * * @return a {@link java.lang.Throwable} object. */ protected Throwable getError() { return m_error; } /** * Attempt to connect, based on the parameters which have been set in * the object. * * @return true if it is able to connect * @throws org.opennms.netmgt.protocols.InsufficientParametersException if any. */ protected boolean tryConnect() throws InsufficientParametersException { if (getAddress() == null) { throw new InsufficientParametersException("you must specify an address"); } try { m_socket = new Socket(); m_socket.setTcpNoDelay(true); m_socket.connect(new InetSocketAddress(getAddress(), getPort()), getTimeout()); m_socket.setSoTimeout(getTimeout()); m_reader = new BufferedReader(new InputStreamReader(m_socket.getInputStream())); m_writer = m_socket.getOutputStream(); // read the banner m_serverBanner = m_reader.readLine(); // write our own m_writer.write((getClientBanner() + "\r\n").getBytes()); // then, disconnect disconnect(); return true; } catch (final NumberFormatException e) { log().debug("unable to parse server version", e); setError(e); disconnect(); } catch (final ConnectException e) { log().debug("connection failed: " + e.getMessage()); setError(e); disconnect(); } catch (final Throwable e) { log().debug("connection failed", e); setError(e); disconnect(); } return false; } /** * <p>disconnect</p> */ protected void disconnect() { if (m_writer != null) { try { m_writer.close(); } catch (final IOException e) { log().warn("error disconnecting output stream", e); } } if (m_reader != null) { try { m_reader.close(); } catch (final IOException e) { log().warn("error disconnecting input stream", e); } } if (m_socket != null) { try { m_socket.close(); } catch (final IOException e) { log().warn("error disconnecting socket", e); } } } /** {@inheritDoc} */ public PollStatus poll(final TimeoutTracker tracker) throws InsufficientParametersException { tracker.startAttempt(); final boolean isAvailable = tryConnect(); final double responseTime = tracker.elapsedTimeInMillis(); PollStatus ps = PollStatus.unavailable(); final String errorMessage; if (getError() != null) { errorMessage = getError().getMessage(); ps.setReason(errorMessage); } else { errorMessage = ""; } if (isAvailable) { ps = PollStatus.available(responseTime); } else if (SSH_AUTHENTICATION_PATTERN.matcher(errorMessage).matches()) { ps = PollStatus.unavailable("authentication failed"); } else if (SSH_NOROUTETOHOST_PATTERN.matcher(errorMessage).matches()) { ps = PollStatus.unavailable("no route to host"); } else if (SSH_SOCKETERROR_PATTERN.matcher(errorMessage).matches()) { ps = PollStatus.unavailable("connection timed out"); } else if (SSH_CONNECTIONERROR_PATTERN.matcher(errorMessage).matches()) { ps = PollStatus.unavailable("connection exception"); } else if (SSH_NUMBERFORMAT_PATTERN.matcher(errorMessage).matches()) { ps = PollStatus.unavailable("an error occurred parsing the server version number"); } else if (SSH_IOEXCEPTION_PATTERN.matcher(errorMessage).matches()) { ps = PollStatus.unavailable("I/O exception"); } return ps; } private ThreadCategory log() { return ThreadCategory.getInstance(getClass()); } }