All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.vngx.jsch.VersionExchange Maven / Gradle / Ivy

Go to download

**vngx-jsch** (beta) is an updated version of the popular JSch SSH library written in pure Java. It has been updated to Java 6 with all the latest language features and improved code clarity.

There is a newer version: 0.10
Show newest version
/*
 * Copyright (c) 2010-2011 Michael Laudati, N1 Concepts LLC.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. The names of the authors may not be used to endorse or promote products
 * derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL N1
 * CONCEPTS LLC OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.vngx.jsch;

import static org.vngx.jsch.constants.TransportLayerProtocol.*;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.vngx.jsch.constants.SSHConstants;
import org.vngx.jsch.exception.JSchException;

/**
 * 

Implementation of SSH2 Protocol Version Exchange as described in RFC 4253 * section 4.2.

* *

When the connection has been established, both sides MUST send an * identification string. This identification string MUST be *

 *	SSH-protoversion-softwareversion SP comments CR LF
 * 
* * Since the protocol being defined in this set of documents is version 2.0, * the 'protoversion' MUST be "2.0". The 'comments' string is OPTIONAL. If the * 'comments' string is included, a 'space' character (denoted above as SP, * ASCII 32) MUST separate the 'softwareversion' and 'comments' strings. The * identification MUST be terminated by a single Carriage Return (CR) and a * single Line Feed (LF) character (ASCII 13 and 10, respectively). * Implementers who wish to maintain compatibility with older, undocumented * versions of this protocol may want to process the identification string * without expecting the presence of the carriage return character for reasons * described in Section 5 of this document. The null character MUST NOT be * sent. The maximum length of the string is 255 characters, including the * Carriage Return and Line Feed.

* *

The part of the identification string preceding the Carriage Return and * Line Feed is used in the Diffie-Hellman key exchange (see Section 8).

* *

The server MAY send other lines of data before sending the version string. * Each line SHOULD be terminated by a Carriage Return and Line Feed. Such * lines MUST NOT begin with "SSH-", and SHOULD be encoded in ISO-10646 UTF-8 * [RFC3629] (language is not specified). Clients MUST be able to process such * lines. Such lines MAY be silently ignored, or MAY be displayed to the client * user. If they are displayed, control character filtering, as discussed in * [SSH-ARCH], SHOULD be used. The primary use of this feature is to allow TCP- * wrappers to display an error message before disconnecting.

* *

Both the 'protoversion' and 'softwareversion' strings MUST consist of * printable US-ASCII characters, with the exception of whitespace characters * and the minus sign (-). The 'softwareversion' string is primarily used to * trigger compatibility extensions and to indicate the capabilities of an * implementation. The 'comments' string SHOULD contain additional information * that might be useful in solving user problems. As such, an example of a * valid identification string is *

 *	SSH-2.0-billsSSH_3.6.3q3
 * 
* * This identification string does not contain the optional 'comments' string * and is thus terminated by a CR and LF immediately after the 'softwareversion' * string.

* *

Key exchange will begin immediately after sending this identifier. All * packets following the identification string SHALL use the binary packet * protocol, which is described in Section 6.

* *

RFC 4253 - The * Secure Shell (SSH) Transport Layer Protocol: Protocol Version Exchange *

* * @author Michael Laudati */ public class VersionExchange { /** * The maximum length of the version string is {@value} characters, * including the Carriage Return '\r' and Line Feed '\n'. */ public final static int MAX_VERSION_LENGTH = 255; /** * The maximum number of lines to read in from the server during version * exchange to prevent a denial of service attack against the client. */ public final static int MAX_READ_LINES = 50; /** * Client SSH version sent to server and used during key exchange. The * version string MUST be in the following format: *
	 *	SSH-protoversion-softwareversion SP comments
	 * 
* Where the 'protoversion' must be 2.0 and the comments are optional. */ private String _clientVersion; /** * Server SSH version received from server and used during key exchange. * The version string MUST be in the following format: *
	 *	SSH-protoversion-softwareversion SP comments
	 * 
*/ private String _serverVersion; /** * List which stores any optionally sent lines received before the server's * SSH version response. The primary use of this feature is to allow TCP- * wrappers to display an error message before disconnecting. */ private List _debugging; /** * Creates a new instance of {@code VersionExchange} with the specified * {@code clientVersion} string to send to server during the exchange. * * @param clientVersion to send to server */ public VersionExchange(final String clientVersion) { if( clientVersion == null || clientVersion.isEmpty() ) { throw new IllegalArgumentException("Client version cannot be null/empty"); } else if( clientVersion.length() > MAX_VERSION_LENGTH ) { throw new IllegalArgumentException("Invalid client version, cannot exceed "+MAX_VERSION_LENGTH+" characters"); } else if( !clientVersion.startsWith(SSHConstants.SSH_VERSION_2_0) ) { throw new IllegalArgumentException("Client version must start with '"+SSHConstants.SSH_VERSION_2_0+"'"); } _clientVersion = clientVersion.trim(); // Ensure no whitespace/CR/LF at end } /** * Exchanges the client and server SSH versions during the initial * connection. First the client version is sent to the SSH server, then the * server version is read from the response. * * @param in stream from server * @param out stream to server * @throws JSchException if invalid/unsupported server version * @throws IOException if any errors occur during IO */ public void exchangeVersions(InputStream in, OutputStream out) throws JSchException, IOException { // Send client version string to server to start version exchange out.write(Util.str2byte(_clientVersion + "\r\n")); // append CR LF out.flush(); Buffer buffer = new Buffer(MAX_VERSION_LENGTH); String currentLine; int counter = 0; // Read in server's response until we find server version, storing any // non-relevant lines; limit number of lines read to prevent a denial // of service attack on client while( counter++ < MAX_READ_LINES ) { // Arbitrary limit amount // Read next line from server into buffer currentLine = readLine(in, buffer); // Capture any possible debugging lines sent before server version if( !currentLine.startsWith("SSH-") ) { if( _debugging == null ) { _debugging = new LinkedList(); } _debugging.add(Util.sanitize(currentLine)); continue; // continue to read next line } else { // Check server SSH version String and verify it's either 2.0 or // 1.99 (supported versions) or thorw exception if( currentLine.startsWith(SSHConstants.SSH_VERSION_2_0) || currentLine.startsWith(SSHConstants.SSH_VERSION_1_99) ) { _serverVersion = currentLine; // Set the server version return; // and return from exchange } // Unsupported version returned from server (not 2.0 or 1.99) throw new JSchException("Unsupported server version: "+Util.sanitize(currentLine), SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED); } } // If no supported version has been found after max read attempts, throw // an exception as this might have been a denial of service on client throw new JSchException("Invalid server version response: exceeds maximum read attempts", SSH_DISCONNECT_PROTOCOL_ERROR); } /** * Attempts to read the next line from the server input stream until a new * line ('\n') is found or the maximum allowed line length is reached. An * exception will be thrown if the connection is closed or if the incoming * line is longer than the buffer's length. * * @param in stream to read from server * @param buffer to store string * @return buffer * @throws IOException if connection closed or line exceeds buffer length */ private String readLine(InputStream in, Buffer buffer) throws IOException { int c; buffer.reset(); // Read in response until new line found or max buffer length while( buffer.getIndex() < buffer.size() ) { if( (c = in.read()) < 0 ) { // Read response byte at a time throw new IOException("Connection closed by remote host"); } buffer.putByte((byte) c); if( c == '\n' ) { // Return buffer once we've reached the end return Util.byte2str(buffer.buffer, 0, buffer.index).trim(); } } // Throw an exception if response exceeds max allowed length throw new IOException("Invalid server version response: exceeds maximum length"); } /** * Returns the client version sent during the protocol version exchange. * * @return client version */ public String getClientVersion() { return _clientVersion; } /** * Returns the server version received during the protocol version exchange. * * @return server version */ public String getServerVersion() { return _serverVersion; } /** * Returns the client SSH protocol version. * * @return client SSH protocol version */ public String getClientProtocol() { return _clientVersion.substring(4, _clientVersion.indexOf('-', 4)); } /** * Returns the server SSH protocol version. If the server exchange has not * yet taken place, then null is returned. * * @return server SSH protocol version */ public String getServerProtocol() { return _serverVersion != null ? _serverVersion.substring(4, _serverVersion.indexOf('-', 4)) : null; } /** * Returns the client's software version. * * @return client's software version */ public String getClientSoftware() { return _clientVersion.substring(_clientVersion.indexOf('-', 5) + 1, _clientVersion.indexOf(' ') > 0 ? _clientVersion.indexOf(' ') : _clientVersion.length()); } /** * Returns the server's software version. If the server exchange has not * yet taken place, then null is returned. * * @return server's software version */ public String getServerSoftware() { return _serverVersion == null ? null : _serverVersion.substring(_serverVersion.indexOf('-', 5) + 1, _serverVersion.indexOf(' ') > 0 ? _serverVersion.indexOf(' ') : _serverVersion.length()); } /** * Returns the client's comment String if it exists. * * @return client comment String or null */ public String getClientComment() { return _clientVersion.indexOf(' ') > 0 ? _clientVersion.substring(_clientVersion.indexOf(' ')+1) : null; } /** * Returns the server's comment String if it exists. * * @return server comment String or null */ public String getServerComment() { return _serverVersion == null ? null : _serverVersion.indexOf(' ') > 0 ? _serverVersion.substring(_serverVersion.indexOf(' ')+1) : null; } /** * Returns the debugging lines received prior to the version exchange if * any. * * @return debugging lines if they exists */ public List getDebugging() { return _debugging != null ? Collections.unmodifiableList(_debugging) : null; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy